diff --git a/.codespell_ignore b/.codespell_ignore index e9bbc037a60..9681ad32218 100644 --- a/.codespell_ignore +++ b/.codespell_ignore @@ -1,3 +1,4 @@ +aci afile alledges allready @@ -45,11 +46,13 @@ lsat manuel methd mis +mone mor multidimension nam nd nin +ninj nome numer oint @@ -74,3 +77,4 @@ thets toom tring ue +vew diff --git a/.github/workflows/build-deb.yml b/.github/workflows/build-deb.yml new file mode 100644 index 00000000000..46a81c469e1 --- /dev/null +++ b/.github/workflows/build-deb.yml @@ -0,0 +1,101 @@ +# This workflow builds Debian packages for the last 3 Debian releases +# (currently trixie, bookworm, and bullseye) + +# After each Macaulay2 release, a new package is prepared for Debian unstable +# (and a few days later, it migrates to Debian testing). The packaging is +# under version control in Debian's git forge, Salsa, at +# https://salsa.debian.org/math-team/macaulay2, in the debian/latest branch + +# Backport the package to each of the supported releases and push to the +# appropriate release branch, e.g., debian/trixie for trixie. In many cases, +# this is as simple as just merging the debian/latest branch. Push each branch +# to Salsa. + +# Once each branch is ready and pushed to Salsa, run this action using "workflow +# dispatch" to generate each Debian package. + +# Download the artifacts and unzip them. Push files using dput to +# Repositories/Debian/mini-dinstall/incoming on the webserver, and then +# run "mini-install -b -v" on the server. Finally sign the Release files +# in each directory (gpg --clearsign -o InRelease Release). + +name: Build Debian packages +on: workflow_dispatch + +jobs: + build-deb: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + release: + - bullseye + - bookworm + - trixie + os: + - ubuntu-latest + - ubuntu-24.04-arm + container: + image: debian:${{ matrix.release }} + steps: + - name: Install dependencies + run: | + apt-get update + apt-get install -y wget ca-certificates + if test ${{ matrix.release }} = "bullseye"; \ + then wget -O /usr/share/keyrings/macaulay2-archive-key.asc https://macaulay2.com/Repositories/Debian/macaulay2-archive-key.asc; \ + fi + wget -O /etc/apt/sources.list.d/macaulay2.sources https://macaulay2.com/Repositories/Debian/${{ matrix.release }}/macaulay2.sources + apt-get update + apt-get install -y 4ti2 bison cohomcalg coinor-csdp \ + debhelper dh-linktree emacs-nox fflas-ffpack flex \ + gfan gfortran install-info libboost-dev libboost-math-dev \ + libboost-regex-dev libboost-stacktrace-dev libcdd-dev \ + libeigen3-dev libffi-dev libflint-dev libfrobby-dev libfplll-dev \ + libgc-dev libgdbm-dev libglpk-dev libgtest-dev libjansson-dev \ + liblapack-dev liblzma-dev libmathic-dev libmathicgb-dev \ + libmemtailor-dev libmpfi-dev libmpfr-dev libmps-dev libnauty-dev \ + libnormaliz-dev libntl-dev libreadline-dev \ + libsingular4-dev libtbb-dev libxml2-dev lrslib \ + lsb-release msolve nauty normaliz pkgconf python3-dev \ + r-base singular-data time topcom git devscripts dh-elpa + + # arch-independent dependencies (amd64 build only) + if test ${{ matrix.os }} = "ubuntu-latest"; then + apt-get install -y faketime gdbmtool jdupes + + # don't need javascript on bullseye (just use the vendored copies) + if test ${{ matrix.release }} != "bullseye"; then + apt-get install -y node-fortawesome-fontawesome-free \ + fonts-katex libjs-bootsidemenu libjs-bootstrap5 libjs-d3 \ + libjs-jquery libjs-katex libjs-nouislider libjs-three \ + node-clipboard node-css-loader node-import-local \ + node-interpret node-prismjs node-prismjs-bibtex \ + node-rechoir node-style-loader npm pkg-js-tools webpack + fi + fi + - name: Clone repository + run: | + git clone https://salsa.debian.org/math-team/macaulay2 + cd macaulay2 + git checkout debian/${{ matrix.release }} + - name: Generate tarball + run: uscan --verbose --rename --force-download + - name: Build package + run: | + cd macaulay2 + git submodule update --init M2/Macaulay2/editors/emacs + if test ${{ matrix.os }} = "ubuntu-latest"; \ + then dpkg-buildpackage; \ + else dpkg-buildpackage -B; \ + fi + - name: Upload build logs + if: always() + uses: actions/upload-artifact@v7 + with: + name: ${{ matrix.os }}-debian-${{ matrix.release }} + path: | + *.build + *.buildinfo + *.changes + *.deb diff --git a/.github/workflows/build-rpm.yml b/.github/workflows/build-rpm.yml index 7eb6541eaf9..6748247ef43 100644 --- a/.github/workflows/build-rpm.yml +++ b/.github/workflows/build-rpm.yml @@ -42,7 +42,7 @@ jobs: RELEASE=${{ matrix.release }} - name: Upload artifacts if: always() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.os }}-${{ matrix.distribution }}-${{ matrix.release }} path: | diff --git a/.github/workflows/make-dist.yml b/.github/workflows/make-dist.yml index 08edc5476c2..f5f16fc9566 100644 --- a/.github/workflows/make-dist.yml +++ b/.github/workflows/make-dist.yml @@ -23,7 +23,7 @@ jobs: make doc-dist - name: Upload artifacts if: always() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: dist-tarballs path: | diff --git a/.github/workflows/package-review.yml b/.github/workflows/package-review.yml index 7602c5049af..088210e2208 100644 --- a/.github/workflows/package-review.yml +++ b/.github/workflows/package-review.yml @@ -25,7 +25,7 @@ jobs: package: ${{ github.event.inputs.package }} - name: Upload errors - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 if: failure() with: name: ${{ github.event.inputs.package }}-errors diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml index 52d9584b75f..6d43bb58b8f 100644 --- a/.github/workflows/test_build.yml +++ b/.github/workflows/test_build.yml @@ -39,17 +39,17 @@ jobs: - cmake os: - ubuntu-24.04 - - macos-14 + - macos-15 compiler: - default include: # This build tests Clang rather than AppleClang (keep) - build-system: cmake - os: macos-14 + os: macos-15 compiler: brew-clang exclude: - build-system: cmake - os: macos-14 + os: macos-15 compiler: default steps: - uses: actions/checkout@v6 @@ -64,8 +64,7 @@ jobs: brew config brew update brew tap macaulay2/tap - brew install --overwrite python - brew install automake bison boost libtool tbb ccache ctags llvm make ninja yasm libffi msolve googletest fplll eigen + brew install automake bison boost libtool tbb ccache ctags llvm make yasm libffi msolve googletest fplll eigen jansson r brew install texinfo || true # sometimes post-install step fails brew install --only-dependencies macaulay2/tap/M2 brew link factory --force @@ -86,7 +85,7 @@ jobs: libgivaro-dev libboost-regex-dev fflas-ffpack libflint-dev libmps-dev libfrobby-dev \ libsingular-dev singular-data libcdd-dev cohomcalg topcom 4ti2 libnormaliz-dev normaliz coinor-csdp \ libnauty-dev nauty lrslib polymake pipx phcpack w3c-markup-validator libtbb-dev qepcad libomp-16-dev \ - msolve libfplll-dev + msolve libfplll-dev libjansson-dev r-base # ---------------------- # Steps common to all build variants @@ -214,7 +213,7 @@ jobs: - name: Run Tests using Autotools if: matrix.build-system == 'autotools' && runner.os == 'Linux' && matrix.compiler == 'default' run: | - make check -o check-in-libraries + make -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu) check -o check-in-libraries make -C Macaulay2/html-check-links check - name: Validate HTML documentation @@ -244,7 +243,7 @@ jobs: - name: Upload build logs if: always() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.build-system }}-${{ matrix.os }}-${{ matrix.compiler }}-logs path: | @@ -257,6 +256,7 @@ jobs: M2/BUILD/build/CMakeCache.txt M2/BUILD/build/CMakeFiles/CMakeError.log M2/BUILD/build/CMakeFiles/CMakeOutput.log + M2/BUILD/build/CMakeFiles/CMakeConfigureLog.yaml M2/BUILD/build/libraries/*/build/config.log # package example errors M2/BUILD/build/usr-dist/common/share/doc/Macaulay2/*/example-output/*.errors @@ -268,7 +268,7 @@ jobs: - name: Upload Macaulay2 package for Ubuntu (x86_64) if: matrix.build-system == 'cmake' && runner.os == 'Linux' && success() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: Macaulay2-${{ env.GIT_COMMIT }}-ubuntu-x86_64 path: | diff --git a/M2/BUILD/anton/Makefile b/M2/BUILD/anton/Makefile index cff81e1b329..465aeffe26e 100644 --- a/M2/BUILD/anton/Makefile +++ b/M2/BUILD/anton/Makefile @@ -3,17 +3,19 @@ always: BRANCH := $(shell git branch --contains | grep '^\* ' | sed -e s=^..== -e s=/=.=g ) -cmake: +cmake: # ~ mike's cmake-appleclang echo "git branch is " $(BRANCH) mkdir -p builds.tmp/$@ cd builds.tmp/$@; cmake \ -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_PREFIX_PATH="`brew --prefix libomp`" \ + -DCMAKE_PREFIX_PATH="`brew --prefix`;`brew --prefix factory`" \ + -DBUILD_NATIVE=off \ + -DCMAKE_INSTALL_PREFIX=`pwd`/installed \ -DBUILD_TESTING=on \ -DBUILD_DOCS=on \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=true \ ../../../.. -# -DLINTING=on auto: echo "git branch is " $(BRANCH) diff --git a/M2/BUILD/docker/fedora/Dockerfile b/M2/BUILD/docker/fedora/Dockerfile index cb1a80be491..577c9ae478d 100644 --- a/M2/BUILD/docker/fedora/Dockerfile +++ b/M2/BUILD/docker/fedora/Dockerfile @@ -14,7 +14,7 @@ RUN microdnf install -y autoconf bison ccache chrpath cmake curl diffutils \ # Libraries we require RUN microdnf install -y openblas-devel libxml2-devel readline-devel gdbm-devel \ - boost-devel libomp-devel tbb-devel python3-devel libffi-devel + boost-devel libomp-devel tbb-devel python3-devel libffi-devel jansson-devel # Libraries we can build (factory not available on ubuntu) RUN microdnf install -y eigen3-devel glpk-devel gmp-devel mpfr-devel ntl-devel \ diff --git a/M2/BUILD/docker/nightly/Dockerfile b/M2/BUILD/docker/nightly/Dockerfile index 8000824ba47..783bbc27b79 100644 --- a/M2/BUILD/docker/nightly/Dockerfile +++ b/M2/BUILD/docker/nightly/Dockerfile @@ -2,32 +2,31 @@ # Net usage: ~100MB # Disk usage: <600MB docker image -FROM ubuntu:20.04 +FROM ubuntu:24.04 RUN apt-get update && \ apt-get install -y software-properties-common apt-transport-https curl git sudo unzip gnupg && \ add-apt-repository -y ppa:macaulay2/macaulay2 && apt-get update && apt-get clean # this seems to be necessary because libstdc++6 is too old on Ubuntu 20.04 -RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test && apt-get update && \ - apt-get install -y -q --no-install-recommends libomp5-11 && apt-get clean +RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test && apt-get update && apt-get clean # Install Macaulay2 COPY Macaulay2-*.zip / RUN unzip Macaulay2-*.zip && apt-get install -y /Macaulay2-*.deb # Install optional packages -RUN apt-get install -y -q --no-install-recommends mlocate bash-completion && apt-get clean && updatedb +RUN apt-get install -y -q --no-install-recommends locate bash-completion && apt-get clean && updatedb #RUN apt-get install -y -q --no-install-recommends emacs && apt-get clean # Add non-root user for building and running Macaulay2 -RUN useradd -G sudo -g root -u 1000 -m macaulay && echo "macaulay ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers +RUN useradd -G sudo -g root -o -u 1000 -m macaulay && echo "macaulay ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers USER 1000:0 # Setting environment variables -ENV LD_LIBRARY_PATH /usr/local/lib/Macaulay2/lib -ENV PATH /usr/local/libexec/Macaulay2/bin:${PATH} +ENV LD_LIBRARY_PATH=/usr/local/lib/Macaulay2/lib +ENV PATH=/usr/local/libexec/Macaulay2/bin:${PATH} WORKDIR /home/macaulay -ENTRYPOINT M2 +ENTRYPOINT ["M2"] diff --git a/M2/BUILD/docker/rhel/Dockerfile b/M2/BUILD/docker/rhel/Dockerfile index 3ff44749620..0e42558c138 100644 --- a/M2/BUILD/docker/rhel/Dockerfile +++ b/M2/BUILD/docker/rhel/Dockerfile @@ -4,11 +4,13 @@ ARG RELEASE=latest FROM $DISTRIBUTION:$RELEASE # Install dependencies +RUN dnf -y install 'dnf-command(config-manager)' || true +RUN dnf config-manager --set-enabled crb || true # for jansson-devel RUN dnf -y install autoconf automake bison boost-devel bzip2 chrpath \ cmake diffutils flex gcc-c++ gcc-gfortran git gmp-devel info \ - libffi-devel libtool libxml2-devel make mpfr-devel ncurses-devel \ - patch python3-devel readline-devel rpm-build tbb-devel which \ - xz-devel zlib-devel + jansson-devel libffi-devel libtool libxml2-devel make mpfr-devel \ + ncurses-devel patch python3-devel readline-devel rpm-build \ + tbb-devel which xz-devel zlib-devel RUN test "$RELEASE" != "8" && dnf -y install flexiblas-devel || true # Add non-root user for building and running Macaulay2 diff --git a/M2/BUILD/docker/testbot/Dockerfile b/M2/BUILD/docker/testbot/Dockerfile index 1d0a0592b26..d5d1ee4ae83 100644 --- a/M2/BUILD/docker/testbot/Dockerfile +++ b/M2/BUILD/docker/testbot/Dockerfile @@ -1,16 +1,16 @@ # Time usage: <5min # Net usage: <200MB -# Disk usage: <800MB +# Disk usage: <850MB -FROM debian:bookworm +FROM debian:trixie LABEL org.opencontainers.image.source=https://github.com/Macaulay2/M2 # Setup the system -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates debian-keyring git && apt-get clean +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates wget git && apt-get clean # Install Macaulay2 -RUN echo "deb [signed-by=/usr/share/keyrings/debian-keyring.gpg] https://people.debian.org/~dtorrance/debian bookworm/" >> /etc/apt/sources.list && apt-get update && apt-get install -y --no-install-recommends macaulay2 4ti2 cohomcalg coinor-csdp gfan nauty normaliz topcom msolve && apt-get clean +RUN wget -O /etc/apt/sources.list.d/macaulay2.sources https://macaulay2.com/Repositories/Debian/trixie/macaulay2.sources && apt-get update && apt-get install -y --no-install-recommends macaulay2 4ti2 cohomcalg coinor-csdp gfan nauty normaliz topcom msolve && apt-get clean # Add non-root user for using Macaulay2 RUN useradd -G sudo -g root -u 1000 -m macaulay && echo "macaulay ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers diff --git a/M2/BUILD/mahrud/atomic_test/test.c b/M2/BUILD/mahrud/atomic_test/test.c deleted file mode 100644 index 0080e1d0bfc..00000000000 --- a/M2/BUILD/mahrud/atomic_test/test.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include - -atomic_int acnt; -int cnt; - -void adding() -{ - for(int i=0; i<10000; i++) - { - acnt++; - cnt++; - } - pthread_exit(NULL); -} diff --git a/M2/BUILD/mahrud/atomic_test/test.cpp b/M2/BUILD/mahrud/atomic_test/test.cpp deleted file mode 100644 index f2bb1febc06..00000000000 --- a/M2/BUILD/mahrud/atomic_test/test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include - -using namespace std; - -extern "C" void adding(); -extern "C" atomic_int acnt; -extern "C" int cnt; - -int main() -{ - vector v; - - for(size_t i {0}; i < 9; ++i) { - v.emplace_back(adding); - } - - for(int i=0; i<10000; i++) { - acnt++; - cnt++; - } - - for(auto &t : v) { t.join(); } - - printf("the value of acnt is %d\n", acnt.load()); - printf("the value of cnt is %d\n", cnt); - - return 0; -} - -/* - - gcc -c test.c -o c11.o --std=c11 - g++ -c test.cpp -o cxx14.o --std=c++14 - g++ cxx14.o c11.o -pthread - ./a.out - -*/ diff --git a/M2/BUILD/mahrud/atomic_test/test.sh b/M2/BUILD/mahrud/atomic_test/test.sh deleted file mode 100644 index 819669611b7..00000000000 --- a/M2/BUILD/mahrud/atomic_test/test.sh +++ /dev/null @@ -1,4 +0,0 @@ -gcc -c test.c -o c11.o --std=c11 -g++ -c test.cpp -o cxx14.o --std=c++14 -g++ cxx14.o c11.o -pthread -./a.out diff --git a/M2/BUILD/mahrud/pthread_test/pthread_test.c b/M2/BUILD/mahrud/pthread_test/pthread_test.c deleted file mode 100644 index 591466f3739..00000000000 --- a/M2/BUILD/mahrud/pthread_test/pthread_test.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include - -atomic_int acnt; -int cnt; - -void *adding(void *input) -{ - for(int i=0; i<10000; i++) - { - acnt++; - cnt++; - } - pthread_exit(NULL); -} - -int main() -{ - pthread_t tid[10]; - - for(int i=0; i<10; i++) - pthread_create(&tid[i],NULL,adding,NULL); - - for(int i=0; i<10; i++) - pthread_join(tid[i],NULL); - - printf("the value of acnt is %d\n", acnt); - printf("the value of cnt is %d\n", cnt); - return 0; -} diff --git a/M2/BUILD/mahrud/tls_test/test.c b/M2/BUILD/mahrud/tls_test/test.c deleted file mode 100644 index dc1e3d0d60e..00000000000 --- a/M2/BUILD/mahrud/tls_test/test.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include - -thread_local unsigned int tlcounter = 0; -unsigned int counter = 0; -pthread_mutex_t cout_mutex; - -void incr(const char name) -{ - ++counter; - ++tlcounter; - pthread_mutex_lock(&cout_mutex); - printf("Value for %c:\t%d\tvs %d\n", name, counter, tlcounter); - pthread_mutex_unlock(&cout_mutex); - pthread_exit(NULL); -} diff --git a/M2/BUILD/mahrud/tls_test/test.cpp b/M2/BUILD/mahrud/tls_test/test.cpp deleted file mode 100644 index 72effbd12fd..00000000000 --- a/M2/BUILD/mahrud/tls_test/test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace std; - -extern "C" thread_local unsigned int tlcounter; -extern "C" unsigned int counter; -extern "C" mutex cout_mutex; -extern "C" void incr(const char name); - -int main() -{ - vector v; - - for(size_t i = 0; i < 26; ++i) { - v.emplace_back(incr, 'A' + i); - } - - { - this_thread::sleep_for(700000ns); - lock_guard lock(cout_mutex); - printf("Value for main:\t%d\tvs %d\n", ++counter, ++tlcounter); - } - - for(auto &t : v) { t.join(); } - - return 0; -} - -/* - gcc -c test.c -o c11.o --std=c11 - g++ -c test.cpp -o cxx14.o --std=c++14 - g++ cxx14.o c11.o -pthread - ./a.out -*/ diff --git a/M2/BUILD/mahrud/tls_test/test.sh b/M2/BUILD/mahrud/tls_test/test.sh deleted file mode 100644 index 819669611b7..00000000000 --- a/M2/BUILD/mahrud/tls_test/test.sh +++ /dev/null @@ -1,4 +0,0 @@ -gcc -c test.c -o c11.o --std=c11 -g++ -c test.cpp -o cxx14.o --std=c++14 -g++ cxx14.o c11.o -pthread -./a.out diff --git a/M2/BUILD/mike/Makefile b/M2/BUILD/mike/Makefile index 8168d5a9f9e..8da86c7bae3 100644 --- a/M2/BUILD/mike/Makefile +++ b/M2/BUILD/mike/Makefile @@ -5,17 +5,13 @@ BRANCH := $(shell git branch --contains | grep '^\* ' | sed -e s=^..== -e s=/=.= BREWPREFIX := `brew --prefix` -####################### -## cmake build files ## -####################### - cmake-appleclang: echo "git branch is " $(BRANCH) mkdir -p builds.tmp/cmake-appleclang cd builds.tmp/cmake-appleclang; cmake \ -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_PREFIX_PATH="`brew --prefix libffi`" \ + -DCMAKE_PREFIX_PATH="/opt/homebrew/opt/mpi;/opt/homebrew/opt/bdw-gc;/opt/homebrew/opt/boost;/opt/homebrew/opt/cohomcalg;/opt/homebrew/opt/csdp;/opt/homebrew/opt/eigen;/opt/homebrew/opt/factory;/opt/homebrew/opt/fflas-ffpack;/opt/homebrew/opt/flint;/opt/homebrew/opt/fourtitwo;/opt/homebrew/opt/frobby;/opt/homebrew/opt/gdbm;/opt/homebrew/opt/gfan;/opt/homebrew/opt/givaro;/opt/homebrew/opt/gmp;/opt/homebrew/opt/libffi;/opt/homebrew/opt/libomp;/opt/homebrew/opt/lrs;/opt/homebrew/opt/mpfi;/opt/homebrew/opt/mpfr;/opt/homebrew/opt/mpsolve;/opt/homebrew/opt/msolve;/opt/homebrew/opt/nauty;/opt/homebrew/opt/node;/opt/homebrew/opt/normaliz;/opt/homebrew/opt/ntl;/opt/homebrew/opt/python;/opt/homebrew/opt/readline;/opt/homebrew/opt/tbb;/opt/homebrew/opt/topcom;/opt/homebrew/opt/" \ -DBUILD_NATIVE=off \ -DCMAKE_INSTALL_PREFIX=`pwd`/installed \ -DBUILD_TESTING=on \ @@ -23,437 +19,62 @@ cmake-appleclang: -DCMAKE_EXPORT_COMPILE_COMMANDS=true \ ../../../.. -cmake-clang: - echo "git branch is " $(BRANCH) - mkdir -p builds.tmp/cmake-clang - cd builds.tmp/cmake-clang; cmake \ - -GNinja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DCMAKE_C_COMPILER="`brew --prefix llvm`/bin/clang" \ - -DCMAKE_CXX_COMPILER="`brew --prefix llvm`/bin/clang++" \ - -DBUILD_DOCS=on \ - -DBUILD_NATIVE=off \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=true \ - ../../../.. - -cmake-gcc14: - mkdir -p builds.tmp/cmake-gcc14 - cd builds.tmp/cmake-gcc14; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=/opt/homebrew/opt/gcc/bin/gcc-14 \ - -DCMAKE_CXX_COMPILER:FILEPATH=/opt/homebrew/opt/gcc/bin/g++-14 \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -## clean up or remove all below here -## grab debug builds, autotools builds, profile builds (on linux at leasst) - - -cmake-make-appleclang: - echo "git branch is " $(BRANCH) - mkdir -p builds.tmp/cmake-make-appleclang-$(BRANCH) - cd builds.tmp/cmake-make-appleclang-$(BRANCH); cmake \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_INSTALL_PREFIX=`pwd`/installed \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -# -DLINTING=on - -cmake-tbb2020-appleclang: - echo "git branch is " $(BRANCH) - mkdir -p builds.tmp/cmake-tbb2020-appleclang - cd builds.tmp/cmake-tbb2020-appleclang; cmake \ - -GNinja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_PREFIX_PATH="`brew --prefix tbb@2020`;`brew --prefix`" \ - -DBUILD_NATIVE=off \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -cmake-no-tbb-appleclang: - echo "git branch is " $(BRANCH) - mkdir -p builds.tmp/cmake-no-tbb-appleclang - cd builds.tmp/cmake-no-tbb-appleclang; cmake \ - -GNinja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_PREFIX_PATH=`brew --prefix` \ - -DWITH_TBB=off \ - -DBUILD_NATIVE=off \ - -DBUILD_TESTING=off \ - -DBUILD_DOCS=on \ - ../../../.. - -cmake-appleclang: - echo "git branch is " $(BRANCH) - mkdir -p builds.tmp/cmake-appleclang - cd builds.tmp/cmake-appleclang; cmake \ - -GNinja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_PREFIX_PATH="`brew --prefix libffi`" \ - -DBUILD_NATIVE=off \ - -DCMAKE_INSTALL_PREFIX=`pwd`/installed \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - cmake-debug-appleclang: + echo "git branch is " $(BRANCH) mkdir -p builds.tmp/cmake-debug-appleclang cd builds.tmp/cmake-debug-appleclang; cmake \ -GNinja \ -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_PREFIX_PATH="`brew --prefix tbb@2021`;`brew --prefix libffi`;`brew --prefix`" \ + -DCMAKE_PREFIX_PATH="/opt/homebrew/opt/mpi;/opt/homebrew/opt/bdw-gc;/opt/homebrew/opt/boost;/opt/homebrew/opt/cohomcalg;/opt/homebrew/opt/csdp;/opt/homebrew/opt/eigen;/opt/homebrew/opt/factory;/opt/homebrew/opt/fflas-ffpack;/opt/homebrew/opt/flint;/opt/homebrew/opt/fourtitwo;/opt/homebrew/opt/frobby;/opt/homebrew/opt/gdbm;/opt/homebrew/opt/gfan;/opt/homebrew/opt/givaro;/opt/homebrew/opt/gmp;/opt/homebrew/opt/libffi;/opt/homebrew/opt/libomp;/opt/homebrew/opt/lrs;/opt/homebrew/opt/mpfi;/opt/homebrew/opt/mpfr;/opt/homebrew/opt/mpsolve;/opt/homebrew/opt/msolve;/opt/homebrew/opt/nauty;/opt/homebrew/opt/node;/opt/homebrew/opt/normaliz;/opt/homebrew/opt/ntl;/opt/homebrew/opt/python;/opt/homebrew/opt/readline;/opt/homebrew/opt/tbb;/opt/homebrew/opt/topcom;/opt/homebrew/opt/" \ -DBUILD_NATIVE=off \ + -DCMAKE_INSTALL_PREFIX=`pwd`/installed \ -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -cmake-profile-appleclang: - echo "git branch is " $(BRANCH) - mkdir -p builds.tmp/cmake-profile-appleclang - cd builds.tmp/cmake-profile-appleclang; cmake \ - -GNinja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DPROFILING=on \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. -# -DLINTING=on - -cmake-profile-appleclang: - mkdir -p builds.tmp/cmake-profile-appleclang - cd builds.tmp/cmake-profile-appleclang; cmake \ - -GNinja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_PREFIX_PATH="`brew --prefix tbb@2020`;`brew --prefix`" \ - -DCMAKE_INSTALL_PREFIX=`pwd`/installed-profile \ - -DBUILD_NATIVE=on \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. -# -DLINTING=on - -# this one still needs to be modified for M1 -cmake-latestclang: - mkdir -p builds.tmp/cmake-latestclang - cd builds.tmp/cmake-latestclang; LIBRARY_PATH=`/usr/local/opt/llvm/bin/llvm-config --libdir` cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=/usr/local/opt/llvm/bin/clang \ - -DCMAKE_CXX_COMPILER:FILEPATH=/usr/local/opt/llvm/bin/clang++ \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -# this one still needs to be modified for M1 -cmake-debug-latestclang: - mkdir -p builds.tmp/cmake-debug-latestclang - cd builds.tmp/cmake-debug-latestclang; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=/usr/local/opt/llvm/bin/clang \ - -DCMAKE_CXX_COMPILER:FILEPATH=/usr/local/opt/llvm/bin/clang++ \ - -DCMAKE_BUILD_TYPE=Debug \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -# this one still needs to be modified for M1, tbb -cmake-gcc9: - mkdir -p builds.tmp/cmake-gcc9 - cd builds.tmp/cmake-gcc9; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=/usr/local/bin/gcc-9 \ - -DCMAKE_CXX_COMPILER:FILEPATH=/usr/local/bin/g++-9 \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -# this one still needs to be modified for M1, tbb -ubuntu-cmake-gcc: - mkdir -p builds.tmp/ubuntu-cmake-gcc - cd builds.tmp/ubuntu-cmake-gcc; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=gcc \ - -DCMAKE_CXX_COMPILER:FILEPATH=g++ \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -ubuntu-arm64-cmake-gcc: - mkdir -p builds.tmp/ubuntu-arm64-cmake-gcc - cd builds.tmp/ubuntu-arm64-cmake-gcc; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=gcc \ - -DCMAKE_CXX_COMPILER:FILEPATH=g++ \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -ubuntu-amd64-cmake-gcc: - mkdir -p builds.tmp/ubuntu-amd64-cmake-gcc - cd builds.tmp/ubuntu-amd64-cmake-gcc; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=gcc \ - -DCMAKE_CXX_COMPILER:FILEPATH=g++ \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - - -# running cmake-gcc9 now: 30 May 2020. -# build-libraries (with testing off): 22m36s -# build-programs (with testing off): 12m19s -# cmake -DBUILD_TESTING=on . -# M2-core: 2m51s -# install-packages: (running) -# M2-unit-tests -# check-packages -# ctest - -# this one still needs to be modified for M1, tbb -cmake-debug-gcc9: - mkdir -p builds.tmp/cmake-debug-gcc9 - cd builds.tmp/cmake-debug-gcc9; cmake \ - -GNinja \ - -DCMAKE_C_COMPILER:FILEPATH=/usr/local/bin/gcc-9 \ - -DCMAKE_CXX_COMPILER:FILEPATH=/usr/local/bin/g++-9 \ - -DCMAKE_BUILD_TYPE=Debug \ - -DBUILD_TESTING=on \ - -DBUILD_DOCS=on \ - ../../../.. - -# does this even work? -xcode: - mkdir -p builds.tmp/xcode - cd builds.tmp/xcode; cmake \ - -GXcode \ + -DBUILD_DOCS=off \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=true \ ../../../.. -########################### -## configure build files ## -########################### - -########### -## MacOS ## -########### -# Some possibly useful configure options -# --build=x86_64-apple-darwin -# --enable-build-libraries="flint ntl" - +# this one currently fails. can't build lrslib. gmp and/or openmp problem? arm64-appleclang : always mkdir -p builds.tmp/arm64-appleclang cd builds.tmp/arm64-appleclang; ../../../../configure \ - CPPFLAGS="-I`brew --prefix tbb@2021`/include -I`brew --prefix`/include" \ - LDFLAGS="-L`brew --prefix tbb@2021`/lib -L`brew --prefix`/lib" \ - --with-boost-regex=boost_regex \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -debug-arm64-appleclang : always - mkdir -p builds.tmp/debug-arm64-appleclang - cd builds.tmp/debug-arm64-appleclang; ../../../../configure \ - CPPFLAGS="-I`brew --prefix tbb@2021`/include -I`brew --prefix`/include" \ - LDFLAGS="-L`brew --prefix tbb@2021`/lib -L`brew --prefix`/lib" \ - --with-boost-regex=boost_regex \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-debug \ - --enable-memdebug \ - --disable-optimize \ - --enable-download - -arm64-tbb2020-appleclang : always - mkdir -p builds.tmp/arm64-tbb2020-appleclang - cd builds.tmp/arm64-tbb2020-appleclang; ../../../../configure \ - CPPFLAGS="-I`brew --prefix tbb@2020`/include -I`brew --prefix`/include" \ - LDFLAGS="-L`brew --prefix tbb@2020`/lib -L`brew --prefix`/lib" \ - --with-boost-regex=boost_regex \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -arm64-no-tbb-appleclang : always - mkdir -p builds.tmp/arm64-no-tbb-appleclang - cd builds.tmp/arm64-no-tbb-appleclang; ../../../../configure \ - CPPFLAGS="-I`brew --prefix tbb@2021`/include -I`brew --prefix`/include" \ - LDFLAGS="-L`brew --prefix tbb@2021`/lib -L`brew --prefix`/lib" \ - --disable-tbb \ - --with-boost-regex=boost_regex \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -darwin64-appleclang : always - mkdir -p builds.tmp/darwin64-appleclang - cd builds.tmp/darwin64-appleclang; ../../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download -# --enable-build-libraries="cohomcalg" - -darwin64-latestclang : always - mkdir -p builds.tmp/darwin64-latestclang - cd builds.tmp/darwin64-latestclang; ../../../../configure \ - CC="/usr/local/opt/llvm/bin/clang" \ - CXX="/usr/local/opt/llvm/bin/clang++" \ - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" \ - CPPFLAGS="-I/usr/local/opt/llvm/include" \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -darwin64-gcc9 : always - mkdir -p builds.tmp/darwin64-gcc9 - cd builds.tmp/darwin64-gcc9; ../../../../configure \ - CC=gcc-9 CXX=g++-9 \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -darwin64-gcc10 : always - mkdir -p builds.tmp/darwin64-gcc9 - cd builds.tmp/darwin64-gcc9; ../../../../configure \ - CC=gcc-10 CXX=g++-10 \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -debug64-latestclang : always - mkdir -p builds.tmp/debug64-latestclang - cd builds.tmp/debug64-latestclang; ../../../../configure \ - CC="/usr/local/opt/llvm/bin/clang" \ - CXX="/usr/local/opt/llvm/bin/clang++" \ - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" \ - CPPFLAGS="-I/usr/local/opt/llvm/include" \ - --prefix="`pwd`/installed" \ + --enable-dmg \ + --enable-download --with-system-gc \ + --with-system-memtailor --with-system-mathic \ + --with-system-mathicgb \ + LDFLAGS="-L`brew --prefix`/lib \ + -L`brew --prefix factory`/lib \ + -L`brew --prefix libomp`/lib \ + -L`brew --prefix readline`/lib \ + -L`brew --prefix python`/Frameworks/Python.framework/Versions/3.13/lib" \ + CPPFLAGS="-I`brew --prefix`/include \ + -I`brew --prefix`/include/cddlib \ + -I`brew --prefix factory`/include \ + -I`brew --prefix libomp`/include \ + -I`brew --prefix readline`/include" \ + F77="gfortran-15" \ + PKG_CONFIG_PATH="`brew --prefix factory`/lib/pkgconfig" + +arm64-debug-appleclang : always + mkdir -p builds.tmp/arm64-debug-appleclang + cd builds.tmp/arm64-debug-appleclang; ../../../../configure \ --disable-strip \ - --enable-download \ - --enable-debug \ - --disable-optimize - -debug64-appleclang : always - mkdir -p builds.tmp/debug64-appleclang - cd builds.tmp/debug64-appleclang; ../../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download \ --enable-debug \ --enable-memdebug \ - --disable-optimize - -debug64-gcc9 : always - mkdir -p builds.tmp/debug64-gcc9 - cd builds.tmp/debug64-gcc9; ../../../../configure \ - CC=gcc-9 CXX=g++-9 \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download \ - --enable-debug \ - --disable-optimize - -profile64-latestclang : always - mkdir -p builds.tmp/profile64-latestclang - cd builds.tmp/profile64-latestclang; ../../../../configure \ - CC="/usr/local/opt/llvm/bin/clang" \ - CXX="/usr/local/opt/llvm/bin/clang++" \ - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" \ - CPPFLAGS="-I/usr/local/opt/llvm/include" \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download \ - --enable-profile - -profile64-appleclang : always - mkdir -p builds.tmp/profile64-appleclang - cd builds.tmp/profile64-appleclang; ../../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download \ - --enable-profile - -profile64-gcc9 : always - mkdir -p builds.tmp/profile64-gcc9 - cd builds.tmp/profile64-gcc9; ../../../../configure \ - CC=gcc-9 CXX=g++-9 \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download \ - --enable-profile - -################### -## linux ########## -################### - -linux-opt64-gcc8 : always - mkdir -p builds.tmp/opt64-gcc8 - cd builds.tmp/opt64-gcc8; ../../../../configure \ - CC=gcc-8 CXX=g++-8 \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -linux-opt64 : always - mkdir -p builds.tmp/opt64 - cd builds.tmp/opt64; ../../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -linux-debug64 : always - mkdir -p builds.tmp/debug64 - cd builds.tmp/debug64; ../../../../configure \ - --prefix="`pwd`/installed" \ - CPPFLAGS="-DENGINE_DEBUG_" \ - CXXFLAGS=" -U__GNUC_STDC_INLINE__" \ - --enable-debug \ --disable-optimize \ - --enable-download - -linux-profile64 : always - mkdir -p builds.tmp/profile64 - cd builds.tmp/profile64; ../../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-profile \ - --enable-download - -######## below this line is older ###################### -opt : always - mkdir -p opt - cd opt; ../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-download - -debug : always - mkdir -p debug - cd debug; ../../../configure \ - --prefix="`pwd`/installed" \ - --enable-debug \ - --disable-strip \ - --disable-optimize \ - --enable-download - -profile : always - mkdir -p profile - cd profile; ../../../configure \ - --prefix="`pwd`/installed" \ - --disable-strip \ - --enable-profile \ - --enable-download \ - -# the following is no longer possible for me. -# TODO: work with Dan to fix that. -upload : always - scp darwin64/*.dmg u00.math.uiuc.edu:/home/html/www/Macaulay2/Uploads - + --enable-download --with-system-gc \ + --with-system-memtailor --with-system-mathic \ + --with-system-mathicgb \ + LDFLAGS="-L`brew --prefix`/lib \ + -L`brew --prefix factory`/lib \ + -L`brew --prefix libomp`/lib \ + -L`brew --prefix readline`/lib \ + -L`brew --prefix python`/Frameworks/Python.framework/Versions/3.13/lib" \ + CPPFLAGS="-I`brew --prefix`/include \ + -I`brew --prefix`/include/cddlib \ + -I`brew --prefix factory`/include \ + -I`brew --prefix libomp`/include \ + -I`brew --prefix readline`/include" \ + F77="gfortran-15" \ + PKG_CONFIG_PATH="`brew --prefix factory`/lib/pkgconfig" + +# todo: add profiling builds diff --git a/M2/BUILD/rpm/Macaulay2.spec b/M2/BUILD/rpm/Macaulay2.spec index 7bfcbca13bc..9edef5e433f 100644 --- a/M2/BUILD/rpm/Macaulay2.spec +++ b/M2/BUILD/rpm/Macaulay2.spec @@ -2,7 +2,7 @@ # https://src.fedoraproject.org/rpms/Macaulay2/blob/rawhide/f/Macaulay2.spec Name: Macaulay2 -Version: 1.25.11 +Version: 1.26.05 # release convention: 0.x.m2 (so official fedora package will take precedence) # increment x as needed, reset to 1 with each new m2 release @@ -150,6 +150,9 @@ rm -fv %{buildroot}%{_infodir}/dir %{_mandir}/man1/* %changelog +* Sat May 09 2026 Doug Torrance - 1.26.05-0.1.m2 +- New release. + * Mon Nov 10 2025 Doug Torrance - 1.25.11-0.1.m2 - New release. diff --git a/M2/Macaulay2/Makefile.in b/M2/Macaulay2/Makefile.in index 75fe0a6cc08..5e4c21a6ab5 100644 --- a/M2/Macaulay2/Makefile.in +++ b/M2/Macaulay2/Makefile.in @@ -3,9 +3,15 @@ VPATH = @srcdir@ include ../include/config.Makefile all: @pre_docdir@/COPYING-GPL-2 @pre_docdir@/COPYING-GPL-3 @pre_docdir@/LAYOUT -SUBDIRS = c d e system bin m2 man packages editors \ +SUBDIRS = e c d system bin m2 man packages editors \ $(if $(filter html, @DOCUMENTATION@), html-check-links) tests +all-in-d: all-in-c +all-in-bin: all-in-e all-in-d all-in-system +all-in-m2: all-in-bin +all-in-packages all-in-editors all-in-tests: all-in-m2 +all-in-html-check-links: all-in-packages + define do-in-subdirs $(foreach d,$(SUBDIRS), $(eval .PHONY: $1-in-subdirs) @@ -25,4 +31,3 @@ distclean:; rm -f Makefile # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2 " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/bin/Makefile.in b/M2/Macaulay2/bin/Makefile.in index e3808aa49bb..95abbf86153 100644 --- a/M2/Macaulay2/bin/Makefile.in +++ b/M2/Macaulay2/bin/Makefile.in @@ -22,7 +22,7 @@ endif NORULES = yes include ../../include/config.Makefile include ../d/Makefile.files -include @srcdir@/../e/Makefile.files +include ../e/Makefile.files include @srcdir@/../system/Makefile.files %.o : %.c @@ -33,14 +33,14 @@ include @srcdir@/../system/Makefile.files %.o : %.s # warning: static initializers are run in left-to-right order, and some of them depend on others of them -M2_OBJECTS := $(SYSTEM_OFILES:%=../system/%) $(ENGINE_OFILES:%=../e/%) $(M2_OBJECTS:%=../d/%) $(patsubst %, ../d/%.o, $(M2_DNAMES)) +M2_OBJECTS := $(SYSTEM_OFILES:%=../system/%) $(M2_OBJECTS:%=../d/%) $(patsubst %, ../d/%.o, $(M2_DNAMES)) $(LIBENGINE) EXEFILE = @pre_bindir@/M2@EXE@ M2SCRIPT = @pre_bindir@/M2 all: $(EXEFILE) $(M2SCRIPT) -$(M2SCRIPT): M2 +$(M2SCRIPT): M2 @pre_bindir@ @INSTALL_SCRIPT@ M2 $(M2SCRIPT) relink: @@ -199,4 +199,3 @@ M2: M2.in; cd ../..; ./config.status Macaulay2/bin/M2 # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/bin relink " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/bin/main.cpp b/M2/Macaulay2/bin/main.cpp index 5e69d9636f4..f03d2a45b6c 100644 --- a/M2/Macaulay2/bin/main.cpp +++ b/M2/Macaulay2/bin/main.cpp @@ -4,10 +4,10 @@ #include #include "interp-exports.h" +#include #include "M2mem.h" #include "types.h" -#include "debug.h" #include /* to get IM2_initialize() : */ #include "supervisorinterface.h" @@ -140,6 +140,7 @@ void M2_flint_abort(void) { void* profFunc(ArgCell* p) { + (void) p; using namespace std::chrono_literals; std::string filename("profile-" + std::to_string(getpid())+ ".raw"); std::cerr << "-- Storing profiling data in " << filename << std::endl; @@ -219,12 +220,14 @@ extern "C" void oursignal(int sig, void (*handler)(int)) { } void trace_handler(int sig) { + (void) sig; if (tryGlobalTrace() == 0) profiler_stacktrace(std::cerr, 1); oursignal(SIGUSR1,trace_handler); } void alarm_handler(int sig) { + (void) sig; if (tryGlobalAlarm() == 0) interrupts_setAlarmedFlag(); oursignal(SIGALRM,alarm_handler); @@ -232,6 +235,7 @@ void alarm_handler(int sig) { void segv_handler(int sig) { static int level; + (void) sig; fprintf(stderr, "-- SIGSEGV\n"); level ++; if (level > 1) { @@ -244,6 +248,7 @@ void segv_handler(int sig) { } void interrupt_handler(int sig) { + (void) sig; if (tryGlobalInterrupt() == 0) { if (test_Field(THREADLOCAL(interrupts_interruptedFlag, struct atomic_field)) || THREADLOCAL(interrupts_interruptPending, bool)) { @@ -266,6 +271,7 @@ void interrupt_handler(int sig) { fprintf(stderr,"returning to top level\n"); fflush(stderr); + interp_setInterpreterDepth(0); interrupts_clearAlarmedFlag(); interrupts_clearInterruptFlag(); diff --git a/M2/Macaulay2/c/Makefile.in b/M2/Macaulay2/c/Makefile.in index abfe79c2eaf..182c544f00e 100644 --- a/M2/Macaulay2/c/Makefile.in +++ b/M2/Macaulay2/c/Makefile.in @@ -81,4 +81,3 @@ Makefile: Makefile.in # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/c " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/c/chk.c b/M2/Macaulay2/c/chk.c index 279dc80da39..0184fba111a 100644 --- a/M2/Macaulay2/c/chk.c +++ b/M2/Macaulay2/c/chk.c @@ -242,6 +242,7 @@ static node arraylength(node arr){ } static node cast(node t, node e, scope v) { + (void) v; node u; if (e == bad__K) return bad__K; u = type(e); @@ -2016,6 +2017,7 @@ static node chkfunctiontype(node e, scope v, node fun){ } static node chkchked(node e, scope v){ + (void) v; return cadr(e); } @@ -2441,6 +2443,7 @@ node chk(node e, scope v){ node headerstrings; static node chkheader(node e, scope v) { + (void) v; headerstrings = cons(cadr(e),headerstrings); return NULL; } @@ -2471,6 +2474,8 @@ node prefixOperator(node e) { } static node chkoperator(node e, scope v) { + (void) e; + (void) v; /* operator definitions in a signature file would happen too late, because the parser parses the entire source file before "checking" it. @@ -2488,6 +2493,7 @@ static node chkdeclarations(node e, scope v) { } static node chkrawtype(node e, scope v) { + (void) v; node name = unpos(cadr(e)); node t = newtype(NULL,NULL,FALSE); assert(isstrconst(name)); @@ -2499,6 +2505,7 @@ static node chkrawtype(node e, scope v) { } static node chkatomicrawtype(node e, scope v) { + (void) v; node name = unpos(cadr(e)); node t = newtype(NULL,NULL,TRUE); assert(isstrconst(name)); @@ -2510,6 +2517,7 @@ static node chkatomicrawtype(node e, scope v) { } static node chkarithmetictype(node e, scope v) { + (void) v; node name = unpos(cadr(e)); node t = newtype(NULL,NULL,TRUE); assert(isstrconst(name)); @@ -2521,6 +2529,7 @@ static node chkarithmetictype(node e, scope v) { } static node chkintegertype(node e, scope v) { + (void) v; node name = unpos(cadr(e)); node t = newtype(NULL,NULL,TRUE); assert(isstrconst(name)); @@ -2532,6 +2541,7 @@ static node chkintegertype(node e, scope v) { } static node chkpointer(node e, scope v) { + (void) v; node name = unpos(cadr(e)); node t = newtype(NULL,NULL,FALSE); assert(isstrconst(name)); @@ -2543,6 +2553,7 @@ static node chkpointer(node e, scope v) { } static node chkatomicpointer(node e, scope v) { + (void) v; node name = unpos(cadr(e)); node t = newtype(NULL,NULL,FALSE); assert(isstrconst(name)); diff --git a/M2/Macaulay2/c/type.c b/M2/Macaulay2/c/type.c index 6aa0a320158..663051e3c28 100644 --- a/M2/Macaulay2/c/type.c +++ b/M2/Macaulay2/c/type.c @@ -172,8 +172,12 @@ node type(node e){ /* assume e is checked previously */ case char_const_tag: return char_T; case int_const_tag: return int_T; case double_const_tag: return double_T; - case string_tag: assert(FALSE); - case unique_string_tag: assert(FALSE); /* was return bad_or_undefined_T; */ + case string_tag: + assert(FALSE); + return NULL; + case unique_string_tag: + assert(FALSE); + return NULL; /* was return bad_or_undefined_T; */ case cons_tag: { node h, ht; h = unpos(CAR(e)); diff --git a/M2/Macaulay2/d/CMakeLists.txt b/M2/Macaulay2/d/CMakeLists.txt index 56df496c6e2..73a307463db 100644 --- a/M2/Macaulay2/d/CMakeLists.txt +++ b/M2/Macaulay2/d/CMakeLists.txt @@ -18,7 +18,6 @@ set(CLIST M2types.c M2lib.c M2mem.c types.h scclib.c ../c/scc-core.h memdebug.c memdebug.h - debug.c debug.h getpagesize.h M2mem.h gmp_aux.c gmp_aux.h gdbm_interface.c @@ -93,6 +92,7 @@ set(DLIST texmacs.d boostmath.dd atomic2.d + json.d ffi.d # removed unless WITH_FFI interp.dd # this one is last, because it contains the top level interpreter version.dd @@ -113,6 +113,9 @@ endif() if(NOT WITH_FFI) list(REMOVE_ITEM DLIST ffi.d) endif() +if(NOT WITH_JANSSON) + list(REMOVE_ITEM DLIST json.d) +endif() ############################################################################### ## Generate TAGS file @@ -169,7 +172,6 @@ target_include_directories(M2-interpreter # for libraries that the interpreter needs $ $ - $ $ $<$:$> PRIVATE @@ -194,6 +196,10 @@ foreach(LIB IN LISTS LIBRARIES_LIST) endif() endforeach() +if(TARGET Eigen3::Eigen) + target_include_directories(M2-interpreter PUBLIC $>) +endif() + foreach(LIB IN LISTS LIBRARY_LIST) if(${LIB}_FOUND) target_link_libraries(M2-interpreter PUBLIC ${${LIB}_LIBRARY}) diff --git a/M2/Macaulay2/d/M2.d b/M2/Macaulay2/d/M2.d index b916b65dff8..15c5bcfe9df 100644 --- a/M2/Macaulay2/d/M2.d +++ b/M2/Macaulay2/d/M2.d @@ -1,6 +1,7 @@ -- Copyright 2010 by Daniel R. Grayson declarations "#include "; +header "#include "; use arithmetic; @@ -24,25 +25,12 @@ const(x:charstarOrNull):constcharstarOrNull := Ccode(constcharstarOrNull,x); header "#include "; header " extern void *GC_check_annotated_obj(void*); "; -export tostring(s:constcharstarOrNull):string := -- we want the name of this function to be "tostring", sigh, so keep it first -Ccode(returns, " - int n = s ? strlen(s) : 0; - M2_string p = getmematomicarraytype(M2_string,n); - p->len = n; - memcpy(p->array,s,n); - GC_CHECK_CLOBBER(p); - return p; -"); +import tostring(s:constcharstarOrNull):string; -- we want the name of this function to be "tostring", sigh, so keep it first export tostring(s:charstarOrNull):string := tostring(const(s)); -- make sure this one is second; teach the language about "const", sigh export tostring(s:charstar):string := tostring(charstarOrNull(s)); export tostring(s:constcharstar):string := tostring(constcharstarOrNull(s)); -export makearrayint(n:int):arrayint := Ccode(returns, " - M2_arrayint z = (M2_arrayint)getmem_atomic(sizeofarray(z,n)); - z->len = n; - GC_CHECK_CLOBBER(z); - return z; /* Note that getmem_atomic returns zeroed memory */ -"); +import makearrayint(n:int):arrayint; export tocharstarn(s:string, n:int):charstar := Ccode(returns, " char *p = getmem_atomic(", n + 1, "); memcpy(p,s->array,", n, "); @@ -50,26 +38,12 @@ export tocharstarn(s:string, n:int):charstar := Ccode(returns, " GC_CHECK_CLOBBER(p); return p; "); -export tocharstar(s:string):charstar := tocharstarn(s, length(s)); +import tocharstar(s:string):charstar; export tocharstarOrNull(s:string):charstarOrNull := ( if length(s) == 0 then charstarOrNull(null()) else charstarOrNull(tocharstar(s))); -export tostringn(s:charstar,n:int):string := Ccode(returns, " - M2_string p = (M2_string)getmem_atomic(sizeofarray(p,n)); - p->len = n; - memcpy(p->array,s,n); - GC_CHECK_CLOBBER(p); - return p; -"); -export join(x:string,y:string):string := Ccode(returns, " - M2_string p; - p = (M2_string) getmem_atomic(sizeofarray(p,x->len+y->len)); - p->len = x->len + y->len; - memcpy(p->array,x->array,x->len); - memcpy(p->array+x->len,y->array,y->len); - GC_CHECK_CLOBBER(p); - return p; -"); +import tostringn(s:constcharstar,n:int):string; +import join(x:string,y:string):string; export tocharstarstar(p:ArrayString):charstarstar := ( Ccode(returns, " unsigned int i, n = p->len; @@ -135,17 +109,15 @@ export substrAlwaysCopy(x:string,start:int,leng:int):string := Ccode(returns, " GC_CHECK_CLOBBER(p); return p;"); -declarations " extern char newline[]; "; -header " char newline[] = \"\\n\"; "; export newline := tostring(Ccode(constcharstarOrNull,"newline")); export envc := 0; export argc := 0; export envp := array(string)(); export argv := array(string)(); export args := array(string)(); -export gbTrace := 0; -export numTBBThreads := 0; -- 0 means use the maximum -export numericalAlgebraicGeometryTrace := 0; +import gbTrace:int; +import numTBBThreads:int; -- 0 means use the maximum +import numericalAlgebraicGeometryTrace:int; export notify := false; export readonlyfiles := false; -- see stdio.d diff --git a/M2/Macaulay2/d/M2lib.c b/M2/Macaulay2/d/M2lib.c index 8be79049a27..8f4c5c99430 100644 --- a/M2/Macaulay2/d/M2lib.c +++ b/M2/Macaulay2/d/M2lib.c @@ -4,7 +4,7 @@ #include "M2mem.h" #include "types.h" -#include "debug.h" +#include #include "supervisorinterface.h" #include diff --git a/M2/Macaulay2/d/M2mem.c b/M2/Macaulay2/d/M2mem.c index af4505ec1b1..e71e7449ee2 100644 --- a/M2/Macaulay2/d/M2mem.c +++ b/M2/Macaulay2/d/M2mem.c @@ -2,7 +2,7 @@ #include "types.h" #include "M2mem.h" -#include "debug.h" +#include #ifdef MEMDEBUG #include @@ -34,97 +34,6 @@ void outofmem(void) { exit(1); } -void outofmem2(size_t new) { - const char *msg = "\n\n *** out of memory trying to allocate %ld bytes, exiting ***\n"; - static char buf[sizeof(msg) + 100]; - sprintf(buf,msg,(long)new); - int r = write(STDERR,buf,strlen(buf)); - if (r == ERROR) exit(1); - exit(1); -} - -char *getmem(size_t n) -{ - char *p; - TRAPCHK_SIZE(n); - enter_getmem(); -#ifdef MEMDEBUG - p = M2_debug_malloc(n); -#else - p = GC_MALLOC(n); /* GC_MALLOC clears its memory; we preserve that */ -#endif - if (p == NULL) outofmem2(n); -#ifndef NDEBUG - memset(p,0xbe,n); /* fill with 0xbebebebe ... */ - trapchk(p); -#endif - exit_getmem(); - return p; -} - -void freememlen(void *s, size_t old) { -# ifndef NDEBUG - trapchk(s); -# endif -#ifdef MEMDEBUG - M2_debug_free(s); -#else - /* GC_FREE(s); */ -#endif -} - -void freemem(void *s) { -# ifndef NDEBUG - trapchk(s); -# endif -#ifdef MEMDEBUG - M2_debug_free(s); -#else - /* GC_FREE(s); */ -#endif -} - -char *getmem_clear(size_t n) -{ - char *p; - enter_getmem(); -#ifdef MEMDEBUG - p = M2_debug_malloc(n); -#else - p = GC_MALLOC(n); -#endif - if (p == NULL) outofmem2(n); -#ifdef MEMDEBUG - memset(p,0,n); -#else - /* note: GC_MALLOC clears memory before returning. - If you switch to another memory allocator, you must clear it explicitly here */ -#endif - #ifndef NDEBUG - trapchk(p); - #endif - exit_getmem(); - return p; -} - -char *getmem_atomic(size_t n) -{ - char *p; - enter_getmem(); -#ifdef MEMDEBUG - p = M2_debug_malloc_atomic(n); -#else - p = GC_MALLOC_ATOMIC(n); -#endif - if (p == NULL) outofmem2(n); -#ifndef NDEBUG - memset(p,0xac,n); /* fill with 0xacacacac ... */ - trapchk(p); -#endif - exit_getmem(); - return p; -} - char *getmem_malloc(size_t n) { char *p; @@ -139,24 +48,6 @@ char *getmem_malloc(size_t n) return p; } -char *getmem_atomic_clear(size_t n) -{ - char *p; - enter_getmem(); -#ifdef MEMDEBUG - p = M2_debug_malloc_atomic(n); -#else - p = GC_MALLOC_ATOMIC(n); -#endif - if (p == NULL) outofmem2(n); - memset(p,0,n); /* GC_MALLOC_ATOMIC does not clear memory */ -#ifndef NDEBUG - trapchk(p); -#endif - exit_getmem(); - return p; -} - char *getmoremem (char *s, size_t old, size_t new) { void *p; enter_getmem(); @@ -204,56 +95,6 @@ char *getmoremem_atomic (char *s, size_t old, size_t new) { return p; } -/* Valgrind helper functions */ -#ifndef NVALGRIND - -void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc)(size_t s){ - long result; - OrigFn fn; - VALGRIND_GET_ORIG_FN(fn); - CALL_FN_W_W(result,fn,s); - VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); - return (void*)result; -} - -void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_atomic)(size_t s){ - long result; - OrigFn fn; - VALGRIND_GET_ORIG_FN(fn); - CALL_FN_W_W(result,fn,s); - VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); - return (void*)result; -} - -void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_ignore_off_page)(size_t s){ - long result; - OrigFn fn; - VALGRIND_GET_ORIG_FN(fn); - CALL_FN_W_W(result,fn,s); - VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); - return (void*)result; -} - -void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_atomic_ignore_off_page)(size_t s){ - long result; - OrigFn fn; - VALGRIND_GET_ORIG_FN(fn); - CALL_FN_W_W(result,fn,s); - VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); - return (void*)result; -} - -void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_realloc)(void *p, size_t s){ - long result; - OrigFn fn; - VALGRIND_GET_ORIG_FN(fn); - CALL_FN_W_WW(result,fn,(long)p,s); - VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); - return (void*)result; -} - -#endif /* NVALGRIND */ - /* Local Variables: compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d " diff --git a/M2/Macaulay2/d/M2mem.h b/M2/Macaulay2/d/M2mem.h index fe4bf83ee20..d58f84908ec 100644 --- a/M2/Macaulay2/d/M2mem.h +++ b/M2/Macaulay2/d/M2mem.h @@ -14,14 +14,9 @@ extern "C" { #endif -extern void outofmem2(size_t); -extern char *getmem(size_t); -extern void freemem(void *); -extern void freememlen(void *, size_t); -extern char *getmem_clear(size_t); -extern char *getmem_atomic(size_t); +#include + extern char *getmem_malloc(size_t); -extern char *getmem_atomic_clear(size_t); extern char *getmoremem(char *, size_t oldsize, size_t newsize); extern char *getmoremem1(char *, size_t newsize); extern char *getmoremem_atomic(char *, size_t oldsize, size_t newsize); diff --git a/M2/Macaulay2/d/Makefile.files.in b/M2/Macaulay2/d/Makefile.files.in index 639d30f40e3..bc5e93827d4 100644 --- a/M2/Macaulay2/d/Makefile.files.in +++ b/M2/Macaulay2/d/Makefile.files.in @@ -5,17 +5,13 @@ M2_OPTCFILES := M2_EXTRACFILES := M2_MADECFILES := M2_SRCFILES := -M2_OPTCFILES += debug.c -ifeq "@DEBUG@" "yes" -M2_OBJECTS += debug.o -endif M2_CFILES += gmp_aux.c M2_LASTCFILES += M2types.c scclib.c M2lib.c M2mem.c gdbm_interface.c memdebug.c M2_MADECFILES := startup.c M2_OBJECTS += $(M2_MADECFILES:.c=.o) M2_CCFILES := boost-regex.cpp main.cpp M2_OBJECTS += $(M2_CCFILES:.cpp=.o) -M2_HFILES := getpagesize.h types.h M2mem.h debug.h startup-header.h startup-trailer.h memdebug.h +M2_HFILES := getpagesize.h types.h M2mem.h startup-header.h startup-trailer.h memdebug.h M2_DFILES := M2_DFILES += arithmetic.d @@ -103,6 +99,9 @@ M2_DFILES += monoid.dd M2_DFILES += monomial_ordering.dd M2_DFILES += boostmath.dd M2_DFILES += atomic2.d +ifeq (@JANSSON@,yes) +M2_DFILES += json.d +endif ifneq (@LIBFFI@,no) M2_DFILES += ffi.d else @@ -117,7 +116,6 @@ actors5.o basic.o : \ @srcdir@/../e/engine.h \ @srcdir@/../e/newdelete.hpp \ @srcdir@/../d/M2mem.h \ - @srcdir@/../d/debug.h \ ../../include/M2/config.h interface2.o : \ @@ -129,8 +127,7 @@ interface2.o : \ @srcdir@/../system/supervisorinterface.h \ @srcdir@/../e/engine.h \ @srcdir@/../e/engine-includes.hpp \ - @srcdir@/../d/M2mem.h \ - @srcdir@/../d/debug.h + @srcdir@/../d/M2mem.h interface.o : \ ../../include/M2/config.h \ @@ -138,7 +135,6 @@ interface.o : \ @srcdir@/../e/newdelete.hpp \ @srcdir@/../d/M2mem.h \ @srcdir@/../c/scc-core.h \ - @srcdir@/../d/debug.h \ @srcdir@/../system/supervisorinterface.h \ @srcdir@/../e/engine.h \ @srcdir@/../e/engine-includes.hpp \ @@ -226,7 +222,6 @@ engine.o : \ @srcdir@/../c/scc-core.h \ @srcdir@/../../include/M2/gc-include.h \ @srcdir@/../c/scc-core.h \ - @srcdir@/../d/debug.h \ @srcdir@/../system/supervisorinterface.h \ @srcdir@/../e/error.h \ @srcdir@/../e/exceptions.hpp \ diff --git a/M2/Macaulay2/d/Makefile.in b/M2/Macaulay2/d/Makefile.in index 181dc54e42d..0a0625a2e8d 100644 --- a/M2/Macaulay2/d/Makefile.in +++ b/M2/Macaulay2/d/Makefile.in @@ -66,8 +66,6 @@ LIBFILES := # to get getmem LIBFILES += M2mem.o -# to get trapchk() -LIBFILES += debug.o LIBFILES += gmp_aux.o arithmetic.o atomic.o M2.o system.o strings.o varstrin.o errio.o vararray.o ctype.o nets.o varnets.o interrupts.o pthread0.o stdio.o stdiop.o err.o gmp.o libd.a: $(LIBFILES) ; ar -r $@ $? rm-libd.a:; rm libd.a @@ -208,9 +206,9 @@ M2lib.o : M2_CFLAGS += -Wno-frame-address endif M2lib.o scclib.o : | interp-exports.h.tmp -scclib.o : expr-exports.h M2mem.h debug.h types.h ../c/compat.h ../e/engine.h -M2lib.o : types.h ../c/compat.c debug.h M2mem.h ../c/compat.h ../e/engine.h -M2lib.o scclib.o gdbm_interface.o gc_cpp.o debug.o : ../../include/M2/config.h +scclib.o : expr-exports.h M2mem.h types.h ../c/compat.h ../e/engine.h +M2lib.o : types.h ../c/compat.c M2mem.h ../c/compat.h ../e/engine.h +M2lib.o scclib.o gdbm_interface.o gc_cpp.o : ../../include/M2/config.h M2.o : ../../include/M2/config.h @srcdir@/../c/scc-core.h clean::; rm -f startup.c diff --git a/M2/Macaulay2/d/actors.d b/M2/Macaulay2/d/actors.d index 4cc0abdf917..fc21f5d613f 100644 --- a/M2/Macaulay2/d/actors.d +++ b/M2/Macaulay2/d/actors.d @@ -16,21 +16,23 @@ export (lhs:Expr) + (rhs:Expr) : Expr := ( when lhs is x:ZZcell do ( when rhs - is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, ZZ, ZZ, ZZ - is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, ZZ, QQ, QQ - is y:RRcell do toExpr(y.v + x.v) -- # typical value: symbol +, ZZ, RR, RR - is y:RRicell do toExpr(y.v + x.v) -- # typical value: symbol +, ZZ, RRi, RRi - is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) + y.v) -- # typical value: symbol +, ZZ, CC, CC - is Error do rhs - else binarymethod(lhs,rhs,PlusS)) + is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, ZZ, ZZ, ZZ + is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, ZZ, QQ, QQ + is y:RRcell do toExpr(y.v + x.v) -- # typical value: symbol +, ZZ, RR, RR + is y:RRicell do toExpr(y.v + x.v) -- # typical value: symbol +, ZZ, RRi, RRi + is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) + y.v) -- # typical value: symbol +, ZZ, CC, CC + is y:CCicell do toExpr(toCCi(x.v,precision(y.v.re)) + y.v)-- # typical value: symbol +, ZZ, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,PlusS)) is x:QQcell do ( when rhs - is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, QQ, ZZ, QQ - is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, QQ, QQ, QQ - is y:RRcell do toExpr(y.v + x.v) -- # typical value: symbol +, QQ, RR, RR - is y:RRicell do toExpr(y.v + x.v) -- # typical value: symbol +, QQ, RRi, RRi - is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) + y.v) -- # typical value: symbol +, QQ, CC, CC - is Error do rhs + is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, QQ, ZZ, QQ + is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, QQ, QQ, QQ + is y:RRcell do toExpr(y.v + x.v) -- # typical value: symbol +, QQ, RR, RR + is y:RRicell do toExpr(y.v + x.v) -- # typical value: symbol +, QQ, RRi, RRi + is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) + y.v) -- # typical value: symbol +, QQ, CC, CC + is y:CCicell do toExpr(toCCi(x.v,precision(y.v.re)) + y.v)-- # typical value: symbol +, QQ, CCi, CCi + is Error do rhs else binarymethod(lhs,rhs,PlusS)) is x:RawRingElementCell do ( when rhs @@ -43,27 +45,42 @@ export (lhs:Expr) + (rhs:Expr) : Expr := ( else binarymethod(lhs,rhs,PlusS)) is x:RRcell do ( when rhs - is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, ZZ, RR - is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, QQ, RR - is y:RRcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, RR, RR - is y:RRicell do toExpr(y.v + x.v) -- # typical value: symbol +, RR, RRi, RRi - is y:CCcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, CC, CC - is Error do rhs - else binarymethod(lhs,rhs,PlusS)) + is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, ZZ, RR + is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, QQ, RR + is y:RRcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, RR, RR + is y:RRicell do toExpr(y.v + x.v) -- # typical value: symbol +, RR, RRi, RRi + is y:CCcell do toExpr(x.v + y.v) -- # typical value: symbol +, RR, CC, CC + is y:CCicell do toExpr(toCCi(x.v) + y.v) -- # typical value: symbol +, RR, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,PlusS)) is x:RRicell do ( - when rhs - is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, ZZ, RRi - is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, QQ, RRi - is y:RRcell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, RR, RRi - is y:RRicell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, RRi, RRi - is Error do rhs - else binarymethod(lhs,rhs,PlusS)) + when rhs + is y:ZZcell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, ZZ, RRi + is y:QQcell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, QQ, RRi + is y:RRcell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, RR, RRi + is y:RRicell do toExpr(x.v + y.v) -- # typical value: symbol +, RRi, RRi, RRi + is y:CCcell do toExpr(toCCi(x.v) + toCCi(y.v)) -- # typical value: symbol +, RRi, CC, CCi + is y:CCicell do toExpr(toCCi(x.v) + y.v) -- # typical value: symbol +, RRi, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,PlusS)) is x:CCcell do ( when rhs - is y:ZZcell do toExpr(x.v + toRR(y.v,precision(x.v.re))) -- # typical value: symbol +, CC, ZZ, CC - is y:QQcell do toExpr(x.v + toRR(y.v,precision(x.v.re))) -- # typical value: symbol +, CC, QQ, CC - is y:RRcell do toExpr(x.v + y.v) -- # typical value: symbol +, CC, RR, CC - is y:CCcell do toExpr(x.v + y.v) -- # typical value: symbol +, CC, CC, CC + is y:ZZcell do toExpr(x.v + toRR(y.v,precision(x.v.re))) -- # typical value: symbol +, CC, ZZ, CC + is y:QQcell do toExpr(x.v + toRR(y.v,precision(x.v.re))) -- # typical value: symbol +, CC, QQ, CC + is y:RRcell do toExpr(x.v + y.v) -- # typical value: symbol +, CC, RR, CC + is y:RRicell do toExpr(toCCi(x.v) + toCCi(y.v)) -- # typical value: symbol +, CC, RRi, CCi + is y:CCcell do toExpr(x.v + y.v) -- # typical value: symbol +, CC, CC, CC + is y:CCicell do toExpr(toCCi(x.v) + y.v) -- # typical value: symbol +, CC, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,PlusS)) + is x:CCicell do ( + when rhs + is y:ZZcell do toExpr(x.v + toCCi(y.v,precision(x.v.re))) -- # typical value: symbol +, CCi, ZZ, CCi + is y:QQcell do toExpr(x.v + toCCi(y.v,precision(x.v.re))) -- # typical value: symbol +, CCi, QQ, CCi + is y:RRcell do toExpr(x.v + toCCi(y.v)) -- # typical value: symbol +, CCi, RR, CCi + is y:RRicell do toExpr(x.v + toCCi(y.v)) -- # typical value: symbol +, CCi, RRi, CCi + is y:CCcell do toExpr(x.v + toCCi(y.v)) -- # typical value: symbol +, CCi, CC, CCi + is y:CCicell do toExpr(x.v + y.v) -- # typical value: symbol +, CCi, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,PlusS)) is x:RawMatrixCell do ( @@ -110,10 +127,11 @@ plusfun1(rhs:Code):Expr := ( is Error do r is ZZcell do r -- # typical value: symbol +, ZZ, ZZ is RRcell do r -- # typical value: symbol +, RR, RR - is RRicell do r -- # typical value: symbol +, RRi, RRi + is RRicell do r -- # typical value: symbol +, RRi, RRi is CCcell do r -- # typical value: symbol +, CC, CC + is CCicell do r -- # typical value: symbol +, CCi, CCi is QQcell do r -- # typical value: symbol +, QQ, QQ - is RawRingElementCell do r -- # typical value: symbol +, RawRingElement, RawRingElement + is RawRingElementCell do r -- # typical value: symbol +, RawRingElement, RawRingElement is RawMatrixCell do r -- # typical value: symbol +, RawMatrix, RawMatrix is RawMutableMatrixCell do r -- # typical value: symbol +, RawMutableMatrix, RawMutableMatrix else unarymethod(rhs,PlusS)); @@ -129,11 +147,12 @@ export - (rhs:Expr) : Expr := ( when rhs is x:ZZcell do toExpr(-x.v) -- # typical value: symbol -, ZZ, ZZ is x:RRcell do toExpr(-x.v) -- # typical value: symbol -, RR, RR - is x:RRicell do toExpr(-x.v) -- # typical value: symbol -, RRi, RRi + is x:RRicell do toExpr(-x.v) -- # typical value: symbol -, RRi, RRi is x:CCcell do toExpr(-x.v) -- # typical value: symbol -, CC, CC + is x:CCicell do toExpr(-x.v) -- # typical value: symbol -, CCi, CCi is x:QQcell do toExpr(-x.v) -- # typical value: symbol -, QQ, QQ is x:RawRingElementCell do toExpr(-x.p) -- # typical value: symbol -, RawRingElement, RawRingElement - is x:RawMatrixCell do ( -- # typical value: symbol -, RawMatrix, RawMatrix + is x:RawMatrixCell do ( -- # typical value: symbol -, RawMatrix, RawMatrix when -x.p is y:RawMatrix do toExpr(y) else buildErrorPacket(EngineError("polynomial minus failed")) ) is x:RawMutableMatrixCell do toExpr(-x.p) -- # typical value: symbol -, RawMutableMatrix, RawMutableMatrix @@ -149,22 +168,24 @@ export (lhs:Expr) - (rhs:Expr) : Expr := ( when lhs is x:ZZcell do ( when rhs - is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, ZZ, ZZ, ZZ - is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, ZZ, QQ, QQ - is y:RRcell do toExpr(toRR(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, ZZ, RR, RR - is y:RRicell do toExpr(toRRi(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, ZZ, RRi, RRi - is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) - y.v) -- # typical value: symbol -, ZZ, CC, CC - is Error do rhs - else binarymethod(lhs,rhs,MinusS)) + is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, ZZ, ZZ, ZZ + is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, ZZ, QQ, QQ + is y:RRcell do toExpr(toRR(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, ZZ, RR, RR + is y:RRicell do toExpr(toRRi(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, ZZ, RRi, RRi + is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) - y.v) -- # typical value: symbol -, ZZ, CC, CC + is y:CCicell do toExpr(toCCi(x.v,precision(y.v.re)) - y.v)-- # typical value: symbol -, ZZ, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,MinusS)) is x:QQcell do ( - when rhs - is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, QQ, ZZ, QQ - is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, QQ, QQ, QQ - is y:RRcell do toExpr(toRR(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, QQ, RR, RR - is y:RRicell do toExpr(toRRi(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, QQ, RRi, RRi - is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) - y.v) -- # typical value: symbol -, QQ, CC, CC - is Error do rhs - else binarymethod(lhs,rhs,MinusS)) + when rhs + is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, QQ, ZZ, QQ + is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, QQ, QQ, QQ + is y:RRcell do toExpr(toRR(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, QQ, RR, RR + is y:RRicell do toExpr(toRRi(x.v,precision(y.v)) - y.v) -- # typical value: symbol -, QQ, RRi, RRi + is y:CCcell do toExpr(toRR(x.v,precision(y.v.re)) - y.v) -- # typical value: symbol -, QQ, CC, CC + is y:CCicell do toExpr(toCCi(x.v,precision(y.v.re)) - y.v)-- # typical value: symbol -, QQ, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,MinusS)) is x:RawRingElementCell do ( when rhs is y:RawRingElementCell do ( -- # typical value: symbol -, RawRingElement, RawRingElement, RawRingElement @@ -177,26 +198,41 @@ export (lhs:Expr) - (rhs:Expr) : Expr := ( is x:RRcell do ( when rhs is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, RR, ZZ, RR - is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, RR, QQ, RR + is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, RR, QQ, RR is y:RRcell do toExpr(x.v - y.v) -- # typical value: symbol -, RR, RR, RR - is y:RRicell do toExpr(toRRi(x.v) - y.v) -- # typical value: symbol -, RR, RRi, RRi + is y:RRicell do toExpr(toRRi(x.v) - y.v) -- # typical value: symbol -, RR, RRi, RRi is y:CCcell do toExpr(x.v - y.v) -- # typical value: symbol -, RR, CC, CC + is y:CCicell do toExpr(toCCi(x.v) - y.v) -- # typical value: symbol -, RR, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,MinusS)) is x:RRicell do ( - when rhs - is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, ZZ, RRi - is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, QQ, RRi - is y:RRcell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, RR, RRi - is y:RRicell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, RRi, RRi - is Error do rhs - else binarymethod(lhs,rhs,MinusS)) + when rhs + is y:ZZcell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, ZZ, RRi + is y:QQcell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, QQ, RRi + is y:RRcell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, RR, RRi + is y:RRicell do toExpr(x.v - y.v) -- # typical value: symbol -, RRi, RRi, RRi + is y:CCcell do toExpr(toCCi(x.v) - toCCi(y.v)) -- # typical value: symbol -, RRi, CC, CCi + is y:CCicell do toExpr(toCCi(x.v) - y.v) -- # typical value: symbol -, RRi, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,MinusS)) is x:CCcell do ( + when rhs + is y:ZZcell do toExpr(x.v - toRR(y.v,precision(x.v.re))) -- # typical value: symbol -, CC, ZZ, CC + is y:QQcell do toExpr(x.v - toRR(y.v,precision(x.v.re))) -- # typical value: symbol -, CC, QQ, CC + is y:RRcell do toExpr(x.v - y.v) -- # typical value: symbol -, CC, RR, CC + is y:RRicell do toExpr(toCCi(x.v) - toCCi(y.v)) -- # typical value: symbol -, CC, RRi, CCi + is y:CCcell do toExpr(x.v - y.v) -- # typical value: symbol -, CC, CC, CC + is y:CCicell do toExpr(toCCi(x.v) - y.v) -- # typical value: symbol -, CC, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,MinusS)) + is x:CCicell do ( when rhs - is y:ZZcell do toExpr(x.v - toRR(y.v,precision(x.v.re))) -- # typical value: symbol -, CC, ZZ, CC - is y:QQcell do toExpr(x.v - toRR(y.v,precision(x.v.re))) -- # typical value: symbol -, CC, QQ, CC - is y:RRcell do toExpr(x.v - y.v) -- # typical value: symbol -, CC, RR, CC - is y:CCcell do toExpr(x.v - y.v) -- # typical value: symbol -, CC, CC, CC + is y:ZZcell do toExpr(x.v - toCCi(y.v,precision(x.v.re))) -- # typical value: symbol -, CCi, ZZ, CCi + is y:QQcell do toExpr(x.v - toCCi(y.v,precision(x.v.re))) -- # typical value: symbol -, CCi, QQ, CCi + is y:RRcell do toExpr(x.v - toCCi(y.v)) -- # typical value: symbol -, CCi, RR, CCi + is y:RRicell do toExpr(x.v - toCCi(y.v)) -- # typical value: symbol -, CCi, RRi, CCi + is y:CCcell do toExpr(x.v - toCCi(y.v)) -- # typical value: symbol -, CCi, CC, CCi + is y:CCicell do toExpr(x.v - y.v) -- # typical value: symbol -, CCi, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,MinusS)) is x:RawMatrixCell do ( @@ -256,19 +292,21 @@ export (lhs:Expr) * (rhs:Expr) : Expr := ( is x:ZZcell do ( when rhs is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, ZZ, ZZ, ZZ - is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, ZZ, QQ, QQ - is y:RRcell do toExpr(toRR(x.v,precision(y.v)) * y.v) -- # typical value: symbol *, ZZ, RR, RR - is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, ZZ, RRi, RRi - is y:CCcell do toExpr(x.v * y.v) -- # typical value: symbol *, ZZ, CC, CC + is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, ZZ, QQ, QQ + is y:RRcell do toExpr(toRR(x.v,precision(y.v)) * y.v) -- # typical value: symbol *, ZZ, RR, RR + is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, ZZ, RRi, RRi + is y:CCcell do toExpr(x.v * y.v) -- # typical value: symbol *, ZZ, CC, CC + is y:CCicell do toExpr(x.v * y.v) -- # typical value: symbol *, ZZ, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,StarS)) is x:QQcell do ( when rhs is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, QQ, ZZ, QQ - is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, QQ, QQ, QQ - is y:RRcell do toExpr(y.v * x.v) -- # typical value: symbol *, QQ, RR, RR - is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, QQ, RRi, RRi - is y:CCcell do toExpr(y.v * toRR(x.v,precision(y.v.re))) -- # typical value: symbol *, QQ, CC, CC + is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, QQ, QQ, QQ + is y:RRcell do toExpr(y.v * x.v) -- # typical value: symbol *, QQ, RR, RR + is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, QQ, RRi, RRi + is y:CCcell do toExpr(y.v * toRR(x.v,precision(y.v.re))) -- # typical value: symbol *, QQ, CC, CC + is y:CCicell do toExpr(y.v * toRR(x.v,precision(y.v.re))) -- # typical value: symbol *, QQ, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,StarS)) is x:RawRingElementCell do ( @@ -293,28 +331,43 @@ export (lhs:Expr) * (rhs:Expr) : Expr := ( is x:RRcell do ( when rhs is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, ZZ, RR - is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, QQ, RR - is y:RRcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, RR, RR - is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, RR, RRi, RRi - is y:CCcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, CC, CC + is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, QQ, RR + is y:RRcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, RR, RR + is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, RR, RRi, RRi + is y:CCcell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, CC, CC + is y:CCicell do toExpr(x.v * y.v) -- # typical value: symbol *, RR, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,StarS)) + is x:RRicell do ( + when rhs + is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, ZZ, RRi + is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, QQ, RRi + is y:RRcell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, RR, RRi + is y:RRicell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, RRi, RRi + is y:CCcell do toExpr(toCCi(x.v) * y.v) -- # typical value: symbol *, RRi, CC, CCi + is y:CCicell do toExpr(toCCi(x.v) * y.v) -- # typical value: symbol *, RRi, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,StarS)) - is x:RRicell do ( - when rhs - is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, ZZ, RRi - is y:QQcell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, QQ, RRi - is y:RRcell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, RR, RRi - is y:RRicell do toExpr(x.v * y.v) -- # typical value: symbol *, RRi, RRi, RRi - is Error do rhs - else binarymethod(lhs,rhs,StarS)) - is x:CCcell do ( + is x:CCcell do ( when rhs - is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, CC, ZZ, CC - is y:QQcell do toExpr(x.v * toRR(y.v,precision(x.v.re))) -- # typical value: symbol *, CC, QQ, CC - is y:RRcell do toExpr(y.v * x.v) -- # typical value: symbol *, CC, RR, CC - is y:CCcell do toExpr(y.v * x.v) -- # typical value: symbol *, CC, CC, CC + is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, CC, ZZ, CC + is y:QQcell do toExpr(x.v * toRR(y.v,precision(x.v.re))) -- # typical value: symbol *, CC, QQ, CC + is y:RRcell do toExpr(y.v * x.v) -- # typical value: symbol *, CC, RR, CC + is y:RRicell do toExpr(toCCi(y.v) * x.v) -- # typical value: symbol *, CC, RRi, CCi + is y:CCcell do toExpr(y.v * x.v) -- # typical value: symbol *, CC, CC, CC + is y:CCicell do toExpr(y.v * x.v) -- # typical value: symbol *, CC, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,StarS)) + is x:CCicell do ( + when rhs + is y:ZZcell do toExpr(x.v * y.v) -- # typical value: symbol *, CCi, ZZ, CCi + is y:QQcell do toExpr(x.v * toRR(y.v,precision(x.v.re))) -- # typical value: symbol *, CCi, QQ, CCi + is y:RRcell do toExpr(y.v * x.v) -- # typical value: symbol *, CCi, RR, CCi + is y:RRicell do toExpr(y.v * x.v) -- # typical value: symbol *, CCi, RRi, CCi + is y:CCcell do toExpr(toCCi(y.v) * x.v) -- # typical value: symbol *, CCi, CC, CCi + is y:CCicell do toExpr(y.v * x.v) -- # typical value: symbol *, CCi, CCi, CCi + is Error do rhs + else binarymethod(lhs,rhs,StarS)) is x:RawMonomialCell do ( when rhs is y:RawMonomialCell do ( -- # typical value: symbol *, RawMonomialIdeal, RawMonomial, RawMonomial @@ -374,15 +427,17 @@ export (lhs:Expr) / (rhs:Expr) : Expr := ( if y.v === 0 then DivisionByZero() else toExpr(x.v / y.v)) - is y:QQcell do ( -- # typical value: symbol /, ZZ, QQ, QQ + is y:QQcell do ( -- # typical value: symbol /, ZZ, QQ, QQ if y.v === 0 then DivisionByZero() else toExpr(x.v / y.v)) - is y:RRcell do ( -- # typical value: symbol /, ZZ, RR, RR + is y:RRcell do ( -- # typical value: symbol /, ZZ, RR, RR toExpr(toRR(x.v,precision(y.v)) / y.v)) - is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, ZZ, RRi, RRi - is y:CCcell do ( -- # typical value: symbol /, ZZ, CC, CC + is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, ZZ, RRi, RRi + is y:CCcell do ( -- # typical value: symbol /, ZZ, CC, CC toExpr(x.v / y.v)) + is y:CCicell do ( -- # typical value: symbol /, ZZ, CCi, CCi + toExpr(toCCi((x.v*y.v.re)/(y.v.re^long(2)+y.v.im^long(2)),(-x.v*y.v.im)/(y.v.re^long(2)+y.v.im^long(2))))) is Error do rhs else binarymethod(lhs,rhs,DivideS)) is x:QQcell do ( @@ -391,16 +446,18 @@ export (lhs:Expr) / (rhs:Expr) : Expr := ( if y.v === 0 then DivisionByZero() else toExpr(x.v / y.v)) - is y:QQcell do ( -- # typical value: symbol /, QQ, QQ, QQ + is y:QQcell do ( -- # typical value: symbol /, QQ, QQ, QQ if y.v === 0 then DivisionByZero() else toExpr(x.v / y.v)) - is y:RRcell do ( -- # typical value: symbol /, QQ, RR, RR + is y:RRcell do ( -- # typical value: symbol /, QQ, RR, RR toExpr(toRR(x.v,precision(y.v)) / y.v)) - is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, QQ, RRi, RRi - is y:CCcell do ( -- # typical value: symbol /, QQ, CC, CC + is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, QQ, RRi, RRi + is y:CCcell do ( -- # typical value: symbol /, QQ, CC, CC if y.v === 0 then DivisionByZero() else toExpr(toRR(x.v,precision(y.v.re)) / y.v)) + is y:CCicell do ( -- # typical value: symbol /, QQ, CCi, CCi + toExpr(toCCi((y.v.re*x.v)/(y.v.re^long(2)+y.v.im^long(2)),(-y.v.im*x.v)/(y.v.re^long(2)+y.v.im^long(2))))) is Error do rhs else binarymethod(lhs,rhs,DivideS)) is x:RRcell do ( @@ -409,31 +466,35 @@ export (lhs:Expr) / (rhs:Expr) : Expr := ( if y.v === 0 then DivisionByZero() else toExpr(x.v / y.v)) - is y:QQcell do ( -- # typical value: symbol /, RR, QQ, RR + is y:QQcell do ( -- # typical value: symbol /, RR, QQ, RR if y.v === 0 then DivisionByZero() else toExpr(x.v / y.v)) - is y:RRcell do ( -- # typical value: symbol /, RR, RR, RR + is y:RRcell do ( -- # typical value: symbol /, RR, RR, RR toExpr(x.v / y.v)) - is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RR, RRi, RRi - is y:CCcell do ( -- # typical value: symbol /, RR, CC, CC + is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RR, RRi, RRi + is y:CCcell do ( -- # typical value: symbol /, RR, CC, CC toExpr(x.v / y.v)) + is y:CCicell do ( -- # typical value: symbol /, RR, CCi, CCi + toExpr(toCCi((y.v.re*x.v)/(y.v.re^long(2)+y.v.im^long(2)),(-y.v.im*x.v)/(y.v.re^long(2)+y.v.im^long(2))))) + is Error do rhs + else binarymethod(lhs,rhs,DivideS)) + is x:RRicell do ( + when rhs + is y:ZZcell do ( -- # typical value: symbol /, RRi, ZZ, RRi + if y.v === 0 + then DivisionByZero() + else toExpr(x.v / y.v)) + is y:QQcell do ( -- # typical value: symbol /, RRi, QQ, RRi + if y.v === 0 + then DivisionByZero() + else toExpr(x.v / y.v)) + is y:RRcell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RRi, RR, RRi + is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RRi, RRi, RRi + is y:CCcell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RRi, CC, CCi + is y:CCicell do (toExpr(toCCi((x.v*y.v.re)/(y.v.re^long(2)+y.v.im^long(2)),(-x.v*y.v.im)/(y.v.re^long(2)+y.v.im^long(2))))) -- # typical value: symbol /, RRi, CCi, CCi is Error do rhs else binarymethod(lhs,rhs,DivideS)) - is x:RRicell do ( - when rhs - is y:ZZcell do ( -- # typical value: symbol /, RRi, ZZ, RRi - if y.v === 0 - then DivisionByZero() - else toExpr(x.v / y.v)) - is y:QQcell do ( -- # typical value: symbol /, RRi, QQ, RRi - if y.v === 0 - then DivisionByZero() - else toExpr(x.v / y.v)) - is y:RRcell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RRi, RR, RRi - is y:RRicell do (toExpr(x.v / y.v)) -- # typical value: symbol /, RRi, RRi, RRi - is Error do rhs - else binarymethod(lhs,rhs,DivideS)) is x:CCcell do ( when rhs is y:ZZcell do ( -- # typical value: symbol /, CC, ZZ, CC @@ -441,15 +502,41 @@ export (lhs:Expr) / (rhs:Expr) : Expr := ( then DivisionByZero() else toExpr(x.v / toRR(y.v,precision(x.v.re))) ) - is y:QQcell do ( -- # typical value: symbol /, CC, QQ, CC + is y:QQcell do ( -- # typical value: symbol /, CC, QQ, CC if y.v === 0 then DivisionByZero() else toExpr(x.v / toRR(y.v,precision(x.v.re))) ) - is y:RRcell do ( -- # typical value: symbol /, CC, RR, CC + is y:RRcell do ( -- # typical value: symbol /, CC, RR, CC toExpr(x.v / y.v)) - is y:CCcell do ( -- # typical value: symbol /, CC, CC, CC + is y:RRicell do ( -- # typical value: symbol /, CC, RRi, CCi + toExpr(toCCi(x.v.re/y.v,x.v.im/y.v))) + is y:CCcell do ( -- # typical value: symbol /, CC, CC, CC toExpr(x.v / y.v)) + is y:CCicell do ( -- # typical value: symbol /, CC, CCi, CCi + toExpr(toCCi((y.v.re*x.v.re+y.v.im*x.v.im)/(y.v.re^long(2)+y.v.im^long(2)),(y.v.re*x.v.im-y.v.im*x.v.re)/(y.v.re^long(2)+y.v.im^long(2))))) + is Error do rhs + else binarymethod(lhs,rhs,DivideS)) + is x:CCicell do ( + when rhs + is y:ZZcell do ( -- # typical value: symbol /, CCi, ZZ, CCi + if y.v === 0 + then DivisionByZero() + else toExpr(toCCi(x.v.re / toRR(y.v,precision(x.v.re)),x.v.im / toRR(y.v,precision(x.v.re)))) + ) + is y:QQcell do ( -- # typical value: symbol /, CCi, QQ, CCi + if y.v === 0 + then DivisionByZero() + else toExpr(toCCi(x.v.re / toRR(y.v,precision(x.v.re)),x.v.im / toRR(y.v,precision(x.v.re)))) + ) + is y:RRcell do ( -- # typical value: symbol /, CCi, RR, CCi + toExpr(toCCi(x.v.re / y.v,x.v.im/y.v))) + is y:RRicell do ( -- # typical value: symbol /, CCi, RRi, CCi + toExpr(toCCi(x.v.re / y.v,x.v.im/y.v))) + is y:CCcell do ( -- # typical value: symbol /, CCi, CC, CCi + toExpr(x.v/y.v)) + is y:CCicell do ( -- # typical value: symbol /, CCi, CCi, CCi + toExpr(toCCi((y.v.re*x.v.re+y.v.im*x.v.im)/(y.v.re^long(2)+y.v.im^long(2)),(y.v.re*x.v.im-y.v.im*x.v.re)/(y.v.re^long(2)+y.v.im^long(2))))) is Error do rhs else binarymethod(lhs,rhs,DivideS)) is x:RawMonomialCell do ( @@ -635,7 +722,8 @@ export (lhs:Expr) ^ (rhs:Expr) : Expr := ( else toExpr(toRR(x.v,precision(y.v))^y.v) ) is y:CCcell do toExpr(toRR(x.v,precision(y.v))^y.v) - is Error do rhs + is y:CCicell do toExpr(toCCi(x.v,precision(y.v))^y.v) + is Error do rhs else binarymethod(lhs,rhs,PowerS)) is x:QQcell do ( when rhs @@ -668,12 +756,13 @@ export (lhs:Expr) ^ (rhs:Expr) : Expr := ( if isNegative(x.v) then toExpr(toCC(x.v,precision(y.v))^y.v) else toExpr(toRR(x.v,precision(y.v))^y.v)) - is y:RRicell do ( + is y:RRicell do ( if isNegative(x.v) then buildErrorPacket("negative base not implemented") else toExpr(toRR(x.v,precision(y.v))^y.v)) is y:CCcell do toExpr(toRR(x.v,precision(y.v))^y.v) - is Error do rhs + is y:CCicell do toExpr(toCCi(x.v,precision(y.v))^y.v) + is Error do rhs else binarymethod(lhs,rhs,PowerS)) is x:RRcell do ( when rhs @@ -695,17 +784,21 @@ export (lhs:Expr) ^ (rhs:Expr) : Expr := ( then toExpr(toCC(x.v)^y.v) else toExpr(x.v^y.v) ) - is y:RRicell do ( + is y:RRicell do ( if isNegative(x.v) then buildErrorPacket("negative base not implemented") else toExpr(x.v^y.v) ) is y:CCcell do toExpr(x.v^y.v) - is Error do rhs + is y:CCicell do toExpr(toCCi(x.v)^y.v) + is Error do rhs else binarymethod(lhs,rhs,PowerS)) is x:RRicell do ( when rhs - is y:ZZcell do toExpr(x.v^y.v) + is y:ZZcell do ( + if y.v < 0 then toExpr(1/(x.v^(-y.v))) + else toExpr(x.v^y.v) + ) is y:QQcell do ( d := denominator(y.v); if d === 1 then toExpr(x.v^numerator(y.v)) @@ -717,12 +810,14 @@ export (lhs:Expr) ^ (rhs:Expr) : Expr := ( then toExpr(x.v^y.v) else buildErrorPacket("negative base not implemented") ) - is y:RRicell do ( + is y:RRicell do ( if x.v >= 0 then toExpr(x.v^y.v) else buildErrorPacket("negative base not implemented") ) - else binarymethod(lhs,rhs,PowerS)) + is y:CCcell do toExpr(toCCi(x.v)^toCCi(y.v)) + is y:CCicell do toExpr(toCCi(x.v)^y.v) + else binarymethod(lhs,rhs,PowerS)) is x:CCcell do ( when rhs is y:ZZcell do toExpr(x.v^y.v) @@ -732,6 +827,17 @@ export (lhs:Expr) ^ (rhs:Expr) : Expr := ( else toExpr(x.v^toRR(y.v,precision(x.v)))) is y:RRcell do toExpr(x.v^y.v) is y:CCcell do toExpr(x.v^y.v) + is y:CCicell do toExpr(toCCi(x.v)^y.v) + is Error do rhs + else binarymethod(lhs,rhs,PowerS)) + is x:CCicell do ( + when rhs + is y:ZZcell do toExpr(x.v^y.v) + is y:QQcell do toExpr(x.v^toCCi(y.v,precision(x.v))) + is y:RRcell do toExpr(x.v^toCCi(y.v)) + is y:RRicell do toExpr(x.v^toCCi(y.v)) + is y:CCcell do toExpr(x.v^toCCi(y.v)) + is y:CCicell do toExpr(x.v^y.v) is Error do rhs else binarymethod(lhs,rhs,PowerS)) is x:RawRingElementCell do ( @@ -992,7 +1098,8 @@ installFun2(a:Expr,args:CodeSequence):Expr := ( when b is Error do b is bcd:Sequence do ( - if length(bcd) == 2 then ( + if length(bcd) == 0 then installMethod(a, eval(args.3)) + else if length(bcd) == 2 then ( when bcd.0 is bb:HashTable do when bcd.1 @@ -1018,7 +1125,7 @@ installFun2(a:Expr,args:CodeSequence):Expr := ( else buildErrorPacket("expected second parameter to be a hash table") else buildErrorPacket("expected first parameter to be a hash table") ) - else buildErrorPacket("expected 1, 2, 3, or 4 parameter types")) + else buildErrorPacket("expected between 0 and 4 parameter types")) is bb:HashTable do installMethod(a,bb,eval(args.3)) else buildErrorPacket("expected right hand parameter to be a hash table or sequence")) else buildErrorPacket("expected adjacency operator ' ' on left")) @@ -1230,6 +1337,7 @@ isFinite(e:Expr):Expr := ( is x:RRcell do toExpr(isfinite(x.v)) is x:RRicell do toExpr(isfinite(x.v)) is x:CCcell do toExpr(isfinite(x.v)) + is x:CCicell do toExpr(isfinite(x.v)) else WrongArg("a number") ); setupfun("isFinite0",isFinite); @@ -1242,6 +1350,7 @@ isANumber(e:Expr):Expr := ( is x:RRcell do toExpr(!isnan(x.v)) is x:RRicell do toExpr(!isnan(x.v)) is x:CCcell do toExpr(!isnan(x.v)) + is x:CCicell do toExpr(!isnan(x.v)) else WrongArg("a number") ); setupfun("isANumber",isANumber); @@ -1253,6 +1362,7 @@ isInfinite(e:Expr):Expr := ( is x:RRcell do toExpr(isinf(x.v)) is x:RRicell do toExpr(isinf(x.v)) is x:CCcell do toExpr(isinf(x.v)) + is x:CCicell do toExpr(isinf(x.v)) else WrongArg("a number") ); setupfun("isInfinite",isInfinite).Protected=false; diff --git a/M2/Macaulay2/d/actors2.dd b/M2/Macaulay2/d/actors2.dd index 0526d047265..5b816620abc 100644 --- a/M2/Macaulay2/d/actors2.dd +++ b/M2/Macaulay2/d/actors2.dd @@ -425,20 +425,25 @@ export setStdIO (f:file):void := ( stdIO = f; setGlobalVariable(stdioS, Ex export setStdError(f:file):void := ( stdError = f; setGlobalVariable(stderrS, Expr(stdError))); openfilesfun(e:Expr):Expr := ( - n := 0; - ff := openfiles; - while true do ( - when ff - is null do break - is f:FileCell do (n=n+1; ff=f.next;)); - v := new Sequence len n do ( - ff = openfiles; - while true do ( - when ff - is null do break - is f:FileCell do (provide f.file; ff=f.next;)); - ); - list(v)); + when e + is a:Sequence do ( + if length(a) == 0 then ( + n := 0; + ff := openfiles; + while true do ( + when ff + is null do break + is f:FileCell do (n=n+1; ff=f.next;)); + v := new Sequence len n do ( + ff = openfiles; + while true do ( + when ff + is null do break + is f:FileCell do (provide f.file; ff=f.next;)); + ); + list(v)) + else WrongNumArgs(0)) + else WrongNumArgs(0)); setupfun("openFiles",openfilesfun); openIn(filename:Expr):Expr := ( when filename @@ -704,6 +709,7 @@ exponent(e:Expr):Expr := ( is x:RRcell do toExpr(exponent(x.v)) -- # typical value: size2, RR, ZZ is x:RRicell do toExpr(exponent(x.v)) -- # typical value: size2, RRi, ZZ is z:CCcell do toExpr(exponent(z.v)) -- # typical value: size2, CC, ZZ + is z:CCicell do toExpr(exponent(z.v)) -- # typical value: size2, CCi, ZZ else WrongArg("a number")); setupfun("size2",exponent); @@ -713,6 +719,7 @@ realPart(e:Expr):Expr := ( is RRcell do e is RRicell do e is z:CCcell do toExpr(realPart(z.v)) + is z:CCicell do toExpr(realPart(z.v)) is QQcell do e else WrongArg("a number")); setupfun("realPart0",realPart); @@ -723,6 +730,7 @@ imaginaryPart(e:Expr):Expr := ( is RRcell do zeroE is RRicell do zeroE is z:CCcell do toExpr(imaginaryPart(z.v)) + is z:CCicell do toExpr(imaginaryPart(z.v)) is QQcell do zeroE else WrongArg("a number")); setupfun("imaginaryPart0",imaginaryPart); @@ -730,6 +738,7 @@ setupfun("imaginaryPart0",imaginaryPart); Foo := { foo:void }; -- make a new type of pointer that's innocuous and unusable numfinalizer := 0; finalizer(x:Foo,msg:string):void := ( + Ccode(void, "(void)", x); numfinalizer = numfinalizer + 1; stderr << "--finalization: (" << tostring(numfinalizer) << ")" << msg << endl ; ); diff --git a/M2/Macaulay2/d/actors3.d b/M2/Macaulay2/d/actors3.d index 9662babf6e5..58eb094a535 100644 --- a/M2/Macaulay2/d/actors3.d +++ b/M2/Macaulay2/d/actors3.d @@ -112,6 +112,7 @@ EqualEqualfun(x:Expr,y:Expr):Expr := ( is yy:RRcell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, ZZ, RR, Boolean is yy:RRicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, ZZ, RRi, Boolean is yy:CCcell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, ZZ, CC, Boolean + is yy:CCicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, ZZ, CCi, Boolean else equalmethod(x,y) ) is xx:SymbolClosure do ( @@ -125,6 +126,7 @@ EqualEqualfun(x:Expr,y:Expr):Expr := ( is yy:RRcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, QQ, RR, Boolean is yy:RRicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, QQ, RRi, Boolean is yy:CCcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, QQ, CC, Boolean + is yy:CCicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, QQ, CCi, Boolean else equalmethod(x,y) ) is xx:RRcell do ( @@ -134,6 +136,7 @@ EqualEqualfun(x:Expr,y:Expr):Expr := ( is yy:RRcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, RR, RR, Boolean is yy:RRicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, RR, RRi, Boolean is yy:CCcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, RR, CC, Boolean + is yy:CCicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, RR, CCi, Boolean else equalmethod(x,y) ) is xx:RRicell do ( @@ -141,6 +144,7 @@ EqualEqualfun(x:Expr,y:Expr):Expr := ( is yy:ZZcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, RRi, ZZ, Boolean is yy:QQcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, RRi, QQ, Boolean is yy:RRcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, RRi, RR, Boolean + is yy:CCicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, RRi, CCi, Boolean else buildErrorPacket(EngineError("equality not implemented"))) is xx:CCcell do ( when y @@ -148,6 +152,17 @@ EqualEqualfun(x:Expr,y:Expr):Expr := ( is yy:QQcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CC, QQ, Boolean is yy:RRcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CC, RR, Boolean is yy:CCcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CC, CC, Boolean + is yy:CCicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, CC, CCi, Boolean + else equalmethod(x,y) + ) + is xx:CCicell do ( + when y + is yy:ZZcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CCi, ZZ, Boolean + is yy:QQcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CCi, QQ, Boolean + is yy:RRcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CCi, RR, Boolean + is yy:RRicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, CCi, RRi, Boolean + is yy:CCcell do toExpr(xx.v === yy.v) -- # typical value: symbol ==, CCi, CC, Boolean + is yy:CCicell do toExpr(yy.v === xx.v) -- # typical value: symbol ==, CCi, CCi, Boolean else equalmethod(x,y) ) is xx:Boolean do ( @@ -728,156 +743,199 @@ setup(StarS,starfun,timesfun); sin(e:Expr):Expr := ( when e - is x:CCcell do toExpr(sin(x.v)) -- # typical value: sin, CC, CC - is x:RRcell do toExpr(sin(x.v)) -- # typical value: sin, RR, RR - is x:RRicell do toExpr(sin(x.v)) -- # typical value: sin, RRi, RRi + -- # typical value: sin, InexactNumber, InexactNumber + is x:CCcell do toExpr(sin(x.v)) + is x:CCicell do toExpr(sin(x.v)) + is x:RRcell do toExpr(sin(x.v)) + is x:RRicell do toExpr(sin(x.v)) else WrongArgRRorCC() ); setupfun("sin",sin).Protected=false; cos(e:Expr):Expr := ( when e - is x:CCcell do toExpr(cos(x.v)) -- # typical value: cos, CC, CC - is x:RRcell do toExpr(cos(x.v)) -- # typical value: cos, RR, RR - is x:RRicell do toExpr(cos(x.v)) -- # typical value: cos, RRi, RRi + -- # typical value: cos, InexactNumber, InexactNumber + is x:CCcell do toExpr(cos(x.v)) + is x:CCicell do toExpr(cos(x.v)) + is x:RRcell do toExpr(cos(x.v)) + is x:RRicell do toExpr(cos(x.v)) else WrongArgRRorCC() ); setupfun("cos",cos).Protected=false; tan(e:Expr):Expr := ( when e - is x:CCcell do toExpr(tan(x.v)) -- # typical value: tan, CC, CC - is x:RRcell do toExpr(tan(x.v)) -- # typical value: tan, RR, RR - is x:RRicell do toExpr(tan(x.v)) -- # typical value: tan, RRi, RRi + -- # typical value: tan, InexactNumber, InexactNumber + is x:CCcell do toExpr(tan(x.v)) + is x:CCicell do toExpr(tan(x.v)) + is x:RRcell do toExpr(tan(x.v)) + is x:RRicell do toExpr(tan(x.v)) else WrongArgRRorCC() ); setupfun("tan",tan).Protected=false; acos(e:Expr):Expr := ( when e - is x:CCcell do toExpr(acos(x.v)) -- # typical value: acos, CC, CC + -- # typical value: acos, InexactNumber, InexactNumber + is x:CCcell do toExpr(acos(x.v)) + is x:CCicell do toExpr(acos(x.v)) is x:RRcell do ( if x.v > 1 || x.v < -1 then toExpr(acos(toCC(x.v))) - else toExpr(acos(x.v)) -- # typical value: acos, RR, RR + else toExpr(acos(x.v)) ) is x:RRicell do ( if x.v <= 1 && x.v >= -1 - then toExpr(acos(x.v)) -- # typical value: acos, RRi, RRi - else buildErrorPacket("Must be between -1 and 1") + then toExpr(acos(x.v)) + else toExpr(acos(toCCi(x.v))) ) else WrongArgRRorCC() ); setupfun("acos",acos).Protected=false; sec(e:Expr):Expr := ( when e - is x:CCcell do toExpr(sec(x.v)) -- # typical value: sec, CC, CC - is x:RRcell do toExpr(sec(x.v)) -- # typical value: sec, RR, RR - is x:RRicell do toExpr(sec(x.v)) -- # typical value: sec, RRi, RRi + -- # typical value: sec, InexactNumber, InexactNumber + is x:CCcell do toExpr(sec(x.v)) + is x:CCicell do toExpr(sec(x.v)) + is x:RRcell do toExpr(sec(x.v)) + is x:RRicell do toExpr(sec(x.v)) else WrongArgRRorCC() ); setupfun("sec",sec).Protected=false; csc(e:Expr):Expr := ( when e - is x:CCcell do toExpr(csc(x.v)) -- # typical value: csc, CC, CC - is x:RRcell do toExpr(csc(x.v)) -- # typical value: csc, RR, RR - is x:RRicell do toExpr(csc(x.v)) -- # typical value: csc, RRi, RRi + -- # typical value: csc, InexactNumber, InexactNumber + is x:CCcell do toExpr(csc(x.v)) + is x:CCicell do toExpr(csc(x.v)) + is x:RRcell do toExpr(csc(x.v)) + is x:RRicell do toExpr(csc(x.v)) else WrongArgRRorCC() ); setupfun("csc",csc).Protected=false; cot(e:Expr):Expr := ( when e - is x:CCcell do toExpr(cot(x.v)) -- # typical value: cot, CC, CC - is x:RRcell do toExpr(cot(x.v)) -- # typical value: cot, RR, RR - is x:RRicell do toExpr(cot(x.v)) -- # typical value: cot, RRi, RRi + -- # typical value: cot, InexactNumber, InexactNumber + is x:CCcell do toExpr(cot(x.v)) + is x:CCicell do toExpr(cot(x.v)) + is x:RRcell do toExpr(cot(x.v)) + is x:RRicell do toExpr(cot(x.v)) else WrongArgRRorCC() ); setupfun("cot",cot).Protected=false; sech(e:Expr):Expr := ( when e - is x:CCcell do toExpr(sech(x.v)) -- # typical value: sech, CC, CC - is x:RRcell do toExpr(sech(x.v)) -- # typical value: sech, RR, RR - is x:RRicell do toExpr(sech(x.v)) -- # typical value: sech, RRi, RRi + -- # typical value: sech, InexactNumber, InexactNumber + is x:CCcell do toExpr(sech(x.v)) + is x:CCicell do toExpr(sech(x.v)) + is x:RRcell do toExpr(sech(x.v)) + is x:RRicell do toExpr(sech(x.v)) else WrongArgRRorCC() ); setupfun("sech",sech).Protected=false; csch(e:Expr):Expr := ( when e - is x:CCcell do toExpr(csch(x.v)) -- # typical value: csch, CC, CC - is x:RRcell do toExpr(csch(x.v)) -- # typical value: csch, RR, RR - is x:RRicell do toExpr(csch(x.v)) -- # typical value: csch, RRi, RRi + -- # typical value: csch, InexactNumber, InexactNumber + is x:CCcell do toExpr(csch(x.v)) + is x:CCicell do toExpr(csch(x.v)) + is x:RRcell do toExpr(csch(x.v)) + is x:RRicell do toExpr(csch(x.v)) else WrongArgRRorCC() ); setupfun("csch",csch).Protected=false; coth(e:Expr):Expr := ( when e - is x:CCcell do toExpr(coth(x.v)) -- # typical value: coth, CC, CC - is x:RRcell do toExpr(coth(x.v)) -- # typical value: coth, RR, RR - is x:RRicell do toExpr(coth(x.v)) -- # typical value: coth, RRi, RRi + -- # typical value: coth, InexactNumber, InexactNumber + is x:CCcell do toExpr(coth(x.v)) + is x:CCicell do toExpr(coth(x.v)) + is x:RRcell do toExpr(coth(x.v)) + is x:RRicell do toExpr(coth(x.v)) else WrongArgRRorCC() ); setupfun("coth",coth).Protected=false; asin(e:Expr):Expr := ( when e - is x:CCcell do toExpr(asin(x.v)) -- # typical value: asin, CC, CC + -- # typical value: asin, InexactNumber, InexactNumber + is x:CCcell do toExpr(asin(x.v)) + is x:CCicell do toExpr(asin(x.v)) is x:RRcell do ( if x.v > 1 || x.v < -1 then toExpr(asin(toCC(x.v))) - else toExpr(asin(x.v)) -- # typical value: asin, RR, RR + else toExpr(asin(x.v)) ) is x:RRicell do ( if x.v <= 1 && x.v >= -1 - then toExpr(asin(x.v)) -- # typical value: asin, RRi, RRi - else buildErrorPacket("Must be between -1 and 1") + then toExpr(asin(x.v)) + else toExpr(asin(toCCi(x.v))) ) else WrongArgRRorCC() ); setupfun("asin",asin).Protected=false; log1p(e:Expr):Expr := ( when e - is x:RRcell do toExpr(log1p(x.v)) -- # typical value: log1p, RR, RR - is x:RRicell do toExpr(log1p(x.v)) -- # typical value: log1p, RRi, RRi - is x:CCcell do toExpr(log(1 + x.v)) -- # typical value: log1p, CC, CC + -- # typical value: log1p, InexactNumber, InexactNumber + is x:RRcell do toExpr(log1p(x.v)) + is x:RRicell do toExpr(log1p(x.v)) + is x:CCcell do toExpr(log1p(x.v)) + is x:CCicell do toExpr(log1p(x.v)) else WrongArgRRorCC() ); setupfun("log1p",log1p).Protected=false; expm1(e:Expr):Expr := ( when e - is x:RRcell do toExpr(expm1(x.v)) -- # typical value: expm1, RR, RR - is x:RRicell do toExpr(expm1(x.v)) -- # typical value: expm1, RRi, RRi - is x:CCcell do toExpr(exp(x.v) - 1) -- # typical value: expm1, CC, CC + -- # typical value: expm1, InexactNumber, InexactNumber + is x:RRcell do toExpr(expm1(x.v)) + is x:RRicell do toExpr(expm1(x.v)) + is x:CCcell do toExpr(expm1(x.v)) + is x:CCicell do toExpr(expm1(x.v)) else WrongArgRRorCC() ); setupfun("expm1",expm1).Protected=false; eint(e:Expr):Expr := ( when e - is x:RRcell do toExpr(eint(x.v)) -- # typical value: eint, RR, RR - is x:RRicell do toExpr(eint(x.v)) -- # typical value: eint, RRi, RRi - is x:CCcell do toExpr(eint(x.v)) -- # typical value: eint, CC, CC + -- # typical value: eint, InexactNumber, InexactNumber + is x:RRcell do toExpr(eint(x.v)) + is x:RRicell do toExpr(eint(x.v)) + is x:CCcell do toExpr(eint(x.v)) + is x:CCicell do toExpr(eint(x.v)) else WrongArgRRorCC() ); setupfun("eint",eint).Protected=false; Gamma(e:Expr):Expr := ( when e - is x:RRcell do toExpr(Gamma(x.v)) -- # typical value: Gamma, RR, RR - is x:RRicell do toExpr(Gamma(x.v)) -- # typical value: Gamma, RRi, RRi - is x:CCcell do toExpr(Gamma(x.v)) -- # typical value: Gamma, CC, CC + -- # typical value: Gamma, InexactNumber, InexactNumber + is x:RRcell do toExpr(Gamma(x.v)) + is x:RRicell do toExpr(Gamma(x.v)) + is x:CCcell do toExpr(Gamma(x.v)) + is x:CCicell do toExpr(Gamma(x.v)) is a:Sequence do if length(a) != 2 then WrongNumArgs(1,2) else ( when a.0 + -- # typical value: Gamma, InexactNumber, InexactNumber, InexactNumber is s:RRcell do ( when a.1 - is x:RRcell do toExpr(Gamma( s.v , x.v)) -- # typical value: Gamma, RR, RR, RR - is x:RRicell do toExpr(Gamma(toRRi(s.v), x.v)) -- # typical value: Gamma, RR, RRi, RRi - is x:CCcell do toExpr(Gamma( toCC(s.v), x.v)) -- # typical value: Gamma, RR, CC, CC + is x:RRcell do toExpr(Gamma( s.v , x.v)) + is x:RRicell do toExpr(Gamma(toRRi(s.v), x.v)) + is x:CCcell do toExpr(Gamma( toCC(s.v), x.v)) + is x:CCicell do toExpr(Gamma(toCCi(s.v), x.v)) else WrongArgRRorCC(2)) is s:RRicell do ( when a.1 - is x:RRcell do toExpr(Gamma(s.v, toRRi(x.v))) -- # typical value: Gamma, RRi, RR, RRi - is x:RRicell do toExpr(Gamma(s.v, x.v )) -- # typical value: Gamma, RRi, RRi, RRi - else WrongArgRRorRRi(2)) + is x:RRcell do toExpr(Gamma( s.v, toRRi(x.v))) + is x:RRicell do toExpr(Gamma( s.v, x.v )) + is x:CCcell do toExpr(Gamma(toCCi(s.v),toCCi(x.v))) + is x:CCicell do toExpr(Gamma(toCCi(s.v), x.v)) + else WrongArgRRorCC(2)) is s:CCcell do ( when a.1 - is x:RRcell do toExpr(Gamma(s.v, toCC(x.v))) -- # typical value: Gamma, CC, RR, CC - is x:CCcell do toExpr(Gamma(s.v, x.v)) -- # typical value: Gamma, CC, CC, CC + is x:RRcell do toExpr(Gamma( s.v, toCC(x.v))) + is x:RRicell do toExpr(Gamma(toCCi(s.v),toCCi(x.v))) + is x:CCcell do toExpr(Gamma( s.v, x.v)) + is x:CCicell do toExpr(Gamma(toCCi(s.v), x.v)) + else WrongArgRRorCC(2)) + is s:CCicell do ( + when a.1 + is x:RRcell do toExpr(Gamma(s.v, toCCi(x.v))) + is x:RRicell do toExpr(Gamma(s.v, toCCi(x.v))) + is x:CCcell do toExpr(Gamma(s.v, toCCi(x.v))) + is x:CCicell do toExpr(Gamma(s.v, x.v)) else WrongArgRRorCC(2)) else WrongArgRRorCC(2)) else WrongArgRRorCC() @@ -889,31 +947,45 @@ regularizedGamma(e:Expr):Expr := ( if length(a) != 2 then WrongNumArgs(2) else ( when a.0 + -- # typical value: regularizedGamma, InexactNumber, InexactNumber, InexactNumber is s:RRcell do ( when a.1 - is x:RRcell do toExpr( - midpointRR(regularizedGamma(toRRi(s.v), toRRi(x.v)))) -- # typical value: regularizedGamma, RR, RR, RR - is x:RRicell do toExpr(regularizedGamma(toRRi(s.v), x.v)) -- # typical value: regularizedGamma, RR, RRi, RRi - is x:CCcell do toExpr(regularizedGamma(toCC(s.v), x.v)) -- # typical value: regularizedGamma, RR, CC, CC + is x:RRcell do toExpr(regularizedGamma(s.v, x.v)) + is x:RRicell do toExpr(regularizedGamma(toRRi(s.v), x.v)) + is x:CCcell do toExpr(regularizedGamma(toCC(s.v), x.v)) + is x:CCicell do toExpr(regularizedGamma(toCCi(s.v), x.v)) else WrongArgRRorCC(2)) is s:RRicell do ( when a.1 - is x:RRcell do toExpr(regularizedGamma(s.v, toRRi(x.v))) -- # typical value: regularizedGamma, RRi, RR, RRi - is x:RRicell do toExpr(regularizedGamma(s.v, x.v)) -- # typical value: regularizedGamma, RRi, RRi, RRi + is x:RRcell do toExpr(regularizedGamma(s.v, toRRi(x.v))) + is x:RRicell do toExpr(regularizedGamma(s.v, x.v)) + is x:CCcell do toExpr(regularizedGamma(toCCi(s.v), toCCi(x.v))) + is x:CCicell do toExpr(regularizedGamma(toCCi(s.v), x.v)) else WrongArgRRorRRi(2)) is s:CCcell do ( when a.1 - is x:RRcell do toExpr(regularizedGamma(s.v, toCC(x.v))) -- # typical value: regularizedGamma, CC, RR, CC - is x:CCcell do toExpr(regularizedGamma(s.v, x.v)) -- # typical value: regularizedGamma, CC, CC, CC + is x:RRcell do toExpr(regularizedGamma(s.v, toCC(x.v))) + is x:RRicell do toExpr(regularizedGamma(toCCi(s.v), toCCi(x.v))) + is x:CCcell do toExpr(regularizedGamma(s.v, x.v)) + is x:CCicell do toExpr(regularizedGamma(toCCi(s.v), x.v)) + else WrongArgRRorCC(2)) + is s:CCicell do ( + when a.1 + is x:RRcell do toExpr(regularizedGamma(s.v, toCCi(x.v))) + is x:RRicell do toExpr(regularizedGamma(s.v, toCCi(x.v))) + is x:CCcell do toExpr(regularizedGamma(s.v, toCCi(x.v))) + is x:CCicell do toExpr(regularizedGamma(s.v, x.v)) else WrongArgRRorCC(2)) else WrongArgRRorCC(2))) else WrongNumArgs(2)); setupfun("regularizedGamma",regularizedGamma).Protected=false; Digamma(e:Expr):Expr := ( when e - is x:RRcell do toExpr(Digamma(x.v)) -- # typical value: Digamma, RR, RR - is x:RRicell do toExpr(Digamma(x.v)) -- # typical value: Digamma, RRi, RRi - is x:CCcell do toExpr(Digamma(x.v)) -- # typical value: Digamma, CC, CC + -- # typical value: Digamma, InexactNumber, InexactNumber + is x:RRcell do toExpr(Digamma(x.v)) + is x:RRicell do toExpr(Digamma(x.v)) + is x:CCcell do toExpr(Digamma(x.v)) + is x:CCicell do toExpr(Digamma(x.v)) else WrongArgRRorCC() ); setupfun("Digamma",Digamma).Protected=false; @@ -924,40 +996,49 @@ export lgamma(x:RR):Expr := ( Expr(Sequence(toExpr(moveToRR(z)),toExpr(i)))); lgamma(e:Expr):Expr := ( when e - is x:RRcell do lgamma(x.v) -- # typical value: lgamma, RR, RR - is x:RRicell do toExpr(lgamma(x.v)) -- # typical value: lgamma, RRi, RRi - is x:CCcell do toExpr(lgamma(x.v)) -- # typical value: lgamma, CC, CC + -- # typical value: lgamma, InexactNumber, InexactNumber + is x:RRcell do lgamma(x.v) + is x:RRicell do toExpr(lgamma(x.v)) + is x:CCcell do toExpr(lgamma(x.v)) + is x:CCicell do toExpr(lgamma(x.v)) else WrongArgRRorCC() ); setupfun("lgamma",lgamma); zeta(e:Expr):Expr := ( when e - is x:RRcell do toExpr(zeta(x.v)) -- # typical value: zeta, RR, RR - is x:RRicell do toExpr(zeta(x.v)) -- # typical value: zeta, RRi, RRi - is x:CCcell do toExpr(zeta(x.v)) -- # typical value: zeta, CC, CC + -- # typical value: zeta, InexactNumber, InexactNumber + is x:RRcell do toExpr(zeta(x.v)) + is x:RRicell do toExpr(zeta(x.v)) + is x:CCcell do toExpr(zeta(x.v)) + is x:CCicell do toExpr(zeta(x.v)) else WrongArgRRorCC() ); setupfun("zeta",zeta).Protected=false; erf(e:Expr):Expr := ( when e - is x:RRcell do toExpr(erf(x.v)) -- # typical value: erf, RR, RR - is x:RRicell do toExpr(erf(x.v)) -- # typical value: erf, RRi, RRi - is x:CCcell do toExpr(erf(x.v)) -- # typical value: erf, CC, CC + -- # typical value: erf, InexactNumber, InexactNumber + is x:RRcell do toExpr(erf(x.v)) + is x:RRicell do toExpr(erf(x.v)) + is x:CCcell do toExpr(erf(x.v)) + is x:CCicell do toExpr(erf(x.v)) else WrongArgRRorCC() ); setupfun("erf",erf).Protected=false; erfc(e:Expr):Expr := ( when e - is x:RRcell do toExpr(erfc(x.v)) -- # typical value: erfc, RR, RR - is x:RRicell do toExpr(erfc(x.v)) -- # typical value: erfc, RRi, RRi - is x:CCcell do toExpr(erfc(x.v)) -- # typical value: erfc, CC, CC + -- # typical value: erfc, InexactNumber, InexactNumber + is x:RRcell do toExpr(erfc(x.v)) + is x:RRicell do toExpr(erfc(x.v)) + is x:CCcell do toExpr(erfc(x.v)) + is x:CCicell do toExpr(erfc(x.v)) else WrongArgRRorCC() ); setupfun("erfc",erfc).Protected=false; inverseErf(e:Expr):Expr := ( when e - is x:RRcell do toExpr(midpointRR(inverseErf(toRRi(x.v)))) -- # typical value: inverseErf, RR, RR - is x:RRicell do toExpr(inverseErf(x.v)) -- # typical value: inverseErf, RRi, RRi + -- # typical value: inverseErf, InexactNumber, InexactNumber + is x:RRcell do toExpr(inverseErf(x.v)) + is x:RRicell do toExpr(inverseErf(x.v)) else WrongArgRRorRRi()); setupfun("inverseErf",inverseErf).Protected=false; BesselJ(n:long,x:RR):RR := ( @@ -973,30 +1054,42 @@ BesselJ(e:Expr):Expr := ( is x:RRcell do toExpr(BesselJ(toLong(n.v), x.v)) is x:RRicell do toExpr(BesselJ(toRRi(n.v,precision(x.v)),x.v)) is x:CCcell do toExpr(BesselJ(toCC(n.v), x.v)) + is x:CCicell do toExpr(BesselJ(toCCi(n.v), x.v)) else WrongArgRRorCC(2)) else ( when s.1 - is x:RRcell do toExpr( - midpointRR(BesselJ(toRRi(n.v), toRRi(x.v)))) + is x:RRcell do toExpr(BesselJ(toRR(n.v), x.v)) is x:RRicell do toExpr(BesselJ(toRRi(n.v,precision(x.v)),x.v)) is x:CCcell do toExpr(BesselJ(toCC(n.v), x.v )) + is x:CCicell do toExpr(BesselJ(toCCi(n.v), x.v )) else WrongArgRRorCC(2))) is n:RRcell do ( when s.1 - is x:RRcell do toExpr( - midpointRR(BesselJ(toRRi(n.v), toRRi(x.v)))) + is x:RRcell do toExpr(BesselJ(n.v, x.v)) is x:RRicell do toExpr(BesselJ(toRRi(n.v), x.v)) is x:CCcell do toExpr(BesselJ(toCC(n.v), x.v )) + is x:CCicell do toExpr(BesselJ(toCCi(n.v), x.v )) else WrongArgRRorCC(2)) is n:RRicell do ( when s.1 is x:RRcell do toExpr(BesselJ(n.v, toRRi(x.v))) is x:RRicell do toExpr(BesselJ(n.v, x.v)) - else WrongArgRRorRRi(2)) + is x:CCcell do toExpr(BesselJ(toCCi(n.v), toCCi(x.v))) + is x:CCicell do toExpr(BesselJ(toCCi(n.v), x.v)) + else WrongArgRRorCC(2)) is n:CCcell do ( when s.1 is x:RRcell do toExpr(BesselJ(n.v, toCC(x.v))) + is x:RRicell do toExpr(BesselJ(toCCi(n.v), toCCi(x.v))) is x:CCcell do toExpr(BesselJ(n.v, x.v )) + is x:CCicell do toExpr(BesselJ(toCCi(n.v), x.v )) + else WrongArgRRorCC(2)) + is n:CCicell do ( + when s.1 + is x:RRcell do toExpr(BesselJ(n.v, toCCi(x.v))) + is x:RRicell do toExpr(BesselJ(n.v, toCCi(x.v))) + is x:CCcell do toExpr(BesselJ(n.v, toCCi(x.v))) + is x:CCicell do toExpr(BesselJ(n.v, x.v)) else WrongArgRRorCC(2)) else WrongArg(1, "an integer, real number or interval, or complex number")) else WrongNumArgs(2)); @@ -1014,53 +1107,68 @@ BesselY(e:Expr):Expr := ( is x:RRcell do toExpr(BesselY(toLong(n.v), x.v)) is x:RRicell do toExpr(BesselY(toRRi(n.v,precision(x.v)),x.v)) is x:CCcell do toExpr(BesselY(toCC(n.v), x.v)) + is x:CCicell do toExpr(BesselY(toCCi(n.v), x.v)) else WrongArgRRorCC(2)) else ( when s.1 - is x:RRcell do toExpr( - midpointRR(BesselY(toRRi(n.v), toRRi(x.v)))) + is x:RRcell do toExpr(BesselY(toRR(n.v), x.v)) is x:RRicell do toExpr(BesselY(toRRi(n.v,precision(x.v)),x.v)) is x:CCcell do toExpr(BesselY(toCC(n.v), x.v )) + is x:CCicell do toExpr(BesselY(toCCi(n.v), x.v)) else WrongArgRRorCC(2))) is n:RRcell do ( when s.1 - is x:RRcell do toExpr( - midpointRR(BesselY(toRRi(n.v), toRRi(x.v)))) + is x:RRcell do toExpr(BesselY(n.v, x.v)) is x:RRicell do toExpr(BesselY(toRRi(n.v), x.v)) is x:CCcell do toExpr(BesselY(toCC(n.v), x.v )) + is x:CCicell do toExpr(BesselY(toCCi(n.v), x.v)) else WrongArgRRorCC(2)) is n:RRicell do ( when s.1 is x:RRcell do toExpr(BesselY(n.v, toRRi(x.v))) is x:RRicell do toExpr(BesselY(n.v, x.v)) + is x:CCcell do toExpr(BesselY(toCCi(n.v), toCCi(x.v))) + is x:CCicell do toExpr(BesselY(toCCi(n.v), x.v)) else WrongArgRRorRRi(2)) is n:CCcell do ( when s.1 is x:RRcell do toExpr(BesselY(n.v, toCC(x.v))) + is x:RRicell do toExpr(BesselY(toCCi(n.v), toCCi(x.v))) is x:CCcell do toExpr(BesselY(n.v, x.v )) + is x:CCicell do toExpr(BesselY(toCCi(n.v), x.v)) + else WrongArgRRorCC(2)) + is n:CCicell do ( + when s.1 + is x:RRcell do toExpr(BesselY(n.v, toCCi(x.v))) + is x:RRicell do toExpr(BesselY(n.v, toCCi(x.v))) + is x:CCcell do toExpr(BesselY(n.v, toCCi(x.v))) + is x:CCicell do toExpr(BesselY(n.v, x.v)) else WrongArgRRorCC(2)) else WrongArg(1, "an integer, real number or interval, or complex number")) else WrongNumArgs(2)); setupfun("BesselY",BesselY).Protected=false; atan2(yy:Expr,xx:Expr):Expr := ( when yy + -- # typical value: atan2, InexactNumber, InexactNumber, InexactNumber is y:RRcell do ( when xx - is x:RRcell do toExpr(atan2(y.v,x.v)) -- # typical value: atan2, RR, RR, RR - is x:RRicell do toExpr(atan2(toRRi(y.v),x.v)) -- # typical value: atan2, RR, RRi, RRi + is x:RRcell do toExpr(atan2(y.v,x.v)) + is x:RRicell do toExpr(atan2(toRRi(y.v),x.v)) else WrongArgRRorRRi(1)) is y:RRicell do ( when xx - is x:RRcell do toExpr(atan2(y.v,toRRi(x.v))) -- # typical value: atan2, RRi, RR, RRi - is x:RRicell do toExpr(atan2(y.v,x.v)) -- # typical value: atan2, RRi, RRi, RRi + is x:RRcell do toExpr(atan2(y.v,toRRi(x.v))) + is x:RRicell do toExpr(atan2(y.v,x.v)) else WrongArgRRorRRi(1)) else WrongArgRRorRRi(2) ); atan(e:Expr):Expr := ( when e - is x:CCcell do toExpr(atan(x.v)) -- # typical value: atan, CC, CC - is x:RRcell do toExpr(atan(x.v)) -- # typical value: atan, RR, RR - is x:RRicell do toExpr(atan(x.v)) -- # typical value: atan, RRi, RRi + -- # typical value: atan, InexactNumber, InexactNumber + is x:CCcell do toExpr(atan(x.v)) + is x:CCicell do toExpr(atan(x.v)) + is x:RRcell do toExpr(atan(x.v)) + is x:RRicell do toExpr(atan(x.v)) else WrongArgRRorCC() ); setupfun("atan",atan).Protected=false; @@ -1072,21 +1180,34 @@ setupfun("atan2",atan2).Protected=false; Beta(yy:Expr,xx:Expr):Expr := ( when yy + -- # typical value: Beta, InexactNumber, InexactNumber, InexactNumber is y:RRcell do ( when xx - is x:RRcell do toExpr(Beta(y.v, x.v)) -- # typical value: Beta, RR, RR, RR - is x:RRicell do toExpr(Beta(toRRi(y.v), x.v)) -- # typical value: Beta, RR, RRi, RRi - is x:CCcell do toExpr(Beta(toCC(y.v), x.v)) -- # typical value: Beta, RR, CC, CC + is x:RRcell do toExpr(Beta(y.v, x.v)) + is x:RRicell do toExpr(Beta(toRRi(y.v), x.v)) + is x:CCcell do toExpr(Beta(toCC(y.v), x.v)) + is x:CCicell do toExpr(Beta(toCCi(y.v), x.v)) else WrongArgRRorCC(2)) is y:RRicell do ( when xx - is x:RRcell do toExpr(Beta(y.v, toRRi(x.v))) -- # typical value: Beta, RRi, RR, RRi - is x:RRicell do toExpr(Beta(y.v, x.v)) -- # typical value: Beta, RRi, RRi, RRi + is x:RRcell do toExpr(Beta(y.v, toRRi(x.v))) + is x:RRicell do toExpr(Beta(y.v, x.v)) + is x:CCcell do toExpr(Beta(toCCi(y.v), toCCi(x.v))) + is x:CCicell do toExpr(Beta(toCCi(y.v), x.v)) else WrongArgRRorRRi(2)) is y:CCcell do ( when xx - is x:RRcell do toExpr(Beta(y.v, toCC(x.v))) -- # typical value: Beta, CC, RR, CC - is x:CCcell do toExpr(Beta(y.v, x.v)) -- # typical value: Beta, CC, CC, CC + is x:RRcell do toExpr(Beta(y.v, toCC(x.v))) + is x:RRicell do toExpr(Beta(toCCi(y.v), toCCi(x.v))) + is x:CCcell do toExpr(Beta(y.v, x.v)) + is x:CCicell do toExpr(Beta(toCCi(y.v), x.v)) + else WrongArgRRorCC(2)) + is y:CCicell do ( + when xx + is x:RRcell do toExpr(Beta(y.v, toCCi(x.v))) + is x:RRicell do toExpr(Beta(y.v, toCCi(x.v))) + is x:CCcell do toExpr(Beta(y.v, toCCi(x.v))) + is x:CCicell do toExpr(Beta(y.v, x.v)) else WrongArgRRorCC(2)) else WrongArgRRorCC(1) ); @@ -1100,48 +1221,127 @@ regularizedBeta(xx:Expr,yy:Expr,zz:Expr):Expr := ( when xx is x:RRcell do ( when yy + -- # typical value: regularizedBeta, InexactNumber, InexactNumber, InexactNumber, InexactNumber is y:RRcell do ( when zz - is z:RRcell do toExpr( - midpointRR(regularizedBeta(toRRi(x.v), toRRi(y.v), toRRi(z.v)))) -- # typical value: regularizedBeta, RR, RR, RR, RR - is z:RRicell do toExpr(regularizedBeta(toRRi(x.v), toRRi(y.v), z.v)) -- # typical value: regularizedBeta, RR, RR, RRi, RRi - is z:CCcell do toExpr(regularizedBeta(toCC(x.v), toCC(y.v), z.v)) -- # typical value: regularizedBeta, RR, RR, CC, CC + is z:RRcell do toExpr(regularizedBeta(x.v, y.v, z.v)) + is z:RRicell do toExpr(regularizedBeta(toRRi(x.v), toRRi(y.v), z.v)) + is z:CCcell do toExpr(regularizedBeta(toCC(x.v), toCC(y.v), z.v)) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) else WrongArgRRorCC(3)) is y:RRicell do ( when zz - is z:RRcell do toExpr(regularizedBeta(toRRi(x.v), y.v, toRRi(z.v))) -- # typical value: regularizedBeta, RR, RRi, RR, RRi - is z:RRicell do toExpr(regularizedBeta(toRRi(x.v), y.v, z.v)) -- # typical value: regularizedBeta, RR, RRi, RRi, RRi + is z:RRcell do toExpr(regularizedBeta(toRRi(x.v), y.v, toRRi(z.v))) + is z:RRicell do toExpr(regularizedBeta(toRRi(x.v), y.v, z.v)) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) else WrongArgRRorRRi(3)) is y:CCcell do ( when zz - is z:RRcell do toExpr(regularizedBeta(toCC(x.v), y.v, toCC(z.v))) -- # typical value: regularizedBeta, RR, CC, RR, CC - is z:CCcell do toExpr(regularizedBeta(toCC(x.v), y.v, z.v)) -- # typical value: regularizedBeta, RR, CC, CC, CC + is z:RRcell do toExpr(regularizedBeta(toCC(x.v), y.v, toCC(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(toCC(x.v), y.v, z.v)) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:CCicell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), y.v, z.v)) else WrongArgRRorCC(3)) else WrongArgRRorCC(2)) is x:RRicell do ( when yy is y:RRcell do ( when zz - is z:RRcell do toExpr(regularizedBeta(x.v, toRRi(y.v), toRRi(z.v))) -- # typical value: regularizedBeta, RRi, RR, RR, RRi - is z:RRicell do toExpr(regularizedBeta(x.v, toRRi(y.v), z.v)) -- # typical value: regularizedBeta, RRi, RR, RRi, RRi - else WrongArgRRorRRi(3)) + is z:RRcell do toExpr(regularizedBeta(x.v, toRRi(y.v), toRRi(z.v))) + is z:RRicell do toExpr(regularizedBeta(x.v, toRRi(y.v), z.v)) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) is y:RRicell do ( when zz - is z:RRcell do toExpr(regularizedBeta((x.v), y.v, toRRi(z.v))) -- # typical value: regularizedBeta, RRi, RRi, RR, RRi - is z:RRicell do toExpr(regularizedBeta(x.v, y.v, z.v)) -- # typical value: regularizedBeta, RRi, RRi, RRi, RRi - else WrongArgRRorRRi(3)) - else WrongArgRRorRRi(2)) + is z:RRcell do toExpr(regularizedBeta((x.v), y.v, toRRi(z.v))) + is z:RRicell do toExpr(regularizedBeta(x.v, y.v, z.v)) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:CCcell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:CCicell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), y.v, z.v)) + else WrongArgRRorCC(3)) + else WrongArgRRorCC(2)) is x:CCcell do ( when yy is y:RRcell do ( when zz - is z:RRcell do toExpr(regularizedBeta(x.v, toCC(y.v), toCC(z.v))) -- # typical value: regularizedBeta, CC, RR, RR, CC - is z:CCcell do toExpr(regularizedBeta(x.v, toCC(y.v), z.v)) -- # typical value: regularizedBeta, CC, RR, CC, CC + is z:RRcell do toExpr(regularizedBeta(x.v, toCC(y.v), toCC(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(x.v, toCC(y.v), z.v)) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:RRicell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:CCcell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(x.v, y.v, toCC(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(x.v, y.v, z.v)) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:CCicell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(toCCi(x.v), y.v, toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(toCCi(x.v), y.v, z.v)) + else WrongArgRRorCC(3)) + else WrongArgRRorCC(2)) + is x:CCicell do ( + when yy + is y:RRcell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(x.v, toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:RRicell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(x.v, toCCi(y.v), z.v)) else WrongArgRRorCC(3)) is y:CCcell do ( when zz - is z:RRcell do toExpr(regularizedBeta(x.v, y.v, toCC(z.v))) -- # typical value: regularizedBeta, CC, CC, RR, CC - is z:CCcell do toExpr(regularizedBeta(x.v, y.v, z.v)) -- # typical value: regularizedBeta, CC, CC, CC, CC + is z:RRcell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(x.v, toCCi(y.v), toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(x.v, toCCi(y.v), z.v)) + else WrongArgRRorCC(3)) + is y:CCicell do ( + when zz + is z:RRcell do toExpr(regularizedBeta(x.v, y.v, toCCi(z.v))) + is z:RRicell do toExpr(regularizedBeta(x.v, y.v, toCCi(z.v))) + is z:CCcell do toExpr(regularizedBeta(x.v, y.v, toCCi(z.v))) + is z:CCicell do toExpr(regularizedBeta(x.v, y.v, z.v)) else WrongArgRRorCC(3)) else WrongArgRRorCC(2)) else WrongArgRRorCC(1)); @@ -1151,35 +1351,90 @@ regularizedBeta(e:Expr):Expr := ( else WrongNumArgs(3)); setupfun("regularizedBeta",regularizedBeta).Protected=false; +polylog(e:Expr):Expr := ( + when e + is a:Sequence do ( + if length(a) == 2 then ( + when a.0 + -- # typical value: polylog, InexactNumber, InexactNumber, InexactNumber + is x:RRcell do ( + when a.1 + is y:RRcell do ( + if y.v < 1 then toExpr(polylog(x.v, y.v)) + else toExpr(polylog(toCC(x.v), toCC(y.v)))) + is y:RRicell do ( + if y.v < 1 then toExpr(polylog(toRRi(x.v), y.v)) + else toExpr(polylog(toCCi(x.v), toCCi(y.v)))) + is y:CCcell do toExpr(polylog(toCC(x.v), y.v)) + is y:CCicell do toExpr(polylog(toCCi(x.v), y.v)) + else WrongArgRRorCC(2)) + is x:RRicell do ( + when a.1 + is y:RRcell do ( + if y.v < 1 then toExpr(polylog(x.v, toRRi(y.v))) + else toExpr(polylog(toCCi(x.v), toCCi(y.v)))) + is y:RRicell do ( + if y.v < 1 then toExpr(polylog(x.v, y.v)) + else toExpr(polylog(toCCi(x.v), toCCi(y.v)))) + is y:CCcell do toExpr(polylog(toCCi(x.v), toCCi(y.v))) + is y:CCicell do toExpr(polylog(toCCi(x.v), y.v)) + else WrongArgRRorCC(2)) + is x:CCcell do ( + when a.1 + is y:RRcell do toExpr(polylog(x.v, toCC(y.v))) + is y:RRicell do toExpr(polylog(toCCi(x.v), toCCi(y.v))) + is y:CCcell do toExpr(polylog(x.v, y.v)) + is y:CCicell do toExpr(polylog(toCCi(x.v), y.v)) + else WrongArgRRorCC(2)) + is x:CCicell do ( + when a.1 + is y:RRcell do toExpr(polylog(x.v, toCCi(y.v))) + is y:RRicell do toExpr(polylog(x.v, toCCi(y.v))) + is y:CCcell do toExpr(polylog(x.v, toCCi(y.v))) + is y:CCicell do toExpr(polylog(x.v, y.v)) + else WrongArgRRorCC(2)) + else WrongArgRRorCC(1)) + else WrongNumArgs(2)) + else WrongNumArgs(2)); +setupfun("polylog", polylog).Protected=false; + cosh(e:Expr):Expr := ( when e - is x:CCcell do toExpr(cosh(x.v)) -- # typical value: cosh, CC, CC - is x:RRcell do toExpr(cosh(x.v)) -- # typical value: cosh, RR, RR - is x:RRicell do toExpr(cosh(x.v)) -- # typical value: cosh, RRi, RRi + -- # typical value: cosh, InexactNumber, InexactNumber + is x:CCcell do toExpr(cosh(x.v)) + is x:CCicell do toExpr(cosh(x.v)) + is x:RRcell do toExpr(cosh(x.v)) + is x:RRicell do toExpr(cosh(x.v)) else WrongArgRRorCC() ); setupfun("cosh",cosh).Protected=false; sinh(e:Expr):Expr := ( when e - is x:CCcell do toExpr(sinh(x.v)) -- # typical value: sinh, CC, CC - is x:RRcell do toExpr(sinh(x.v)) -- # typical value: sinh, RR, RR - is x:RRicell do toExpr(sinh(x.v)) -- # typical value: sinh, RRi, RRi + -- # typical value: sinh, InexactNumber, InexactNumber + is x:CCcell do toExpr(sinh(x.v)) + is x:CCicell do toExpr(sinh(x.v)) + is x:RRcell do toExpr(sinh(x.v)) + is x:RRicell do toExpr(sinh(x.v)) else WrongArgRRorCC() ); setupfun("sinh",sinh).Protected=false; tanh(e:Expr):Expr := ( when e - is x:CCcell do toExpr(tanh(x.v)) -- # typical value: tanh, CC, CC - is x:RRcell do toExpr(tanh(x.v)) -- # typical value: tanh, RR, RR - is x:RRicell do toExpr(tanh(x.v)) -- # typical value: tanh, RRi, RRi + -- # typical value: tanh, InexactNumber, InexactNumber + is x:CCcell do toExpr(tanh(x.v)) + is x:CCicell do toExpr(tanh(x.v)) + is x:RRcell do toExpr(tanh(x.v)) + is x:RRicell do toExpr(tanh(x.v)) else WrongArgRRorCC() ); setupfun("tanh",tanh).Protected=false; exp(e:Expr):Expr := ( when e - is x:CCcell do toExpr(exp(x.v)) -- # typical value: exp, CC, CC - is x:RRcell do toExpr(exp(x.v)) -- # typical value: exp, RR, RR - is x:RRicell do toExpr(exp(x.v)) -- # typical value: exp, RRi, RRi + -- # typical value: exp, InexactNumber, InexactNumber + is x:CCcell do toExpr(exp(x.v)) + is x:CCicell do toExpr(exp(x.v)) + is x:RRcell do toExpr(exp(x.v)) + is x:RRicell do toExpr(exp(x.v)) else WrongArgRRorCC() ); setupfun("exp",exp).Protected=false; @@ -1187,37 +1442,55 @@ log(e:Expr):Expr := ( when e is a:Sequence do if length(a) != 2 then WrongNumArgs(1,2) else ( + -- # typical value: log, InexactNumber, InexactNumber, InexactNumber when a.0 is b:RRcell do ( when a.1 - is x:CCcell do toExpr(log(b.v,x.v)) -- # typical value: log, RR, CC, CC - is x:RRcell do ( -- # typical value: log, RR, RR, CC - if b.v>0 && x.v>0 then toExpr(log(b.v,x.v)) else toExpr(logc(b.v,x.v)) + is x:CCcell do toExpr(log(b.v,x.v)) + is x:CCicell do toExpr(log(toCCi(b.v), x.v)) + is x:RRcell do ( + if b.v>0 && x.v>0 then toExpr(log(b.v,x.v)) else toExpr(logc(b.v,x.v)) ) - is x:RRicell do ( -- # typical value: log, RR, RRi, RRi - if b.v>0 && x.v>=0 then toExpr(log(toRRi(b.v,precision(b.v)),x.v)) - else - buildErrorPacket("Not defined") + is x:RRicell do ( + if b.v>0 && x.v>=0 then toExpr(log(toRRi(b.v,precision(b.v)),x.v)) + else toExpr(log(toCCi(b.v), toCCi(x.v))) ) else WrongArgRRorCC(1)) - is b:RRicell do ( + is b:RRicell do ( when a.1 - is x:RRcell do ( -- # typical value: log, RRi, RR, RRi - if b.v>0 && x.v>=0 then toExpr(log(b.v,toRRi(x.v,precision(x.v)))) - else - buildErrorPacket("Not defined") - ) - is x:RRicell do ( -- # typical value: log, RRi, RRi, RRi - if b.v>0 && x.v>=0 then toExpr(log(b.v,x.v)) - else - buildErrorPacket("Not defined") + is x:CCcell do toExpr(log(toCCi(b.v), toCCi(x.v))) + is x:CCicell do toExpr(log(toCCi(b.v), x.v)) + is x:RRcell do ( + if b.v>0 && x.v>=0 then toExpr(log(b.v,toRRi(x.v,precision(x.v)))) + else toExpr(log(toCCi(b.v), toCCi(x.v))) + ) + is x:RRicell do ( + if b.v>0 && x.v>=0 then toExpr(log(b.v,x.v)) + else toExpr(log(toCCi(b.v), toCCi(x.v))) ) else WrongArgRRorRRi(2)) - else WrongArgRRorRRi(1)) - is x:CCcell do toExpr(log(x.v)) -- # typical value: log, CC, CC - is x:RRcell do if isNegative(x.v) then toExpr(logc(x.v)) else toExpr(log(x.v)) -- # typical value: log, RR, RR - is x:RRicell do if x.v >= 0 then toExpr(log(x.v)) -- # typical value: log, RRi, RRi - else buildErrorPacket("Not defined") + is b:CCcell do ( + when a.1 + is x:CCcell do toExpr(log(b.v, x.v)) + is x:CCicell do toExpr(log(toCCi(b.v), x.v)) + is x:RRcell do toExpr(log(b.v, x.v)) + is x:RRicell do toExpr(log(toCCi(b.v), toCCi(x.v))) + else WrongArgRRorCC(2)) + is b:CCicell do ( + when a.1 + is x:CCcell do toExpr(log(b.v, toCCi(x.v))) + is x:CCicell do toExpr(log(b.v, x.v)) + is x:RRcell do toExpr(log(b.v, toCCi(x.v))) + is x:RRicell do toExpr(log(b.v, toCCi(x.v))) + else WrongArgRRorCC(2)) + else WrongArgRRorCC(1)) + -- # typical value: log, InexactNumber, InexactNumber + is x:CCcell do toExpr(log(x.v)) + is x:CCicell do toExpr(log(x.v)) + is x:RRcell do if isNegative(x.v) then toExpr(logc(x.v)) else toExpr(log(x.v)) + is x:RRicell do ( + if x.v >= 0 then toExpr(log(x.v)) + else toExpr(log(toCCi(x.v)))) else WrongArgRRorCC() ); setupfun("log",log).Protected=false; @@ -1226,15 +1499,34 @@ agm(e:Expr):Expr := ( is a:Sequence do if length(a) != 2 then WrongNumArgs(2) else ( when a.0 + -- # typical value: agm, InexactNumber, InexactNumber, InexactNumber is x:CCcell do ( when a.1 - is y:CCcell do toExpr(agm(x.v,y.v)) -- # typical value: agm, CC, CC, CC - is y:RRcell do toExpr(agm(x.v,toCC(y.v))) -- # typical value: agm, CC, RR, CC + is y:CCcell do toExpr(agm(x.v,y.v)) + is y:CCicell do toExpr(agm(toCCi(x.v), y.v)) + is y:RRcell do toExpr(agm(x.v,toCC(y.v))) + is y:RRicell do toExpr(agm(toCCi(x.v),toCCi(y.v))) + else WrongArgRRorCC(2)) + is x:CCicell do ( + when a.1 + is y:CCcell do toExpr(agm(x.v,toCCi(y.v))) + is y:CCicell do toExpr(agm(x.v, y.v)) + is y:RRcell do toExpr(agm(x.v,toCCi(y.v))) + is y:RRicell do toExpr(agm(x.v, toCCi(y.v))) else WrongArgRRorCC(2)) is x:RRcell do ( when a.1 - is y:CCcell do toExpr(agm(toCC(x.v),y.v)) -- # typical value: agm, RR, CC, CC - is y:RRcell do toExpr(agm(x.v,y.v)) -- # typical value: agm, RR, RR, RR + is y:CCcell do toExpr(agm(toCC(x.v),y.v)) + is y:CCicell do toExpr(agm(toCCi(x.v), y.v)) + is y:RRcell do toExpr(agm(x.v,y.v)) + is y:RRicell do toExpr(agm(toRRi(x.v), y.v)) + else WrongArgRRorCC(2)) + is x:RRicell do ( + when a.1 + is y:CCcell do toExpr(agm(toCCi(x.v),toCCi(y.v))) + is y:CCicell do toExpr(agm(toCCi(x.v), y.v)) + is y:RRcell do toExpr(agm(x.v,toRRi(y.v))) + is y:RRicell do toExpr(agm(x.v, y.v)) else WrongArgRRorCC(2)) else WrongArgRRorCC(1)) else WrongNumArgs(2) @@ -1293,17 +1585,19 @@ header "#include "; sqrt(a:Expr):Expr := ( when a + -- # typical value: sqrt, InexactNumber, InexactNumber is x:RRcell do ( if x.v < 0 then toExpr(toCC(0,sqrt(-x.v))) - else toExpr(sqrt(x.v)) -- # typical value: sqrt, RR, CC + else toExpr(sqrt(x.v)) ) is x:RRicell do ( if leftRR(x.v) >= 0 - then toExpr(sqrt(x.v)) -- # typical value: sqrt, RRi, RRi - else buildErrorPacket("Not implemented") + then toExpr(sqrt(x.v)) + else toExpr(sqrt(toCCi(x.v))) ) - is x:CCcell do toExpr(sqrt(x.v)) -- # typical value: sqrt, CC, CC + is x:CCcell do toExpr(sqrt(x.v)) + is x:CCicell do toExpr(sqrt(x.v)) is Error do a else WrongArgRRorCC()); setupfun("sqrt",sqrt).Protected=false; @@ -2377,6 +2671,28 @@ scanPairs(e:Expr):Expr := ( else WrongNumArgs(2)); setupfun("scanPairs", scanPairs); +newMutableListFromZZ(e:Expr):Expr := ( + when e + is a:Sequence do ( + if length(a) == 2 then ( + when a.0 + is T:HashTable do ( + if ancestor(T, mutableListClass) then ( + when a.1 + is n:ZZcell do ( + if isInt(n) then ( + k := toInt(n); + if k >= 0 then list(T, + new Sequence len k do provide nullE, true) + else WrongArg(2, "a nonnegative integer")) + else WrongArgSmallInteger(2)) + else WrongArgZZ(2)) + else WrongArg(1, "a type of mutable list")) + else WrongArgHashTable(1)) + else WrongNumArgs(2)) + else WrongNumArgs(2)); +installMethod(NewFromS, mutableListClass, ZZClass, newMutableListFromZZ); + nextPrime(e:Expr):Expr := ( when e is x:ZZcell do toExpr(nextPrime(x.v - oneZZ)) diff --git a/M2/Macaulay2/d/actors4.d b/M2/Macaulay2/d/actors4.d index 15a7fcc38bb..d4e663e3497 100644 --- a/M2/Macaulay2/d/actors4.d +++ b/M2/Macaulay2/d/actors4.d @@ -102,6 +102,7 @@ absfun(e:Expr):Expr := ( is x:RRcell do toExpr(if signbit(x.v) then -x.v else x.v) is x:RRicell do toExpr(abs(x.v)) is x:CCcell do toExpr(abs(x.v)) + is x:CCicell do toExpr(abs(x.v)) is r:QQcell do toExpr(abs(r.v)) else WrongArg("a number, real or complex")); setupfun("abs0",absfun); @@ -111,9 +112,11 @@ sign(e:Expr):Expr := ( is x:ZZcell do toExpr(sign(x.v)) is x:QQcell do toExpr(sign(x.v)) is x:RRcell do toExpr(sign(x.v)) + is x:RRicell do toExpr(sign(x.v)) is x:CCcell do ( if isZero(x.v) then toExpr(toCC(0, 0, precision(x.v))) else toExpr(x.v / abs(x.v))) + is x:CCicell do toExpr(sign(x.v)) else WrongArg("a number, real or complex")); setupfun("sign0", sign); @@ -662,13 +665,6 @@ stringcatfun(e:Expr):Expr := ( else WrongArg("a sequence or list of strings, integers, or symbols")); setupfun("concatenate",stringcatfun); -errorfun(e:Expr):Expr := ( - e = stringcatfun(e); - when e - is s:stringCell do buildErrorPacket(s.v) - else buildErrorPacket("expects a string or sequence of strings as its argument")); -setupfun("error",errorfun).Protected = false; -- this will be replaced by a toplevel function that calls this one - mingleseq(a:Sequence):Expr := ( n := length(a); b := new array(Sequence) len n do provide emptySequence; @@ -992,10 +988,14 @@ tostringfun(e:Expr):Expr := ( is x:RRcell do toExpr(tostringRR(x.v)) is x:RRicell do toExpr(tostringRRi(x.v)) is z:CCcell do toExpr(tostringCC(z.v)) - is Error do toExpr("<>") + is x:CCicell do toExpr(tostringCCi(x.v))--toExpr(concatenate(array(string)(tostringRRi(x.v.re),"+",tostringRRi(x.v.im),"*ii"))) + is err:Error do toExpr(err.message) is Sequence do toExpr("<>") is HashTable do toExpr("<>") - is List do toExpr("<>") + is x:List do ( + if ancestor(x.Class, filePositionClass) + then toExpr(tostringFilePosition(e)) + else toExpr("<>")) is s:SpecialExpr do tostringfun(s.e) is x:RawMonomialCell do toExpr(tostring(x.p)) is x:RawFreeModuleCell do toExpr(Ccode(string, "IM2_FreeModule_to_string(",x.p,")" )) @@ -1015,10 +1015,7 @@ tostringfun(e:Expr):Expr := ( is x:RawMonoidCell do toExpr(Ccode(string, "rawMonoidToString(",x.p,")" )) is x:RawRingCell do toExpr(Ccode(string, "IM2_Ring_to_string(",x.p,")" )) is x:RawRingElementCell do toExpr( Ccode(string, "IM2_RingElement_to_string(",x.p,")" ) ) - is x:RawMonomialIdealCell do toExpr( - "<>" - -- Ccode(string, "IM2_MonomialIdeal_to_string(",x.p,")" ) - ) + is x:RawMonomialIdealCell do toExpr( Ccode(string, "IM2_MonomialIdeal_to_string(",x.p,")" ) ) is c:RawComputationCell do toExpr(Ccode(string, "IM2_GB_to_string(",c.p,")" )) is pythonObjectCell do toExpr("<>") is x:xmlNodeCell do toExpr(toString(x.v)) @@ -1048,6 +1045,7 @@ tostringfun(e:Expr):Expr := ( Ccode(void, "sprintf((char *)", buf, "->array, \"%d\", ", load(x.v), ")"); Ccode(void, buf, "->len = strlen((char *)", buf, "->array)"); toExpr(buf)) + is x:mutexCell do toExpr("<>") ); setupfun("simpleToString",tostringfun); @@ -1094,7 +1092,8 @@ setupfun("connectionCount", connectionCount); format(e:Expr):Expr := ( when e - is s:stringCell do toExpr("\"" + present(s.v) + "\"") + is s:stringCell do toExpr(format(s.v)) + is s:SpecialExpr do format(s.e) is ZZcell do e is QQcell do e is RRcell do format(Expr(Sequence(e))) @@ -1121,7 +1120,7 @@ format(e:Expr):Expr := ( is x:ZZcell do toExpr(concatenate(format(s,ac,l,t,sep,toRR(x.v,defaultPrecision)))) is x:QQcell do toExpr(concatenate(format(s,ac,l,t,sep,toRR(x.v,defaultPrecision)))) is x:RRcell do toExpr(concatenate(format(s,ac,l,t,sep,x.v))) - is z:CCcell do toExpr(format(s,ac,l,t,sep,false,false,z.v)) + is z:CCcell do toExpr(format(s,ac,l,t,sep,z.v)) else WrongArgRR(n) ) else WrongArg("string, or real number, integer, integer, integer, string")); @@ -1404,39 +1403,112 @@ toRRi(e:Expr):Expr := ( else WrongArg(1,"a pair of integral, rational, or real numbers, with a precision")) else WrongArg(1,"a pair of integral, rational, or real numbers, with a precision")) else buildErrorPacket(EngineError("The first argument should be an integer"))) - else WrongArg(1,"a pair or triple of integral, rational, or real numbers")); + else WrongArg(1,"a pair or triple of integral, rational, or real numbers")); setupfun("toRRi",toRRi); - + +toCCi(e:Expr):Expr := ( + when e + is x:ZZcell do toExpr(toCCi(toRRi(x.v,defaultPrecision),toRRi(0,defaultPrecision))) + is x:QQcell do toExpr(toCCi(toRRi(x.v,defaultPrecision),toRRi(0,defaultPrecision))) + is x:RRcell do toExpr(toCCi(toRRi(x.v),toRRi(0, precision(x.v)))) + is x:RRicell do toExpr(toCCi(x.v,toRRi(0, precision(x.v)))) + is x:CCcell do toExpr(toCCi(toRRi(realPart(x.v)),toRRi(imaginaryPart(x.v)))) + is x:CCicell do e + is s:Sequence do ( + if length(s) > 3 then WrongNumArgs(1,3) else + if length(s) == 2 then ( + when s.0 + is x:ZZcell do ( + when s.1 is y:ZZcell do toExpr(toCCi(toRRi(x.v),toRRi(y.v))) + is y:QQcell do toExpr(toCCi(toRRi(x.v),toRRi(y.v))) + is y:RRcell do toExpr(toCCi(toRRi(x.v, precision(y.v)),toRRi(y.v))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,precision(y.v)),y.v)) + is y:CCcell do toExpr(toCCi(toRRi(realPart(y.v), toULong(x.v)),toRRi(imaginaryPart(y.v), toULong(x.v)))) + is y:CCicell do toExpr(toCCi(toRRi(realPart(y.v), toULong(x.v)),toRRi(imaginaryPart(y.v), toULong(x.v)))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + is x:QQcell do ( + when s.1 is y:ZZcell do toExpr(toCCi(toRRi(x.v), toRRi(y.v))) + is y:QQcell do toExpr(toCCi(toRRi(x.v), toRRi(y.v))) + is y:RRcell do toExpr(toCCi(toRRi(x.v, precision(y.v)),toRRi(y.v))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,precision(y.v)),y.v)) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + is x:RRcell do ( + when s.1 is y:ZZcell do toExpr(toCCi(toRRi(x.v),toRRi(y.v,precision(x.v)))) + is y:QQcell do toExpr(toCCi(toRRi(x.v),toRRi(y.v,precision(x.v)))) + is y:RRcell do toExpr(toCCi(toRRi(x.v,min(precision(x.v),precision(y.v))),toRRi(y.v,min(precision(x.v),precision(y.v))))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,min(precision(x.v),precision(y.v))),toRRi(y.v,min(precision(x.v),precision(y.v))))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + is x:RRicell do ( + when s.1 is y:ZZcell do toExpr(toCCi(x.v,toRRi(y.v,precision(x.v)))) + is y:QQcell do toExpr(toCCi(x.v,toRRi(y.v,precision(x.v)))) + is y:RRcell do toExpr(toCCi(toRRi(x.v,min(precision(x.v),precision(y.v))),toRRi(y.v,min(precision(x.v),precision(y.v))))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,min(precision(x.v),precision(y.v))),toRRi(y.v,min(precision(x.v),precision(y.v))))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + else ( + when s.0 + is prec:ZZcell do ( + when s.1 is x:ZZcell do ( + when s.2 is y:ZZcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:QQcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + is x:QQcell do ( + when s.2 is y:ZZcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:QQcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + is x:RRcell do ( + when s.2 is y:ZZcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:QQcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + is x:RRicell do ( + when s.2 is y:ZZcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:QQcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRcell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + is y:RRicell do toExpr(toCCi(toRRi(x.v,toULong(prec.v)),toRRi(y.v,toULong(prec.v)),toULong(prec.v))) + else WrongArg(1,"a pair of intervals, rational, real numbers, or interval")) + else WrongArg(1,"a pair or triple of integral, rational, or real numbers, with a precision")) + else WrongNumArgs(1,2))) + else WrongArg(1,"a pair or triple of integral, rational, or real numbers, with a precision")); +setupfun("toCCi",toCCi); + rightRR(e:Expr):Expr := ( when e is x:RRicell do toExpr(rightRR(x.v)) else WrongArg("an interval")); -setupfun("right",rightRR); - +setupfun("right0",rightRR); + leftRR(e:Expr):Expr := ( when e is x:RRicell do toExpr(leftRR(x.v)) else WrongArg("an interval")); -setupfun("left",leftRR); - -widthRR(e:Expr):Expr := ( +setupfun("left0",leftRR); + +width0(e:Expr):Expr := ( when e is x:RRicell do toExpr(widthRR(x.v)) - else WrongArg("an interval")); -setupfun("diameter",widthRR).Protected = false; - -midpointRR(e:Expr):Expr := ( + is x:CCicell do toExpr(sqrt(widthRR(realPart(x.v))*widthRR(realPart(x.v))+widthRR(imaginaryPart(x.v))*widthRR(imaginaryPart(x.v)))) + else WrongArg("an interval or complex interval or ball")); +setupfun("diameter",width0).Protected = false; + +midpoint0(e:Expr):Expr := ( when e is x:RRicell do toExpr(midpointRR(x.v)) - else WrongArg("an interval")); -setupfun("midpoint",midpointRR); - + is x:CCicell do toExpr(toCC(midpointRR(realPart(x.v)),midpointRR(imaginaryPart(x.v)))) + else WrongArg("an interval or ball")); +setupfun("midpoint0",midpoint0); + isEmptyRRi(e:Expr):Expr := ( when e is x:RRicell do toExpr(isEmpty(x.v)) else WrongArg("an interval")); setupfun("isEmptyRRi",isEmptyRRi); - + subsetRRi(e:Expr):Expr := ( when e is s:Sequence do ( if length(s) > 3 then WrongNumArgs(1,3) else @@ -1553,6 +1625,7 @@ precision(e:Expr):Expr := ( is x:RRcell do toExpr(precision(x.v)) is x:RRicell do toExpr(precision(x.v)) is x:CCcell do toExpr(precision(x.v)) + is x:CCicell do toExpr(precision(x.v)) else WrongArgRR()); setupfun("precision0",precision); diff --git a/M2/Macaulay2/d/actors5.d b/M2/Macaulay2/d/actors5.d index a3ebca3cba1..b29ec9de9c0 100644 --- a/M2/Macaulay2/d/actors5.d +++ b/M2/Macaulay2/d/actors5.d @@ -210,38 +210,39 @@ setup(BoxTimesS, boxtimesfun); shuffleproductfun(lhs:Code, rhs:Code):Expr := binarymethod(lhs, rhs, ShuffleProductS); setup(ShuffleProductS, shuffleproductfun); -Tildefun(rhs:Code):Expr := unarymethod(rhs,TildeS); -setuppostfix(TildeS,Tildefun); +Tildefun2(lhs:Code,rhs:Code):Expr := binarymethod(lhs,rhs,TildeS); +Tildefun1(rhs:Code):Expr := unarymethod(rhs,TildeS); +setup(TildeS,Tildefun1,Tildefun2); PowerTildefun(rhs:Code):Expr := unarymethod(rhs,PowerTildeS); -setuppostfix(PowerTildeS,PowerTildefun); +setupop(PowerTildeS,PowerTildefun); UnderscoreTildefun(rhs:Code):Expr := unarymethod(rhs,UnderscoreTildeS); -setuppostfix(UnderscoreTildeS,UnderscoreTildefun); +setupop(UnderscoreTildeS,UnderscoreTildefun); ParenStarParenfun(rhs:Code):Expr := unarymethod(rhs,ParenStarParenS); -setuppostfix(ParenStarParenS,ParenStarParenfun); +setupop(ParenStarParenS,ParenStarParenfun); UnderscoreStarfun(rhs:Code):Expr := unarymethod(rhs,UnderscoreStarS); -setuppostfix(UnderscoreStarS,UnderscoreStarfun); +setupop(UnderscoreStarS,UnderscoreStarfun); PowerStarfun(rhs:Code):Expr := unarymethod(rhs,PowerStarS); -setuppostfix(PowerStarS,PowerStarfun); +setupop(PowerStarS,PowerStarfun); --PowerSharpfun(rhs:Code):Expr := unarymethod(rhs,PowerSharpS); ---setuppostfix(PowerSharpS,PowerSharpfun); +--setupop(PowerSharpS,PowerSharpfun); --UnderscoreSharpfun(rhs:Code):Expr := unarymethod(rhs,UnderscoreSharpS); ---setuppostfix(UnderscoreSharpS,UnderscoreSharpfun); +--setupop(UnderscoreSharpS,UnderscoreSharpfun); Exclamationfun(rhs:Code):Expr := unarymethod(rhs,ExclamationS); -setuppostfix(ExclamationS,Exclamationfun); +setupop(ExclamationS,Exclamationfun); PowerExclamationfun(rhs:Code):Expr := unarymethod(rhs,PowerExclamationS); -setuppostfix(PowerExclamationS,PowerExclamationfun); +setupop(PowerExclamationS,PowerExclamationfun); UnderscoreExclamationfun(rhs:Code):Expr := unarymethod(rhs,UnderscoreExclamationS); -setuppostfix(UnderscoreExclamationS,UnderscoreExclamationfun); +setupop(UnderscoreExclamationS,UnderscoreExclamationfun); factorial(x:Expr):Expr := ( when x diff --git a/M2/Macaulay2/d/ballarith.d b/M2/Macaulay2/d/ballarith.d index dc912047828..b8a87f68ced 100644 --- a/M2/Macaulay2/d/ballarith.d +++ b/M2/Macaulay2/d/ballarith.d @@ -26,74 +26,90 @@ header " #endif "; --- TODO: RRball and CCball at top level? +-- TODO: RRb and CCb at top level? ------------ --- RRball -- +-- RRb -- ------------ -RRball := Pointer "arb_ptr"; -init(x:RRball) ::= ( +export RRb := Pointer "arb_ptr"; +init(x:RRb) ::= ( Ccode(void, "arb_init(", x, ")"); x); -newRRball():RRball := init(GCmalloc(RRball)); -clear(x:RRball) ::= Ccode(void, "arb_clear(", x, ")"); +newRRb():RRb := init(GCmalloc(RRb)); +clear(x:RRb) ::= Ccode(void, "arb_clear(", x, ")"); -- clear after using -toRRball(x:RR, y:RR, prec:ulong):RRball := ( - z := newRRball(); +export toRRb(x:RR, y:RR, prec:ulong):RRb := ( + z := newRRb(); Ccode(void, "arb_set_interval_mpfr(", z, ", ", x, ", ", y, ", ", prec, ")"); z); -toRRball(x:RR):RRball := toRRball(x, x, precision(x)); -toRRball(x:RRi):RRball := toRRball(leftRR(x), rightRR(x), precision(x)); +export toRRb(x:RR):RRb := toRRb(x, x, precision(x)); +export toRRb(x:RRi):RRb := toRRb(leftRR(x), rightRR(x), precision(x)); -toRR(x:RRball, prec:ulong):RR := ( +toRR(x:RRb, prec:ulong):RR := ( y := newRRmutable(prec); Ccode(int, "arf_get_mpfr(", y, ", arb_midref(", x, "), MPFR_RNDN)"); moveToRRandclear(y)); -toRRi(x:RRball, prec:ulong):RRi := ( +moveToRRandclear(x:RRb, prec:ulong):RR := ( + r := toRR(x, prec); + clear(x); + r); + +toRRi(x:RRb, prec:ulong):RRi := ( y := newRRimutable(prec); Ccode(void, "arb_get_interval_mpfr((mpfr_ptr)&", y, "->left, (mpfr_ptr)&", y, "->right, ", x, ")"); moveToRRiandclear(y)); -moveToRRiandclear(x:RRball, prec:ulong):RRi := ( +moveToRRiandclear(x:RRb, prec:ulong):RRi := ( r := toRRi(x, prec); clear(x); r); -- special functions export eint(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_hypgeom_ei(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); export Gamma(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_gamma(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); export Gamma(z:RRi,w:RRi):RRi := ( prec := min(precision(z), precision(w)); - x := toRRball(z); - y := toRRball(w); - r := newRRball(); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); Ccode(void, "arb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 0, ", prec, ")"); clear(x); clear(y); moveToRRiandclear(r, prec)); +export regularizedGamma(z:RR,w:RR):RR := ( + prec := min(precision(z), precision(w)); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); + Ccode(void, "arb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 1, ", + prec, ")"); + clear(x); + clear(y); + moveToRRandclear(r, prec)); + export regularizedGamma(z:RRi,w:RRi):RRi := ( prec := min(precision(z), precision(w)); - x := toRRball(z); - y := toRRball(w); - r := newRRball(); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); Ccode(void, "arb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 1, ", prec, ")"); clear(x); @@ -101,62 +117,89 @@ export regularizedGamma(z:RRi,w:RRi):RRi := ( moveToRRiandclear(r, prec)); export Digamma(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_digamma(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); export lgamma(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_lgamma(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); export zeta(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_zeta(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); export erf(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_hypgeom_erf(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); export erfc(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_hypgeom_erfc(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); +export inverseErf(x:RR):RR := ( + y := toRRb(x); + r := newRRb(); + Ccode(void, "arb_hypgeom_erfinv(", r, ", ", y, ", ", precision(x), ")"); + clear(y); + moveToRRandclear(r, precision(x))); + export inverseErf(x:RRi):RRi := ( - y := toRRball(x); - r := newRRball(); + y := toRRb(x); + r := newRRb(); Ccode(void, "arb_hypgeom_erfinv(", r, ", ", y, ", ", precision(x), ")"); clear(y); moveToRRiandclear(r, precision(x))); +export BesselJ(z:RR,w:RR):RR := ( + prec := min(precision(z), precision(w)); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); + Ccode(void, "arb_hypgeom_bessel_j(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToRRandclear(r, prec)); + export BesselJ(z:RRi,w:RRi):RRi := ( prec := min(precision(z), precision(w)); - x := toRRball(z); - y := toRRball(w); - r := newRRball(); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); Ccode(void, "arb_hypgeom_bessel_j(", r, ", ", x, ", ", y, ", ", prec, ")"); clear(x); clear(y); moveToRRiandclear(r, prec)); +export BesselY(z:RR,w:RR):RR := ( + prec := min(precision(z), precision(w)); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); + Ccode(void, "arb_hypgeom_bessel_y(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToRRandclear(r, prec)); + export BesselY(z:RRi,w:RRi):RRi := ( prec := min(precision(z), precision(w)); - x := toRRball(z); - y := toRRball(w); - r := newRRball(); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); Ccode(void, "arb_hypgeom_bessel_y(", r, ", ", x, ", ", y, ", ", prec, ")"); clear(x); clear(y); @@ -164,22 +207,35 @@ export BesselY(z:RRi,w:RRi):RRi := ( export Beta(z:RRi,w:RRi):RRi := ( prec := min(precision(z), precision(w)); - x := toRRball(z); - y := toRRball(w); - v := toRRball(toRRi(1, prec)); - r := newRRball(); + x := toRRb(z); + y := toRRb(w); + v := toRRb(toRRi(1, prec)); + r := newRRb(); Ccode(void, "arb_hypgeom_beta_lower(", r, ", ", x, ", ", y, ", ", v, ", 0, ", prec, ")"); clear(x); clear(y); moveToRRiandclear(r, prec)); +export regularizedBeta(u:RR,v:RR,w:RR):RR := ( + prec := min(min(precision(u), precision(v)), precision(w)); + x := toRRb(u); + y := toRRb(v); + z := toRRb(w); + r := newRRb(); + Ccode(void, "arb_hypgeom_beta_lower(", r, ", ", y, ", ", z, ", ", x, + ", 1, ", prec, ")"); + clear(x); + clear(y); + clear(z); + moveToRRandclear(r, prec)); + export regularizedBeta(u:RRi,v:RRi,w:RRi):RRi := ( prec := min(min(precision(u), precision(v)), precision(w)); - x := toRRball(u); - y := toRRball(v); - z := toRRball(w); - r := newRRball(); + x := toRRb(u); + y := toRRb(v); + z := toRRb(w); + r := newRRb(); Ccode(void, "arb_hypgeom_beta_lower(", r, ", ", y, ", ", z, ", ", x, ", 1, ", prec, ")"); clear(x); @@ -187,149 +243,537 @@ export regularizedBeta(u:RRi,v:RRi,w:RRi):RRi := ( clear(z); moveToRRiandclear(r, prec)); +export agm(z:RRi, w:RRi):RRi := ( + prec := min(precision(z), precision(w)); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); + Ccode(void, "arb_agm(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToRRiandclear(r, prec)); + +export sign(z:RRi):RRi := ( + w := toRRb(z); + r := newRRb(); + Ccode(void, "arb_sgn(", r, ", ", w, ")"); + clear(w); + moveToRRiandclear(r, precision(z))); + +export polylog(z:RR, w:RR):RR := ( + prec := min(precision(z), precision(w)); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); + Ccode(void, "arb_polylog(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToRRandclear(r, prec)); + +export polylog(z:RRi, w:RRi):RRi := ( + prec := min(precision(z), precision(w)); + x := toRRb(z); + y := toRRb(w); + r := newRRb(); + Ccode(void, "arb_polylog(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + + moveToRRiandclear(r, prec)); + ------------ -- CCBall -- ------------ -CCball := Pointer "acb_ptr"; -init(z:CCball) ::= ( +export CCb := Pointer "acb_ptr"; +init(z:CCb) ::= ( Ccode(void, "acb_init(", z, ")"); z); -newCCball():CCball := init(GCmalloc(CCball)); -clear(z:CCball) ::= Ccode(void, "acb_clear(", z, ")"); +newCCb():CCb := init(GCmalloc(CCb)); +clear(z:CCb) ::= Ccode(void, "acb_clear(", z, ")"); -- clear after using -toCCball(z:CC):CCball := ( - x := toRRball(realPart(z)); - y := toRRball(imaginaryPart(z)); - w := newCCball(); - Ccode(void, "acb_set_arb_arb(", w, ", ", x, ", ", y, ")"); +export toCCb(x:RRb, y:RRb):CCb := ( + z := newCCb(); + Ccode(void, "acb_set_arb_arb(", z, ", ", x, ", ", y, ")"); + z); + +export toCCb(z:CC):CCb := ( + x := toRRb(realPart(z)); + y := toRRb(imaginaryPart(z)); + w := toCCb(x, y); + clear(x); + clear(y); + w); + +toCCb(z:CCi):CCb := ( + x := toRRb(realPart(z)); + y := toRRb(imaginaryPart(z)); + w := toCCb(x, y); clear(x); clear(y); w); -toCC(z:CCball, prec:ulong):CC := ( - x := Ccode(RRball, "acb_realref(", z, ")"); - y := Ccode(RRball, "acb_imagref(", z, ")"); +toCC(z:CCb, prec:ulong):CC := ( + x := Ccode(RRb, "acb_realref(", z, ")"); + y := Ccode(RRb, "acb_imagref(", z, ")"); toCC(toRR(x, prec), toRR(y, prec))); -moveToCCandclear(z:CCball, prec:ulong):CC := ( +moveToCCandclear(z:CCb, prec:ulong):CC := ( r := toCC(z, prec); clear(z); r); +toCCi(z:CCb, prec:ulong):CCi := ( + x := Ccode(RRb, "acb_realref(", z, ")"); + y := Ccode(RRb, "acb_imagref(", z, ")"); + toCCi(toRRi(x, prec), toRRi(y, prec))); + +moveToCCiandclear(z:CCb, prec:ulong):CCi := ( + r := toCCi(z, prec); + clear(z); + r); + +export sin(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_sin(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export cos(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_cos(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export tan(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_tan(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export acos(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_acos(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export sec(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_sec(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export csc(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_csc(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export cot(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_cot(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export sech(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_sech(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export csch(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_csch(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export coth(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_coth(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export asin(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_asin(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export log1p(z:CC):CC := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_log1p(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCandclear(r, precision(z))); + +export log1p(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_log1p(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export expm1(z:CC):CC := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_expm1(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCandclear(r, precision(z))); + +export expm1(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_expm1(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export eint(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_hypgeom_ei(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export eint(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_hypgeom_ei(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export Gamma(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_gamma(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export Gamma(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_gamma(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export Gamma(z:CC,w:CC):CC := ( prec := min(precision(z), precision(w)); - x := toCCball(z); - y := toCCball(w); - r := newCCball(); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); Ccode(void, "acb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 0, ", prec, ")"); clear(x); clear(y); moveToCCandclear(r, prec)); +export Gamma(z:CCi,w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 0, ", + prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + export regularizedGamma(z:CC,w:CC):CC := ( prec := min(precision(z), precision(w)); - x := toCCball(z); - y := toCCball(w); - r := newCCball(); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); Ccode(void, "acb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 1, ", prec, ")"); clear(x); clear(y); moveToCCandclear(r, prec)); +export regularizedGamma(z:CCi,w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_hypgeom_gamma_upper(", r, ", ", x, ", ", y, ", 1, ", + prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + export Digamma(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_digamma(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export Digamma(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_digamma(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export lgamma(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_lgamma(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export lgamma(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_lgamma(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export zeta(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_zeta(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export zeta(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_zeta(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export erf(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_hypgeom_erf(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export erf(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_hypgeom_erf(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export erfc(z:CC):CC := ( - w := toCCball(z); - r := newCCball(); + w := toCCb(z); + r := newCCb(); Ccode(void, "acb_hypgeom_erfc(", r, ", ", w, ", ", precision(z), ")"); clear(w); moveToCCandclear(r, precision(z))); +export erfc(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_hypgeom_erfc(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export BesselJ(z:CC,w:CC):CC := ( prec := min(precision(z), precision(w)); - x := toCCball(z); - y := toCCball(w); - r := newCCball(); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); Ccode(void, "acb_hypgeom_bessel_j(", r, ", ", x, ", ", y, ", ", prec, ")"); clear(x); clear(y); moveToCCandclear(r, prec)); +export BesselJ(z:CCi,w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_hypgeom_bessel_j(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + export BesselY(z:CC,w:CC):CC := ( prec := min(precision(z), precision(w)); - x := toCCball(z); - y := toCCball(w); - r := newCCball(); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); Ccode(void, "acb_hypgeom_bessel_y(", r, ", ", x, ", ", y, ", ", prec, ")"); clear(x); clear(y); moveToCCandclear(r, prec)); +export BesselY(z:CCi,w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_hypgeom_bessel_y(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + +export atan(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_atan(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + export Beta(z:CC,w:CC):CC := ( prec := min(precision(z), precision(w)); - x := toCCball(z); - y := toCCball(w); - v := toCCball(toCC(1, prec)); - r := newCCball(); + x := toCCb(z); + y := toCCb(w); + v := toCCb(toCC(1, prec)); + r := newCCb(); Ccode(void, "acb_hypgeom_beta_lower(", r, ", ", x, ", ", y, ", ", v, ", 0, ", prec, ")"); clear(x); clear(y); moveToCCandclear(r, prec)); +export Beta(z:CCi,w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + v := toCCb(toCC(1, prec)); + r := newCCb(); + Ccode(void, "acb_hypgeom_beta_lower(", r, ", ", x, ", ", y, ", ", v, + ", 0, ", prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + export regularizedBeta(u:CC,v:CC,w:CC):CC := ( prec := min(min(precision(u), precision(v)), precision(w)); - x := toCCball(u); - y := toCCball(v); - z := toCCball(w); - r := newCCball(); + x := toCCb(u); + y := toCCb(v); + z := toCCb(w); + r := newCCb(); Ccode(void, "acb_hypgeom_beta_lower(", r, ", ", y, ", ", z, ", ", x, ", 1, ", prec, ")"); clear(x); clear(y); clear(z); moveToCCandclear(r, prec)); + +export regularizedBeta(u:CCi,v:CCi,w:CCi):CCi := ( + prec := min(min(precision(u), precision(v)), precision(w)); + x := toCCb(u); + y := toCCb(v); + z := toCCb(w); + r := newCCb(); + Ccode(void, "acb_hypgeom_beta_lower(", r, ", ", y, ", ", z, ", ", x, + ", 1, ", prec, ")"); + clear(x); + clear(y); + clear(z); + moveToCCiandclear(r, prec)); + +export cosh(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_cosh(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export sinh(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_sinh(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export tanh(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_tanh(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export exp(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_exp(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export log(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_log(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export log(z:CCi, w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_log(", x, ", ", x, ", ", prec, ")"); + Ccode(void, "acb_log(", y, ", ", y, ", ", prec, ")"); + Ccode(void, "acb_div(", r, ", ", y, ", ", x, ", ", prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + +export (z:CCi) ^ (w:CCi):CCi := exp(log(z)*w); + +export agm(z:CCi, w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_agm(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); + +export sqrt(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_sqrt(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export abs(z:CCi):RRi := ( + w := toCCb(z); + r := newRRb(); + Ccode(void, "acb_abs(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToRRiandclear(r, precision(z))); + +export sign(z:CCi):CCi := ( + w := toCCb(z); + r := newCCb(); + Ccode(void, "acb_sgn(", r, ", ", w, ", ", precision(z), ")"); + clear(w); + moveToCCiandclear(r, precision(z))); + +export polylog(z:CC, w:CC):CC := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_polylog(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToCCandclear(r, prec)); + +export polylog(z:CCi, w:CCi):CCi := ( + prec := min(precision(z), precision(w)); + x := toCCb(z); + y := toCCb(w); + r := newCCb(); + Ccode(void, "acb_polylog(", r, ", ", x, ", ", y, ", ", prec, ")"); + clear(x); + clear(y); + moveToCCiandclear(r, prec)); diff --git a/M2/Macaulay2/d/basic.d b/M2/Macaulay2/d/basic.d index f0a22f9a839..caf624c23cc 100644 --- a/M2/Macaulay2/d/basic.d +++ b/M2/Macaulay2/d/basic.d @@ -4,6 +4,10 @@ use expr; header "#include "; -- required for raw hash functions +-- used to hash sequences below and for quick method lookup in hashtables.dd +export seqHashSeed := hash_t(27449); +export seqHashMult := hash_t(27457); + export hash(e:Expr):hash_t := ( when e is x:HashTable do x.hash @@ -25,10 +29,10 @@ export hash(e:Expr):hash_t := ( is x:RRcell do hash(x.v) is x:RRicell do hash(x.v) is x:CCcell do hash(x.v) + is x:CCicell do hash(x.v.re + x.v.im) is x:Sequence do ( - -- the numbers here are the same as in binary lookup() in objects.d!! - h := hash_t(27449); - foreach y in x do h = h * 27457 + hash(y); + h := seqHashSeed; + foreach y in x do h = h * seqHashMult + hash(y); h) is x:stringCell do hash(x.v) -- for strings, keep internal and external hash the same is n:Net do hash(n) @@ -72,6 +76,7 @@ export hash(e:Expr):hash_t := ( -- cast to long first to avoid "different size" compiler warning is x:pointerCell do Ccode(hash_t, "(long)", x.v) is x:atomicIntCell do x.hash + is x:mutexCell do x.hash ); export hash(x:List):hash_t := ( diff --git a/M2/Macaulay2/d/binding.d b/M2/Macaulay2/d/binding.d index 6654db5ee4e..a2b9049d0c7 100644 --- a/M2/Macaulay2/d/binding.d +++ b/M2/Macaulay2/d/binding.d @@ -110,7 +110,7 @@ export makeEntry(word:Word,position:Position,dictionary:Dictionary,thread:bool,l word, word.hash + 9898989, position, - dummyUnaryFun,dummyPostfixFun,dummyBinaryFun, + dummyUnaryFun,dummyBinaryFun, dictionary.frameID, frameindex, 1, -- first lookup is now @@ -220,6 +220,7 @@ bumpPrecedence(); thenW = token("then"); makeKeyword(thenW); doW = token("do"); makeKeyword(doW); listW = token("list"); makeKeyword(listW); + exceptW = token("except"); makeKeyword(exceptW); bumpPrecedence(); export ColonEqualW := binaryright(":="); export ColonEqualS := makeKeyword(ColonEqualW); export EqualW := binaryright("="); export EqualS := makeKeyword(EqualW); @@ -267,6 +268,7 @@ bumpPrecedence(); export QuestionS := makeKeyword(unarybinaryright("?")); export NotEqualEqualEqualS := makeKeyword(binaryright("=!=")); export NotEqualS := makeKeyword(binaryright("!=")); + export TildeS := makeKeyword(unarybinaryright("~")); -- operations on terms that yield terms: bumpPrecedence(); export BarBarS := makeKeyword(binaryleft("||")); @@ -318,6 +320,7 @@ bumpPrecedence(); export breakpointS := special("breakpoint", unaryop, precSpace, wide); export profileS := special("profile", unaryop, precSpace, wide); export shieldS := special("shield", unaryop, precSpace, wide); + export trapS := special("trap", unaryop, precSpace, wide); export throwS := special("throw", nunaryop, precSpace, wide); export returnS := special("return", nunaryop, precSpace, wide); export breakS := special("break", nunaryop, precSpace, wide); @@ -335,7 +338,6 @@ bumpPrecedence(); export AtAtS := makeKeyword(binaryleft("@@")); export AtAtQuestionS := makeKeyword(binaryleft("@@?")); bumpPrecedence(); - export TildeS := makeKeyword(postfix("~")); export PowerTildeS := makeKeyword(postfix("^~")); export UnderscoreTildeS := makeKeyword(postfix("_~")); export UnderscoreStarS := makeKeyword(postfix("_*")); @@ -402,8 +404,11 @@ export NewOfFromE := Expr(NewOfFromS); export InverseS := makeProtectedSymbolClosure("InverseMethod"); export InverseE := Expr(InverseS); -export RobustPrintS := makeProtectedSymbolClosure("RobustPrintMethod"); -export RobustPrintE := Expr(RobustPrintS); +export RobustPrintNetS := makeProtectedSymbolClosure("RobustPrintNetMethod"); +export RobustPrintNetE := Expr(RobustPrintNetS); + +export RobustPrintStringS := makeProtectedSymbolClosure("RobustPrintStringMethod"); +export RobustPrintStringE := Expr(RobustPrintStringS); export StopIterationS := makeProtectedSymbolClosure("StopIteration"); export StopIterationE := Expr(StopIterationS); @@ -500,7 +505,7 @@ export opsWithBinaryMethod := array(SymbolClosure)( LongDoubleRightArrowS, LongLongDoubleRightArrowS, LongDoubleLeftArrowS, LongLongDoubleLeftArrowS, ColonS, BarS, HatHatS, AmpersandS, DotDotS, DotDotLessS, MinusS, PlusS, PlusPlusS, StarStarS, StarS, BackslashBackslashS, DivideS, LeftDivideS, PercentS, SlashSlashS, AtS, - AdjacentS, AtAtS, AtAtQuestionS, orS, andS, xorS, + AdjacentS, AtAtS, AtAtQuestionS, orS, andS, xorS, TildeS, -- TODO: why are these four not listed here? -- GreaterS, GreaterEqualS, LessS, LessEqualS, BarUnderscoreS, @@ -516,13 +521,13 @@ export opsWithBinaryMethod := array(SymbolClosure)( ); export opsWithUnaryMethod := array(SymbolClosure)( StarS, MinusS, PlusS, LessLessS, QuestionQuestionS, - LongDoubleLeftArrowS, LongLongDoubleLeftArrowS, + LongDoubleLeftArrowS, LongLongDoubleLeftArrowS, TildeS, notS, DeductionS, QuestionS,LessS,GreaterS,LessEqualS,GreaterEqualS); export opsWithPostfixMethod := array(SymbolClosure)( ExclamationS, PowerExclamationS, UnderscoreExclamationS, -- FIXME: PowerSharpS, UnderscoreSharpS, ParenStarParenS, PowerStarS, UnderscoreStarS, - TildeS, PowerTildeS, UnderscoreTildeS + PowerTildeS, UnderscoreTildeS ); -- ":=" "=" "<-" "->" "=>" "===" "=!=" "!=" "#" "#?" "." ".?" ";" "," "<" ">" "<=" ">=" @@ -678,9 +683,15 @@ bindParallelAssignmentItem(e:ParseTree,dictionary:Dictionary,colon:bool):void := if token.word.typecode != TCid then makeErrorTree(token,"syntax error: parallel assignment expected symbol") else bindToken(token,dictionary,colon); ) - else makeErrorTree(e,"syntax error: parallel assignment expected symbol")); + else bind(e, dictionary)); bindParallelAssignmentList(e:ParseTree,dictionary:Dictionary,colon:bool):void := ( when e + is unary:Unary do ( + if unary.Operator.word == CommaW + then ( + bindop(unary.Operator, dictionary); + bindParallelAssignmentItem(unary.rhs, dictionary, colon)) + else bind(e, dictionary)) is binary:Binary do ( if binary.Operator.word == CommaW then ( @@ -688,7 +699,7 @@ bindParallelAssignmentList(e:ParseTree,dictionary:Dictionary,colon:bool):void := bindop(binary.Operator,dictionary); bindParallelAssignmentItem(binary.rhs,dictionary,colon); ) - else makeErrorTree(e,"syntax error: parallel assignment expected symbol list") + else bind(e, dictionary) ) else bindParallelAssignmentItem(e,dictionary,colon)); bindassignment(assn:Binary,dictionary:Dictionary,colon:bool):void := ( @@ -699,6 +710,7 @@ bindassignment(assn:Binary,dictionary:Dictionary,colon:bool):void := ( bindParallelAssignmentList(p.contents,dictionary,colon); bind(body,dictionary); ) + is p:EmptyParentheses do bind(body,dictionary) is token:Token do ( if token.word.typecode != TCid then ( makeErrorTree(assn.Operator, "expected a symbol to left of '"+assn.Operator.entry.word.name+"'"); @@ -915,6 +927,21 @@ export bind(e:ParseTree,dictionary:Dictionary):void := ( is i:Try do ( bind(i.primary,dictionary); ) + is i:TryDo do ( + bind(i.primary,dictionary); + newdict := newLocalDictionary(dictionary); + bindSingleParm(i.variable,newdict); + bind(i.doClause,newdict); + i.dictionary = newdict; + ) + is i:TryThenDo do ( + bind(i.primary,dictionary); + bind(i.sequel,dictionary); + newdict := newLocalDictionary(dictionary); + bindSingleParm(i.variable,newdict); + bind(i.doClause,newdict); + i.dictionary = newdict; + ) is i:Catch do ( bind(i.primary,dictionary); ) diff --git a/M2/Macaulay2/d/boost-regex.cpp b/M2/Macaulay2/d/boost-regex.cpp index 6b74f016705..9671b18bc63 100644 --- a/M2/Macaulay2/d/boost-regex.cpp +++ b/M2/Macaulay2/d/boost-regex.cpp @@ -54,6 +54,7 @@ M2_arrayint regex_search(const M2_string pattern, int regex_flags, int match_flags) { + (void) range; #if DEBUG_REGEX & 2 std::cerr << "string:\t" << M2_tocharstar(text) << std::endl; #endif diff --git a/M2/Macaulay2/d/boostmath.dd b/M2/Macaulay2/d/boostmath.dd index 08335f2b5c7..b8a61c11079 100644 --- a/M2/Macaulay2/d/boostmath.dd +++ b/M2/Macaulay2/d/boostmath.dd @@ -34,7 +34,7 @@ handleBoostError(x:RRorNull):Expr:= is null do buildErrorPacket("Boost Math Toolkit error") is y:RR do toExpr(y); --- # typical value: inverseRegularizedGamma, RR, RR, RR +-- # typical value: inverseRegularizedGamma, InexactNumber, InexactNumber, InexactNumber inverseRegularizedGamma(a:RR,q:RR):RRorNull:= ( prec := min(precision(a), precision(q)); Ccode(returns, "BOOST_MATH_CALL(gamma_q_inv, ", prec, ", RR_TO_BOOST(", a, @@ -49,7 +49,7 @@ inverseRegularizedGamma(e:Expr):Expr := else WrongNumArgs(2); setupfun("inverseRegularizedGamma",inverseRegularizedGamma).Protected=false; --- # typical value: inverseRegularizedBeta, RR, RR, RR, RR +-- # typical value: inverseRegularizedBeta, InexactNumber, InexactNumber, InexactNumber, InexactNumber inverseRegularizedBeta(p:RR,a:RR,b:RR):RRorNull:= ( prec := min(precision(p), min(precision(a), precision(b))); Ccode(returns, "BOOST_MATH_CALL(ibeta_inv, ", prec, ", RR_TO_BOOST(", a, diff --git a/M2/Macaulay2/d/classes.dd b/M2/Macaulay2/d/classes.dd index 8bcf7fea0f3..d5fd09990bd 100644 --- a/M2/Macaulay2/d/classes.dd +++ b/M2/Macaulay2/d/classes.dd @@ -24,6 +24,7 @@ setupconst("InexactFieldFamily",Expr(inexactNumberTypeClass)); setupconst("RRi",Expr(RRiClass)); setupconst("RR",Expr(RRClass)); setupconst("CC",Expr(CCClass)); +setupconst("CCi",Expr(CCiClass)); setupconst("RingElement",Expr(ringElementClass)); setupconst("Number",Expr(numberClass)); setupconst("InexactNumber",Expr(inexactNumberClass)); @@ -82,6 +83,8 @@ setupconst("Task",Expr(taskClass)); setupconst("FileOutputSyncState",Expr(fileOutputSyncStateClass)); setupconst("Pointer",Expr(pointerClass)); setupconst("AtomicInt",Expr(atomicIntClass)); +setupconst("Error",Expr(errorClass)); +setupconst("Mutex",Expr(mutexClass)); export ancestor(o:HashTable,p:HashTable):bool := ( while true do ( @@ -116,6 +119,13 @@ export Parent(e:Expr):HashTable := ( export parentfun(e:Expr):Expr := Expr(Parent(e)); setupfun("parent",parentfun); +export Class(err:Error):HashTable := ( + when err.value + is x:HashTable do ( + if ancestor(x, errorClass) then x + else errorClass) + else errorClass); + export Class(e:Expr):HashTable := ( when e is obj:HashTable do obj.Class @@ -139,7 +149,7 @@ export Class(e:Expr):HashTable := ( is FunctionClosure do functionClosureClass is Net do netClass is NetFile do netFileClass - is Error do errorClass + is err:Error do Class(err) is Sequence do sequenceClass is CompiledFunction do compiledFunctionClass is CompiledFunctionClosure do compiledFunctionClosureClass @@ -148,6 +158,7 @@ export Class(e:Expr):HashTable := ( is RRicell do RRiClass is RRcell do RRClass is CCcell do CCClass + is CCicell do CCiClass is RawComputationCell do rawComputationClass is Nothing do nothingClass is Database do dbClass @@ -175,6 +186,7 @@ export Class(e:Expr):HashTable := ( is fileOutputSyncState do fileOutputSyncStateClass is pointerCell do pointerClass is atomicIntCell do atomicIntClass + is mutexCell do mutexClass ); classfun(e:Expr):Expr := Expr(Class(e)); -- # typical value: class, Thing, Type diff --git a/M2/Macaulay2/d/common.d b/M2/Macaulay2/d/common.d index 24fb0a50bb1..bab7f50ca02 100644 --- a/M2/Macaulay2/d/common.d +++ b/M2/Macaulay2/d/common.d @@ -45,10 +45,6 @@ export codePosition(c:Code):Position := ( -- TODO retire is f:Error do f.position ); -export setuppostfix(e:SymbolClosure,fn:unop):void := ( - unopNameList = unopNameListCell(fn,e.symbol,unopNameList); - e.symbol.postfix = fn; - ); export setup(e:SymbolClosure,fn:binop):void := ( binopNameList = binopNameListCell(fn,e.symbol,binopNameList); e.symbol.binary = fn; diff --git a/M2/Macaulay2/d/convertr.d b/M2/Macaulay2/d/convertr.d index 252e462f9cf..9c49aff06b7 100644 --- a/M2/Macaulay2/d/convertr.d +++ b/M2/Macaulay2/d/convertr.d @@ -57,43 +57,6 @@ makeCodeSequence(e:ParseTree,separator:Word):CodeSequence := ( v := new CodeSequence len CodeSequenceLength(e,separator) do provide dummyCode; fillCodeSequence(e,v,length(v),separator); v); -SymbolSequenceLength(e:ParseTree):int := ( - i := 0; - while true do ( - when e - is p:Parentheses do e = p.contents - is b:Binary do ( - i = i+1; - e = b.lhs; - ) - else ( -- should be the first token - i = i+1; - return i; - ) - ) - ); -makeSymbolSequence(e:ParseTree):SymbolSequence := ( -- but replace local symbols by dummySymbol - m := SymbolSequenceLength(e); - v := new SymbolSequence len m do provide dummySymbol; - while true do ( - when e - is p:Parentheses do e = p.contents - is b:Binary do ( - when b.rhs is t:Token do ( - m = m-1; - v.m = t.entry; - ) - else nothing; -- shouldn't happen - e = b.lhs; - ) - is t:Token do ( - m = m-1; - v.m = t.entry; - break; - ) - else break; -- shouldn't happen - ); - v); nestingDepth(frameID:int,d:Dictionary):int := ( if frameID == 0 then return -1; @@ -123,8 +86,8 @@ convertTokenReference(token:Token):Code := ( else ( if var.frameID == 0 then if var.thread - then Code(threadMemoryReferenceCode(var.frameindex, pos)) - else Code(globalMemoryReferenceCode(var.frameindex, pos)) + then Code(threadMemoryReferenceCode(var, pos)) + else Code(globalMemoryReferenceCode(var, pos)) else Code(localMemoryReferenceCode(nestingDepth(var.frameID, token.dictionary), var.frameindex, pos))) ); @@ -138,15 +101,11 @@ convertTokenAssignment(token:Token, rhs:ParseTree):Code := ( nestingDepth(sym.frameID, token.dictionary), sym.frameindex, val, pos)) ); -convertParallelAssignment(par:Parentheses,rhs:ParseTree,d:Dictionary):Code := ( - syms := makeSymbolSequence(ParseTree(par)); -- silly -- rethink +convertParallelAssignment(colon:bool, par:Parentheses, rhs:ParseTree):Code := ( + lhs := makeCodeSequence(par.contents, CommaW); vals := convert(rhs); pos := combinePositionR(par.left.position, treePosition(rhs)); - n := length(syms); - nd := new array(int) len n do foreach x in syms do provide nestingDepth(x.frameID, d); -- rethink dictionary - fr := new array(int) len n do foreach x in syms do provide x.frameindex; - foreach x in syms do if x.frameID != 0 then x = dummySymbol; - Code(parallelAssignmentCode(nd, fr, syms, vals, pos)) + Code(parallelAssignmentCode(colon, lhs, vals, pos)) ); export convertGlobalOperator(oper:Token):Code := Code(globalSymbolClosureCode(oper.entry, oper.position)); @@ -189,7 +148,7 @@ export convert0(e:ParseTree):Code := ( rhs := convertGlobalOperator(token); -- TODO: is this check necessary? if token.word.typecode == TCid - then Code(binaryCode(b.Operator.entry.binary, convert(b.lhs), rhs, pos)) + then Code(binaryCode(b.Operator.entry, convert(b.lhs), rhs, pos)) else dummyCode -- should not occur ) else dummyCode -- should not occur @@ -207,7 +166,12 @@ export convert0(e:ParseTree):Code := ( -- e.g. x = ... is t:Token do convertTokenAssignment(t, b.rhs) -- e.g. (x,y,z) = (...) - is p:Parentheses do convertParallelAssignment(p, b.rhs, b.Operator.dictionary) + is p:Parentheses do convertParallelAssignment(false, p, b.rhs) + is p:EmptyParentheses do Code(parallelAssignmentCode( + false, + CodeSequence(), + convert(b.rhs), + combinePositionR(p.left.position, treePosition(b.rhs)))) -- Note: usable, but not used anywhere yet is u:Unary do convertUnaryInstallCode(UnaryInstallValueFun, convertGlobalOperator(u.Operator), convert(u.rhs), convert(b.rhs), pos) @@ -243,7 +207,12 @@ export convert0(e:ParseTree):Code := ( -- e.g. x := ... is t:Token do convertTokenAssignment(t, b.rhs) -- e.g. (x,y,z) := (...) - is p:Parentheses do convertParallelAssignment(p, b.rhs, b.Operator.dictionary) + is p:Parentheses do convertParallelAssignment(true, p, b.rhs) + is p:EmptyParentheses do Code(parallelAssignmentCode( + true, + CodeSequence(), + convert(b.rhs), + combinePositionR(p.left.position, treePosition(b.rhs)))) -- e.g. - Matrix := ... -- TODO: can #T be implemented here? is u:Unary do convertUnaryInstallCode(UnaryInstallMethodFun, @@ -261,7 +230,7 @@ export convert0(e:ParseTree):Code := ( if n.newParent == dummyTree then if n.newInitializer == dummyTree -- e.g. new ChainComplex := ChainComplex => T -> ... - then Code(binaryCode(AssignNewFun, + then Code(binaryCode(NewS.symbol, convert(n.newClass), convert(b.rhs), pos)) -- e.g. new Set from List := Set => ... @@ -288,25 +257,25 @@ export convert0(e:ParseTree):Code := ( then ( when b.lhs is a:Adjacent do Code(augmentedAssignmentCode( - b.Operator.entry, convert(b.lhs), convert(b.rhs), AdjacentS.symbol, pos)) + b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) is o:Unary do Code(augmentedAssignmentCode( - b.Operator.entry, convert(b.lhs), convert(b.rhs), o.Operator.entry, pos)) + b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) is o:Postfix do Code(augmentedAssignmentCode( - b.Operator.entry, convert(b.lhs), convert(b.rhs), o.Operator.entry, pos)) + b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) is o:Binary do Code(augmentedAssignmentCode( - b.Operator.entry, convert(b.lhs), convert(b.rhs), o.Operator.entry, pos)) + b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) is t:Token do Code(augmentedAssignmentCode( - b.Operator.entry, convert(b.lhs), convert(b.rhs), t.entry, pos)) + b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) else Code(augmentedAssignmentCode( - b.Operator.entry, convert(b.lhs), convert(b.rhs), dummySymbol, pos)) + b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) ) - else Code(binaryCode(b.Operator.entry.binary, convert(b.lhs), convert(b.rhs), pos)) + else Code(binaryCode(b.Operator.entry, convert(b.lhs), convert(b.rhs), pos)) ) is u:Unary do ( if u.Operator.word == CommaW then Code(sequenceCode(makeCodeSequence(e, CommaW), pos)) else if u.Operator.word == SemicolonW then Code(semiCode( makeCodeSequence(e, SemicolonW), pos)) - else Code(unaryCode(u.Operator.entry.unary, convert(u.rhs), pos))) - is u:Postfix do Code(unaryCode(u.Operator.entry.postfix, convert(u.lhs), pos)) + else Code(unaryCode(u.Operator.entry, convert(u.rhs), pos))) + is u:Postfix do Code(unaryCode(u.Operator.entry, convert(u.lhs), pos)) is q:Quote do ( token := q.rhs; sym := token.entry; @@ -329,10 +298,12 @@ export convert0(e:ParseTree):Code := ( Code(localSymbolClosureCode(nestingDepth(sym.frameID, token.dictionary), sym, pos))) is i:IfThen do Code(ifCode(convert(i.predicate), convert(i.thenClause), NullCode, pos)) is i:IfThenElse do Code(ifCode(convert(i.predicate), convert(i.thenClause), convert(i.elseClause), pos)) - is i:Try do Code(tryCode(convert(i.primary), NullCode, NullCode, pos)) - is i:TryThen do Code(tryCode(convert(i.primary), convert(i.sequel), NullCode, pos)) - is i:TryThenElse do Code(tryCode(convert(i.primary), convert(i.sequel), convert(i.alternate), pos)) - is i:TryElse do Code(tryCode(convert(i.primary), NullCode, convert(i.alternate), pos)) + is i:Try do Code(tryCode(convert(i.primary), NullCode, NullCode, NullCode, 0, 0, pos)) + is i:TryThen do Code(tryCode(convert(i.primary), convert(i.sequel), NullCode, NullCode, 0, 0, pos)) + is i:TryThenElse do Code(tryCode(convert(i.primary), convert(i.sequel), convert(i.alternate), NullCode, 0, 0, pos)) + is i:TryElse do Code(tryCode(convert(i.primary), NullCode, convert(i.alternate), NullCode, 0, 0, pos)) + is i:TryDo do Code(tryCode(convert(i.primary), NullCode, NullCode, convert(i.doClause), i.dictionary.frameID, i.dictionary.framesize, pos)) + is i:TryThenDo do Code(tryCode(convert(i.primary), convert(i.sequel), NullCode, convert(i.doClause), i.dictionary.frameID, i.dictionary.framesize, pos)) is i:Catch do Code(catchCode(convert(i.primary), pos)) is w:WhileDo do Code(whileDoCode( convert(w.predicate), convert(w.doClause), pos)) is w:WhileListDo do Code(whileListDoCode(convert(w.predicate), convert(w.listClause), convert(w.doClause), pos)) diff --git a/M2/Macaulay2/d/ctype.d b/M2/Macaulay2/d/ctype.d index 6a15b13a536..7e676c240b7 100644 --- a/M2/Macaulay2/d/ctype.d +++ b/M2/Macaulay2/d/ctype.d @@ -67,11 +67,12 @@ ismathoperator(c1:char, c2:char):bool := ( ismathoperator((int(uchar(c1)) << 8) | int(uchar(c2)))); export isvalidsymbol(s:string):bool := ( - if !isalpha(s.0) then return false; - if ismathoperator(s.0, s.1) && length(s) == utf8charlength(s.0) + n := length(s); + if n > 0 && !isalpha(s.0) then return false; + if n > 1 && ismathoperator(s.0, s.1) && n == utf8charlength(s.0) then return true; - for i from 0 to length(s) - 1 do ( - if !isalnum(s.i) || ismathoperator(s.i, s.(i + 1)) + for i from 0 to n - 1 do ( + if !isalnum(s.i) || (n > i + 1 && ismathoperator(s.i, s.(i + 1))) then return false); true); diff --git a/M2/Macaulay2/d/debug.c b/M2/Macaulay2/d/debug.c deleted file mode 100644 index d763e8de6c8..00000000000 --- a/M2/Macaulay2/d/debug.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#ifndef NDEBUG - -#include "debug.h" -#include -#include -#include - -void *trapaddr = (void *)1; -int trapcount = 0; -int trapset = 0; -size_t trapsize = (size_t)-1; - -void trap(void) {} /* I used to be concerned that this function would get optimized away, but it isn't static ... */ - -void *pointers[10]; /* during debugging we can put pointers here, visible to the garbage collector */ -void trapchk(void *p) { - trapcount++; - if (trapcount == trapset || p == trapaddr || p == (void *)~(intptr_t)trapaddr) trap(); -} -void trapchk_size(size_t n) { - trapcount++; - if (trapcount == trapset || trapsize == n) trap(); -} - -#define STDERR 2 -int badBlock() { - char buf[120]; - sprintf(buf,"%s:%d: internal error: smashed block in memory block allocator\n",__FILE__,__LINE__); - if (write(STDERR,buf,strlen(buf))) abort(); - abort(); -} - -#endif /* NDEBUG */ - -/* - Local Variables: - compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d " - End: -*/ diff --git a/M2/Macaulay2/d/debug.h b/M2/Macaulay2/d/debug.h deleted file mode 100644 index 75fb24b7c2f..00000000000 --- a/M2/Macaulay2/d/debug.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef _DEBUG_H_ -#define _DEBUG_H_ 1 - -#ifndef NDEBUG - -#if defined(__cplusplus) -extern "C" { -#endif - - extern void trap(void); - extern void *trapaddr; - extern int trapcount; - extern int trapset; - extern void trapchk(void *); - extern void trapchk_size(size_t); - extern int badBlock(); - - static __attribute__ ((unused)) void debug_version() {} - -#if defined(__cplusplus) -} -#endif - -#define TRAPCHK(p) trapchk(p) -#define TRAPCHK_SIZE(n) trapchk_size(n) - -#else - -#define TRAPCHK(p) -#define TRAPCHK_SIZE(n) - -#endif -#endif - diff --git a/M2/Macaulay2/d/debugging.dd b/M2/Macaulay2/d/debugging.dd index b138752b7fb..5018473a861 100644 --- a/M2/Macaulay2/d/debugging.dd +++ b/M2/Macaulay2/d/debugging.dd @@ -5,7 +5,10 @@ use hashtables; -- debugger is not defined until interp.dd -- so we use a pointer and populate it later. -dummydebugger(f:Frame,c:Code):Expr := nullE; +dummydebugger(f:Frame,c:Code):Expr := ( + Ccode(void, "(void)", f); + Ccode(void, "(void)", c); + nullE); export debuggerpointer := dummydebugger; -- enters the debugger (defined in interp.dd) at that spot @@ -31,31 +34,30 @@ export tostring(c:Code):string := ( is x:threadSymbolClosureCode do concatenate(array(string)("(thread ", x.symbol.word.name, ")")) -- is x:localMemoryReferenceCode do concatenate(array(string)("(local-fetch ", tostring(x.frameindex), " ", tostring(x.nestingDepth), ")")) - is x:globalMemoryReferenceCode do concatenate(array(string)("(global-fetch ", tostring(x.frameindex), ")")) - is x:threadMemoryReferenceCode do concatenate(array(string)("(thread-fetch ", tostring(x.frameindex), ")")) + is x:globalMemoryReferenceCode do concatenate(array(string)("(global-fetch ", tostring(x.var.frameindex), ")")) + is x:threadMemoryReferenceCode do concatenate(array(string)("(thread-fetch ", tostring(x.var.frameindex), ")")) -- is x:localAssignmentCode do concatenate(array(string)("(local-assign ", tostring(x.frameindex), " ", tostring(x.nestingDepth), " ", tostring(x.rhs), ")")) is x:globalAssignmentCode do concatenate(array(string)("(global-assign ", x.lhs.word.name, " ", tostring(x.rhs), ")")) - is x:augmentedAssignmentCode do concatenate(array(string)("(augmented-assign ", x.oper.word.name, " ", tostring(x.lhs), " ", tostring(x.rhs), " ", x.info.word.name, ")")) - is x:parallelAssignmentCode do ( - n := length(x.nestingDepth); - concatenate(array(string)( - "(parallel-assign (", between(" ", - new array(string) len n do - for i from 0 to n-1 do - if x.lhs.i == dummySymbol - then provide concatenate(array(string)("(", tostring(x.frameindex.i), " ", tostring(x.nestingDepth.i), ")")) - else provide join("'", x.lhs.i.word.name)), - ") ", tostring(x.rhs), ")"))) + is x:augmentedAssignmentCode do concatenate(array(string)("(augmented-assign ", x.oper.word.name, " ", tostring(x.lhs), " ", tostring(x.rhs), ")")) + is x:parallelAssignmentCode do concatenate(array(string)("(parallel-assign ( ", if x.colon then ":=" else "=", " (", + between(" ", new array(string) len length(x.lhs) do foreach s in x.lhs do provide tostring(s)), ") ", tostring(x.rhs), ")")) -- TODO: what is an example that yields this? is x:evaluatedCode do concatenate(array(string)("(expr ", tostring(hash(x.expr)), ")")) -- is x:ifCode do concatenate(array(string)("(if ", tostring(x.predicate), " then: ", tostring(x.thenClause), " else: ", tostring(x.elseClause), ")")) - is x:tryCode do concatenate(array(string)("(try ", tostring(x.code), " ", tostring(x.thenClause), " ", tostring(x.elseClause), ")")) + is x:tryCode do concatenate(array(string)( + "(try ", + " framesize: ", tostring(x.framesize), + " frameID: ", tostring(x.frameID), + " code: ", tostring(x.code), + " then: ", tostring(x.thenClause), + " else: ", tostring(x.elseClause), + " do: ", tostring(x.doClause), ")")) is x:catchCode do concatenate(array(string)("(catch ", tostring(x.code), ")")) -- - is x:unaryCode do concatenate(array(string)("(1-OP ", getUnopName(x.f).word.name, " ", tostring(x.rhs), ")")) - is x:binaryCode do concatenate(array(string)("(2-OP ", getBinopName(x.f).word.name, " ", tostring(x.lhs), " ", tostring(x.rhs), ")")) + is x:unaryCode do concatenate(array(string)("(1-OP ", x.oper.word.name, " ", tostring(x.rhs), ")")) + is x:binaryCode do concatenate(array(string)("(2-OP ", x.oper.word.name, " ", tostring(x.lhs), " ", tostring(x.rhs), ")")) is x:ternaryCode do concatenate(array(string)("(3-OP ", getTernopName(x.f).word.name, " ", tostring(x.arg1), " ", tostring(x.arg2), " ", tostring(x.arg3), ")")) is x:multaryCode do concatenate(array(string)("(OP ", getMultopName(x.f).word.name, " ", between(" ", new array(string) len length(x.args) do foreach c in x.args do provide tostring(c)), ")")) @@ -109,28 +111,30 @@ export toList(c:Code):Expr := ( is x:threadSymbolClosureCode do list(toExpr("thread"), Expr(SymbolBody(x.symbol)), toExpr(x.symbol.frameindex)) -- is x:localMemoryReferenceCode do list(toExpr("local-fetch"), toExpr(x.frameindex), toExpr(x.nestingDepth)) - is x:globalMemoryReferenceCode do list(toExpr("global-fetch"), toExpr(x.frameindex)) - is x:threadMemoryReferenceCode do list(toExpr("thread-fetch"), toExpr(x.frameindex)) + is x:globalMemoryReferenceCode do list(toExpr("global-fetch"), toExpr(x.var.frameindex)) + is x:threadMemoryReferenceCode do list(toExpr("thread-fetch"), toExpr(x.var.frameindex)) -- is x:localAssignmentCode do list(toExpr("local-assign"), toExpr(x.frameindex), toExpr(x.nestingDepth), seq(toExpr(x.rhs))) is x:globalAssignmentCode do list(toExpr("global-assign"), Expr(SymbolBody(x.lhs)), toExpr(x.lhs.frameindex), seq(toExpr(x.rhs))) is x:augmentedAssignmentCode do list(toExpr("augmented-assign"), Expr(SymbolBody(x.oper)), seq(toExpr(x.lhs), toExpr(x.rhs))) - is x:parallelAssignmentCode do list(toExpr("parallel-assign"), seq( - Expr(new Sequence len length(x.nestingDepth) do - for i from 0 to length(x.nestingDepth) - 1 do - if x.lhs.i == dummySymbol - then provide seq(toExpr(x.frameindex.i), toExpr(x.nestingDepth.i)) - else provide seq(Expr(SymbolBody(x.lhs.i)), toExpr(x.lhs.i.frameindex))), - toExpr(x.rhs))) + is x:parallelAssignmentCode do list(toExpr("parallel-assign"), Expr(SymbolBody(if x.colon then ColonEqualS.symbol else EqualS.symbol)), + Expr(new Sequence len length(x.lhs) do foreach s in x.lhs do provide toExpr(s)), toExpr(x.rhs)) -- used along augmentedAssignmentCode is x:evaluatedCode do toExpr(tostring(c)) -- is x:ifCode do list(toExpr("if"), seq(toExpr(x.predicate), list(toExpr("then"), toExpr(x.thenClause)), list(toExpr("else"), toExpr(x.elseClause)))) - is x:tryCode do list(toExpr("try"), seq(toExpr(x.code), list(toExpr("then"), toExpr(x.thenClause)), list(toExpr("else"), toExpr(x.elseClause)))) + is x:tryCode do list(toExpr("try"), + Expr(Sequence( + list(toExpr("framesize"), toExpr(x.framesize)), + list(toExpr("frameID"), toExpr(x.frameID)), + list(toExpr("code"), toExpr(x.code)), + list(toExpr("then"), toExpr(x.thenClause)), + list(toExpr("else"), toExpr(x.elseClause)), + list(toExpr("do"), toExpr(x.doClause))))) is x:catchCode do list(toExpr("catch"), seq(toExpr(x.code))) -- - is x:unaryCode do list(toExpr("1-OP"), Expr(SymbolBody(getUnopName(x.f))), seq(toExpr(x.rhs))) - is x:binaryCode do list(toExpr("2-OP"), Expr(SymbolBody(getBinopName(x.f))), seq(toExpr(x.lhs), toExpr(x.rhs))) + is x:unaryCode do list(toExpr("1-OP"), Expr(SymbolBody(x.oper)), seq(toExpr(x.rhs))) + is x:binaryCode do list(toExpr("2-OP"), Expr(SymbolBody(x.oper)), seq(toExpr(x.lhs), toExpr(x.rhs))) is x:ternaryCode do list(toExpr("3-OP"), Expr(SymbolBody(getTernopName(x.f))), seq(toExpr(x.arg1), toExpr(x.arg2), toExpr(x.arg3))) is x:multaryCode do list(toExpr("OP"), Expr(SymbolBody(getMultopName(x.f))), Expr(new Sequence len length(x.args) do foreach c in x.args do provide toExpr(c))) @@ -222,5 +226,206 @@ locate(e:Expr):Expr := ( is c:Pseudocode do locate(codePosition(c.code)) is s:SymbolClosure do locate(s.symbol.position) is s:SpecialExpr do locate(s.e) + is err:Error do locate(err.position) else WrongArg("a function, symbol, sequence, or null")); setupfun("locate", locate).Protected = false; -- will be overloaded in m2/methods.m2 + +invalidFilePosition := "-* invalid file position *-"; + +export tostringFilePosition(e:Expr):string := ( + when e + is p:List do ( + if length(p.v) < 3 then return invalidFilePosition; + when p.v.0 + is s:stringCell do ( + r := s.v; + for i from 0 to length(r) - 1 do ( + if r.i == ' ' + then ( + r = format(r); + break)); + when p.v.1 + is n:ZZcell do r = r + ":" + tostring(n.v) + else return invalidFilePosition; + when p.v.2 + is n:ZZcell do r = r + ":" + tostring(n.v) + else return invalidFilePosition; + if length(p.v) == 4 then ( + when p.v.3 + is n:ZZcell do r = r + "(" + tostring(n.v) + ")" + else return invalidFilePosition) + else if length(p.v) > 4 then ( + when p.v.3 + is n:ZZcell do r = r + "-" + tostring(n.v) + else return invalidFilePosition; + when p.v.4 + is n:ZZcell do r = r + ":" + tostring(n.v) + else return invalidFilePosition); + r) + else invalidFilePosition) + else invalidFilePosition); + +processErrorArgs0(a:Sequence):string := ( + r := concatenate(new array(string) len length(a) at i do ( + when a.i + is s:stringCell do provide s.v + is s:SymbolClosure do provide "'" + s.symbol.word.name + "'" + else provide robustPrintString(a.i))); + foreach x in a do ( + when x + is s:SymbolClosure do r = concatenate(array(string)( + r, "\n", + tostringFilePosition(locate(s.symbol.position)), + ": here is the first use of '", + s.symbol.word.name, "'")) + else nothing); + r); + +processErrorArgs0(e:Expr):string := ( + when e + is a:Sequence do processErrorArgs0(a) + else processErrorArgs0(seq(e))); + +processErrorArgs(e:Expr):Expr := toExpr(processErrorArgs0(e)); +setupfun("processErrorArgs", processErrorArgs); + +errorfun(e:Expr):Expr := ( + when e + is s:stringCell do buildErrorPacket(s.v) + is err:Error do (err.printed = false; e) + is x:SpecialExpr do errorfun(x.e) + else buildErrorPacket(processErrorArgs0(e))); +setupfun("error",errorfun); + +newError(e:Expr):Expr := ( + when e + is a:Sequence do ( + if length(a) == 2 then ( + when a.0 + is T:HashTable do ( + if ancestor(T, errorClass) then Expr(SpecialExpr(T, + Error(dummyPosition, processErrorArgs0(a.1), T, false, + dummyFrame))) + else WrongArg(1, "a type of error")) + else WrongArgHashTable(1)) + else WrongNumArgs(2)) + else WrongNumArgs(2)); +installMethod(NewFromS, errorClass, thingClass, newError); + +------------ +-- parse -- +----------- + +toExpr(t:Token):Expr := list(toExpr("Token"), toExpr(t.word.name)); +export toExpr(e:ParseTree):Expr := ( + when e + is t:Token do toExpr(t) + is p:Parentheses do list( + toExpr("Parentheses"), + toExpr(p.left), + toExpr(p.contents), + toExpr(p.right)) + is p:EmptyParentheses do list( + toExpr("EmptyParentheses"), + toExpr(p.left), + toExpr(p.right)) + is a:Adjacent do list( + toExpr("Adjacent"), + toExpr(a.lhs), + toExpr(a.rhs)) + is a:Arrow do list( + toExpr("Arrow"), + toExpr(a.lhs), + toExpr(a.rhs)) + is q:Quote do list( + toExpr("Quote"), + toExpr(q.rhs)) + is q:GlobalQuote do list( + toExpr("GlobalQuote"), + toExpr(q.rhs)) + is q:ThreadQuote do list( + toExpr("ThreadQuote"), + toExpr(q.rhs)) + is q:LocalQuote do list( + toExpr("LocalQuote"), + toExpr(q.rhs)) + is u:Unary do list( + toExpr("Unary"), + toExpr(u.Operator), + toExpr(u.rhs)) + is b:Binary do list( + toExpr("Binary"), + toExpr(b.lhs), + toExpr(b.Operator), + toExpr(b.rhs)) + is p:Postfix do list( + toExpr("Postfix"), + toExpr(p.lhs), + toExpr(p.Operator)) + is i:IfThen do list( + toExpr("IfThen"), + toExpr(i.predicate), + toExpr(i.thenClause)) + is i:IfThenElse do list( + toExpr("IfThenElse"), + toExpr(i.predicate), + toExpr(i.thenClause), + toExpr(i.elseClause)) + is t:Try do list( + toExpr("Try"), + toExpr(t.primary)) + is t:TryThen do list( + toExpr("TryThen"), + toExpr(t.primary), + toExpr(t.sequel)) + is t:TryThenElse do list( + toExpr("TryThenElse"), + toExpr(t.primary), + toExpr(t.sequel), + toExpr(t.alternate)) + is t:TryElse do list( + toExpr("TryElse"), + toExpr(t.primary), + toExpr(t.alternate)) + is t:TryThenDo do list(Sequence( + toExpr("TryThenDo"), + toExpr(t.primary), + toExpr(t.sequel), + toExpr(t.variable), + toExpr(t.doClause))) + is t:TryDo do list( + toExpr("TryDo"), + toExpr(t.primary), + toExpr(t.variable), + toExpr(t.doClause)) + is c:Catch do list( + toExpr("Catch"), + toExpr(c.primary)) + is w:WhileDo do list( + toExpr("WhileDo"), + toExpr(w.predicate), + toExpr(w.doClause)) + is w:WhileListDo do list( + toExpr("WhileListDo"), + toExpr(w.predicate), + toExpr(w.listClause), + toExpr(w.doClause)) + is w:WhileList do list( + toExpr("WhileList"), + toExpr(w.predicate), + toExpr(w.listClause)) + is f:For do list(Sequence( + toExpr("For"), + toExpr(f.variable), + toExpr(f.inClause), + toExpr(f.fromClause), + toExpr(f.toClause), + toExpr(f.whenClause), + toExpr(f.listClause), + toExpr(f.doClause))) + is n:New do list( + toExpr("New"), + toExpr(n.newClass), + toExpr(n.newParent), + toExpr(n.newInitializer)) + is dummy do list(toExpr("dummy"))); diff --git a/M2/Macaulay2/d/engine.dd b/M2/Macaulay2/d/engine.dd index d820e8b4f0e..10cf1c86588 100644 --- a/M2/Macaulay2/d/engine.dd +++ b/M2/Macaulay2/d/engine.dd @@ -56,6 +56,8 @@ export RawMutableMatrixArray := array(RawMutableMatrix); export RawMutableMatrixArrayOrNull := array(RawMutableMatrix) or null; export RawRingElementArray := array(RawRingElement); export RawRingElementArrayOrNull := array(RawRingElement) or null; +export RawRingElementArrayArray := array(RawRingElementArray); +export RawRingElementArrayArrayOrNull := array(RawRingElementArray) or null; export RawArrayPair := { monoms:array(RawMonomial), coeffs:array(RawRingElement) }; export RawArrayPairOrNull := RawArrayPair or null; export RawMonomialPair := { a:RawMonomial, b:RawMonomial }; diff --git a/M2/Macaulay2/d/equality.dd b/M2/Macaulay2/d/equality.dd index 8f4345ff03c..bf13bccfa93 100644 --- a/M2/Macaulay2/d/equality.dd +++ b/M2/Macaulay2/d/equality.dd @@ -154,6 +154,12 @@ export equal(lhs:Expr,rhs:Expr):Expr := ( if strictequality(x.v,y.v) then True else False ) else False) + is x:CCicell do ( + when rhs + is y:CCicell do ( + if strictequality(x.v.re,y.v.re) && strictequality(x.v.im,y.v.im) then True else False + ) + else False) is x:SymbolClosure do ( when rhs is y:SymbolClosure do ( @@ -263,6 +269,10 @@ export equal(lhs:Expr,rhs:Expr):Expr := ( when rhs is y:atomicIntCell do if x == y then True else False else False) + is x:mutexCell do ( + when rhs + is y:mutexCell do if x == y then True else False + else False) ); -- Local Variables: diff --git a/M2/Macaulay2/d/evaluate.d b/M2/Macaulay2/d/evaluate.d index 2982715dcd5..cab60aa6758 100644 --- a/M2/Macaulay2/d/evaluate.d +++ b/M2/Macaulay2/d/evaluate.d @@ -36,6 +36,8 @@ export applyIteratorS := setupvar("applyIterator", nullE); export joinIteratorsS := setupvar("joinIterators", nullE); export pairsIteratorS := setupvar("pairsIterator", nullE); +export toExpr(err:Error):Expr := Expr(SpecialExpr(Class(err), Expr(err))); + eval(c:Code):Expr; applyEE(f:Expr,e:Expr):Expr; export evalAllButTail(c:Code):Code := while true do c = ( @@ -1187,46 +1189,123 @@ globalAssignment(frameindex:int,t:Symbol,newvalue:Expr):Expr := ( -- frameID = 0 vals.frameindex = newvalue; newvalue); -assignment(nestingDepth:int,frameindex:int,t:Symbol,newvalue:Expr):Expr := ( - if nestingDepth == -1 - then globalAssignment(frameindex,t,newvalue) - else localAssignment(nestingDepth,frameindex,newvalue)); - globalAssignmentFun(x:globalAssignmentCode):Expr := ( t := x.lhs; newvalue := eval(x.rhs); when newvalue is Error do return newvalue else nothing; globalAssignment(t.frameindex,t,newvalue)); +ParallelAssignmentError(n:int):Expr := buildErrorPacket( + "parallel assignment expected a sequence of length " + tostring(n)); +ParallelAssignmentErrorAt(n:int):Expr := buildErrorPacket( + "parallel assignment failure at argument " + tostring(n)); parallelAssignmentFun(x:parallelAssignmentCode):Expr := ( - syms := x.lhs; - nestingDepth := x.nestingDepth; - frameindex := x.frameindex; - nlhs := length(frameindex); - foreach sym in syms do if sym.Protected then return buildErrorPacket("assignment to protected variable '" + sym.word.name + "'"); - value := eval(x.rhs); - when value - is Error do return value - is values:Sequence do - if nlhs == length(values) then ( - for i from 0 to nlhs-1 do ( - r := assignment(nestingDepth.i,frameindex.i,syms.i,values.i); - when r is Error do return r else nothing; - ); - value - ) - else buildErrorPacket("parallel assignment: expected a sequence of " + tostring(nlhs) + " values") - else buildErrorPacket("parallel assignment: expected a sequence of " + tostring(nlhs) + " values")); + nlhs := length(x.lhs); + value := eval(x.rhs); + when value is Error do return value else nothing; + -- (x) = y should behave just like x = y + if nlhs == 1 then ( + when value is a:Sequence do ( + -- unless y is a sequence of length 1, then it behaves like x = y#0 + if length(a) == 1 then nothing + else return ParallelAssignmentError(1)) + is a:List do ( + if length(a.v) == 1 then nothing + else return ParallelAssignmentError(1)) + else value = seq(value)); + klass := sequenceClass; + when value is a:List do ( + value = Expr(a.v); + klass = a.Class) + else nothing; + when value + is values:Sequence do ( + if nlhs == length(values) then ( + pos := codePosition(x.rhs); + e := nullE; + result := new Sequence len nlhs at i do provide ( + when e is Error do nullE + else ( + c := Code(evaluatedCode(values.i, pos)); + r := when x.lhs.i + is y:globalMemoryReferenceCode do ( + globalAssignment(y.var.frameindex, y.var, values.i)) + is y:localMemoryReferenceCode do ( + localAssignment(y.nestingDepth, y.frameindex, values.i)) + is y:threadMemoryReferenceCode do ( + globalAssignment(y.var.frameindex, y.var, values.i)) + is y:binaryCode do ( + codeseq := CodeSequence( + convertGlobalOperator(y.oper), y.lhs, y.rhs, c); + if x.colon then InstallMethodFun(codeseq) + else ( + if y.oper == DotS.symbol || y.oper == SharpS.symbol + then AssignElemFun(y.lhs, y.rhs, c) + else InstallValueFun(codeseq))) + is y:adjacentCode do ( + codeseq := CodeSequence( + convertGlobalOperator(AdjacentS.symbol), + y.lhs, y.rhs, c); + if x.colon then InstallMethodFun(codeseq) + else InstallValueFun(codeseq)) + is y:unaryCode do ( + oper := convertGlobalOperator(y.oper); + if x.colon then UnaryInstallMethodFun(oper, y.rhs, c) + else UnaryInstallValueFun(oper, y.rhs, c)) + is y:sequenceCode do ( + parallelAssignmentFun(parallelAssignmentCode(x.colon, + y.x, c, y.position))) + is y:listCode do ( + parallelAssignmentFun(parallelAssignmentCode(x.colon, + y.y, c, y.position))) + is y:arrayCode do ( + parallelAssignmentFun(parallelAssignmentCode(x.colon, + y.z, c, y.position))) + is y:angleBarListCode do ( + parallelAssignmentFun(parallelAssignmentCode(x.colon, + y.t, c, y.position))) + is y:newCode do ( + if x.colon + then AssignNewFun(y.newClause, c) + else ParallelAssignmentErrorAt(i + 1)) + is y:newOfCode do ( + if x.colon + then AssignNewOfFun(y.newClause, y.ofClause, c) + else ParallelAssignmentErrorAt(i + 1)) + is y:newFromCode do ( + if x.colon + then AssignNewFromFun(y.newClause, y.fromClause, c) + else ParallelAssignmentErrorAt(i + 1)) + is y:newOfFromCode do ( + if x.colon + then AssignNewOfFromFun(CodeSequence( + y.newClause, y.ofClause, y.fromClause, c)) + else ParallelAssignmentErrorAt(i + 1)) + is nullCode do nullE + else ParallelAssignmentErrorAt(i + 1); + when r + is Error do ( + e = r; + nullE) + else r)); + when e + is Error do e + else if nlhs == 1 then result.0 + else ( + if klass == sequenceClass then Expr(result) + else list(klass, result))) + else ParallelAssignmentError(nlhs)) + else ParallelAssignmentError(nlhs)); -- helper function used when evaluating tryCode and by null coalescion --- tryEvalSuccess is true unless an (non-interrupting) error occurred -threadLocal tryEvalSuccess := true; +-- tryCaughtError is false unless an (non-interrupting) error occurred +threadLocal tryCaughtError := false; tryEval(c:Code):Expr := ( oldSuppressErrors := SuppressErrors; SuppressErrors = true; p := eval(c); if !SuppressErrors then ( -- eval could have turned it off - tryEvalSuccess = true) + tryCaughtError = false) else ( SuppressErrors = oldSuppressErrors; when p @@ -1238,14 +1317,46 @@ tryEval(c:Code):Expr := ( err.message == continueMessageWithArg || err.message == unwindMessage || err.message == throwMessage) - then tryEvalSuccess = true - else tryEvalSuccess = false) - else tryEvalSuccess = true); + then tryCaughtError = false + else tryCaughtError = true) + else tryCaughtError = false); p); +evalTryCode(c:tryCode):Expr := ( + ret := tryEval(c.code); + -- certain errors should not be caught, see above + if !tryCaughtError then when ret is Error do ret + -- then ... + else if c.thenClause != NullCode then eval(c.thenClause) else ret + -- else ... + else if c.elseClause != NullCode then eval(c.elseClause) + -- except .. do ... + else if c.doClause != NullCode then ( + when ret is err:Error do ( + localFrame = Frame( + localFrame, c.frameID, c.framesize, false, + new Sequence len c.framesize do provide nullE); + -- variable specified by "except" + localFrame.values.0 = toExpr(err); + p := eval(c.doClause); + localFrame = localFrame.outerFrame; + p) + -- shouldn't happen since tryCaughtError is only true when ret is an Error + else buildErrorPacket("internal error: unable to catch error")) + else nullE); + +trapfun(c:Code):Expr := ( + ret := tryEval(c); + if !tryCaughtError + then when ret is Error do ret else seq(ret, nullE) + else when ret is err:Error do seq(nullE, toExpr(err)) + -- shouldn't happen since tryCaughtError is only true when ret is an Error + else buildErrorPacket("internal error: unable to trap error")); +setupop(trapS, trapfun); + nullify(c:Code):Expr := ( e := tryEval(c); - if tryEvalSuccess + if !tryCaughtError then ( when e is Nothing do e @@ -1262,10 +1373,49 @@ nullCoalescion(lhs:Code,rhs:Code):Expr := ( else e); setup(QuestionQuestionS, nullify, nullCoalescion); +-- TODO: is there a better way to declare this? +augmentedAssignmentFun(c:augmentedAssignmentCode):Expr; +header "static parse_Expr augmentedAssignmentFun(parse_augmentedAssignmentCode c);"; +augmentedParallelAssignmentFun(oper:Symbol, lhs:CodeSequence, rhs:Code):Expr:= ( + n := length(lhs); + rexpr := eval(rhs); + when rexpr + is Error do return rexpr else nothing; + if n == 1 then rexpr = seq(rexpr); + when rexpr + is rvals:Sequence do ( + if length(rvals) == n then ( + pos := codePosition(rhs); + e := nullE; + result := new Sequence len n at i do provide ( + when e is Error do nullE + else ( + r := augmentedAssignmentFun(augmentedAssignmentCode(oper, + lhs.i, Code(evaluatedCode(rvals.i, pos)), pos)); + when r is Error do ( + e = r; + nullE) + else r)); + when e + is Error do e + else if n == 1 then result.0 else Expr(result))) + else ParallelAssignmentError(n)); + augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := ( when lookup(x.oper.word, augmentedAssignmentOperatorTable) is null do buildErrorPacket("unknown augmented assignment operator") is s:Symbol do ( + -- parallel assignment? + when x.lhs + is y:sequenceCode do ( + return augmentedParallelAssignmentFun(x.oper, y.x, x.rhs)) + is y:listCode do ( + return augmentedParallelAssignmentFun(x.oper, y.y, x.rhs)) + is y:arrayCode do ( + return augmentedParallelAssignmentFun(x.oper, y.z, x.rhs)) + is y:angleBarListCode do ( + return augmentedParallelAssignmentFun(x.oper, y.t, x.rhs)) + else nothing; -- evaluate the left-hand side first lexpr := nullE; if s.word.name === "??" -- x ??= y is treated like x ?? (x = y) @@ -1288,33 +1438,35 @@ augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := ( else return r) else return r); -- if not, use default behavior - left := evaluatedCode(lexpr, codePosition(x.lhs)); + c := ( + if s.word.name === "??" then x.rhs + else Code(binaryCode(s, + Code(evaluatedCode(lexpr, codePosition(x.lhs))), + x.rhs, x.position))); when x.lhs is y:globalMemoryReferenceCode do ( - r := s.binary(Code(left), x.rhs); - when r is e:Error do r - else globalAssignment(y.frameindex, x.info, r)) + r := eval(c); + when r is Error do r + else globalAssignment(y.var.frameindex, y.var, r)) is y:localMemoryReferenceCode do ( - r := s.binary(Code(left), x.rhs); - when r is e:Error do r + r := eval(c); + when r is Error do r else localAssignment(y.nestingDepth, y.frameindex, r)) is y:threadMemoryReferenceCode do ( - r := s.binary(Code(left), x.rhs); - when r is e:Error do r - else globalAssignment(y.frameindex, x.info, r)) + r := eval(c); + when r is Error do r + else globalAssignment(y.var.frameindex, y.var, r)) is y:binaryCode do ( - r := Code(binaryCode(s.binary, Code(left), x.rhs, x.position)); - if y.f == DotS.symbol.binary || y.f == SharpS.symbol.binary - then AssignElemFun(y.lhs, y.rhs, r) + if y.oper == DotS.symbol || y.oper == SharpS.symbol + then AssignElemFun(y.lhs, y.rhs, c) else InstallValueFun(CodeSequence( - convertGlobalOperator(x.info), y.lhs, y.rhs, r))) + convertGlobalOperator(y.oper), y.lhs, y.rhs, c))) is y:adjacentCode do ( - r := Code(binaryCode(s.binary, Code(left), x.rhs, x.position)); InstallValueFun(CodeSequence( - convertGlobalOperator(AdjacentS.symbol), y.lhs, y.rhs, r))) + convertGlobalOperator(AdjacentS.symbol), y.lhs, y.rhs, c))) is y:unaryCode do ( - r := Code(binaryCode(s.binary, Code(left), x.rhs, x.position)); - UnaryInstallValueFun(convertGlobalOperator(x.info), y.rhs, r)) + UnaryInstallValueFun(convertGlobalOperator(y.oper), y.rhs, c)) + is nullCode do nullE -- for use w/ parallel assignment else buildErrorPacket( "augmented assignment not implemented for this code"))); @@ -1423,8 +1575,8 @@ export evalraw(c:Code):Expr := ( buildErrorPacket("unknown exception") )) else when c - is u:unaryCode do u.f(u.rhs) - is b:binaryCode do b.f(b.lhs,b.rhs) + is u:unaryCode do u.oper.unary(u.rhs) + is b:binaryCode do b.oper.binary(b.lhs,b.rhs) is b:adjacentCode do ( left := eval(b.lhs); when left @@ -1462,9 +1614,9 @@ export evalraw(c:Code):Expr := ( ); if r.frameindex>=length(f.values) then buildErrorPacket("frame error") else return f.values.(r.frameindex)) - is r:globalMemoryReferenceCode do return globalFrame.values.(r.frameindex) + is r:globalMemoryReferenceCode do return globalFrame.values.(r.var.frameindex) is r:threadMemoryReferenceCode do return ( - i := r.frameindex; + i := r.var.frameindex; v := threadFrame.values; if i < length(v) then v.i else nullE) is x:localAssignmentCode do ( @@ -1476,12 +1628,7 @@ export evalraw(c:Code):Expr := ( is c:augmentedAssignmentCode do augmentedAssignmentFun(c) is c:globalSymbolClosureCode do return Expr(SymbolClosure(globalFrame,c.symbol)) is c:threadSymbolClosureCode do return Expr(SymbolClosure(threadFrame,c.symbol)) - is c:tryCode do ( - ret := tryEval(c.code); - if tryEvalSuccess then - when ret is Error do ret - else if c.thenClause == NullCode then ret else eval(c.thenClause) - else if c.elseClause == NullCode then nullE else eval(c.elseClause)) + is c:tryCode do evalTryCode(c) is c:catchCode do ( p := eval(c.code); when p is err:Error do if err.message == throwMessage then err.value else p @@ -2072,6 +2219,7 @@ export notFun(a:Expr):Expr := if a == True then False else if a == False then Tr -- evaluate.d depends on hashtables.dd, so we use a pointer -- to evaluate methods in hashtables.dd before it is defined. +applyEEpointer = applyEE; applyEEEpointer = applyEEE; -- Local Variables: diff --git a/M2/Macaulay2/d/expr.d b/M2/Macaulay2/d/expr.d index 7958fd5a315..4a65238a88e 100644 --- a/M2/Macaulay2/d/expr.d +++ b/M2/Macaulay2/d/expr.d @@ -22,20 +22,13 @@ threadLocal export recursionDepth := 0; --Maximum function depth before triggering errors threadLocal export recursionLimit := 300; - -threadCounter := 0; -threadLocal HashCounter := ( - threadCounter = threadCounter + 1; - hash_t(1000000 + 3 + (threadCounter-1) * 10000 )); --- give 32-bit machines enough space to store a 64-bit hash code --- TODO: instead, allow 64-bit entries in the array of thread local variables -threadLocal HashCounterExtraBits := 0; +header "_Atomic uint64_t HashCounter = 1000004;"; export nextHash():hash_t := ( - if HashCounter == Ccode(hash_t, "18446744073709551615ull") -- check for integer overflow + currentHash := Ccode(hash_t, "atomic_load(&HashCounter)"); + if currentHash == Ccode(hash_t, "18446744073709551615ull") -- check for integer overflow then Ccode(void, " fprintf(stderr, \" *** hash code serial number counter overflow (too many mutable objects created)\\n\"); abort(); "); - HashCounter = HashCounter + 1; - HashCounter); + Ccode(hash_t, "atomic_fetch_add(&HashCounter, 1)")); ------------------------------------------------------------ -- hash codes for mutable objects that don't use nextHash -- @@ -208,9 +201,6 @@ export newParseinfo():parseinfo := parseinfo(nopr,nopr,nopr,parsefuns(dummyunary export dummyUnaryFun(c:Code):Expr := ( anywhereError("dummy unary function called"); nullE); -export dummyPostfixFun(c:Code):Expr := ( - anywhereError("dummy postfix function called"); - nullE); export dummyBinaryFun(c:Code,d:Code):Expr := ( anywhereError("dummy binary function called"); nullE); @@ -226,7 +216,7 @@ export emptySequenceE := Expr(emptySequence); export dummySymbol := Symbol( Word("-*dummy symbol*-",TCnone,hash_t(0),newParseinfo()),dummySymbolHash,dummyPosition, - dummyUnaryFun,dummyPostfixFun,dummyBinaryFun, + dummyUnaryFun,dummyBinaryFun, Macaulay2Dictionary.frameID,dummySymbolFrameIndex,1, false, -- not protected, so we can use it in parallelAssignmentFun false, @@ -355,6 +345,8 @@ export RRiClass := newbignumbertype(); export pointerClass := newbasictype(); export atomicIntClass := newbasictype(); export pseudocodeClosureClass := newtypeof(pseudocodeClass); +export mutexClass := newbasictype(); +export CCiClass := newbignumbertype(); -- all new types, dictionaries, and classes go just above this line, if possible, so hash codes don't change gratuitously! diff --git a/M2/Macaulay2/d/ffi.d b/M2/Macaulay2/d/ffi.d index 19a0be1ff3e..9f0c664046a 100644 --- a/M2/Macaulay2/d/ffi.d +++ b/M2/Macaulay2/d/ffi.d @@ -250,6 +250,10 @@ ffiIntegerType(e:Expr):Expr := ( else WrongNumArgs(2)); setupfun("ffiIntegerType", ffiIntegerType); +gmpFinalizer(obj:voidPointer, clientData:voidPointer):void := ( + Ccode(void, "(void)", obj); + Ccode(void, "mpz_clear((mpz_ptr)", clientData, ")")); + -- returns pointer to integer object with given value -- inputs (x:ZZ, y:ZZ, signed:Boolean) -- x = value @@ -270,7 +274,7 @@ ffiIntegerAddress(e:Expr):Expr := ( ptr := getMem(pointerSize); Ccode(void, "*(mpz_ptr *)", ptr, " = ", z); Ccode(void, "GC_REGISTER_FINALIZER(", ptr, ", ", - "(GC_finalization_proc)mpz_clear, ", z, ", 0, 0)"); + gmpFinalizer, ", ", z, ", 0, 0)"); toExpr(ptr)) else when a.2 is signed:Boolean do ( @@ -381,6 +385,10 @@ ffiRealType(e:Expr):Expr := ( else WrongArgZZ()); setupfun("ffiRealType", ffiRealType); +mpfrFinalizer(obj:voidPointer, clientData:voidPointer):void := ( + Ccode(void, "(void)", obj); + Ccode(void, "mpfr_clear((mpfr_ptr)", clientData, ")")); + -- returns pointer to real number object with given value -- inputs: (x:RR, y:ZZ) -- x = value @@ -400,10 +408,8 @@ ffiRealAddress(e:Expr):Expr := ( Ccode(void, "mpfr_set(", z, ", ", x.v, ", MPFR_RNDN)"); ptr := getMem(pointerSize); Ccode(void, "*(mpfr_ptr *)", ptr, " = ", z); - -- TODO: we get segfaults during garbage collection - -- if the following is uncommented - -- Ccode(void, "GC_REGISTER_FINALIZER(", ptr, ", ", - -- "(GC_finalization_proc)mpfr_clear, ", z, ", 0, 0)"); + Ccode(void, "GC_REGISTER_FINALIZER(", ptr, ", ", + mpfrFinalizer, ", ", z, ", 0, 0)"); toExpr(ptr)) else if bits == 32 || bits == 64 then ( ptr := getMemAtomic(bits / 8); diff --git a/M2/Macaulay2/d/gmp.d b/M2/Macaulay2/d/gmp.d index d68d94cd87a..c9d6a53066d 100644 --- a/M2/Macaulay2/d/gmp.d +++ b/M2/Macaulay2/d/gmp.d @@ -64,17 +64,24 @@ export CC := { re:RR, im:RR }; export CCorNull := CC or null; export CCcell := {+v:CC}; -dummy(x:RR):string := ""; -dummyi(x:RRi):string := ""; -- Added for MPFI -export tostringRRpointer := dummy; -export tostringRRipointer := dummyi; -- Added for MPFI -dummy(x:CC):string := ""; +export CCimutable := { re:RRimutable, im:RRimutable }; -export tonetCCpointer := dummy; +export CCi := { re:RRi, im:RRi }; -export tonetCCparenpointer := dummy; +export CCiorNull := CCi or null; +export CCicell := {+v:CCi}; + +dummyCCi(x:CCi):string := ""; + +export tostringCCipointer := dummyCCi; + +dummy(x:CCi):string := ""; + +export tonetCCipointer := dummy; + +export tonetCCiparenpointer := dummy; export min(x:int,y:int):int := if x> (n:int) : RRi := x << long(-n); export (x:CC) + (y:CC) : CC := toCC(x.re+y.re, x.im+y.im); +export (x:CCi) + (y:CCi) : CCi := toCCi(x.re+y.re, x.im+y.im); + export (x:CC) - (y:CC) : CC := toCC(x.re-y.re, x.im-y.im); +export (x:CCi) - (y:CCi) : CCi := toCCi(x.re-y.re, x.im-y.im); + export (x:RR) - (y:CC) : CC := toCC(x-y.re,-y.im); export (x:int) - (y:CC) : CC := toCC(x-y.re,-y.im); + +export (x:int) - (y:CCi) : CCi := toCCi(x-y.re,-y.im); + export (x:CC) - (y:int) : CC := toCC(x.re-y,x.im); export (x:CC) - (y:RR) : CC := toCC(x.re-y,x.im); @@ -2084,18 +2196,56 @@ export (x:int) + (y:CC) : CC := toCC(x+y.re,y.im); export -(y:CC) : CC := toCC(-y.re,-y.im); +export -(y:CCi) : CCi := toCCi(-y.re,-y.im); + export (x:CC) * (y:RR) : CC := ( if isfinite0(x.re) && isfinite0(x.im) && isfinite0(y) then toCC(x.re*y, x.im*y) else if isnan(x) || isnan(y) then nanCC(min(precision(x),precision(y))) else infinityCC(min(precision(x),precision(y)))); +export (x:CC) * (y:RRi) : CCi := ( + if isfinite0(x.re) && isfinite0(x.im) && isfinite0(y) + then toCCi(y*x.re, y*x.im) + else if isnan(x) || isnan(y) then nanCCi(min(precision(x),precision(y))) + else infinityCCi(min(precision(x),precision(y)))); + +export (x:CCi) * (y:RRi) : CCi := ( + if isfinite0(x.re) && isfinite0(x.im) && isfinite0(y) + then toCCi(x.re*y, x.im*y) + else if isnan(x) || isnan(y) then nanCCi(min(precision(x),precision(y))) + else infinityCCi(min(precision(x),precision(y)))); + +export (x:CCi) * (y:RR) : CCi := ( + if isfinite0(x.re) && isfinite0(x.im) && isfinite0(y) + then toCCi(x.re*y, x.im*y) + else if isnan(x) || isnan(y) then nanCCi(min(precision(x),precision(y))) + else infinityCCi(min(precision(x),precision(y)))); + export (y:RR) * (x:CC) : CC := ( if isfinite0(x.re) && isfinite0(x.im) && isfinite(y) then toCC(x.re*y, x.im*y) else if isnan(x) || isnan(y) then nanCC(min(precision(x),precision(y))) else infinityCC(min(precision(x),precision(y)))); +export (y:RRi) * (x:CC) : CCi := ( + if isfinite0(x.re) && isfinite0(x.im) && isfinite(y) + then toCCi(y*x.re, y*x.im) + else if isnan(x) || isnan(y) then nanCCi(min(precision(x),precision(y))) + else infinityCCi(min(precision(x),precision(y)))); + +export (y:RRi) * (x:CCi) : CCi := ( + if isfinite0(x.re) && isfinite0(x.im) && isfinite(y) + then toCCi(x.re*y, x.im*y) + else if isnan(x) || isnan(y) then nanCCi(min(precision(x),precision(y))) + else infinityCCi(min(precision(x),precision(y)))); + +export (y:RR) * (x:CCi) : CCi := ( + if isfinite0(x.re) && isfinite0(x.im) && isfinite(y) + then toCCi(x.re*y, x.im*y) + else if isnan(x) || isnan(y) then nanCCi(min(precision(x),precision(y))) + else infinityCCi(min(precision(x),precision(y)))); + export (y:int) * (x:CC) : CC := ( if isinf(x) && y != 0 then infinityCC(precision(x)) @@ -2106,16 +2256,41 @@ export (x:CC) * (y:ZZ) : CC := ( then infinityCC(precision(x)) else toCC(x.re*y, x.im*y)); +export (x:CCi) * (y:ZZ) : CCi := ( + if isinf(x) && !isZero(y) + then infinityCCi(precision(x)) + else toCCi(x.re*y, x.im*y)); + export (y:ZZ) * (x:CC) : CC := ( if isinf(x) && !isZero(y) then infinityCC(precision(x)) else toCC(x.re*y, x.im*y)); +export (y:ZZ) * (x:CCi) : CCi := ( + if isinf(x) && !isZero(y) + then infinityCCi(precision(x)) + else toCCi(x.re*y, x.im*y)); + export (x:CC) * (y:CC) : CC := ( if isinf(x) && !isZero(y) && !isnan(y) || isinf(y) && !isZero(x) && !isnan(x) then infinityCC(min(precision(x),precision(y))) else toCC(x.re*y.re-x.im*y.im, x.im*y.re+x.re*y.im)); +export (x:CC) * (y:CCi) : CCi := ( + if isinf(x) && !isZero(y) && !isnan(y) || isinf(y) && !isZero(x) && !isnan(x) + then infinityCCi(min(precision(x),precision(y))) + else toCCi(y.re*x.re-y.im*x.im, y.re*x.im+y.im*x.re)); + +export (x:CCi) * (y:CC) : CCi := ( + if isinf(x) && !isZero(y) && !isnan(y) || isinf(y) && !isZero(x) && !isnan(x) + then infinityCCi(min(precision(x),precision(y))) + else toCCi(x.re*y.re-x.im*y.im, x.im*y.re+x.re*y.im)); + +export (x:CCi) * (y:CCi) : CCi := ( + if isinf(x) && !isZero(y) && !isnan(y) || isinf(y) && !isZero(x) && !isnan(x) + then infinityCCi(min(precision(x),precision(y))) + else toCCi(x.re*y.re-x.im*y.im, x.im*y.re+x.re*y.im)); + export (x:CC) / (y:RR) : CC := ( if isZero(y) && !isnan(x) && !isZero(x) then infinityCC(min(precision(x),precision(y))) @@ -2130,8 +2305,12 @@ export conj(x:CC):CC := toCC(x.re,-x.im); export norm2(x:CC):RR := x.re*x.re + x.im*x.im; +export norm2(x:CCi):RRi := x.re*x.re + x.im*x.im; + export (x:CC) << (n:long) : CC := if n == long(0) then x else CC(x.re<> (n:long) : CCi := if n == long(0) then x else CCi(x.re>>n,x.im>>n); + export (x:CC) >> (n:long) : CC := if n == long(0) then x else CC(x.re>>n,x.im>>n); export (x:CC) << (n:int) : CC := if n == 0 then x else CC(x.re< "; export sqrt(x:CC):CC := ( @@ -2664,12 +2887,20 @@ square(z:CC):CC := ( else infinityCC(precision0(z.re)) ); +square(z:CCi):CCi := ( + if isfinite0(z.re) && isfinite0(z.im) then toCCi(z.re^long(2)-z.im^long(2),2*z.re*z.im) + else if isnan0(z.re) || isnan0(z.im) then nanCCi(precision0(z.re)) + else infinityCCi(precision0(z.re)) + ); + export acos(z:CC):CC := idiv(log(z+itimes(sqrt(1-square(z))))); export asin(z:CC):CC := idiv(log(sqrt(1-square(z))+itimes(z))); export abs2(z:CC):RR := z.re^long(2) + z.im^long(2); +export abs2(z:CCi):RRi := z.re^long(2) + z.im^long(2); + export atan(x:CC):CC := ( if isnan(x) then return x; if isinf(x) then return toCC(atan(infinityRR(precision(x)))); @@ -2696,6 +2927,22 @@ export (x:CC) ^ (y:ZZ):CC := ( ); exp(log(x)*y)); +export (x:CCi) ^ (y:ZZ):CCi := ( + if isZero0(y) then return toCCi(1,0,precision0(x.re)); + if isZero0(x.re) && isZero0(x.im) && isfinite0(x.re) && isfinite0(x.im) then return if isNegative0(y) then infinityCCi(precision0(x.re)) else x; + if isinf(x) then return if isNegative0(y) then toCCi(0,precision0(x.re)) else x; + if isLong(y) then ( + n := toLong(y); + if n == long(0) then return toCCi(1,precision(x)); + if n == long(1) then return x; + if n == long(-1) then return inverse(x); + if n == long(2) then return square(x); + if n == long(-2) then return inverse(square(x)); + -- we could do a few more of these optimizations here... + ); + if isEven(y) then return square(x^(y >> 1)) + else (return x*(x^(y-1)))); + export (x:RR) ^ (y:CC):CC := if isNegative(x) then exp(log(toCC(x))*y) else exp(log(x)*y); export arrayZZ := array(ZZ); diff --git a/M2/Macaulay2/d/gmp1.d b/M2/Macaulay2/d/gmp1.d index f0c121b7fe8..cc2bdf131da 100644 --- a/M2/Macaulay2/d/gmp1.d +++ b/M2/Macaulay2/d/gmp1.d @@ -6,6 +6,7 @@ header ""; --Functions in this file may make calls to stdio. use gmp; +use ballarith; use stdio; use err; @@ -162,7 +163,6 @@ export printingSeparator := "e"; -- was "*10^" -- this can be used by the engine for printing matrices to a uniform precision export tostringRR(x:RR):string := concatenate(format(printingPrecision,printingAccuracy,printingLeadLimit,printingTrailLimit,printingSeparator,x)); -tostringRRpointer = tostringRR; export tostringRRi(x:RRi):string := concatenate( array(string)( @@ -173,7 +173,29 @@ export tostringRRi(x:RRi):string := concatenate( "]", if isEmpty(x) then " (an empty interval)" else "" )); -tostringRRipointer = tostringRRi; + + +export tostringRRiforCCi(x:RRi):string := concatenate( + array(string)( + "[", + tostringRR(leftRR(x)), + ",", + tostringRR(rightRR(x)), + "]" + )); +--tostringRRiforCCipointer = tostringRRiforCCi; + +export tostringCCi(x:CCi):string := ( + re := tostringRRiforCCi(x.re); + im := tostringRRiforCCi(x.im) + "*ii"; + r := ( + if isZero(x.im) then re + else if isZero(x.re) then im + else re + "+" + im); + if isEmpty(x) then r = r + " (an empty interval)"; + r +); +tostringCCipointer = tostringCCi; numericstr(prec:ulong, str:string, ng:bool):string := ( "numeric(" + tostring(prec) + ", " + if ng then "-" else "" + str + ")"); @@ -210,46 +232,24 @@ export format( l:int, -- max number leading zeroes t:int, -- max number extra trailing digits sep:string, -- separator between mantissa and exponent - abb:bool, -- whether to abbreviate "*ii" to "i" - paren:bool, -- whether to parenthesize a sum and prepend a possible '-' z:CC -- the number to format ) : string := ( if isnan(z.re) || isnan(z.im) then return "NotANumber"; if isinf(z.re) || isinf(z.im) then return "infinity"; if s != 0 && ac == -1 && !isZero(z.re) && !isZero(z.im) then ac = s - int(floor(double(exponent(z))/log2ten)); - star := if abb then "" else "*" ; - i := if abb then "i" else "ii"; + star := "*"; + i := "ii"; x := format(s,ac,l,t,sep,z.re); y := format(s,ac,l,t,sep,z.im); if y.1 === "0" then return if x.1 === "0" then "0" else concatenate(x); if y.1 === "1" then (y.1 = ""; star = ""); if x.1 === "0" then return concatenate(array(string)(y.0,y.1,star,i)); if y.0 === "" then y.0 = "+"; - lp := ""; - rp := ""; - if paren then ( - if x.0 === "-" - then ( - x.0 = ""; - lp = "-("; - if y.0 .0 == '-' then y.0 = "+" else y.0 = "-") - else lp = "("; - rp = ")"; - ); - concatenate(array(string)(lp,x.0,x.1,y.0,y.1,star,i,rp))); + concatenate(array(string)(x.0,x.1,y.0,y.1,star,i))); export tostringCC(z:CC):string := ( - format(printingPrecision,printingAccuracy,printingLeadLimit,printingTrailLimit,printingSeparator,false,false,z) - ); -export tonetCC(z:CC):string := ( - format(printingPrecision,printingAccuracy,printingLeadLimit,printingTrailLimit,printingSeparator,true,false,z) + format(printingPrecision,printingAccuracy,printingLeadLimit,printingTrailLimit,printingSeparator,z) ); -tonetCCpointer = tonetCC; -export tonetCCparen(z:CC):string := ( - format(printingPrecision,printingAccuracy,printingLeadLimit,printingTrailLimit,printingSeparator,true,true,z) - ); -tonetCCparenpointer = tonetCCparen; - export toExternalString(z:CC):string := concatenate(array(string)( "toCC(", toExternalString(realPart(z)), @@ -258,6 +258,14 @@ export toExternalString(z:CC):string := concatenate(array(string)( ")" )); +export toExternalString(z:CCi):string := concatenate(array(string)( + "toCCi(", + toExternalString(z.re), + ",", + toExternalString(z.im), + ")" + )); + export (o:file) << (s:charstarOrNull) : file := o << if s == null() then "(null)" else tostring(s); export (o:file) << (x:ZZ) : file := o << tostring(x); diff --git a/M2/Macaulay2/d/gmp_aux.c b/M2/Macaulay2/d/gmp_aux.c index 5aebc5c67bb..a19f7740b0f 100644 --- a/M2/Macaulay2/d/gmp_aux.c +++ b/M2/Macaulay2/d/gmp_aux.c @@ -1,38 +1,51 @@ /* some routines to augment the gmp library */ #include #include "M2/math-include.h" +#include #include -int mpz_hash(mpz_srcptr x) { - int h = 0; - int n = x->_mp_size; - int i; - if (n < 0) n = -n; - for (i = 0; i_mp_d[i]; - if (x->_mp_size < 0) h = -h; - return h; -} +uint64_t mpz_hash(mpz_srcptr x) { + size_t n_bytes, count, i; + uint64_t h; + unsigned char *buf; + + n_bytes = (mpz_sizeinbase(x, 2) + 7) / 8; + buf = malloc(n_bytes + 1); + mpz_export(buf, &count, 1, 1, 1, 0, x); + h = 0; -int mpfr_hash(mpfr_srcptr x) { - int h = 0; - int n = (x->_mpfr_prec+mp_bits_per_limb-1)/mp_bits_per_limb; - int i; - if (0 != mpfr_sgn(x)) - for (i = 0; i_mpfr_d[i]; - return 777 + h * 3737 + x->_mpfr_exp + 11 * x->_mpfr_sign; + for (i = 0; i < count; i++) + h = h * 3737 + buf[i]; + + free(buf); + + return h + 6701 * mpz_sgn(x); } +uint64_t mpfr_hash(mpfr_srcptr x) { + mpfr_exp_t exp; + mpz_t sig; + uint64_t h; + + h = 3737 * mpfr_get_prec(x); + + if (mpfr_zero_p(x)) + return h + 6599 * mpfr_signbit(x) + 569; + if (mpfr_nan_p(x)) + return h + 3581; + if (mpfr_inf_p(x)) + return h + 5039 * mpfr_sgn(x) + 9733; + + mpz_init(sig); + exp = mpfr_get_z_2exp(sig, x); + + h += 3449 * mpz_hash(sig) + 2143 * exp; + mpz_clear(sig); + return h; +} -int mpfi_hash(mpfi_srcptr x) { // Really not sure if this is doing the right thing. - int h = 0; - int n_left = (x->left._mpfr_prec+mp_bits_per_limb-1)/mp_bits_per_limb; - int n_right = (x->right._mpfr_prec+mp_bits_per_limb-1)/mp_bits_per_limb; - int i; - if (0 != mpfr_sgn(&x->left)) - for (i = 0; ileft._mpfr_d[i]; - if (0 != mpfr_sgn(&x->right)) - for (i = 0; iright._mpfr_d[i]; - return 777 + h * 3737 + x->left._mpfr_exp + x->right._mpfr_exp + 11 * x->left._mpfr_sign + 11 * x->right._mpfr_sign; +uint64_t mpfi_hash(mpfi_srcptr x) { + return mpfr_hash(&x->left) + 3737 * mpfr_hash(&x->right); } void mp_free_str(char *str){ diff --git a/M2/Macaulay2/d/gmp_aux.h b/M2/Macaulay2/d/gmp_aux.h index 471c4e6b9a2..5ccb8a597d7 100644 --- a/M2/Macaulay2/d/gmp_aux.h +++ b/M2/Macaulay2/d/gmp_aux.h @@ -1,6 +1,8 @@ /* some routines to augment the gmp library */ -extern int mpz_hash(mpz_srcptr x); -extern int mpfr_hash(mpfr_srcptr x); -extern int mpfi_hash(mpfi_srcptr x); +#include + +extern uint64_t mpz_hash(mpz_srcptr x); +extern uint64_t mpfr_hash(mpfr_srcptr x); +extern uint64_t mpfi_hash(mpfi_srcptr x); extern void mp_free_str(char* str); diff --git a/M2/Macaulay2/d/hashtables.dd b/M2/Macaulay2/d/hashtables.dd index 316b9df4b91..88faad25d08 100644 --- a/M2/Macaulay2/d/hashtables.dd +++ b/M2/Macaulay2/d/hashtables.dd @@ -5,9 +5,18 @@ use classes; header "#include "; --- applyEEE is not defined until evaluate.d, --- so we use a pointer and populate it later. -dummyapplyEEE(g:Expr,e0:Expr,e1:Expr):Expr := g; +-- applyEE and applyEEE are not defined until evaluate.d, +-- so we use pointers and populate them later. +dummyapplyEE(g:Expr,e:Expr):Expr := ( + Ccode(void, "(void)", g); + Ccode(void, "(void)", e); + nullE); +dummyapplyEEE(g:Expr,e0:Expr,e1:Expr):Expr := ( + Ccode(void, "(void)", g); + Ccode(void, "(void)", e0); + Ccode(void, "(void)", e1); + nullE); +export applyEEpointer := dummyapplyEE; export applyEEEpointer := dummyapplyEEE; export hash(x:HashTable):hash_t := ( @@ -317,14 +326,26 @@ export copy(o:HashTable,newClass:HashTable,newMutable:bool):HashTable := copy(o, lookup(object:HashTable, key:Expr):Expr; -- forward declaration +export robustPrintNet(e:Expr):Net := ( + see := lookup(Class(e), RobustPrintNetE); + if see != notfoundE then + when applyEEpointer(see, e) + is n:Net do n else dummyNet); + +export robustPrintString(e:Expr):string := ( + see := lookup(Class(e), RobustPrintStringE); + if see != notfoundE then + when applyEEpointer(see, e) + is s:stringCell do s.v else ""); + KeyNotFound(object:string, key:Expr):Expr := ( -- TODO: implement a similar trick to call synonym(object) msg := "key not found in " + object; if !SuppressErrors then ( - see := lookup(Class(key), RobustPrintE); - if see != notfoundE then - when applyEEEpointer(see, toExpr(msg), key) - is str:stringCell do msg = str.v else nothing); + n := robustPrintNet(key); + if n != dummyNet then ( + info := toString(HorizontalJoin(array(Net)(toNet("\t"), n))); + msg = msg + ":\n" + info)); buildErrorPacket(msg)); export lookup1(object:HashTable,key:Expr,keyhash:hash_t):Expr := ( @@ -454,8 +475,7 @@ export lookupUnaryValue(s:HashTable,meth:Expr,methhash:hash_t):Expr := ( key1 := Key1; key1.0 = Expr(s); key1.1 = meth; - -- the big numbers here are the same as in hash() for sequences in structure.d - lookup1(s, Expr(key1), (27449 * 27457 + s.hash) * 27457 + methhash)); + lookup1(s, Expr(key1), (seqHashSeed * seqHashMult + s.hash) * seqHashMult + methhash)); ----------------------------------------------------------------------------- -- binary methods export installMethod(meth:Expr,lhs:HashTable,rhs:HashTable,value:Expr):Expr := ( @@ -475,17 +495,16 @@ threadLocal Key2 := Sequence(nullE,nullE,nullE); export lookupBinaryMethod(lhs:HashTable,rhs:HashTable,meth:Expr,methhash:hash_t):Expr := ( key2 := Key2; key2.0 = meth; - -- the big numbers here are the same as in hash() for sequences in structure.d - keyhash0 := 27449 * 27457 + methhash; + keyhash0 := seqHashSeed * seqHashMult + methhash; while true do ( -- loop through ancestors of lhs key2.1 = Expr(lhs); lefthash := lhs.hash; - keyhash1 := keyhash0 * 27457 + lefthash; + keyhash1 := keyhash0 * seqHashMult + lefthash; rhsptr := rhs; while true do ( -- loop through ancestors of rhs key2.2 = Expr(rhsptr); righthash := rhsptr.hash; - keyhash := keyhash1 * 27457 + righthash; + keyhash := keyhash1 * seqHashMult + righthash; s := lookup1( if lefthash > righthash then lhs else rhsptr, Expr(key2), keyhash); @@ -503,8 +522,7 @@ export lookupBinaryValue(lhs:HashTable,rhs:HashTable,meth:Expr,methhash:hash_t): key2.0 = Expr(lhs); key2.1 = Expr(rhs); key2.2 = meth; - -- the big numbers here are the same as in hash() for sequences in structure.d - keyhash := ((27449 * 27457 + lhs.hash) * 27457 + rhs.hash) * 27457 + methhash; + keyhash := ((seqHashSeed * seqHashMult + lhs.hash) * seqHashMult + rhs.hash) * seqHashMult + methhash; lookup1(if lhs.hash > rhs.hash then lhs else rhs, Expr(key2), keyhash)); export lookupBinaryMethod(lhs:HashTable,rhs:HashTable,meth:Expr):Expr := ( lookupBinaryMethod(lhs,rhs,meth,hash(meth))); @@ -530,22 +548,21 @@ threadLocal Key3 := Sequence(nullE,nullE,nullE,nullE); export lookupTernaryMethod(s1:HashTable,s2:HashTable,s3:HashTable,meth:Expr,methhash:hash_t):Expr := ( key3 := Key3; key3.0 = meth; - -- the big numbers here are the same is in hash() for sequences in structure.d - keyhash0 := 27449 * 27457 + methhash; + keyhash0 := seqHashSeed * seqHashMult + methhash; while true do ( -- loop through ancestors of s1 key3.1 = Expr(s1); s1hash := s1.hash; - keyhash1 := keyhash0 * 27457 + s1hash; + keyhash1 := keyhash0 * seqHashMult + s1hash; s2ptr := s2; while true do ( -- loop through ancestors of s2 key3.2 = Expr(s2ptr); s2hash := s2ptr.hash; - keyhash2 := keyhash1 * 27457 + s2hash; + keyhash2 := keyhash1 * seqHashMult + s2hash; s3ptr := s3; while true do ( -- loop through ancestors of s3 key3.3 = Expr(s3ptr); s3hash := s3ptr.hash; - keyhash3 := keyhash2 * 27457 + s3hash; + keyhash3 := keyhash2 * seqHashMult + s3hash; s := lookup1( if s1hash > s2hash then ( if s1hash > s3hash then s1 else s3ptr @@ -595,27 +612,26 @@ threadLocal Key4 := Sequence(nullE,nullE,nullE,nullE,nullE); export lookupQuaternaryMethod(s1:HashTable,s2:HashTable,s3:HashTable,s4:HashTable,meth:Expr,methhash:hash_t):Expr := ( key4 := Key4; key4.0 = meth; - -- the big numbers here are the same is in hash() for sequences in structure.d - keyhash0 := 27449 * 27457 + methhash; + keyhash0 := seqHashSeed * seqHashMult + methhash; while true do ( -- loop through ancestors of s1 key4.1 = Expr(s1); s1hash := s1.hash; - keyhash1 := keyhash0 * 27457 + s1hash; + keyhash1 := keyhash0 * seqHashMult + s1hash; s2ptr := s2; while true do ( -- loop through ancestors of s2 key4.2 = Expr(s2ptr); s2hash := s2ptr.hash; - keyhash2 := keyhash1 * 27457 + s2hash; + keyhash2 := keyhash1 * seqHashMult + s2hash; s3ptr := s3; while true do ( -- loop through ancestors of s3 key4.3 = Expr(s3ptr); s3hash := s3ptr.hash; - keyhash3 := keyhash2 * 27457 + s3hash; + keyhash3 := keyhash2 * seqHashMult + s3hash; s4ptr := s4; while true do ( -- loop through ancestors of s4 key4.4 = Expr(s4ptr); s4hash := s4ptr.hash; - keyhash4 := keyhash3 * 27457 + s4hash; + keyhash4 := keyhash3 * seqHashMult + s4hash; s := lookup1( if s1hash > s2hash then ( if s1hash > s3hash diff --git a/M2/Macaulay2/d/interface.dd b/M2/Macaulay2/d/interface.dd index 2450e2f291b..356ac209310 100644 --- a/M2/Macaulay2/d/interface.dd +++ b/M2/Macaulay2/d/interface.dd @@ -32,7 +32,12 @@ header "// TODO: remove the following headers #include // for RingElement"; -- debugging: -export engineMemory(e:Expr):Expr := toExpr(Ccode(string, "engineMemory()")); +export engineMemory(e:Expr):Expr := ( + when e + is a:Sequence do ( + if length(a) == 0 then toExpr(Ccode(string, "engineMemory()")) + else WrongNumArgs(0)) + else WrongNumArgs(0)); setupfun("engineMemory", engineMemory); -- testing C++ exceptions @@ -40,7 +45,12 @@ header "#include void test_catch0() { throw (std::runtime_error(\"test\")); } int test_catch() { try { test_catch0(); } catch (const std::runtime_error& x) { return 1; } return 0; }"; -export testCatch(e:Expr):Expr := toExpr(Ccode(bool,"0 != test_catch()")); +export testCatch(e:Expr):Expr := ( + when e + is a:Sequence do ( + if length(a) == 0 then toExpr(Ccode(bool,"0 != test_catch()")) + else WrongNumArgs(0)) + else WrongNumArgs(0)); setupfun("testCatch",testCatch); -- random numbers @@ -87,6 +97,18 @@ export rawRandomCC(e:Expr):Expr := ( else toExpr(Ccode(CC, "rawRandomCC(", toULong(prec.v), ")")) else WrongArgZZ()); setupfun("rawRandomCC",rawRandomCC); +export rawRandomRRi(e:Expr):Expr := ( + when e + is prec:ZZcell do if !isULong(prec.v) then WrongArgSmallUInteger() + else toExpr(Ccode(RRi, "rawRandomRRi(", toULong(prec.v), ")")) + else WrongArgZZ()); +setupfun("rawRandomRRi", rawRandomRRi); +export rawRandomCCi(e:Expr):Expr := ( + when e + is prec:ZZcell do if !isULong(prec.v) then WrongArgSmallUInteger() + else toExpr(Ccode(CCi, "rawRandomCCi(", toULong(prec.v), ")")) + else WrongArgZZ()); +setupfun("rawRandomCCi", rawRandomCCi); ----------------------------------------------------------------------------- -- monomials @@ -485,6 +507,12 @@ export rawRRi(e:Expr):Expr := ( else WrongArgZZ(1)); setupfun("rawRRi",rawRRi); +export rawCCi(e:Expr):Expr := ( + when e is prec:ZZcell do if !isInt(prec) then WrongArgSmallInteger(1) + else toExpr(Ccode(RawRingOrNull, "IM2_Ring_CCi(",toInt(prec),")" )) + else WrongArgZZ(1)); +setupfun("rawCCi",rawCCi); + export rawRR(e:Expr):Expr := ( when e is prec:ZZcell do if !isULong(prec) then WrongArgSmallUInteger(1) else toExpr(Ccode(RawRingOrNull, "IM2_Ring_RRR(",toULong(prec),")" )) @@ -941,6 +969,7 @@ export rawFromNumber(e:Expr):Expr := ( is x:RRcell do toExpr(Ccode(RawRingElementOrNull, "IM2_RingElement_from_BigReal(",R.p,",",x.v,")")) is x:CCcell do toExpr(Ccode(RawRingElementOrNull, "IM2_RingElement_from_BigComplex(",R.p,",",x.v,")")) is x:RRicell do toExpr(Ccode(RawRingElementOrNull, "IM2_RingElement_from_Interval(",R.p,",",x.v,")")) + is x:CCicell do toExpr(Ccode(RawRingElementOrNull, "IM2_RingElement_from_ComplexInterval(",R.p,",",x.v,")")) else WrongArg(2,"an integer, real number, or complex number") else WrongArg(1,"a raw ring") else WrongNumArgs(2) @@ -1055,6 +1084,13 @@ export rawToRRi(e:Expr):Expr := ( WrongArg("a raw ring element")); setupfun("rawToRRi",rawToRRi); +export rawToCCi(e:Expr):Expr := ( + when e + is x:RawRingElementCell do toExpr(Ccode(CCiorNull, "IM2_RingElement_to_ComplexInterval(",x.p, ")" )) + else + WrongArg("a raw ring element")); +setupfun("rawToCCi",rawToCCi); + export rawToCC(e:Expr):Expr := ( when e is x:RawRingElementCell do toExpr(Ccode(CCorNull, "IM2_RingElement_to_BigComplex(",x.p, ")" )) @@ -2127,6 +2163,25 @@ export rawMatrixEntry(e:Expr):Expr := ( ); setupfun("rawMatrixEntry",rawMatrixEntry); +entriesArrayToExpr(entriesOrNull:RawRingElementArrayArrayOrNull):Expr := ( + when entriesOrNull is entries:RawRingElementArrayArray do ( + list(new Sequence len length(entries) do ( + foreach row in entries do ( + provide list(listClass, toExpr(row)))))) + else ( + nullE + )); + +export rawMatrixEntries(e:Expr):Expr := ( + when e + is M:RawMutableMatrixCell do ( + entriesArrayToExpr(Ccode(RawRingElementArrayArrayOrNull, "IM2_MutableMatrix_get_entries(", M.p, ")"))) + is M:RawMatrixCell do ( + entriesArrayToExpr(Ccode(RawRingElementArrayArrayOrNull, "IM2_Matrix_get_entries(", M.p, ")"))) + else WrongArg("a raw matrix or mutable matrix") + ); +setupfun("rawMatrixEntries",rawMatrixEntries); + export rawSortColumns(e:Expr):Expr := ( when e is s:Sequence do if length(s) != 3 then WrongNumArgs(3) else diff --git a/M2/Macaulay2/d/interp.dd b/M2/Macaulay2/d/interp.dd index 5b90009bab2..9401567023b 100644 --- a/M2/Macaulay2/d/interp.dd +++ b/M2/Macaulay2/d/interp.dd @@ -258,7 +258,7 @@ readeval4(file:TokenFile,printout:bool,dictionary:Dictionary,returnLastvalue:boo else buildErrorPacket("error occurred in parsing"))))))))); interpreterDepthS := setupvar("interpreterDepth",zeroE); -setInterpreterDepth(n:int):void := ( +export setInterpreterDepth(n:int):void := ( interpreterDepth = n; setGlobalVariable(interpreterDepthS, toExpr(interpreterDepth))); incrementInterpreterDepth():void := setInterpreterDepth(interpreterDepth + 1); @@ -538,6 +538,7 @@ value(e:Expr):Expr := ( || err.message == breakMessage then if err.value == dummyExpr then nullE else err.value else r) else r) + is x:SpecialExpr do value(x.e) else WrongArg(1,"a string, a symbol, or pseudocode")); setupfun("value'",value); @@ -588,6 +589,52 @@ capture(e:Expr):Expr := ( else WrongArg(1,"a string")); setupfun("capture", capture).Protected = false; -- will be overloaded in m2/examples.m2 +parse(e:Expr):Expr := ( + when e + is code:stringCell do ( + done := false; + numParsed := 0; + capacity := 4; + result := Sequence(nullE, nullE, nullE, nullE); + stringFile := stringTokenFile("currentString", code.v); + stringFile.posFile.file.fulllines = stdIO.fulllines; -- for TeXmacs mode + while true do ( + -- mimic the interpreter + while true do ( + -- lex for a bit until we see something to parse + t := peektoken(stringFile, true).word; + if t == wordEOF then ( -- end of string + done = true; + break) + else if t.parse.precedence <= SemicolonW.parse.precedence then ( + if t == SemicolonW || t == NewlineW || t == wordEOC then ( + gettoken(stringFile, true); + nothing) + -- extra right paren + else return buildErrorPacket("syntax error: unmatched " + + t.name)) + -- we found something to parse! + else break); + if done then break; + p := parse(stringFile, SemicolonW.parse.precedence, true); + if p == errorTree + then return buildErrorPacket("--backtrace: parse error--"); + numParsed = numParsed + 1; + -- ran out of space -- time to grow our sequence + if numParsed > capacity then ( + capacity = 2 * capacity; + newResult := new Sequence len capacity do ( + foreach x in result do provide x; + while true do provide nullE); + result = newResult); + result.(numParsed - 1) = toExpr(p)); + if numParsed == 0 then emptyList + else if numParsed == capacity then list(result) + else list(new Sequence len numParsed do ( + foreach x in result do provide x))) + else WrongArgString()); +setupfun("parse", parse); + normalExit := 0; errorExit := 1; interruptExit := 2; -- see also M2lib.c @@ -621,6 +668,7 @@ export process():void := ( setMaxAllowableThreads(); -- setLoadDepth(loadDepth); -- loaddata() in M2lib.c increments it, so we have to reflect that at top level everytimeRun(); + setStdError(stdError); -- we don't know the right directory; calls commandInterpreter and eventually returns: ret := readeval(stringTokenFile(startupFile.filename,startupFile.contents),false,false); when ret is err:Error do ( diff --git a/M2/Macaulay2/d/json.d b/M2/Macaulay2/d/json.d new file mode 100644 index 00000000000..16b90610a98 --- /dev/null +++ b/M2/Macaulay2/d/json.d @@ -0,0 +1,71 @@ +use common; +use util; +use hashtables; + +header "#include "; + +json_tstar := Pointer "struct json_t *"; +json_error_t := Type "struct json_error_t"; + +toExpr(json:json_tstar):Expr := ( + type := Ccode(int, "json_typeof(", json, ")"); + if type == Ccode(int, "JSON_OBJECT") + then ( + h := newHashTable(hashTableClass,nothingClass); + iter := Ccode(voidPointer, "json_object_iter(", json, ")"); + while iter != nullPointer() do ( + storeInHashTable(h, + -- TODO: use json_object_iter_key_len so we can have \0 in keys + -- problem: added in jansson 2.14, which isn't available on + -- some older systems like Ubuntu 18.04 + toExpr(Ccode(constcharstar, + "json_object_iter_key(", iter, ")")), + toExpr(Ccode(json_tstar, + "json_object_iter_value(", iter, ")"))); + iter = Ccode(voidPointer, + "json_object_iter_next(", json, ", ", iter, ")")); + Expr(sethash(h, false))) + else if type == Ccode(int, "JSON_ARRAY") + then list(new Sequence len Ccode(int, "json_array_size(", json, ")") + at i do provide toExpr( + Ccode(json_tstar, "json_array_get(", json, ", ", i, ")"))) + else if type == Ccode(int, "JSON_STRING") + then toExpr(tostringn( + Ccode(constcharstar, "json_string_value(", json, ")"), + Ccode(int, "json_string_length(", json, ")"))) + else if type == Ccode(int, "JSON_INTEGER") + then toExpr(Ccode(long, "json_integer_value(", json, ")")) + else if type == Ccode(int, "JSON_REAL") + then toExpr(Ccode(double, "json_real_value(", json, ")")) + else if type == Ccode(int, "JSON_TRUE") then True + else if type == Ccode(int, "JSON_FALSE") then False + else if type == Ccode(int, "JSON_NULL") then nullE + else buildErrorPacket("unknown json type")); + +jsonFlags := Ccode(int, "JSON_DECODE_ANY | JSON_ALLOW_NUL"); + +fromJSON(e:Expr):Expr := ( + j := Ccode(json_tstar or null, "NULL"); + jerr := Ccode(json_error_t, "(json_error_t){0}"); + when e + is buf:stringCell do ( + j = Ccode(json_tstar or null, "json_loadb((const char *)", buf.v, + "->array, ", length(buf.v), ", ", jsonFlags, ", &", jerr, ")")) + is f:file do ( + j = Ccode(json_tstar or null, "json_loadfd(", f.infd, + ", ", jsonFlags, ", &", jerr, ")")) + else return WrongArg("a string or file"); + when j + is json:json_tstar do ( + ret := toExpr(json); + Ccode(void, "json_decref(", json, ")"); + ret) + is null do ( + n := 55 + Ccode(int, "JSON_ERROR_TEXT_LENGTH"); + buf := newstring(n); + Ccode(void, "snprintf((char *)", buf, "->array, ", n, + ", \"json error at line %d, column %d: %s\", ", jerr, ".line, ", + jerr, ".column, ", jerr, ".text)"); + Ccode(void, buf, "->len = strlen((char *)", buf, "->array)"); + buildErrorPacket(buf))); +setupfun("fromJSON0", fromJSON); diff --git a/M2/Macaulay2/d/lex.d b/M2/Macaulay2/d/lex.d index 8b9a4ab05f6..67d211c79ae 100644 --- a/M2/Macaulay2/d/lex.d +++ b/M2/Macaulay2/d/lex.d @@ -323,6 +323,8 @@ gettoken1(file:PosFile,sawNewline:bool):Token := ( else if ismathoperator(peek2(file)) then ( for i from 1 to utf8charlength(char(ch)) do tokenbuf << char(getc(file)); + if peek(file) == int('=') -- augmented assignment operator + then tokenbuf << char(getc(file)); return Token(makeUniqueWord(takestring(tokenbuf), parseWORD), newPosition(file, line, column), globalDictionary, dummySymbol, sawNewline)) else if isalpha(ch) then ( -- valid symbols are an alpha (letters, any unicode) followed by any number of alphanum (alpha, digit, dollar, prime) diff --git a/M2/Macaulay2/d/parse.d b/M2/Macaulay2/d/parse.d index daf87278af0..6322ba40b46 100644 --- a/M2/Macaulay2/d/parse.d +++ b/M2/Macaulay2/d/parse.d @@ -22,6 +22,7 @@ extern void err_error(struct M2_string_struct*); use nets; use gmp; +use ballarith; use xml; use engine; use varnets; @@ -74,7 +75,6 @@ export Symbol := { -- symbol table entry for a symbol hash:hash_t, -- based on the hash code of word, unchanging position:Position, -- the position where the definition was made unary:unop, - postfix:unop, binary:binop, frameID:int, -- seqno of frame for dictionary containing it -- 0 for the globalFrame @@ -122,18 +122,20 @@ export Adjacent := {+lhs:ParseTree, rhs:ParseTree}; export For := {+ forToken:Token, variable:ParseTree, inClause:ParseTree, fromClause:ParseTree, toClause:ParseTree, whenClause:ParseTree, listClause:ParseTree, doClause:ParseTree, dictionary:Dictionary -- filled in later }; -export WhileDo := {+ whileToken:Token, predicate:ParseTree, dotoken:Token, doClause:ParseTree}; -export WhileList := {+ whileToken:Token, predicate:ParseTree, listtoken:Token, listClause:ParseTree}; -export WhileListDo := {+ whileToken:Token, predicate:ParseTree, listtoken:Token, listClause:ParseTree, dotoken:Token, doClause:ParseTree }; -export TryThenElse := {+ tryToken:Token, primary:ParseTree, thenToken:Token, sequel:ParseTree, elseToken:Token, alternate:ParseTree}; -export TryThen := {+ tryToken:Token, primary:ParseTree, thenToken:Token, sequel:ParseTree}; -export TryElse := {+ tryToken:Token, primary:ParseTree, elseToken:Token, alternate:ParseTree}; -export Try := {+ tryToken:Token, primary:ParseTree}; +export WhileDo := {+ whileToken:Token, predicate:ParseTree, doClause:ParseTree }; +export WhileList := {+ whileToken:Token, predicate:ParseTree, listClause:ParseTree }; +export WhileListDo := {+ whileToken:Token, predicate:ParseTree, listClause:ParseTree, doClause:ParseTree }; +export TryDo := {+ tryToken:Token, primary:ParseTree, variable:ParseTree, doClause:ParseTree, dictionary:Dictionary}; +export TryThenDo := {+ tryToken:Token, primary:ParseTree, sequel:ParseTree, variable:ParseTree, doClause:ParseTree, dictionary:Dictionary}; +export TryThenElse := {+ tryToken:Token, primary:ParseTree, sequel:ParseTree, alternate:ParseTree }; +export TryThen := {+ tryToken:Token, primary:ParseTree, sequel:ParseTree }; +export TryElse := {+ tryToken:Token, primary:ParseTree, alternate:ParseTree }; +export Try := {+ tryToken:Token, primary:ParseTree }; export Catch := {+ catchToken:Token, primary:ParseTree}; export IfThen := {+ ifToken:Token, predicate:ParseTree, thenClause:ParseTree }; export IfThenElse := {+ ifToken:Token, predicate:ParseTree, thenClause:ParseTree, elseClause:ParseTree}; export New := {+ newToken:Token, newClass:ParseTree, newParent:ParseTree, newInitializer:ParseTree }; -export Arrow := {+lhs:ParseTree, Operator:Token, rhs:ParseTree, desc:functionDescription}; +export Arrow := {+lhs:ParseTree, rhs:ParseTree, desc:functionDescription}; export Quote := {+Operator:Token, rhs:Token}; export GlobalQuote := {+Operator:Token, rhs:Token, global:void}; export ThreadQuote := {+Operator:Token, rhs:Token, thread:void}; @@ -149,7 +151,7 @@ export ParseTree := ( Token or Parentheses or EmptyParentheses or Adjacent or Arrow or Quote or GlobalQuote or ThreadQuote or LocalQuote or Unary or Binary or Postfix or IfThen or IfThenElse - or Try or TryThen or TryThenElse or TryElse or Catch + or Try or TryThen or TryThenElse or TryElse or TryThenDo or TryDo or Catch or WhileDo or WhileListDo or WhileList or For or New or dummy ); @@ -176,11 +178,11 @@ export localMemoryReferenceCode := {+ position:Position }; export globalMemoryReferenceCode := {+ - frameindex:int, + var:Symbol, position:Position }; export threadMemoryReferenceCode := {+ - frameindex:int, + var:Symbol, position:Position, x:void -- just to distinguish it }; @@ -196,14 +198,20 @@ export globalAssignmentCode := {+ position:Position }; export ifCode := {+ predicate:Code, thenClause:Code, elseClause:Code, position:Position }; -export tryCode := {+ code:Code, thenClause:Code, elseClause:Code, position:Position }; +export tryCode := {+ + code:Code, + thenClause:Code, + elseClause:Code, + doClause:Code, + frameID:int, + framesize:int, + position:Position }; export catchCode := {+ code:Code, position:Position }; export SymbolSequence := array(Symbol); export parallelAssignmentCode := {+ - nestingDepth:array(int), -- spots corresponding to global and thread variables are filled with -1 - frameindex:array(int), - lhs:SymbolSequence, -- spots corresponding to local variables are filled with dummySymbol + colon:bool, -- := or = + lhs:CodeSequence, rhs:Code, position:Position}; @@ -211,7 +219,6 @@ export augmentedAssignmentCode := {+ oper:Symbol, lhs:Code, rhs:Code, - info:Symbol, -- variable name or operator position:Position}; -- code that's already been evaluated; needed for augmented assignment @@ -221,8 +228,8 @@ export nullCode := {+}; export realCode := {+x:RR,position:Position}; export integerCode := {+x:ZZ,position:Position}; export stringCode := {+x:string,position:Position}; -export unaryCode := {+f:unop,rhs:Code,position:Position}; -export binaryCode := {+f:binop,lhs:Code,rhs:Code,position:Position}; +export unaryCode := {+oper:Symbol,rhs:Code,position:Position}; +export binaryCode := {+oper:Symbol,lhs:Code,rhs:Code,position:Position}; export adjacentCode := {+lhs:Code,rhs:Code,position:Position}; export whileDoCode := {+predicate:Code,doClause:Code,position:Position}; export whileListCode := {+predicate:Code,listClause:Code,position:Position}; @@ -357,9 +364,11 @@ export TaskCell := {+ body:TaskCellBody }; export pointerCell := {+ v:voidPointer }; export atomicIntCell := {+ v:atomicField, hash:hash_t }; +export mutexCell := {+ v:ThreadMutex, hash:hash_t }; export Expr := ( CCcell or + CCicell or RRcell or RRicell or Boolean or @@ -414,7 +423,8 @@ export Expr := ( TaskCell or fileOutputSyncState or pointerCell or - atomicIntCell + atomicIntCell or + mutexCell ); --Unique True expression diff --git a/M2/Macaulay2/d/parser.d b/M2/Macaulay2/d/parser.d index b05316448e2..3c712703e67 100644 --- a/M2/Macaulay2/d/parser.d +++ b/M2/Macaulay2/d/parser.d @@ -51,6 +51,9 @@ export hexvalue (c:char ):int := ( ); export hexvalue (c:int ):int := hexvalue(char(c)); +hexvalue(a:char, b:char, c:char, d:char):int := ( + hexvalue(d) + 16*(hexvalue(c) + 16*(hexvalue(b) + 16*hexvalue(a)))); + export parseInt(s:string):ZZ := ( i := zeroZZ; n := length(s); @@ -90,7 +93,18 @@ export parseString(s:string):string := ( else if c == '\\' then v << '\\' else if c == 'u' then ( i = i+4; - utf8(v, ((hexvalue(s.(i-3)) * 16 + hexvalue(s.(i-2))) * 16 + hexvalue(s.(i-1))) * 16 + hexvalue(s.i))) + val := hexvalue(s.(i-3), s.(i-2), s.(i-1), s.i); + -- high surrogate (0xd800 - 0xdbff) + if 55296 <= val && val <= 56319 then ( + if i + 6 < length(s) && s.(i+1) == '\\' && s.(i+2) == 'u' + then ( + val2 := hexvalue(s.(i+3), s.(i+4), s.(i+5), s.(i+6)); + -- low surrogate (0xdc00 - 0xdfff) + if 56320 <= val2 && val2 <= 57343 then ( + i = i + 6; + -- 0x10000 + 0x400*(high-0xd800) + low-0xdc00 + val = 65536 + 1024*(val-55296) + val2-56320))); + utf8(v, val)) else if c == 'x' then ( i = i + 2; v << char(hexvalue(s.(i - 1)) * 16 + hexvalue(s.i))) @@ -126,6 +140,7 @@ export elseW := Word("-*dummy word: else*-",TCnone,hash_t(0),newParseinfo()); export ofW := Word("-*dummy word: of*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d export doW := Word("-*dummy word: do*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d export listW := Word("-*dummy word: list*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d +export exceptW := Word("-*dummy word: except*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d export fromW := Word("-*dummy word: from*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d export inW := Word("-*dummy word: in*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d export toW := Word("-*dummy word: to*-",TCnone,hash_t(0),newParseinfo()); -- filled in by binding.d @@ -254,7 +269,7 @@ export nbinaryop(lhs:ParseTree, token2:Token, file:TokenFile, prec:int, obeyline if ret == errorTree then ret else ParseTree(Binary(lhs, token2, ret))); export arrowop(lhs:ParseTree, token2:Token, file:TokenFile, prec:int, obeylines:bool):ParseTree := ( e := parse(file,token2.word.parse.binaryStrength,obeylines); - if e == errorTree then e else ParseTree(Arrow(lhs, token2, e, dummyDesc))); + if e == errorTree then e else ParseTree(Arrow(lhs, e, dummyDesc))); MatchPair := {left:string, right:string, next:(null or MatchPair)}; matchList := (null or MatchPair)(NULL); @@ -292,7 +307,7 @@ export unarywhile(whileToken:Token,file:TokenFile,prec:int,obeylines:bool):Parse if token2.word == doW then ( doClause := parse(file,doW.parse.unaryStrength,obeylines); if doClause == errorTree then return errorTree; - r := ParseTree(WhileDo(whileToken,predicate,token2,doClause)); + r := ParseTree(WhileDo(whileToken,predicate,doClause)); accumulate(r,file,prec,obeylines)) else if token2.word == listW then ( listClause := parse(file,listW.parse.unaryStrength,obeylines); @@ -302,10 +317,10 @@ export unarywhile(whileToken:Token,file:TokenFile,prec:int,obeylines:bool):Parse if doToken == errorToken then return errorTree; doClause := parse(file,doW.parse.unaryStrength,obeylines); if doClause == errorTree then return errorTree; - ret := ParseTree(WhileListDo(whileToken,predicate,token2,listClause,doToken,doClause)); + ret := ParseTree(WhileListDo(whileToken,predicate,listClause,doClause)); accumulate(ret,file,prec,obeylines)) else ( - ret := ParseTree(WhileList(whileToken,predicate,token2,listClause)); + ret := ParseTree(WhileList(whileToken,predicate,listClause)); accumulate(ret,file,prec,obeylines))) else ( makeParseError(token2,"syntax error : expected 'do' or 'list'"); @@ -403,13 +418,13 @@ export unaryglobal(quotetoken:Token,file:TokenFile,prec:int,obeylines:bool):Pars export unarythread(quotetoken:Token,file:TokenFile,prec:int,obeylines:bool):ParseTree := ( arg := gettoken(file,false); if arg == errorToken then return errorTree; - if arg.word.typecode != TCid then makeParseError(arg, "syntax error: " + arg.word.name); + if arg.word.typecode != TCid then return makeParseError(arg, "syntax error: " + arg.word.name); r := ParseTree(ThreadQuote(quotetoken,arg)); accumulate(r,file,prec,obeylines)); export unarylocal(quotetoken:Token,file:TokenFile,prec:int,obeylines:bool):ParseTree := ( arg := gettoken(file,false); if arg == errorToken then return errorTree; - if arg.word.typecode != TCid then makeParseError(arg, "syntax error: " + arg.word.name); + if arg.word.typecode != TCid then return makeParseError(arg, "syntax error: " + arg.word.name); r := ParseTree(LocalQuote(quotetoken,arg)); accumulate(r,file,prec,obeylines)); export unaryif(ifToken:Token,file:TokenFile,prec:int,obeylines:bool):ParseTree := ( @@ -444,19 +459,43 @@ export unarytry(tryToken:Token,file:TokenFile,prec:int,obeylines:bool):ParseTree return makeParseError(tryToken," ... to match this 'try'")); elseClause := parse(file,elseW.parse.unaryStrength,obeylines); if elseClause == errorTree then return errorTree; - accumulate(ParseTree(TryElse(tryToken,primary,elseToken,elseClause)),file,prec,obeylines)) + accumulate(ParseTree(TryElse(tryToken,primary,elseClause)),file,prec,obeylines)) else if peektoken(file,obeylines).word == thenW then ( thenToken := gettoken(file,false); if thenToken == errorToken then return errorTree; thenClause := parse(file,thenW.parse.unaryStrength,obeylines); if thenClause == errorTree then return errorTree; - if peektoken(file,obeylines).word == elseW then ( + if peektoken(file,obeylines).word == exceptW then ( + exceptToken := gettoken(file,false); + if exceptToken == errorToken then return errorTree; + variable := parse(file,exceptW.parse.unaryStrength,obeylines); + if variable == errorTree then return errorTree; + if peektoken(file,obeylines).word == doW then ( + doToken := gettoken(file,false); + if doToken == errorToken then return errorTree; + doClause := parse(file,doW.parse.unaryStrength,obeylines); + if doClause == errorTree then return errorTree; + accumulate(ParseTree(TryThenDo(tryToken,primary,thenClause,variable,doClause,dummyDictionary)),file,prec,obeylines)) + else return makeParseError(exceptToken,"syntax error: expected 'do'")) + else if peektoken(file,obeylines).word == elseW then ( elseToken := gettoken(file,false); if elseToken == errorToken then return errorTree; elseClause := parse(file,elseW.parse.unaryStrength,obeylines); if elseClause == errorTree then return errorTree; - accumulate(ParseTree(TryThenElse(tryToken,primary,thenToken,thenClause,elseToken,elseClause)),file,prec,obeylines)) - else accumulate(ParseTree(TryThen(tryToken,primary,thenToken,thenClause)),file,prec,obeylines)) + accumulate(ParseTree(TryThenElse(tryToken,primary,thenClause,elseClause)),file,prec,obeylines)) + else accumulate(ParseTree(TryThen(tryToken,primary,thenClause)),file,prec,obeylines)) + else if peektoken(file,obeylines).word == exceptW then ( + exceptToken := gettoken(file,false); + if exceptToken == errorToken then return errorTree; + variable := parse(file,exceptW.parse.unaryStrength,obeylines); + if variable == errorTree then return errorTree; + if peektoken(file,obeylines).word == doW then ( + doToken := gettoken(file,false); + if doToken == errorToken then return errorTree; + doClause := parse(file,doW.parse.unaryStrength,obeylines); + if doClause == errorTree then return errorTree; + accumulate(ParseTree(TryDo(tryToken,primary,variable,doClause,dummyDictionary)),file,prec,obeylines)) + else return makeParseError(exceptToken,"syntax error: expected 'do'")) else accumulate(ParseTree(Try(tryToken,primary)),file,prec,obeylines)); export unarycatch(catchToken:Token,file:TokenFile,prec:int,obeylines:bool):ParseTree := ( primary := parse(file,catchToken.word.parse.unaryStrength,obeylines); @@ -501,6 +540,8 @@ export treePosition(e:ParseTree):Position := ( is t:TryThen do combinePositionL(t.tryToken.position, treePosition(t.sequel)) is t:TryThenElse do combinePositionL(t.tryToken.position, treePosition(t.alternate)) is t:TryElse do combinePositionL(t.tryToken.position, treePosition(t.alternate)) + is t:TryDo do combinePositionL(t.tryToken.position, treePosition(t.doClause)) + is t:TryThenDo do combinePositionL(t.tryToken.position, treePosition(t.doClause)) is t:Catch do combinePositionL(t.catchToken.position, treePosition(t.primary)) is t:WhileDo do combinePositionL(t.whileToken.position, treePosition(t.doClause)) is t:WhileListDo do combinePositionL(t.whileToken.position, treePosition(t.doClause)) @@ -527,7 +568,7 @@ export size(e:ParseTree):int := ( is x:Token do size(x) is x:Adjacent do Ccode(int,"sizeof(*",x,")") + size(x.lhs) + size(x.rhs) is x:Binary do Ccode(int,"sizeof(*",x,")") + size(x.lhs) + size(x.rhs) + size(x.Operator) - is x:Arrow do Ccode(int,"sizeof(*",x,")") + size(x.lhs) + size(x.rhs) + size(x.Operator) + size(x.desc) + is x:Arrow do Ccode(int,"sizeof(*",x,")") + size(x.lhs) + size(x.rhs) + size(x.desc) is x:Unary do Ccode(int,"sizeof(*",x,")") + size(x.rhs) + size(x.Operator) is x:Postfix do Ccode(int,"sizeof(*",x,")") + size(x.lhs) + size(x.Operator) is x:Quote do Ccode(int,"sizeof(*",x,")") + size(x.rhs) + size(x.Operator) @@ -538,15 +579,17 @@ export size(e:ParseTree):int := ( is x:EmptyParentheses do Ccode(int,"sizeof(*",x,")") + size(x.left) + size(x.right) is x:IfThen do Ccode(int,"sizeof(*",x,")") + size(x.ifToken) + size(x.predicate) + size(x.thenClause) is x:IfThenElse do Ccode(int,"sizeof(*",x,")") + size(x.ifToken) + size(x.predicate) + size(x.thenClause) + size(x.elseClause) - is x:TryThenElse do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.thenToken) + size(x.sequel) + size(x.elseToken) + size(x.alternate) - is x:TryThen do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.thenToken) + size(x.sequel) - is x:TryElse do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.elseToken) + size(x.alternate) + is x:TryThenElse do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.sequel) + size(x.alternate) + is x:TryThen do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.sequel) + is x:TryElse do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.alternate) is x:Try do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + is x:TryDo do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.variable) + size(x.doClause) + is x:TryThenDo do Ccode(int,"sizeof(*",x,")") + size(x.tryToken) + size(x.primary) + size(x.sequel) + size(x.variable) + size(x.doClause) is x:Catch do Ccode(int,"sizeof(*",x,")") + size(x.catchToken) + size(x.primary) is x:For do Ccode(int,"sizeof(*",x,")")+ size(x.forToken) + size(x.variable) + size(x.inClause) + size(x.fromClause) + size(x.toClause) + size(x.whenClause) + size(x.listClause) + size(x.doClause) - is x:WhileDo do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.dotoken) + size(x.doClause) - is x:WhileList do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.listtoken) + size(x.listClause) - is x:WhileListDo do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.dotoken) + size(x.doClause) + size(x.listtoken) + size(x.listClause) + is x:WhileDo do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.doClause) + is x:WhileList do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.listClause) + is x:WhileListDo do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.doClause) + size(x.listClause) is x:New do Ccode(int,"sizeof(*",x,")") + size(x.newToken) + size(x.newClass) + size(x.newParent) + size(x.newInitializer) ); diff --git a/M2/Macaulay2/d/pthread.d b/M2/Macaulay2/d/pthread.d index acd635f8e95..f047ee10a1b 100644 --- a/M2/Macaulay2/d/pthread.d +++ b/M2/Macaulay2/d/pthread.d @@ -231,6 +231,54 @@ export getIOThreadMode(e:Expr):Expr := ( else WrongArg("a file or ()")); setupfun("getIOThreadMode", getIOThreadMode); +WrongArgMutex():Expr := WrongArg("a mutex"); + +mutexFinalizer(obj:voidPointer, data:voidPointer):void := ( + mutex := Ccode(ThreadMutex, "*(pthread_mutex_t *)", obj); + destroy(mutex);); + +mutexInit(e:Expr):Expr := ( + when e + is HashTable do ( + ptr := GCmalloc(Pointer "pthread_mutex_t *"); + mutex := Ccode(ThreadMutex, "*", ptr); + r := init(mutex); + if r != 0 then return buildErrorPacketErrno("pthread_mutex_init", r); + Ccode(void, "GC_REGISTER_FINALIZER(", ptr, ", ", + "(GC_finalization_proc)", mutexFinalizer, ", NULL, NULL, NULL)"); + cell := mutexCell(mutex, hash_t(0)); + cell.hash = hashFromAddress(Expr(cell)); + Expr(cell)) + else WrongArgHashTable()); +installMethod(NewS, mutexClass, mutexInit); + +lock(e:Expr):Expr := ( + when e + is m:mutexCell do ( + r := lock(m.v); + if r == 0 then nullE + else buildErrorPacketErrno("pthread_mutex_lock", r)) + else WrongArgMutex()); +setupfun("lock0", lock); + +trylock(e:Expr):Expr := ( + when e + is m:mutexCell do ( + r := trylock(m.v); + if r == 0 then nullE + else buildErrorPacketErrno("pthread_mutex_trylock", r)) + else WrongArgMutex()); +setupfun("tryLock0", trylock); + +unlock(e:Expr):Expr := ( + when e + is m:mutexCell do ( + r := unlock(m.v); + if r == 0 then nullE + else buildErrorPacketErrno("pthread_mutex_unlock", r)) + else WrongArgMutex()); +setupfun("unlock0", unlock); + -- Local Variables: -- compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d pthread.o " -- End: diff --git a/M2/Macaulay2/d/pthread0.d b/M2/Macaulay2/d/pthread0.d index d6d2d6607cc..1210ffb168f 100644 --- a/M2/Macaulay2/d/pthread0.d +++ b/M2/Macaulay2/d/pthread0.d @@ -30,6 +30,7 @@ export SpinLock := atomicType "struct spinlockStructure"; export init(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_init(&(",lvalue(x),"),NULL)"); export destroy(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_destroy(&(",lvalue(x),"))"); export lock(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_lock(&(",lvalue(x),"))"); +export trylock(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_trylock(&(",lvalue(x),"))"); export unlock(x:ThreadMutex) ::= Ccode(int, "pthread_mutex_unlock(&(",lvalue(x),"))"); export getthreadself() ::= Ccode(Thread, "pthread_self()"); diff --git a/M2/Macaulay2/d/python-c.c b/M2/Macaulay2/d/python-c.c index 0eab9f15462..c9916fe38f1 100644 --- a/M2/Macaulay2/d/python-c.c +++ b/M2/Macaulay2/d/python-c.c @@ -1,5 +1,6 @@ #include #include "python-exports.h" +#include "pythoncapi_compat.h" #include @@ -89,92 +90,128 @@ void python_initspam() { /* GMP <-> Python integer conversion routines from gmpy2 * https://github.com/aleaxit/gmpy * Copyright 2000-2009 Alex Martelli - * Copyright 2008-2023 Case Van Horsen + * Copyright 2008-2025 Case Van Horsen * LGPL-3.0+ */ -/* #define's from src/gmpy2_convert.h */ -#if PY_VERSION_HEX >= 0x030C0000 -# define TAG_FROM_SIGN_AND_SIZE(is_neg, size) ((is_neg?2:(size==0)) | (((size_t)size) << 3)) -# define _PyLong_SetSignAndDigitCount(obj, is_neg, size) (obj->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(is_neg, size)) -#elif PY_VERSION_HEX >= 0x030900A4 -# define _PyLong_SetSignAndDigitCount(obj, is_neg, size) (Py_SET_SIZE(obj, (is_neg?-1:1)*Py_SIZE(result))) -#else -# define _PyLong_SetSignAndDigitCount(obj, is_neg, size) (Py_SIZE(obj) = (is_neg?-1:1)*Py_SIZE(result)) -#endif - -#if PY_VERSION_HEX >= 0x030C0000 -# define GET_OB_DIGIT(obj) obj->long_value.ob_digit -# define _PyLong_IsNegative(obj) ((obj->long_value.lv_tag & 3) == 2) -# define _PyLong_DigitCount(obj) (obj->long_value.lv_tag >> 3) -#else -# define GET_OB_DIGIT(obj) obj->ob_digit -# define _PyLong_IsNegative(obj) (Py_SIZE(obj) < 0) -# define _PyLong_DigitCount(obj) (_PyLong_IsNegative(obj)? -Py_SIZE(obj):Py_SIZE(obj)) -#endif - /* mpz_set_PyLong from src/gmpy2_convert_gmp.c */ -void python_LongAsZZ(mpz_ptr z, PyObject *obj) +int python_LongAsZZ(mpz_ptr z, PyObject *obj) { - int negative; - Py_ssize_t len; - PyLongObject *templong = (PyLongObject*)obj; - - len = _PyLong_DigitCount(templong); - negative = _PyLong_IsNegative(templong); - - switch (len) { - case 1: - mpz_set_si(z, (sdigit)GET_OB_DIGIT(templong)[0]); - break; - case 0: - mpz_set_si(z, 0); - break; - default: - mpz_import(z, len, -1, sizeof(GET_OB_DIGIT(templong)[0]), 0, - sizeof(GET_OB_DIGIT(templong)[0])*8 - PyLong_SHIFT, - GET_OB_DIGIT(templong)); - } - - if (negative) { - mpz_neg(z, z); - } - return; +#ifndef PYPY_VERSION + const PyLongLayout *layout = PyLong_GetNativeLayout(); + PyLongExport long_export = {0, 0, 0, 0, 0}; + + if (PyLong_Export(obj, &long_export) < 0) { + /* LCOV_EXCL_START */ + return -1; + /* LCOV_EXCL_STOP */ + } + if (long_export.digits) { + mpz_import(z, long_export.ndigits, layout->digits_order, + layout->digit_size, layout->digit_endianness, + layout->digit_size*8 - layout->bits_per_digit, + long_export.digits); + if (long_export.negative) { + mpz_neg(z, z); + } + PyLong_FreeExport(&long_export); + } + else { + const int64_t value = long_export.value; + + if (LONG_MIN <= value && value <= LONG_MAX) { + mpz_set_si(z, value); + } + else { + mpz_import(z, 1, -1, sizeof(int64_t), 0, 0, &value); + if (value < 0) { + mpz_t tmp; + mpz_init(tmp); + mpz_ui_pow_ui(tmp, 2, 64); + mpz_sub(z, z, tmp); + mpz_clear(tmp); + } + } + } + return 0; +#else + int overflow; + long value = PyLong_AsLongAndOverflow(obj, &overflow); + if (!overflow) { + mpz_set_si(z, value); + return 0; + } + + PyObject *s = PyNumber_ToBase(obj, 16); + + if (!s) { + /* LCOV_EXCL_START */ + return -1; + /* LCOV_EXCL_STOP */ + } + + const char *str = PyUnicode_AsUTF8(s), *p = str; + + if (!str) { + /* LCOV_EXCL_START */ + Py_DECREF(s); + return -1; + /* LCOV_EXCL_STOP */ + } + + int negative = (str[0] == '-'); + + p += 2; + if (negative) { + p++; + } + mpz_init_set_str(z, p, 16); + Py_DECREF(s); + if (negative) { + mpz_neg(z, z); + } + return 0; +#endif } /* GMPy_PyLong_From_MPZ from src/gmpy2_convert_gmp.c */ /* replace obj->z with z when updating */ PyObject *python_LongFromZZ(mpz_srcptr z) { - int negative; - size_t count, size; - PyLongObject *result; - - /* Assume gmp uses limbs as least as large as the builtin longs do */ - - negative = mpz_sgn(z) < 0; - size = (mpz_sizeinbase(z, 2) + PyLong_SHIFT - 1) / PyLong_SHIFT; - - if (!(result = _PyLong_New(size))) { - /* LCOV_EXCL_START */ - return NULL; - /* LCOV_EXCL_STOP */ - } - - mpz_export(GET_OB_DIGIT(result), &count, -1, sizeof(GET_OB_DIGIT(result)[0]), 0, - sizeof(GET_OB_DIGIT(result)[0])*8 - PyLong_SHIFT, z); + if (mpz_fits_slong_p(z)) { + return PyLong_FromLong(mpz_get_si(z)); + } + +#ifndef PYPY_VERSION + const PyLongLayout *layout = PyLong_GetNativeLayout(); + size_t size = (mpz_sizeinbase(z, 2) + + layout->bits_per_digit - 1)/layout->bits_per_digit; + void *digits; + PyLongWriter *writer = PyLongWriter_Create(mpz_sgn(z) < 0, size, + &digits); + if (writer == NULL) { + /* LCOV_EXCL_START */ + return NULL; + /* LCOV_EXCL_STOP */ + } + + mpz_export(digits, NULL, layout->digits_order, layout->digit_size, + layout->digit_endianness, + layout->digit_size*8 - layout->bits_per_digit, z); + return PyLongWriter_Finish(writer); +#else + PyObject *str = GMPy_PyStr_From_MPZ(obj, 16, 0, NULL); - if (count == 0) { - GET_OB_DIGIT(result)[0] = 0; - } + if (!str) { + /* LCOV_EXCL_START */ + return NULL; + /* LCOV_EXCL_STOP */ + } - /* long_normalize() is file-static so we must reimplement it */ - /* longobjp = long_normalize(longobjp); */ - while ((size>0) && (GET_OB_DIGIT(result)[size-1] == 0)) { - size--; - } + PyObject *res = PyLong_FromUnicodeObject(str, 16); - _PyLong_SetSignAndDigitCount(result, negative, size); - return (PyObject*)result; + Py_DECREF(str); + return res; +#endif } #if 0 diff --git a/M2/Macaulay2/d/python.d b/M2/Macaulay2/d/python.d index 4eac27cfe2d..3308288a886 100644 --- a/M2/Macaulay2/d/python.d +++ b/M2/Macaulay2/d/python.d @@ -206,7 +206,7 @@ setupfun("pythonObjectIsTrue", PyObjectIsTrue); -- ints -- ---------- -import LongAsZZ(z:ZZmutable, x:pythonObject):void; +import LongAsZZ(z:ZZmutable, x:pythonObject):int; PyLongAsLong(e:Expr):Expr := when e is x:pythonObjectCell do ( @@ -217,8 +217,8 @@ PyLongAsLong(e:Expr):Expr := else if overflow == 0 then toExpr(y) else ( z := newZZmutable(); - LongAsZZ(z, x.v); - toExpr(moveToZZandclear(z)))) + if LongAsZZ(z, x.v) == -1 then buildPythonErrorPacket() + else toExpr(moveToZZandclear(z)))) is s:SpecialExpr do PyLongAsLong(s.e) else WrongArgPythonObject(); setupfun("pythonLongAsLong",PyLongAsLong); diff --git a/M2/Macaulay2/d/pythoncapi_compat.h b/M2/Macaulay2/d/pythoncapi_compat.h new file mode 100644 index 00000000000..cdfdafa84eb --- /dev/null +++ b/M2/Macaulay2/d/pythoncapi_compat.h @@ -0,0 +1,2666 @@ +// Header file providing new C API functions to old Python versions. +// +// File distributed under the Zero Clause BSD (0BSD) license. +// Copyright Contributors to the pythoncapi_compat project. +// +// Homepage: +// https://github.com/python/pythoncapi_compat +// +// Latest version: +// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h +// +// SPDX-License-Identifier: 0BSD + +#ifndef PYTHONCAPI_COMPAT +#define PYTHONCAPI_COMPAT + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include // offsetof() + +// Python 3.11.0b4 added PyFrame_Back() to Python.h +#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) +# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() +#endif + + +#ifndef _Py_CAST +# define _Py_CAST(type, expr) ((type)(expr)) +#endif + +// Static inline functions should use _Py_NULL rather than using directly NULL +// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, +// _Py_NULL is defined as nullptr. +#ifndef _Py_NULL +# if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ + || (defined(__cplusplus) && __cplusplus >= 201103) +# define _Py_NULL nullptr +# else +# define _Py_NULL NULL +# endif +#endif + +// Cast argument to PyObject* type. +#ifndef _PyObject_CAST +# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +#endif + +#ifndef Py_BUILD_ASSERT +# define Py_BUILD_ASSERT(cond) \ + do { \ + (void)sizeof(char [1 - 2 * !(cond)]); \ + } while(0) +#endif + + +// bpo-42262 added Py_NewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) +static inline PyObject* _Py_NewRef(PyObject *obj) +{ + Py_INCREF(obj); + return obj; +} +#define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-42262 added Py_XNewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) +static inline PyObject* _Py_XNewRef(PyObject *obj) +{ + Py_XINCREF(obj); + return obj; +} +#define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) +static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +{ + ob->ob_refcnt = refcnt; +} +#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) +#endif + + +// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. +// It is excluded from the limited C API. +#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ + } while (0) + +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ + } while (0) +#endif + + +// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() +// to Python 3.10.0b1. +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) +# define Py_Is(x, y) ((x) == (y)) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) +# define Py_IsNone(x) Py_Is(x, Py_None) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) +# define Py_IsTrue(x) Py_Is(x, Py_True) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) +# define Py_IsFalse(x) Py_Is(x, Py_False) +#endif + + +// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +{ + ob->ob_type = type; +} +#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +{ + ob->ob_size = size; +} +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) +#endif + + +// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + assert(frame->f_code != _Py_NULL); + return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code)); +} +#endif + +static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) +{ + PyCodeObject *code = PyFrame_GetCode(frame); + Py_DECREF(code); + return code; +} + + +// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) +{ + PyFrameObject *back = PyFrame_GetBack(frame); + Py_XDECREF(back); + return back; +} +#endif + + +// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030400B1 + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } +#else + PyFrame_FastToLocals(frame); +#endif + return Py_NewRef(frame->f_locals); +} +#endif + + +// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_globals); +} +#endif + + +// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_builtins); +} +#endif + + +// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline int PyFrame_GetLasti(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030A00A7 + // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, + // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) + // instructions. + if (frame->f_lasti < 0) { + return -1; + } + return frame->f_lasti * 2; +#else + return frame->f_lasti; +#endif +} +#endif + + +// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +{ + PyObject *locals, *value; + + locals = PyFrame_GetLocals(frame); + if (locals == NULL) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + value = PyDict_GetItemWithError(locals, name); +#else + value = _PyDict_GetItemWithError(locals, name); +#endif + Py_DECREF(locals); + + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + PyErr_Format(PyExc_NameError, "variable %R does not exist", name); +#else + PyErr_SetString(PyExc_NameError, "variable does not exist"); +#endif + return NULL; + } + return Py_NewRef(value); +} +#endif + + +// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* +PyFrame_GetVarString(PyFrameObject *frame, const char *name) +{ + PyObject *name_obj, *value; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(name); +#else + name_obj = PyString_FromString(name); +#endif + if (name_obj == NULL) { + return NULL; + } + value = PyFrame_GetVar(frame, name_obj); + Py_DECREF(name_obj); + return value; +} +#endif + + +// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) +static inline PyInterpreterState * +PyThreadState_GetInterpreter(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->interp; +} +#endif + + +// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject* +_PyThreadState_GetFrameBorrow(PyThreadState *tstate) +{ + PyFrameObject *frame = PyThreadState_GetFrame(tstate); + Py_XDECREF(frame); + return frame; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState* PyInterpreterState_Get(void) +{ + PyThreadState *tstate; + PyInterpreterState *interp; + + tstate = PyThreadState_GET(); + if (tstate == _Py_NULL) { + Py_FatalError("GIL released (tstate is NULL)"); + } + interp = tstate->interp; + if (interp == _Py_NULL) { + Py_FatalError("no current interpreter"); + } + return interp; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 +#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->id; +} +#endif + +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_EnterTracing(PyThreadState *tstate) +{ + tstate->tracing++; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = 0; +#else + tstate->use_tracing = 0; +#endif +} +#endif + +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) +{ + int use_tracing = (tstate->c_tracefunc != _Py_NULL + || tstate->c_profilefunc != _Py_NULL); + tstate->tracing--; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = use_tracing; +#else + tstate->use_tracing = use_tracing; +#endif +} +#endif + + +// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 +// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +static inline PyObject* PyObject_CallNoArgs(PyObject *func) +{ + return PyObject_CallFunctionObjArgs(func, NULL); +} +#endif + + +// bpo-39245 made PyObject_CallOneArg() public (previously called +// _PyObject_CallOneArg) in Python 3.9.0a4 +// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) +{ + return PyObject_CallFunctionObjArgs(func, arg, NULL); +} +#endif + + +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 +static inline int +PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) +{ + int res; + + if (!value && !PyErr_Occurred()) { + // PyModule_AddObject() raises TypeError in this case + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + return -1; + } + + Py_XINCREF(value); + res = PyModule_AddObject(module, name, value); + if (res < 0) { + Py_XDECREF(value); + } + return res; +} +#endif + + +// bpo-40024 added PyModule_AddType() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 +static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +{ + const char *name, *dot; + + if (PyType_Ready(type) < 0) { + return -1; + } + + // inline _PyType_Name() + name = type->tp_name; + assert(name != _Py_NULL); + dot = strrchr(name, '.'); + if (dot != _Py_NULL) { + name = dot + 1; + } + + return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); +} +#endif + + +// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. +// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. +#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsTracked(PyObject* obj) +{ + return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); +} +#endif + +// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. +// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. +#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsFinalized(PyObject *obj) +{ + PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; + return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); +} +#endif + + +// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) +static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { + return Py_TYPE(ob) == type; +} +#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal +// C API: Python 3.11a2-3.11a6 versions are not supported. +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack2(double x, char *p, int le) +{ return _PyFloat_Pack2(x, (unsigned char*)p, le); } + +static inline double PyFloat_Unpack2(const char *p, int le) +{ return _PyFloat_Unpack2((const unsigned char *)p, le); } +#endif + + +// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and +// PyFloat_Unpack8() to Python 3.11a7. +// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() +// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions +// are not supported. +#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack4(double x, char *p, int le) +{ return _PyFloat_Pack4(x, (unsigned char*)p, le); } + +static inline int PyFloat_Pack8(double x, char *p, int le) +{ return _PyFloat_Pack8(x, (unsigned char*)p, le); } + +static inline double PyFloat_Unpack4(const char *p, int le) +{ return _PyFloat_Unpack4((const unsigned char *)p, le); } + +static inline double PyFloat_Unpack8(const char *p, int le) +{ return _PyFloat_Unpack8((const unsigned char *)p, le); } +#endif + + +// gh-92154 added PyCode_GetCode() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCode(PyCodeObject *code) +{ + return Py_NewRef(code->co_code); +} +#endif + + +// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) +{ + return Py_NewRef(code->co_varnames); +} +#endif + +// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) +{ + return Py_NewRef(code->co_freevars); +} +#endif + +// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) +{ + return Py_NewRef(code->co_cellvars); +} +#endif + + +// Py_UNUSED() was added to Python 3.4.0b2. +#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) +# if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +# else +# define Py_UNUSED(name) _unused_ ## name +# endif +#endif + + +// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A0 +static inline PyObject* PyImport_AddModuleRef(const char *name) +{ + return Py_XNewRef(PyImport_AddModule(name)); +} +#endif + + +// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D0000 +static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) +{ + PyObject *obj; + if (ref != NULL && !PyWeakref_Check(ref)) { + *pobj = NULL; + PyErr_SetString(PyExc_TypeError, "expected a weakref"); + return -1; + } + obj = PyWeakref_GetObject(ref); + if (obj == NULL) { + // SystemError if ref is NULL + *pobj = NULL; + return -1; + } + if (obj == Py_None) { + *pobj = NULL; + return 0; + } + *pobj = Py_NewRef(obj); + return 1; +} +#endif + + +// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 +#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET +# define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) +#endif + +// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 +#if PY_VERSION_HEX < 0x030800B1 +static inline Py_ssize_t PyVectorcall_NARGS(size_t n) +{ + return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; +} +#endif + + +// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 +static inline PyObject* +PyObject_Vectorcall(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ +#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) + // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 + return _PyObject_Vectorcall(callable, args, nargsf, kwnames); +#else + PyObject *posargs = NULL, *kwargs = NULL; + PyObject *res; + Py_ssize_t nposargs, nkwargs, i; + + if (nargsf != 0 && args == NULL) { + PyErr_BadInternalCall(); + goto error; + } + if (kwnames != NULL && !PyTuple_Check(kwnames)) { + PyErr_BadInternalCall(); + goto error; + } + + nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); + if (kwnames) { + nkwargs = PyTuple_GET_SIZE(kwnames); + } + else { + nkwargs = 0; + } + + posargs = PyTuple_New(nposargs); + if (posargs == NULL) { + goto error; + } + if (nposargs) { + for (i=0; i < nposargs; i++) { + PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); + args++; + } + } + + if (nkwargs) { + kwargs = PyDict_New(); + if (kwargs == NULL) { + goto error; + } + + for (i = 0; i < nkwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = *args; + args++; + if (PyDict_SetItem(kwargs, key, value) < 0) { + goto error; + } + } + } + else { + kwargs = NULL; + } + + res = PyObject_Call(callable, posargs, kwargs); + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return res; + +error: + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return NULL; +#endif +} +#endif + + +// gh-106521 added PyObject_GetOptionalAttr() and +// PyObject_GetOptionalAttrString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) +{ + // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 +#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) + return _PyObject_LookupAttr(obj, attr_name, result); +#else + *result = PyObject_GetAttr(obj, attr_name); + if (*result != NULL) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; +#endif +} + +static inline int +PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) +{ + PyObject *name_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(attr_name); +#else + name_obj = PyString_FromString(attr_name); +#endif + if (name_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyObject_GetOptionalAttr(obj, name_obj, result); + Py_DECREF(name_obj); + return rc; +} +#endif + + +// gh-106307 added PyObject_GetOptionalAttr() and +// PyMapping_GetOptionalItemString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) +{ + *result = PyObject_GetItem(obj, key); + if (*result) { + return 1; + } + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; +} + +static inline int +PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) +{ + PyObject *key_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + key_obj = PyUnicode_FromString(key); +#else + key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyMapping_GetOptionalItem(obj, key_obj, result); + Py_DECREF(key_obj); + return rc; +} +#endif + +// gh-108511 added PyMapping_HasKeyWithError() and +// PyMapping_HasKeyStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItem(obj, key, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItemString(obj, key, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-108511 added PyObject_HasAttrWithError() and +// PyObject_HasAttrStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttr(obj, attr, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttrString(obj, attr, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *item = PyDict_GetItemWithError(mp, key); +#else + PyObject *item = _PyDict_GetItemWithError(mp, key); +#endif + if (item != NULL) { + *result = Py_NewRef(item); + return 1; // found + } + if (!PyErr_Occurred()) { + *result = NULL; + return 0; // not found + } + *result = NULL; + return -1; +} + +static inline int +PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) +{ + int res; +#if PY_VERSION_HEX >= 0x03000000 + PyObject *key_obj = PyUnicode_FromString(key); +#else + PyObject *key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + res = PyDict_GetItemRef(mp, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-106307 added PyModule_Add() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + + +// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 +// bpo-1856 added _Py_Finalizing to Python 3.2.1b1. +// _Py_IsFinalizing() was added to PyPy 7.3.0. +#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \ + && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +static inline int Py_IsFinalizing(void) +{ +#if PY_VERSION_HEX >= 0x030700A1 + // _Py_IsFinalizing() was added to Python 3.7.0a1. + return _Py_IsFinalizing(); +#else + return (_Py_Finalizing != NULL); +#endif +} +#endif + + +// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_ContainsString(PyObject *op, const char *key) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + return -1; + } + int res = PyDict_Contains(op, key_obj); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyLong_AsInt(PyObject *obj) +{ +#ifdef PYPY_VERSION + long value = PyLong_AsLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + if (value < (long)INT_MIN || (long)INT_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)value; +#else + return _PyLong_AsInt(obj); +#endif +} +#endif + + +// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (dict == NULL || *dict == NULL) { + return -1; + } + Py_VISIT(*dict); + return 0; +} + +static inline void +PyObject_ClearManagedDict(PyObject *obj) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (dict == NULL || *dict == NULL) { + return; + } + Py_CLEAR(*dict); +} +#endif + +// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 +// Python 3.5.2 added _PyThreadState_UncheckedGet(). +#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 +static inline PyThreadState* +PyThreadState_GetUnchecked(void) +{ + return _PyThreadState_UncheckedGet(); +} +#endif + +// gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) +{ + Py_ssize_t len; + const void *utf8; + PyObject *exc_type, *exc_value, *exc_tb; + int res; + + // API cannot report errors so save/restore the exception + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() +#if PY_VERSION_HEX >= 0x030300A1 + if (PyUnicode_IS_ASCII(unicode)) { + utf8 = PyUnicode_DATA(unicode); + len = PyUnicode_GET_LENGTH(unicode); + } + else { + utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); + if (utf8 == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + } + + if (len != str_len) { + res = 0; + goto done; + } + res = (memcmp(utf8, str, (size_t)len) == 0); +#else + PyObject *bytes = PyUnicode_AsUTF8String(unicode); + if (bytes == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + +#if PY_VERSION_HEX >= 0x03000000 + len = PyBytes_GET_SIZE(bytes); + utf8 = PyBytes_AS_STRING(bytes); +#else + len = PyString_GET_SIZE(bytes); + utf8 = PyString_AS_STRING(bytes); +#endif + if (len != str_len) { + Py_DECREF(bytes); + res = 0; + goto done; + } + + res = (memcmp(utf8, str, (size_t)len) == 0); + Py_DECREF(bytes); +#endif + +done: + PyErr_Restore(exc_type, exc_value, exc_tb); + return res; +} + +static inline int +PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) +{ + return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str)); +} +#endif + + +// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyList_Extend(PyObject *list, PyObject *iterable) +{ + return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable); +} + +static inline int +PyList_Clear(PyObject *list) +{ + return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); +} +#endif + +// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) +{ + PyObject *value; + + if (!PyDict_Check(dict)) { + PyErr_BadInternalCall(); + if (result) { + *result = NULL; + } + return -1; + } + + // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. + // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. + // Python 3.13.0a1 removed _PyDict_Pop(). +#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000 + value = PyObject_CallMethod(dict, "pop", "O", key); +#elif PY_VERSION_HEX < 0x030600b3 + value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL); +#else + value = _PyDict_Pop(dict, key, NULL); +#endif + if (value == NULL) { + if (result) { + *result = NULL; + } + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; + } + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; +} + +static inline int +PyDict_PopString(PyObject *dict, const char *key, PyObject **result) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + if (result != NULL) { + *result = NULL; + } + return -1; + } + + int res = PyDict_Pop(dict, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +#if PY_VERSION_HEX < 0x030200A4 +// Python 3.2.0a4 added Py_hash_t type +typedef Py_ssize_t Py_hash_t; +#endif + + +// gh-111545 added Py_HashPointer() to Python 3.13.0a3 +#if PY_VERSION_HEX < 0x030D00A3 +static inline Py_hash_t Py_HashPointer(const void *ptr) +{ +#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) + return _Py_HashPointer(ptr); +#else + return _Py_HashPointer(_Py_CAST(void*, ptr)); +#endif +} +#endif + + +// Python 3.13a4 added a PyTime API. +// Use the private API added to Python 3.5. +#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 +typedef _PyTime_t PyTime_t; +#define PyTime_MIN _PyTime_MIN +#define PyTime_MAX _PyTime_MAX + +static inline double PyTime_AsSecondsDouble(PyTime_t t) +{ return _PyTime_AsSecondsDouble(t); } + +static inline int PyTime_Monotonic(PyTime_t *result) +{ return _PyTime_GetMonotonicClockWithInfo(result, NULL); } + +static inline int PyTime_Time(PyTime_t *result) +{ return _PyTime_GetSystemClockWithInfo(result, NULL); } + +static inline int PyTime_PerfCounter(PyTime_t *result) +{ +#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) + return _PyTime_GetPerfCounterWithInfo(result, NULL); +#elif PY_VERSION_HEX >= 0x03070000 + // Call time.perf_counter_ns() and convert Python int object to PyTime_t. + // Cache time.perf_counter_ns() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter_ns"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + long long value = PyLong_AsLongLong(res); + Py_DECREF(res); + + if (value == -1 && PyErr_Occurred()) { + return -1; + } + + Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t)); + *result = (PyTime_t)value; + return 0; +#else + // Call time.perf_counter() and convert C double to PyTime_t. + // Cache time.perf_counter() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + double d = PyFloat_AsDouble(res); + Py_DECREF(res); + + if (d == -1.0 && PyErr_Occurred()) { + return -1; + } + + // Avoid floor() to avoid having to link to libm + *result = (PyTime_t)(d * 1e9); + return 0; +#endif +} + +#endif + +// gh-111389 added hash constants to Python 3.13.0a5. These constants were +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8. +#if (!defined(PyHASH_BITS) \ + && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ + && PYPY_VERSION_NUM >= 0x07030800))) +# define PyHASH_BITS _PyHASH_BITS +# define PyHASH_MODULUS _PyHASH_MODULUS +# define PyHASH_INF _PyHASH_INF +# define PyHASH_IMAG _PyHASH_IMAG +#endif + + +// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() +// to Python 3.13.0a6 +#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) + +#define Py_CONSTANT_NONE 0 +#define Py_CONSTANT_FALSE 1 +#define Py_CONSTANT_TRUE 2 +#define Py_CONSTANT_ELLIPSIS 3 +#define Py_CONSTANT_NOT_IMPLEMENTED 4 +#define Py_CONSTANT_ZERO 5 +#define Py_CONSTANT_ONE 6 +#define Py_CONSTANT_EMPTY_STR 7 +#define Py_CONSTANT_EMPTY_BYTES 8 +#define Py_CONSTANT_EMPTY_TUPLE 9 + +static inline PyObject* Py_GetConstant(unsigned int constant_id) +{ + static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; + + if (constants[Py_CONSTANT_NONE] == NULL) { + constants[Py_CONSTANT_NONE] = Py_None; + constants[Py_CONSTANT_FALSE] = Py_False; + constants[Py_CONSTANT_TRUE] = Py_True; + constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis; + constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented; + + constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0); + if (constants[Py_CONSTANT_ZERO] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_ONE] = PyLong_FromLong(1); + if (constants[Py_CONSTANT_ONE] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_STR] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0); + if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) { + goto fatal_error; + } + // goto dance to avoid compiler warnings about Py_FatalError() + goto init_done; + +fatal_error: + // This case should never happen + Py_FatalError("Py_GetConstant() failed to get constants"); + } + +init_done: + if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { + return Py_NewRef(constants[constant_id]); + } + else { + PyErr_BadInternalCall(); + return NULL; + } +} + +static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) +{ + PyObject *obj = Py_GetConstant(constant_id); + Py_XDECREF(obj); + return obj; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline PyObject * +PyList_GetItemRef(PyObject *op, Py_ssize_t index) +{ + PyObject *item = PyList_GetItem(op, index); + Py_XINCREF(item); + return item; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline int +PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, + PyObject **result) +{ + PyObject *value; + if (PyDict_GetItemRef(d, key, &value) < 0) { + // get error + if (result) { + *result = NULL; + } + return -1; + } + if (value != NULL) { + // present + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; + } + + // missing: set the item + if (PyDict_SetItem(d, key, default_value) < 0) { + // set error + if (result) { + *result = NULL; + } + return -1; + } + if (result) { + *result = Py_NewRef(default_value); + } + return 0; +} +#endif + +#if PY_VERSION_HEX < 0x030D00B3 +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } +#endif + +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) +typedef struct PyUnicodeWriter PyUnicodeWriter; + +static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + PyMem_Free(writer); +} + +static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == _Py_NULL) { + PyErr_NoMemory(); + return _Py_NULL; + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + return pub_writer; +} + +static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); + assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + PyMem_Free(writer); + return str; +} + +static inline int +PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); +} + +static inline int +PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Repr(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteASCII(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + return _PyUnicodeWriter_WriteASCIIString((_PyUnicodeWriter*)writer, + str, size); +} + +static inline int +PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, + const wchar_t *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)wcslen(str); + } + + PyObject *str_obj = PyUnicode_FromWideChar(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %s", + Py_TYPE(str)->tp_name); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, + start, end); +} + +static inline int +PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyUnicode_FromFormatV(format, vargs); + va_end(vargs); + if (str == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030E0000 + +// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyLong_GetSign(PyObject *obj, int *sign) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + + *sign = _PyLong_Sign(obj); + return 0; +} +#endif + +// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2 +#if PY_VERSION_HEX < 0x030E00A2 +static inline int PyLong_IsPositive(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == 1; +} + +static inline int PyLong_IsNegative(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == -1; +} + +static inline int PyLong_IsZero(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == 0; +} +#endif + + +// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) +{ + if (!PyUnicode_Check(str1)) { + PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", + Py_TYPE(str1)->tp_name); + return -1; + } + if (!PyUnicode_Check(str2)) { + PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", + Py_TYPE(str2)->tp_name); + return -1; + } + +#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) + PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); + + return _PyUnicode_Equal(str1, str2); +#elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#else + return (PyUnicode_Compare(str1, str2) == 0); +#endif +} +#endif + + +// gh-121645 added PyBytes_Join() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) +{ + return _PyBytes_Join(sep, iterable); +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) +{ +#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) + PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); + + return _Py_HashBytes(ptr, len); +#else + Py_hash_t hash; + PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len); + if (bytes == NULL) { + return -1; + } + hash = PyObject_Hash(bytes); + Py_DECREF(bytes); + return hash; +#endif +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyIter_NextItem(PyObject *iter, PyObject **item) +{ + iternextfunc tp_iternext; + + assert(iter != NULL); + assert(item != NULL); + + tp_iternext = Py_TYPE(iter)->tp_iternext; + if (tp_iternext == NULL) { + *item = NULL; + PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", + Py_TYPE(iter)->tp_name); + return -1; + } + + if ((*item = tp_iternext(iter))) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + return 0; + } + return -1; +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyLong_FromInt32(int32_t value) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + return PyLong_FromLong(value); +} + +static inline PyObject* PyLong_FromInt64(int64_t value) +{ + Py_BUILD_ASSERT(sizeof(long long) >= 8); + return PyLong_FromLongLong(value); +} + +static inline PyObject* PyLong_FromUInt32(uint32_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); + return PyLong_FromUnsignedLong(value); +} + +static inline PyObject* PyLong_FromUInt64(uint64_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); + return PyLong_FromUnsignedLongLong(value); +} + +static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(int) == 4); + int value = PyLong_AsInt(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int32_t)value; + return 0; +} + +static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + long long value = PyLong_AsLongLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int64_t)value; + return 0; +} + +static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + unsigned long value = PyLong_AsUnsignedLong(obj); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return -1; + } +#if SIZEOF_LONG > 4 + if ((unsigned long)UINT32_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint32_t"); + return -1; + } +#endif + *pvalue = (uint32_t)value; + return 0; +} + +static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + unsigned long long value = PyLong_AsUnsignedLongLong(obj); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (uint64_t)value; + return 0; +} +#endif + + +// gh-102471 added import and export API for integers to 3.14.0a2. +#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +// Helpers to access PyLongObject internals. +static inline void +_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) +{ +#if PY_VERSION_HEX >= 0x030C0000 + op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3); +#elif PY_VERSION_HEX >= 0x030900A4 + Py_SET_SIZE(op, sign * size); +#else + Py_SIZE(op) = sign * size; +#endif +} + +static inline Py_ssize_t +_PyLong_DigitCount(const PyLongObject *op) +{ +#if PY_VERSION_HEX >= 0x030C0000 + return (Py_ssize_t)(op->long_value.lv_tag >> 3); +#else + return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); +#endif +} + +static inline digit* +_PyLong_GetDigits(const PyLongObject *op) +{ +#if PY_VERSION_HEX >= 0x030C0000 + return (digit*)(op->long_value.ob_digit); +#else + return (digit*)(op->ob_digit); +#endif +} + +typedef struct PyLongLayout { + uint8_t bits_per_digit; + uint8_t digit_size; + int8_t digits_order; + int8_t digit_endianness; +} PyLongLayout; + +typedef struct PyLongExport { + int64_t value; + uint8_t negative; + Py_ssize_t ndigits; + const void *digits; + Py_uintptr_t _reserved; +} PyLongExport; + +typedef struct PyLongWriter PyLongWriter; + +static inline const PyLongLayout* +PyLong_GetNativeLayout(void) +{ + static const PyLongLayout PyLong_LAYOUT = { + PyLong_SHIFT, + sizeof(digit), + -1, // least significant first + PY_LITTLE_ENDIAN ? -1 : 1, + }; + + return &PyLong_LAYOUT; +} + +static inline int +PyLong_Export(PyObject *obj, PyLongExport *export_long) +{ + if (!PyLong_Check(obj)) { + memset(export_long, 0, sizeof(*export_long)); + PyErr_Format(PyExc_TypeError, "expected int, got %s", + Py_TYPE(obj)->tp_name); + return -1; + } + + // Fast-path: try to convert to a int64_t + PyLongObject *self = (PyLongObject*)obj; + int overflow; +#if SIZEOF_LONG == 8 + long value = PyLong_AsLongAndOverflow(obj, &overflow); +#else + // Windows has 32-bit long, so use 64-bit long long instead + long long value = PyLong_AsLongLongAndOverflow(obj, &overflow); +#endif + Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t)); + // the function cannot fail since obj is a PyLongObject + assert(!(value == -1 && PyErr_Occurred())); + + if (!overflow) { + export_long->value = value; + export_long->negative = 0; + export_long->ndigits = 0; + export_long->digits = 0; + export_long->_reserved = 0; + } + else { + export_long->value = 0; + export_long->negative = _PyLong_Sign(obj) < 0; + export_long->ndigits = _PyLong_DigitCount(self); + if (export_long->ndigits == 0) { + export_long->ndigits = 1; + } + export_long->digits = _PyLong_GetDigits(self); + export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj); + } + return 0; +} + +static inline void +PyLong_FreeExport(PyLongExport *export_long) +{ + PyObject *obj = (PyObject*)export_long->_reserved; + + if (obj) { + export_long->_reserved = 0; + Py_DECREF(obj); + } +} + +static inline PyLongWriter* +PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) +{ + if (ndigits <= 0) { + PyErr_SetString(PyExc_ValueError, "ndigits must be positive"); + return NULL; + } + assert(digits != NULL); + + PyLongObject *obj = _PyLong_New(ndigits); + if (obj == NULL) { + return NULL; + } + _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits); + + *digits = _PyLong_GetDigits(obj); + return (PyLongWriter*)obj; +} + +static inline void +PyLongWriter_Discard(PyLongWriter *writer) +{ + PyLongObject *obj = (PyLongObject *)writer; + + assert(Py_REFCNT(obj) == 1); + Py_DECREF(obj); +} + +static inline PyObject* +PyLongWriter_Finish(PyLongWriter *writer) +{ + PyObject *obj = (PyObject *)writer; + PyLongObject *self = (PyLongObject*)obj; + Py_ssize_t j = _PyLong_DigitCount(self); + Py_ssize_t i = j; + int sign = _PyLong_Sign(obj); + + assert(Py_REFCNT(obj) == 1); + + // Normalize and get singleton if possible + while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) { + --i; + } + if (i != j) { + if (i == 0) { + sign = 0; + } + _PyLong_SetSignAndDigitCount(self, sign, i); + } + if (i <= 1) { + long val = sign * (long)(_PyLong_GetDigits(self)[0]); + Py_DECREF(obj); + return PyLong_FromLong(val); + } + + return obj; +} +#endif + + +#if PY_VERSION_HEX < 0x030C00A3 +# define Py_T_SHORT 0 +# define Py_T_INT 1 +# define Py_T_LONG 2 +# define Py_T_FLOAT 3 +# define Py_T_DOUBLE 4 +# define Py_T_STRING 5 +# define _Py_T_OBJECT 6 +# define Py_T_CHAR 7 +# define Py_T_BYTE 8 +# define Py_T_UBYTE 9 +# define Py_T_USHORT 10 +# define Py_T_UINT 11 +# define Py_T_ULONG 12 +# define Py_T_STRING_INPLACE 13 +# define Py_T_BOOL 14 +# define Py_T_OBJECT_EX 16 +# define Py_T_LONGLONG 17 +# define Py_T_ULONGLONG 18 +# define Py_T_PYSSIZET 19 + +# if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +# define _Py_T_NONE 20 +# endif + +# define Py_READONLY 1 +# define Py_AUDIT_READ 2 +# define _Py_WRITE_RESTRICTED 4 +#endif + + +// gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4 +#if PY_VERSION_HEX < 0x030E00A4 +static inline FILE* Py_fopen(PyObject *path, const char *mode) +{ +#if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION) + PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode); + + return _Py_fopen_obj(path, mode); +#else + FILE *f; + PyObject *bytes; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_FSConverter(path, &bytes)) { + return NULL; + } +#else + if (!PyString_Check(path)) { + PyErr_SetString(PyExc_TypeError, "except str"); + return NULL; + } + bytes = Py_NewRef(path); +#endif + const char *path_bytes = PyBytes_AS_STRING(bytes); + + f = fopen(path_bytes, mode); + Py_DECREF(bytes); + + if (f == NULL) { + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); + return NULL; + } + return f; +#endif +} + +static inline int Py_fclose(FILE *file) +{ + return fclose(file); +} +#endif + + +#if 0x03080000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) +PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); + +static inline PyObject* +PyConfig_Get(const char *name) +{ + typedef enum { + _PyConfig_MEMBER_INT, + _PyConfig_MEMBER_UINT, + _PyConfig_MEMBER_ULONG, + _PyConfig_MEMBER_BOOL, + _PyConfig_MEMBER_WSTR, + _PyConfig_MEMBER_WSTR_OPT, + _PyConfig_MEMBER_WSTR_LIST, + } PyConfigMemberType; + + typedef struct { + const char *name; + size_t offset; + PyConfigMemberType type; + const char *sys_attr; + } PyConfigSpec; + +#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ + {#MEMBER, offsetof(PyConfig, MEMBER), \ + _PyConfig_MEMBER_##TYPE, sys_attr} + + static const PyConfigSpec config_spec[] = { + PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"), + PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"), + PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"), + PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"), + PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"), + PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL), +#if 0x03090000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"), +#endif + PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"), + PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"), + PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"), +#endif + PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"), + PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), + PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL), +#if 0x030D0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL), +#endif +#ifdef Py_GIL_DISABLED + PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL), +#ifdef MS_WINDOWS + PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"), +#endif + PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL), +#endif + }; + +#undef PYTHONCAPI_COMPAT_SPEC + + const PyConfigSpec *spec; + int found = 0; + for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) { + spec = &config_spec[i]; + if (strcmp(spec->name, name) == 0) { + found = 1; + break; + } + } + if (found) { + if (spec->sys_attr != NULL) { + PyObject *value = PySys_GetObject(spec->sys_attr); + if (value == NULL) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); + return NULL; + } + return Py_NewRef(value); + } + + const PyConfig *config = _Py_GetConfig(); + void *member = (char *)config + spec->offset; + switch (spec->type) { + case _PyConfig_MEMBER_INT: + case _PyConfig_MEMBER_UINT: + { + int value = *(int *)member; + return PyLong_FromLong(value); + } + case _PyConfig_MEMBER_BOOL: + { + int value = *(int *)member; + return PyBool_FromLong(value != 0); + } + case _PyConfig_MEMBER_ULONG: + { + unsigned long value = *(unsigned long *)member; + return PyLong_FromUnsignedLong(value); + } + case _PyConfig_MEMBER_WSTR: + case _PyConfig_MEMBER_WSTR_OPT: + { + wchar_t *wstr = *(wchar_t **)member; + if (wstr != NULL) { + return PyUnicode_FromWideChar(wstr, -1); + } + else { + return Py_NewRef(Py_None); + } + } + case _PyConfig_MEMBER_WSTR_LIST: + { + const PyWideStringList *list = (const PyWideStringList *)member; + PyObject *tuple = PyTuple_New(list->length); + if (tuple == NULL) { + return NULL; + } + + for (Py_ssize_t i = 0; i < list->length; i++) { + PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); + if (item == NULL) { + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; + } + default: + Py_UNREACHABLE(); + } + } + + PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name); + return NULL; +} + +static inline int +PyConfig_GetInt(const char *name, int *value) +{ + PyObject *obj = PyConfig_Get(name); + if (obj == NULL) { + return -1; + } + + if (!PyLong_Check(obj)) { + Py_DECREF(obj); + PyErr_Format(PyExc_TypeError, "config option %s is not an int", name); + return -1; + } + + int as_int = PyLong_AsInt(obj); + Py_DECREF(obj); + if (as_int == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_OverflowError, + "config option %s value does not fit into a C int", name); + return -1; + } + + *value = as_int; + return 0; +} +#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) + +// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14.0b1. +// Adapted from _PyObject_IsUniquelyReferenced() implementation. +#if PY_VERSION_HEX < 0x030E00B0 +static inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj) +{ +#if !defined(Py_GIL_DISABLED) + return Py_REFCNT(obj) == 1; +#else + // NOTE: the entire ob_ref_shared field must be zero, including flags, to + // ensure that other threads cannot concurrently create new references to + // this object. + return (_Py_IsOwnedByCurrentThread(obj) && + _Py_atomic_load_uint32_relaxed(&obj->ob_ref_local) == 1 && + _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared) == 0); +#endif +} +#endif + +// gh-128926 added PyUnstable_TryIncRef() and PyUnstable_EnableTryIncRef() to +// Python 3.14.0a5. Adapted from _Py_TryIncref() and _PyObject_SetMaybeWeakref(). +#if PY_VERSION_HEX < 0x030E00A5 +static inline int PyUnstable_TryIncRef(PyObject *op) +{ +#ifndef Py_GIL_DISABLED + if (Py_REFCNT(op) > 0) { + Py_INCREF(op); + return 1; + } + return 0; +#else + // _Py_TryIncrefFast() + uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); + local += 1; + if (local == 0) { + // immortal + return 1; + } + if (_Py_IsOwnedByCurrentThread(op)) { + _Py_INCREF_STAT_INC(); + _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, local); +#ifdef Py_REF_DEBUG + _Py_INCREF_IncRefTotal(); +#endif + return 1; + } + + // _Py_TryIncRefShared() + Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared); + for (;;) { + // If the shared refcount is zero and the object is either merged + // or may not have weak references, then we cannot incref it. + if (shared == 0 || shared == _Py_REF_MERGED) { + return 0; + } + + if (_Py_atomic_compare_exchange_ssize( + &op->ob_ref_shared, + &shared, + shared + (1 << _Py_REF_SHARED_SHIFT))) { +#ifdef Py_REF_DEBUG + _Py_INCREF_IncRefTotal(); +#endif + _Py_INCREF_STAT_INC(); + return 1; + } + } +#endif +} + +static inline void PyUnstable_EnableTryIncRef(PyObject *op) +{ +#ifdef Py_GIL_DISABLED + // _PyObject_SetMaybeWeakref() + if (_Py_IsImmortal(op)) { + return; + } + for (;;) { + Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared); + if ((shared & _Py_REF_SHARED_FLAG_MASK) != 0) { + // Nothing to do if it's in WEAKREFS, QUEUED, or MERGED states. + return; + } + if (_Py_atomic_compare_exchange_ssize( + &op->ob_ref_shared, &shared, shared | _Py_REF_MAYBE_WEAKREF)) { + return; + } + } +#else + (void)op; // unused argument +#endif +} +#endif + + +#if PY_VERSION_HEX < 0x030F0000 +static inline PyObject* +PySys_GetAttrString(const char *name) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *value = Py_XNewRef(PySys_GetObject(name)); +#else + PyObject *value = Py_XNewRef(PySys_GetObject((char*)name)); +#endif + if (value != NULL) { + return value; + } + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", name); + } + return NULL; +} + +static inline PyObject* +PySys_GetAttr(PyObject *name) +{ +#if PY_VERSION_HEX >= 0x03000000 + const char *name_str = PyUnicode_AsUTF8(name); +#else + const char *name_str = PyString_AsString(name); +#endif + if (name_str == NULL) { + return NULL; + } + + return PySys_GetAttrString(name_str); +} + +static inline int +PySys_GetOptionalAttrString(const char *name, PyObject **value) +{ +#if PY_VERSION_HEX >= 0x03000000 + *value = Py_XNewRef(PySys_GetObject(name)); +#else + *value = Py_XNewRef(PySys_GetObject((char*)name)); +#endif + if (*value != NULL) { + return 1; + } + return 0; +} + +static inline int +PySys_GetOptionalAttr(PyObject *name, PyObject **value) +{ +#if PY_VERSION_HEX >= 0x03000000 + const char *name_str = PyUnicode_AsUTF8(name); +#else + const char *name_str = PyString_AsString(name); +#endif + if (name_str == NULL) { + *value = NULL; + return -1; + } + + return PySys_GetOptionalAttrString(name_str, value); +} +#endif // PY_VERSION_HEX < 0x030F00A1 + + +#if PY_VERSION_HEX < 0x030F00A1 +typedef struct PyBytesWriter { + char small_buffer[256]; + PyObject *obj; + Py_ssize_t size; +} PyBytesWriter; + +static inline Py_ssize_t +_PyBytesWriter_GetAllocated(PyBytesWriter *writer) +{ + if (writer->obj == NULL) { + return sizeof(writer->small_buffer); + } + else { + return PyBytes_GET_SIZE(writer->obj); + } +} + + +static inline int +_PyBytesWriter_Resize_impl(PyBytesWriter *writer, Py_ssize_t size, + int resize) +{ + int overallocate = resize; + assert(size >= 0); + + if (size <= _PyBytesWriter_GetAllocated(writer)) { + return 0; + } + + if (overallocate) { +#ifdef MS_WINDOWS + /* On Windows, overallocate by 50% is the best factor */ + if (size <= (PY_SSIZE_T_MAX - size / 2)) { + size += size / 2; + } +#else + /* On Linux, overallocate by 25% is the best factor */ + if (size <= (PY_SSIZE_T_MAX - size / 4)) { + size += size / 4; + } +#endif + } + + if (writer->obj != NULL) { + if (_PyBytes_Resize(&writer->obj, size)) { + return -1; + } + assert(writer->obj != NULL); + } + else { + writer->obj = PyBytes_FromStringAndSize(NULL, size); + if (writer->obj == NULL) { + return -1; + } + + if (resize) { + assert((size_t)size > sizeof(writer->small_buffer)); + memcpy(PyBytes_AS_STRING(writer->obj), + writer->small_buffer, + sizeof(writer->small_buffer)); + } + } + return 0; +} + +static inline void* +PyBytesWriter_GetData(PyBytesWriter *writer) +{ + if (writer->obj == NULL) { + return writer->small_buffer; + } + else { + return PyBytes_AS_STRING(writer->obj); + } +} + +static inline Py_ssize_t +PyBytesWriter_GetSize(PyBytesWriter *writer) +{ + return writer->size; +} + +static inline void +PyBytesWriter_Discard(PyBytesWriter *writer) +{ + if (writer == NULL) { + return; + } + + Py_XDECREF(writer->obj); + PyMem_Free(writer); +} + +static inline PyBytesWriter* +PyBytesWriter_Create(Py_ssize_t size) +{ + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "size must be >= 0"); + return NULL; + } + + PyBytesWriter *writer = (PyBytesWriter*)PyMem_Malloc(sizeof(PyBytesWriter)); + if (writer == NULL) { + PyErr_NoMemory(); + return NULL; + } + + writer->obj = NULL; + writer->size = 0; + + if (size >= 1) { + if (_PyBytesWriter_Resize_impl(writer, size, 0) < 0) { + PyBytesWriter_Discard(writer); + return NULL; + } + writer->size = size; + } + return writer; +} + +static inline PyObject* +PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size) +{ + PyObject *result; + if (size == 0) { + result = PyBytes_FromStringAndSize("", 0); + } + else if (writer->obj != NULL) { + if (size != PyBytes_GET_SIZE(writer->obj)) { + if (_PyBytes_Resize(&writer->obj, size)) { + goto error; + } + } + result = writer->obj; + writer->obj = NULL; + } + else { + result = PyBytes_FromStringAndSize(writer->small_buffer, size); + } + PyBytesWriter_Discard(writer); + return result; + +error: + PyBytesWriter_Discard(writer); + return NULL; +} + +static inline PyObject* +PyBytesWriter_Finish(PyBytesWriter *writer) +{ + return PyBytesWriter_FinishWithSize(writer, writer->size); +} + +static inline PyObject* +PyBytesWriter_FinishWithPointer(PyBytesWriter *writer, void *buf) +{ + Py_ssize_t size = (char*)buf - (char*)PyBytesWriter_GetData(writer); + if (size < 0 || size > _PyBytesWriter_GetAllocated(writer)) { + PyBytesWriter_Discard(writer); + PyErr_SetString(PyExc_ValueError, "invalid end pointer"); + return NULL; + } + + return PyBytesWriter_FinishWithSize(writer, size); +} + +static inline int +PyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size) +{ + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "size must be >= 0"); + return -1; + } + if (_PyBytesWriter_Resize_impl(writer, size, 1) < 0) { + return -1; + } + writer->size = size; + return 0; +} + +static inline int +PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t size) +{ + if (size < 0 && writer->size + size < 0) { + PyErr_SetString(PyExc_ValueError, "invalid size"); + return -1; + } + if (size > PY_SSIZE_T_MAX - writer->size) { + PyErr_NoMemory(); + return -1; + } + size = writer->size + size; + + if (_PyBytesWriter_Resize_impl(writer, size, 1) < 0) { + return -1; + } + writer->size = size; + return 0; +} + +static inline void* +PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, + Py_ssize_t size, void *buf) +{ + Py_ssize_t pos = (char*)buf - (char*)PyBytesWriter_GetData(writer); + if (PyBytesWriter_Grow(writer, size) < 0) { + return NULL; + } + return (char*)PyBytesWriter_GetData(writer) + pos; +} + +static inline int +PyBytesWriter_WriteBytes(PyBytesWriter *writer, + const void *bytes, Py_ssize_t size) +{ + if (size < 0) { + size_t len = strlen((const char*)bytes); + if (len > (size_t)PY_SSIZE_T_MAX) { + PyErr_NoMemory(); + return -1; + } + size = (Py_ssize_t)len; + } + + Py_ssize_t pos = writer->size; + if (PyBytesWriter_Grow(writer, size) < 0) { + return -1; + } + char *buf = (char*)PyBytesWriter_GetData(writer); + memcpy(buf + pos, bytes, (size_t)size); + return 0; +} + +static inline int +PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 2, 3))); + +static inline int +PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyBytes_FromFormatV(format, vargs); + va_end(vargs); + + if (str == NULL) { + return -1; + } + int res = PyBytesWriter_WriteBytes(writer, + PyBytes_AS_STRING(str), + PyBytes_GET_SIZE(str)); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030F00A1 + + +#if PY_VERSION_HEX < 0x030F00A1 +static inline PyObject* +PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) +{ + PyObject *tuple = PyTuple_New(size); + if (tuple == NULL) { + return NULL; + } + for (Py_ssize_t i=0; i < size; i++) { + PyObject *item = array[i]; + PyTuple_SET_ITEM(tuple, i, Py_NewRef(item)); + } + return tuple; +} +#endif + + +#if PY_VERSION_HEX < 0x030F00A1 +static inline Py_hash_t +PyUnstable_Unicode_GET_CACHED_HASH(PyObject *op) +{ +#ifdef PYPY_VERSION + (void)op; // unused argument + return -1; +#elif PY_VERSION_HEX >= 0x03000000 + return ((PyASCIIObject*)op)->hash; +#else + return ((PyUnicodeObject*)op)->hash; +#endif +} +#endif + + +#ifdef __cplusplus +} +#endif +#endif // PYTHONCAPI_COMPAT diff --git a/M2/Macaulay2/d/regex.dd b/M2/Macaulay2/d/regex.dd index 7cb553f1ed5..be118f1e7e4 100644 --- a/M2/Macaulay2/d/regex.dd +++ b/M2/Macaulay2/d/regex.dd @@ -230,6 +230,8 @@ regexSeparate(e:Expr):Expr := ( toExpr(r)) else WrongArgZZ(4) else WrongArgZZ(3) + -- for subclasses of string + is text:SpecialExpr do regexSeparate(Expr(Sequence(s.0, text.e, s.2, s.3))) else WrongArgString(2) else WrongArgString(1)) else WrongNumArgs(1,4)) diff --git a/M2/Macaulay2/d/scclib.c b/M2/Macaulay2/d/scclib.c index 6ea015b5cf9..6332da6a264 100644 --- a/M2/Macaulay2/d/scclib.c +++ b/M2/Macaulay2/d/scclib.c @@ -6,7 +6,7 @@ #include "types.h" #include "M2mem.h" #include "../c/compat.c" -#include "debug.h" +#include #include "../system/supervisorinterface.h" @@ -854,7 +854,7 @@ int system_run(M2_string command){ return r; } -struct FUNCTION_CELL *pre_final_list, *final_list, *thread_prepare_list; +struct FUNCTION_CELL *pre_final_list, *final_list; void system_atend(void (*func)()){ struct FUNCTION_CELL *this_final = (struct FUNCTION_CELL *)getmem(sizeof(struct FUNCTION_CELL)); diff --git a/M2/Macaulay2/d/stdio.d b/M2/Macaulay2/d/stdio.d index 1918e49fbe1..5ea85a9ae03 100644 --- a/M2/Macaulay2/d/stdio.d +++ b/M2/Macaulay2/d/stdio.d @@ -136,6 +136,28 @@ texmacsprompt():string := ( ); texmacsreward():string := "\2verbatim:"; +export errmsg := {+ message:string }; +export FileCell := {file:file,next:(null or FileCell)}; +export openfiles := (null or FileCell)(NULL); +addfile(o:file):file := ( + openfiles = FileCell(o,openfiles); + o); +rmfile(o:file):void := ( + prevcell := (null or FileCell)(NULL); + x := openfiles; + while true do ( + when x is null do break + is thiscell:FileCell do ( + if thiscell.file == o then ( + when prevcell is null do openfiles = thiscell.next + is prevcell:FileCell do prevcell.next = thiscell.next + ) + else prevcell = x; + x = thiscell.next; + ))); +addfile(stdIO); +addfile(stdError); + init():void := ( stdIO.readline = stdIO.infd == STDIN && stdIO.inisatty ; foreach arg in args do ( @@ -165,12 +187,14 @@ init():void := ( stdIO.inisatty = true; -- otherwise hangs after first syntax error stdIO.outisatty = true; -- not so important? STDERR = 1; + rmfile(stdError); stdError = newFile( "stderr", 0, false, "", false,NOFD,NOFD,0, false,NOFD ,false, "", 0,0,false,false,noprompt,noprompt,false,true,false,0, true, STDERR,0!=isatty(2), newbuffer(), 0,0,false,dummyNetList,0,-1,false,1); + addfile(stdError); ) else if arg === "--read-only-files" then ( @@ -182,27 +206,6 @@ init():void := ( everytime(init); -export errmsg := {+ message:string }; -export FileCell := {file:file,next:(null or FileCell)}; -export openfiles := (null or FileCell)(NULL); -addfile(o:file):file := ( - openfiles = FileCell(o,openfiles); - o); -rmfile(o:file):void := ( - prevcell := (null or FileCell)(NULL); - x := openfiles; - while true do ( - when x is null do break - is thiscell:FileCell do ( - if thiscell.file == o then ( - when prevcell is null do openfiles = thiscell.next - is prevcell:FileCell do prevcell.next = thiscell.next - ) - else prevcell = x; - x = thiscell.next; - ))); -addfile(stdIO); -addfile(stdError); opensocket(filename:string,input:bool,output:bool,listener:bool):(file or errmsg) := ( if readonlyfiles then return (file or errmsg)(errmsg("--read-only-files: opening a socket not permitted")); host0 := substr(filename,1); @@ -694,6 +697,8 @@ export present(x:string):string := ( provide c))) else x); +export format(s:string):string := "\"" + present(s) + "\""; + export filbuf(o:file):int := ( -- if o.fulllines then ( -- stdIO << flush; diff --git a/M2/Macaulay2/d/tokens.d b/M2/Macaulay2/d/tokens.d index 15d661261f8..f205c09ed10 100644 --- a/M2/Macaulay2/d/tokens.d +++ b/M2/Macaulay2/d/tokens.d @@ -129,18 +129,6 @@ export unopNameList := unopNameListCell(dummyUnop,dummySymbol,self); export binopNameList := binopNameListCell(dummyBinop,dummySymbol,self); export ternopNameList := ternopNameListCell(dummyTernop,dummySymbol,self); export multopNameList := multopNameListCell(dummyMultop,dummySymbol,self); -export getUnopName(f:unop):Symbol := ( - p := unopNameList; - while true do ( - if p == p.next || p.f == f then return p.name; - p = p.next; - )); -export getBinopName(f:binop):Symbol := ( - p := binopNameList; - while true do ( - if p == p.next || p.f == f then return p.name; - p = p.next; - )); export getTernopName(f:ternop):Symbol := ( p := ternopNameList; while true do ( diff --git a/M2/Macaulay2/d/util.d b/M2/Macaulay2/d/util.d index 0403063f2b0..b121a0720ac 100644 --- a/M2/Macaulay2/d/util.d +++ b/M2/Macaulay2/d/util.d @@ -223,6 +223,7 @@ export toExpr(x:QQ):Expr := Expr(QQcell(x)); export toExpr(x:RR):Expr := Expr(RRcell(x)); export toExpr(x:RRi):Expr := Expr(RRicell(x)); export toExpr(x:CC):Expr := Expr(CCcell(x)); +export toExpr(x:CCi):Expr := Expr(CCicell(x)); export toExpr(x:float):Expr := Expr(RRcell(toRR(x,ulong(24)))); export toExpr(x:double):Expr := Expr(RRcell(toRR(x,ulong(53)))); export toExpr(x:RawComputation):Expr := Expr(RawComputationCell(x)); @@ -278,6 +279,7 @@ export toExpr(x:QQorNull):Expr := when x is i:QQ do Expr(QQcell(i)) is null do e export toExpr(x:RRorNull):Expr := when x is i:RR do Expr(RRcell(i)) is null do engineErrorMessage(); export toExpr(x:RRiorNull):Expr := when x is i:RRi do Expr(RRicell(i)) is null do engineErrorMessage(); export toExpr(x:CCorNull):Expr := when x is i:CC do Expr(CCcell(i)) is null do engineErrorMessage(); +export toExpr(x:CCiorNull):Expr := when x is i:CCi do Expr(CCicell(i)) is null do engineErrorMessage(); export toExpr(x:RawMatrixPairOrNull):Expr := when x is p:RawMatrixPair do seq(Expr(RawMatrixCell(p.a)),Expr(RawMatrixCell(p.b))) is null do engineErrorMessage(); export toExpr(x:RawMatrixArray):Expr := Expr( list( new Sequence len length(x) do foreach m in x do provide Expr(RawMatrixCell(m)) ) ); export toExpr(x:RawMatrixArrayOrNull):Expr := when x is r:RawMatrixArray do toExpr(r) is null do engineErrorMessage(); diff --git a/M2/Macaulay2/d/version.dd b/M2/Macaulay2/d/version.dd index 4bd11977290..f4d249c9fc9 100644 --- a/M2/Macaulay2/d/version.dd +++ b/M2/Macaulay2/d/version.dd @@ -69,6 +69,12 @@ header " #ifdef HAVE_FPLLL #include #endif + + #include + + #ifdef WITH_JANSSON + #include + #endif "; header " @@ -173,6 +179,7 @@ setupconst("version", Expr(toHashTable(Sequence( \"not present\" #endif "), + "libxml2 version" => Ccode(constcharstar, "LIBXML_DOTTED_VERSION"), "mpsolve version" => Ccode(constcharstar, "stringize(MPS_MAJOR_VERSION) \".\" stringize(MPS_MINOR_VERSION) \".\" stringize(MPS_PATCH_VERSION)" ), "python version" => Ccode(constcharstar," #ifdef WITH_PYTHON @@ -187,6 +194,13 @@ setupconst("version", Expr(toHashTable(Sequence( "ntl version" => Ccode(constcharstar,"NTL_VERSION"), "frobby version" => Ccode(constcharstar,"frobby_version"), "flint version" => Ccode(constcharstar,"FLINT_VERSION"), + "jansson version" => Ccode(constcharstar, " + #ifdef WITH_JANSSON + JANSSON_VERSION + #else + \"not present\" + #endif + "), "scscp version" => Ccode(constcharstar," #ifdef HAVE_SCSCP stringize(SCSCP_VERSION_MAJOR) \".\" stringize(SCSCP_VERSION_MINOR) \".\" stringize(SCSCP_VERSION_PATCH) diff --git a/M2/Macaulay2/d/xml-c.c b/M2/Macaulay2/d/xml-c.c index d2c780cdc83..a32ca7e305c 100644 --- a/M2/Macaulay2/d/xml-c.c +++ b/M2/Macaulay2/d/xml-c.c @@ -18,7 +18,10 @@ static char *copystring(const char *s) { static void initxml() __attribute__ ((constructor)); static void initxml() { - xmlGcMemSetup(freemem,(void *(*)(size_t))getmem,(void *(*)(size_t))getmem_atomic,(void *(*)(void *,size_t))getmoremem1,copystring); + xmlMemSetup((xmlFreeFunc) freemem, + (xmlMallocFunc) getmem, + (xmlReallocFunc) getmoremem1, + (xmlStrdupFunc) copystring); } @@ -67,10 +70,17 @@ xmlNode *xml_AddText(xmlNode *parent, M2_string content){ M2_string xml_toString(xmlNode *n) { M2_string s; - xmlBuffer *buf = xmlBufferCreate(); - int len = xmlNodeDump(buf,NULL,n,2,1); - s = M2_tostringn((char*)buf->content,len); - xmlBufferFree(buf); + xmlOutputBuffer *buf; + const xmlChar* content; + int len; + + buf = xmlAllocOutputBuffer(NULL); + xmlNodeDumpOutput(buf, n->doc, n, 2, 1, NULL); + content = xmlOutputBufferGetContent(buf); + len = xmlOutputBufferGetSize(buf); + s = M2_tostringn((const char *)content, len); + xmlOutputBufferClose(buf); + return s; } diff --git a/M2/Macaulay2/e/CMakeLists.txt b/M2/Macaulay2/e/CMakeLists.txt index 72fd6ae7a45..f9f8907d91e 100644 --- a/M2/Macaulay2/e/CMakeLists.txt +++ b/M2/Macaulay2/e/CMakeLists.txt @@ -131,6 +131,10 @@ set(CPPONLYFILES interface/gmp-util.h # only has inline definitions interface/groebner.cpp interface/groebner.h + interface/m2-mem.cpp + interface/m2-mem.h + interface/m2-types.cpp + interface/m2-types.h interface/matrix.cpp interface/matrix.h interface/monoid.cpp @@ -182,11 +186,12 @@ set(SRCLIST SLP ZZ ZZp - aring-RRi aring-CC aring-CCC aring-RR aring-RRR + aring-RRi + aring-CCi aring-gf-flint-big aring-gf-flint aring-m2-gf @@ -364,7 +369,7 @@ target_include_directories(M2-engine PUBLIC $) # TODO: make engine independent of interpreter headers -target_link_libraries(M2-engine PUBLIC M2-interpreter memtailor mathic mathicgb) +target_link_libraries(M2-engine PUBLIC memtailor mathic mathicgb) if(EIGEN3_FOUND) target_link_libraries(M2-engine PUBLIC Eigen3::Eigen) @@ -374,6 +379,20 @@ if(OpenMP_FOUND) target_link_libraries(M2-engine PUBLIC OpenMP::OpenMP_CXX) endif() +foreach(LIB IN LISTS PKGLIB_LIST) + if(${LIB}_FOUND) + target_link_libraries(M2-engine PUBLIC PkgConfig::${LIB}) + endif() +endforeach() + +foreach(LIB IN LISTS LIBRARIES_LIST) + if(${LIB}_FOUND) + target_link_libraries(M2-engine PUBLIC ${${LIB}_LIBRARIES}) + target_include_directories(M2-engine PUBLIC + "$") + endif() +endforeach() + # Compiler warning flags target_compile_options(M2-engine PRIVATE -Wno-cast-qual # FIXME @@ -422,11 +441,8 @@ if(BUILD_TESTING) # TODO: overflow tests # TODO: break up into multiple branches - add_executable(M2-unit-tests + add_executable(M2-unit-tests EXCLUDE_FROM_ALL unit-tests/M2-cpp-replacement.cpp - unit-tests/M2-replacement.c - unit-tests/M2mem-replacement.c - unit-tests/M2mem-replacement.h unit-tests/util-polyring-creation.hpp unit-tests/util-polyring-creation.cpp @@ -437,14 +453,16 @@ if(BUILD_TESTING) unit-tests/ARingTest.hpp unit-tests/ARingZZTest.cpp unit-tests/ARingZZpTest.cpp -# unit-tests/ARingGFTest.cpp # FIXME: see aring-gf-givaro.cpp:199 +# unit-tests/ARingGFTest.cpp # TODO: needs rework - ARingGFFlint API has changed unit-tests/ARingQQFlintTest.cpp unit-tests/ARingQQGmpTest.cpp unit-tests/ARingRRTest.cpp unit-tests/ARingCCTest.cpp unit-tests/ARingRRRTest.cpp + unit-tests/ARingRRiTest.cpp unit-tests/ARingCCCTest.cpp unit-tests/NCGroebnerTest.cpp +# unit-tests/WeylAlgebraTest.cpp # TODO: add this file unit-tests/RingTest.hpp unit-tests/RingZZTest.cpp diff --git a/M2/Macaulay2/e/M2FreeAlgebra.cpp b/M2/Macaulay2/e/M2FreeAlgebra.cpp index e4bf3e64028..83477da1998 100644 --- a/M2/Macaulay2/e/M2FreeAlgebra.cpp +++ b/M2/Macaulay2/e/M2FreeAlgebra.cpp @@ -77,6 +77,7 @@ void M2FreeAlgebra::text_out(buffer &o) const unsigned int M2FreeAlgebra::computeHashValue(const ring_elem a) const { + (void) a; return 0; // TODO: change this to a more reasonable hash code. } @@ -202,7 +203,7 @@ ring_elem M2FreeAlgebra::copy(const ring_elem f) const void M2FreeAlgebra::remove(ring_elem &f) const { - // do nothing + (void) f; } ring_elem M2FreeAlgebra::negate(const ring_elem f1) const @@ -277,6 +278,10 @@ ring_elem M2FreeAlgebra::divide(const ring_elem f, const ring_elem g) const void M2FreeAlgebra::syzygy(const ring_elem a, const ring_elem b, ring_elem &x, ring_elem &y) const { + (void) a; + (void) b; + (void) x; + (void) y; throw exc::internal_error("M2FreeAlgebra::syzygy is not yet written!"); // TODO: In the commutative case, this function is to find x and y (as simple as possible) diff --git a/M2/Macaulay2/e/M2FreeAlgebraQuotient.cpp b/M2/Macaulay2/e/M2FreeAlgebraQuotient.cpp index b43fdc2cf6a..4feeb7d06c7 100644 --- a/M2/Macaulay2/e/M2FreeAlgebraQuotient.cpp +++ b/M2/Macaulay2/e/M2FreeAlgebraQuotient.cpp @@ -57,6 +57,7 @@ void M2FreeAlgebraQuotient::text_out(buffer &o) const unsigned int M2FreeAlgebraQuotient::computeHashValue(const ring_elem a) const { + (void) a; return 0; // TODO: change this to a more reasonable hash code. } @@ -192,7 +193,7 @@ ring_elem M2FreeAlgebraQuotient::copy(const ring_elem f) const void M2FreeAlgebraQuotient::remove(ring_elem &f) const { - // do nothing + (void) f; } ring_elem M2FreeAlgebraQuotient::negate(const ring_elem f1) const @@ -259,6 +260,10 @@ ring_elem M2FreeAlgebraQuotient::divide(const ring_elem f, const ring_elem g) co void M2FreeAlgebraQuotient::syzygy(const ring_elem a, const ring_elem b, ring_elem &x, ring_elem &y) const { + (void) a; + (void) b; + (void) x; + (void) y; // TODO: In the commutative case, this function is to find x and y (as simple as possible) // such that ax + by = 0. No such x and y may exist in the noncommutative case, however. // In this case, the function should return x = y = 0. diff --git a/M2/Macaulay2/e/Makefile.common.in b/M2/Macaulay2/e/Makefile.common.in index d2b8b730055..62ad6b369e2 100644 --- a/M2/Macaulay2/e/Makefile.common.in +++ b/M2/Macaulay2/e/Makefile.common.in @@ -73,6 +73,14 @@ COMPILE.cpp = $(COMPILE.cc) $(SHOW)"MKDEP $*.c" $(HIDE)@CC@ -MT $@ -MT $*.o -MM -MP $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(MORE_OPTIONS) $< >$*.dep +@pre_librariesdir@/libengine.so: $(LOFILES) + $(HIDE)$(MKDIR_P) $(@D) + @CC@ -shared $^ $(OUTPUT_OPTION) + +@pre_librariesdir@/libengine.a: $(ENGINE_OFILES) + $(HIDE)$(MKDIR_P) $(@D) + @AR@ rcs $@ $^ + # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/e DEPENDS=no initialize && make -C $M2BUILDDIR/Macaulay2/e Makefile.common " # End: diff --git a/M2/Macaulay2/e/Makefile.files b/M2/Macaulay2/e/Makefile.files.in similarity index 95% rename from M2/Macaulay2/e/Makefile.files rename to M2/Macaulay2/e/Makefile.files.in index f75b786285a..faae2ab85ee 100644 --- a/M2/Macaulay2/e/Makefile.files +++ b/M2/Macaulay2/e/Makefile.files.in @@ -82,11 +82,12 @@ INTERFACE = \ aring-zz-flint \ aring-qq-flint \ aring-zzp-flint \ - aring-RRi \ aring-RR \ aring-CC \ aring-RRR \ aring-CCC \ + aring-RRi \ + aring-CCi \ aring-tower \ aring-gf-flint-big \ aring-gf-flint \ @@ -178,6 +179,8 @@ INTERFACE = \ weylalg COMMANDS = \ + interface/m2-types \ + interface/m2-mem \ ntl-internal \ ntl-debugio \ matrix-kbasis \ @@ -221,7 +224,7 @@ NAMES_H = \ f4/varpower-monomial \ res-a0-pair -C_FILES = error table exptable complex +C_FILES = error table exptable complex # interface/m2-types interface/m2-mem D_FILES = DD_FILES = #test @@ -241,8 +244,10 @@ ENGINE_OFILES := $(addsuffix .o,$(C_FILES) $(CC_FILES) $(D_FILES) $(DD_FILES)) ifeq "$(SHARED)" "yes" ENGINE_OBJFILES := $(LOFILES) +LIBENGINE = @pre_librariesdir@/libengine.so else ENGINE_OBJFILES := $(ENGINE_OFILES) +LIBENGINE = @pre_librariesdir@/libengine.a endif HHFILES := $(addsuffix .hpp, $(NAMES_H) $(NAMES) $(INTERFACE) $(INTERFACE2)) $(E_H) diff --git a/M2/Macaulay2/e/Makefile.in b/M2/Macaulay2/e/Makefile.in index 6b9a3757884..e0ae52ed76f 100644 --- a/M2/Macaulay2/e/Makefile.in +++ b/M2/Macaulay2/e/Makefile.in @@ -6,9 +6,13 @@ DEPENDS = yes PRE.cc = $(COMPILE.cc) -E -include @srcdir@/Makefile.files +include Makefile.files include Makefile.common -CPPFLAGS := -I@srcdir@ $(CPPFLAGS) -I../d -I@srcdir@/../c -I@srcdir@/../d -Wno-fatal-errors -Wno-unused-variable +CPPFLAGS := -I@srcdir@ $(CPPFLAGS) \ + -Wno-fatal-errors -Wno-unused-variable + +# -I../d -I@srcdir@/../c -I@srcdir@/../d \ + CXXFLAGS += -Wno-unknown-pragmas -std=gnu++17 -Wno-macro-redefined DOXYGEN_CONF_DIR :=$(SRCDIR)/doxygen-settings @@ -48,16 +52,12 @@ run-overflow-test : overflow-test Makefile ./overflow-test add || if [ $$? = 1 ] ; then exit 1 ; else echo OVERFLOW TEST SUCCEEDED ; fi touch $@ clean :: ; rm -f run-overflow-test -overflow-test : overflow.o ../d/debug.o ../d/M2mem.o ../d/memdebug.o +overflow-test : overflow.o #../d/debug.o ../d/M2mem.o ../d/memdebug.o clean :: ; rm -f overflow-test #all:; $(MAKE) -C f4 -ifeq "$(SHARED)" "yes" -all: @pre_librariesdir@/libengine.so -else -all: $(ENGINE_OFILES) -endif +all: $(LIBENGINE) %.ii: %.cpp; $(PRE.cc) $< $(OUTPUT_OPTION) %.s : %.cpp; $(COMPILE.cc) -S $< $(OUTPUT_OPTION) @@ -68,7 +68,6 @@ endif %.lo : %.c ; $(COMPILE.c) -fPIC $< $(OUTPUT_OPTION) %.lo : %.cc ; $(COMPILE.cc) -fPIC $< $(OUTPUT_OPTION) %.lo : %.cpp; $(COMPILE.cc) -fPIC $< $(OUTPUT_OPTION) -@pre_librariesdir@/libengine.so : $(LOFILES); @CC@ -shared $^ $(OUTPUT_OPTION) MORE_OPTIONS = -Wno-cast-qual COMPILE.c += $(MORE_OPTIONS) @@ -85,7 +84,7 @@ clean:: Makefile : Makefile.in; cd ../..; ./config.status Macaulay2/e/Makefile all: Makefile.common Makefile.common : Makefile.common.in; cd ../..; ./config.status Macaulay2/e/Makefile.common -Makefile.include : Makefile.include.in; cd ../..; ./config.status Macaulay2/e/Makefile.include +Makefile.files : Makefile.files.in; cd ../..; ./config.status Macaulay2/e/Makefile.files clean::; rm -f *.dep dep-*.tmp typecode.db TAGS ifeq "$(DEPENDS)" "yes" include $(ENGINE_CFILES:.c=.dep) $(ENGINE_CCFILES:.cpp=.dep) @@ -107,7 +106,7 @@ dups-okay: dups-tmp doxygen : ; cd @srcdir@ && doxygen distclean: clean; rm -f Makefile @srcdir@/TAGS -SCCFLAGS += -I../d +#SCCFLAGS += -I../d # we like using "struct" and "class" interchangeably CXXFLAGS += -Wno-mismatched-tags @@ -179,4 +178,3 @@ help: # compile-command: "make -C $M2BUILDDIR/Macaulay2/e DEPENDS=no initialize && make -C $M2BUILDDIR/Macaulay2/e " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/e/NCAlgebras/FreeAlgebra.hpp b/M2/Macaulay2/e/NCAlgebras/FreeAlgebra.hpp index 8bde7eb1892..b3c875a5106 100644 --- a/M2/Macaulay2/e/NCAlgebras/FreeAlgebra.hpp +++ b/M2/Macaulay2/e/NCAlgebras/FreeAlgebra.hpp @@ -44,7 +44,7 @@ class FreeAlgebra : public our_new_delete unsigned int computeHashValue(const Poly& a) const; // TODO - void init(Poly& f) const {} + void init(Poly& f) const { (void) f; } void clear(Poly& f) const; void setZero(Poly& f) const; diff --git a/M2/Macaulay2/e/NCAlgebras/FreeAlgebraQuotient.hpp b/M2/Macaulay2/e/NCAlgebras/FreeAlgebraQuotient.hpp index e8f1014a82e..213865409ff 100644 --- a/M2/Macaulay2/e/NCAlgebras/FreeAlgebraQuotient.hpp +++ b/M2/Macaulay2/e/NCAlgebras/FreeAlgebraQuotient.hpp @@ -41,7 +41,7 @@ class FreeAlgebraQuotient : public our_new_delete void normalizeInPlace(Poly& f) const; - void init(Poly& f) const {} + void init(Poly& f) const { (void) f; } void clear(Poly& f) const; void setZero(Poly& f) const; diff --git a/M2/Macaulay2/e/NCAlgebras/NCF4.cpp b/M2/Macaulay2/e/NCAlgebras/NCF4.cpp index e21ffe904b0..dc48806ecc9 100644 --- a/M2/Macaulay2/e/NCAlgebras/NCF4.cpp +++ b/M2/Macaulay2/e/NCAlgebras/NCF4.cpp @@ -6,7 +6,7 @@ #include "VectorArithmetic.hpp" // for VectorArithmetic #include "NCAlgebras/WordTable.hpp" // for Overlap, WordTable #include "buffer.hpp" // for buffer -#include "engine-exports.h" // for M2_gbTrace +#include "interface/m2-types.h" // for M2_gbTrace #include "ring.hpp" // for Ring #include "ringelem.hpp" // for ring_elem #include "../system/supervisorinterface.h" // for getAllowableThreads @@ -36,6 +36,8 @@ NCF4::NCF4(const FreeAlgebra& A, mScheduler(mNumThreads) { // std::cout << "number of processors being used: " << mNumThreads << std::endl; + (void) hardDegreeLimit; + (void) strategy; if (M2_gbTrace >= 1) { buffer o; diff --git a/M2/Macaulay2/e/NCAlgebras/NCGroebner.cpp b/M2/Macaulay2/e/NCAlgebras/NCGroebner.cpp index edb421b2e0e..69ab7d7c6bf 100644 --- a/M2/Macaulay2/e/NCAlgebras/NCGroebner.cpp +++ b/M2/Macaulay2/e/NCAlgebras/NCGroebner.cpp @@ -7,7 +7,7 @@ #include "NCAlgebras/Word.hpp" // for Word #include "NCAlgebras/WordTable.hpp" // for Overlap, WordTable #include "buffer.hpp" // for buffer -#include "engine-exports.h" // for M2_gbTrace, newline +#include "interface/m2-types.h" // for M2_gbTrace, newline #include "myalloc.hpp" // for operator<<, AllocLogger #include "ring.hpp" // for Ring #include "ringelem.hpp" // for ring_elem diff --git a/M2/Macaulay2/e/NCAlgebras/NCReduction.cpp b/M2/Macaulay2/e/NCAlgebras/NCReduction.cpp index 4a0774f4a98..835b9c526a4 100644 --- a/M2/Macaulay2/e/NCAlgebras/NCReduction.cpp +++ b/M2/Macaulay2/e/NCAlgebras/NCReduction.cpp @@ -852,6 +852,7 @@ class EntryConfig size_t operator()(Monom m) const // hash function { + (void) m; return 0; } diff --git a/M2/Macaulay2/e/NCAlgebras/OverlapTable.cpp b/M2/Macaulay2/e/NCAlgebras/OverlapTable.cpp index 8ddf5ecb42e..3955d29223e 100644 --- a/M2/Macaulay2/e/NCAlgebras/OverlapTable.cpp +++ b/M2/Macaulay2/e/NCAlgebras/OverlapTable.cpp @@ -1,5 +1,5 @@ #include "NCAlgebras/OverlapTable.hpp" -#include "engine-exports.h" // for M2_gbTrace +#include "interface/m2-types.h" // for M2_gbTrace #include // for cout // will call find to see if degree exists, and if not will call diff --git a/M2/Macaulay2/e/NCResolutions/nc-res-computation.cpp b/M2/Macaulay2/e/NCResolutions/nc-res-computation.cpp index 13aa0bd985b..a28e38f2a09 100644 --- a/M2/Macaulay2/e/NCResolutions/nc-res-computation.cpp +++ b/M2/Macaulay2/e/NCResolutions/nc-res-computation.cpp @@ -8,12 +8,15 @@ NCResComputation::NCResComputation(const FreeAlgebraQuotient& ring, mInputModuleGB(gbModuleMatrix) //, //mMaxLevel(max_level) { + (void) ring; + (void) max_level; } ResolutionComputation* createNCRes(const Matrix* gbModuleMatrix, int max_level, int strategy) { + (void) strategy; const M2FreeAlgebraQuotient* ring = gbModuleMatrix->get_ring()->cast_to_M2FreeAlgebraQuotient(); if (ring != nullptr) return new NCResComputation(ring->freeAlgebraQuotient(),*gbModuleMatrix,max_level); diff --git a/M2/Macaulay2/e/NCResolutions/nc-res-computation.hpp b/M2/Macaulay2/e/NCResolutions/nc-res-computation.hpp index 1b0aa1e880e..8574007f2a8 100644 --- a/M2/Macaulay2/e/NCResolutions/nc-res-computation.hpp +++ b/M2/Macaulay2/e/NCResolutions/nc-res-computation.hpp @@ -43,7 +43,12 @@ class NCResComputation : public ResolutionComputation return matCon.to_matrix(); } - MutableMatrix /* or null */* get_matrix(int slanted_degree, int level) {return nullptr; } + MutableMatrix /* or null */* get_matrix(int slanted_degree, int level) + { + (void) slanted_degree; + (void) level; + return nullptr; + } const FreeModule /* or null */* get_free(int level) { if (level == 0) return mInputModuleGB.rows(); @@ -51,7 +56,11 @@ class NCResComputation : public ResolutionComputation return mInputModuleGB.get_ring()->make_FreeModule(0); } - M2_arrayint get_betti(int type) const { return nullptr; } + M2_arrayint get_betti(int type) const + { + (void) type; + return nullptr; + } // type is documented under rawResolutionBetti, in engine.h void text_out(buffer& o) const { diff --git a/M2/Macaulay2/e/Polynomial.hpp b/M2/Macaulay2/e/Polynomial.hpp index 85bd181e89a..0ba6e221e8d 100644 --- a/M2/Macaulay2/e/Polynomial.hpp +++ b/M2/Macaulay2/e/Polynomial.hpp @@ -137,6 +137,7 @@ inline ModuleMonom monomToModuleMonom(const Monom& a, int comp, std::pair void appendModuleMonomToMonom(const ModuleMonom& a, int& comp, T& inserter) { + (void) comp; inserter.push_back(a.size()-3); for (int i=4; i::SLEvaluatorConcrete( const MutableMat >* consts /* DMat& DMat_consts */) : mRing(consts->getMat().ring()) { + (void) SLP; + (void) cPos; + (void) vPos; std::cerr << "SLEvaluatorConcrete constructor not defined for sparse matrices\n"; abort(); } @@ -31,6 +34,9 @@ SLEvaluatorConcrete::SLEvaluatorConcrete( const MutableMat >* empty ): mRing(empty->getMat().ring()) { + (void) libName; + (void) nInputs; + (void) nOutputs; std::cerr << "SLEvaluatorConcrete constructor not defined for sparse matrices\n"; abort(); } @@ -777,6 +783,15 @@ bool HomotopyConcrete::track( gmp_RR infinity_threshold, bool checkPrecision) { + (void) inputs; + (void) outputs; + (void) output_extras; + (void) init_dt; + (void) min_dt; + (void) epsilon; + (void) max_corr_steps; + (void) infinity_threshold; + (void) checkPrecision; ERROR("track: not implemented for this type of ring"); return false; } diff --git a/M2/Macaulay2/e/VectorArithmetic.hpp b/M2/Macaulay2/e/VectorArithmetic.hpp index a7380af9cb9..a1b13ea9c66 100644 --- a/M2/Macaulay2/e/VectorArithmetic.hpp +++ b/M2/Macaulay2/e/VectorArithmetic.hpp @@ -463,6 +463,8 @@ class ConcreteVectorArithmetic long to_modp_long(const ElementArray& coeffs, size_t loc) const { + (void) coeffs; + (void) loc; return 0; // DANGER WILL ROBINSON! } @@ -518,6 +520,7 @@ class ConcreteVectorArithmetic void from_ring_elem(ElementArray& coeffs, ring_elem numer, ring_elem denom) const // Appends numer/denom to coeffs array { + (void) denom; auto& svec = * elementArray(coeffs); FieldElement inumer; mRing->init(inumer); @@ -554,6 +557,7 @@ inline void ConcreteVectorArithmetic::from_ring_elem(ElementArra { // TODO: this function ignores denom, this is non-intuitive and bug-prone. // TODO: this will fail: input is alas ZZ integers... not QQ elements... + (void) denom; auto& svec = * elementArray(coeffs); //ring_elem val = numer; FieldElement inumer; diff --git a/M2/Macaulay2/e/ZZ.cpp b/M2/Macaulay2/e/ZZ.cpp index 011821340aa..6a00fd6d933 100644 --- a/M2/Macaulay2/e/ZZ.cpp +++ b/M2/Macaulay2/e/ZZ.cpp @@ -80,6 +80,7 @@ void RingZZ::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { + (void) p_parens; mpz_srcptr a = ap.get_mpz(); char s[1000]; @@ -134,6 +135,9 @@ bool RingZZ::from_rational(mpq_srcptr q, ring_elem &result) const bool RingZZ::promote(const Ring *R, const ring_elem a, ring_elem &result) const { + (void) R; + (void) a; + (void) result; return false; } @@ -189,7 +193,7 @@ ring_elem RingZZ::copy(const ring_elem f) const void RingZZ::remove(ring_elem &f) const { - // NOTHING + (void) f; } ring_elem RingZZ::preferred_associate(ring_elem f) const diff --git a/M2/Macaulay2/e/ZZp.cpp b/M2/Macaulay2/e/ZZp.cpp index 41679ba604f..3285be12c9c 100644 --- a/M2/Macaulay2/e/ZZp.cpp +++ b/M2/Macaulay2/e/ZZp.cpp @@ -117,6 +117,7 @@ void Z_mod::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { + (void) p_parens; int n = to_int(a.get_int()); if (n < 0) { diff --git a/M2/Macaulay2/e/aring-CC.cpp b/M2/Macaulay2/e/aring-CC.cpp index ce4ff65d383..acb01022aa3 100644 --- a/M2/Macaulay2/e/aring-CC.cpp +++ b/M2/Macaulay2/e/aring-CC.cpp @@ -9,20 +9,26 @@ void ARingCC::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { - gmp_CC g = toBigComplex(ap); - M2_string s = - p_parens ? (*gmp_tonetCCparenpointer)(g) : (*gmp_tonetCCpointer)(g); - - bool prepend_plus = p_plus && (s->array[0] != '-'); - bool strip_last = - !p_one && ((s->len == 1 && s->array[0] == '1') || - (s->len == 2 && s->array[1] == '1' && s->array[0] == '-')); + if (p_plus && (ap.re > 0 || (ap.re == 0 && ap.im > 0))) + o << "+"; - if (prepend_plus) o << "+"; - if (strip_last) - o.put((char *)s->array, s->len - 1); - else - o.put((char *)s->array, s->len); + if (p_parens && ap.re != 0 && ap.im != 0) { + if (ap.re < 0) { + ElementType neg; + + init(neg); + negate(neg, ap); + o << "-(" << &neg << ")"; + clear(neg); + } else { + o << "(" << &ap << ")"; + } + } else { + if (!p_one && ap.re == -1 && ap.im == 0) + o << "-"; + else if (p_one || ap.re != 1 || ap.im != 0) + o << ≈ + } } }; // end namespace M2 diff --git a/M2/Macaulay2/e/aring-CC.hpp b/M2/Macaulay2/e/aring-CC.hpp index 35f0651d1f4..de16e01416d 100644 --- a/M2/Macaulay2/e/aring-CC.hpp +++ b/M2/Macaulay2/e/aring-CC.hpp @@ -115,6 +115,7 @@ class ARingCC : public SimpleARing static void clear(ElementType& result) { + (void) result; // do nothing } @@ -125,7 +126,12 @@ class ARingCC : public SimpleARing result.im = 0.0; } - void set_var(ElementType& result, int v) const { set_from_long(result, 1); } + void set_var(ElementType& result, int v) const + { + (void) v; + set_from_long(result, 1); + } + void set_from_mpz(ElementType& result, mpz_srcptr a) const { result.re = mpz_get_d(a); @@ -378,6 +384,7 @@ class ARingCC : public SimpleARing int first_var, ring_elem& result) const { + (void) first_var; if (!map->get_ring()->from_complex_double(f.re, f.im, result)) { result = map->get_ring()->from_long(0); diff --git a/M2/Macaulay2/e/aring-CCC.cpp b/M2/Macaulay2/e/aring-CCC.cpp index 544397139de..818eead09ff 100644 --- a/M2/Macaulay2/e/aring-CCC.cpp +++ b/M2/Macaulay2/e/aring-CCC.cpp @@ -10,27 +10,27 @@ void ARingCCC::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { - gmp_CC_struct g; - g.re = &const_cast(f).re; - g.im = &const_cast(f).im; - M2_string s = - p_parens ? (*gmp_tonetCCparenpointer)(&g) : (*gmp_tonetCCpointer)(&g); + if (p_plus && (mpfr_cmp_si(&f.re, 0) > 0 || + (mpfr_cmp_si(&f.re, 0) == 0 && mpfr_cmp_si(&f.im, 0) > 0))) + o << "+"; - // if: first char is a "-", and p_plus, o << "+" - // if: an internal "+" or "-", then put parens around it. - // otherwise: if the string is "1" or "-1", and !p_one - // then leave out the last character. + if (p_parens && mpfr_cmp_si(&f.re, 0) != 0 && mpfr_cmp_si(&f.im, 0) != 0) { + if (mpfr_cmp_si(&f.re, 0) < 0) { + ElementType neg; - bool prepend_plus = p_plus && (s->array[0] != '-'); - bool strip_last = - !p_one && ((s->len == 1 && s->array[0] == '1') || - (s->len == 2 && s->array[1] == '1' && s->array[0] == '-')); - - if (prepend_plus) o << "+"; - if (strip_last) - o.put((char *)s->array, s->len - 1); - else - o.put((char *)s->array, s->len); + init(neg); + negate(neg, f); + o << "-(" << &neg << ")"; + clear(neg); + } else { + o << "(" << &f << ")"; + } + } else { + if (!p_one && mpfr_cmp_si(&f.re, -1) == 0 && mpfr_cmp_si(&f.im, 0) == 0) + o << "-"; + else if (p_one || mpfr_cmp_si(&f.re, 1) != 0 || mpfr_cmp_si(&f.im, 0) != 0) + o << &f; + } } }; // end namespace M2 diff --git a/M2/Macaulay2/e/aring-CCC.hpp b/M2/Macaulay2/e/aring-CCC.hpp index ba734a0f592..02a7c548d66 100644 --- a/M2/Macaulay2/e/aring-CCC.hpp +++ b/M2/Macaulay2/e/aring-CCC.hpp @@ -137,7 +137,12 @@ class ARingCCC : public SimpleARing mpfr_set_si(&result.im, 0, MPFR_RNDN); } - void set_var(ElementType& result, int v) const { set_from_long(result, 1); } + void set_var(ElementType& result, int v) const + { + (void) v; + set_from_long(result, 1); + } + void set_from_mpz(ElementType& result, mpz_srcptr a) const { mpfr_set_z(&result.re, a, MPFR_RNDN); @@ -483,6 +488,7 @@ class ARingCCC : public SimpleARing ring_elem& result) const { gmp_CC_struct g; + (void) first_var; g.re = &f.re; g.im = &f.im; if (!map->get_ring()->from_BigComplex(&g, result)) diff --git a/M2/Macaulay2/e/aring-CCi.cpp b/M2/Macaulay2/e/aring-CCi.cpp new file mode 100644 index 00000000000..6fe449c9b02 --- /dev/null +++ b/M2/Macaulay2/e/aring-CCi.cpp @@ -0,0 +1,27 @@ +#include "aring-CCi.hpp" + +namespace M2 { + +void ARingCCi::text_out(buffer &o) const { o << "ACCi_" << mPrecision; } +void ARingCCi::elem_text_out(buffer &o, + const ElementType &ap, + bool p_one, + bool p_plus, + bool p_parens) const +{ + + if (p_plus) + o << "+"; + + if (p_parens && !mpfi_is_zero(&ap.re) && !mpfi_is_zero(&ap.im)) + o << "(" << &ap << ")"; + else if (p_one || mpfi_cmp_si(&ap.re, 1) != 0 || !mpfi_is_zero(&ap.im)) + o << ≈ +} + +}; // end namespace M2 + +// Local Variables: +// compile-command: "make -C $M2BUILDDIR/Macaulay2/e " +// indent-tabs-mode: nil +// End: diff --git a/M2/Macaulay2/e/aring-CCi.hpp b/M2/Macaulay2/e/aring-CCi.hpp new file mode 100644 index 00000000000..70aa5752f08 --- /dev/null +++ b/M2/Macaulay2/e/aring-CCi.hpp @@ -0,0 +1,574 @@ +// Copyright 2012 Michael E. Stillman + +#ifndef _aring_CCi_hpp_ +#define _aring_CCi_hpp_ + +#include + +#include +#include "interface/random.h" +#include "interface/gmp-util.h" +#include "aring.hpp" +#include "buffer.hpp" +#include "ringelem.hpp" +#include "ringmap.hpp" +#include "aring-RRR.hpp" +#include "aring-RRi.hpp" +#include "aring-CCC.hpp" + +class CCi; +class RingMap; + +namespace M2 { +/** +\ingroup rings +*/ +class ARingCCi : public SimpleARing +{ + // Higher precision real intervals + + public: + static const RingID ringID = ring_CCi; + + typedef cci_struct elem; + typedef elem ElementType; + + ARingCCi(unsigned long precision) : mPrecision(precision) {} + // ring informational + size_t characteristic() const { return 0; } + unsigned long get_precision() const { return mPrecision; } + void text_out(buffer &o) const; + + unsigned int computeHashValue(const elem &a) const + { + double d1 = mpfr_get_d(&a.re.left, MPFR_RNDN); + double d2 = mpfr_get_d(&(a.re.right), MPFR_RNDN); + double d3 = mpfr_get_d(&(a.im.left), MPFR_RNDN); + double d4 = mpfr_get_d(&(a.im.right), MPFR_RNDN); + double d = 12347. * d1 + 865800. * d2 + 72158. * d3 + 86429. * d4; + return static_cast(d); + } + + ///////////////////////////////// + // ElementType informational //// + ///////////////////////////////// + + bool is_unit(const ElementType &f) const { return !is_zero(f); } + bool is_zero(const ElementType &f) const { return mpfr_cmp_si(&(f.im.left), 0) == 0 and mpfr_cmp_si(&(f.im.right), 0) == 0 and + mpfr_cmp_si(&(f.re.left), 0) == 0 and + mpfr_cmp_si(&(f.re.right), 0) == 0; } + bool is_equal(const ElementType &f, const ElementType &g) const + { return + mpfr_cmp(&(f.re.left), &(g.re.left)) == 0 and mpfr_cmp(&(f.re.right), &(g.re.right)) == 0 and + mpfr_cmp(&(f.im.left), &(g.im.left)) == 0 and mpfr_cmp(&(f.im.right), &(g.im.right)) == 0; + } + + int compare_elems(const ElementType &f, const ElementType &g) const + { + int cmp = mpfi_cmp(&f.re, &g.re); + if (cmp < 0) return -1; + if (cmp > 0) return 1; + cmp = mpfi_cmp(&f.im, &g.im); + if (cmp < 0) return -1; + if (cmp > 0) return 1; + return 0; + } + + bool is_empty(const ElementType &f) const { return mpfi_is_empty(&f.re)>0 || mpfi_is_empty(&f.im)>0; } + bool is_member(const ARingCCC::ElementType &a, const ElementType &f) const { return mpfi_cmp_fr(&f.re,&a.re) == 0 && mpfi_cmp_fr(&f.im,&a.im) == 0; } + bool is_member(const ARingRRi::ElementType &a, const ElementType &f) const { return mpfi_cmp(&f.re,&a) == 0 && mpfi_cmp_si(&f.im,0); } + bool is_member(const ARingRRR::ElementType &a, const ElementType &f) const { return mpfi_cmp_fr(&f.re,&a) == 0 && mpfi_cmp_si(&f.im,0); } + bool is_member(mpq_srcptr a, const ElementType &f) const { return mpfi_cmp_q(&f.re,a) == 0 && mpfi_cmp_si(&f.im,0); } + bool is_member(mpz_srcptr a, const ElementType &f) const { return mpfi_cmp_z(&f.re,a) == 0 && mpfi_cmp_si(&f.im,0); } + bool is_member(long a, const ElementType &f) const { return mpfi_cmp_si(&f.re,a) == 0 && mpfi_cmp_si(&f.im,0); } + bool is_member(double a, const ElementType &f) const { return mpfi_cmp_d(&f.re,a) == 0 && mpfi_cmp_si(&f.im,0); } + + bool is_subset(const ElementType &g, const ElementType &f) const { return mpfi_cmp_fr(&f.re,&(g.re.left)) == 0 and mpfi_cmp_fr(&f.re,&(g.re.right)) == 0 and mpfi_cmp_fr(&f.im,&(g.im.left)) == 0 and mpfi_cmp_fr(&f.im,&(g.im.right)) == 0; } + + //////////////////////////// + // to/from ringelem //////// + //////////////////////////// + // These simply repackage the element as either a ringelem or an + // 'ElementType'. + // No reinitialization is done. + // Do not take the same element and store it as two different ring_elem's!! + void to_ring_elem(ring_elem &result, const ElementType &a) const + { + cci_ptr res = getmemstructtype(cci_ptr); + mpfi_init2(&res->re,mPrecision); + mpfi_init2(&res->im,mPrecision); + mpfi_set(&res->re,&a.re); + mpfi_set(&res->im,&a.im); + mpfi_reallocate_limbs(&res->re); + mpfi_reallocate_limbs(&res->im); + result = ring_elem(res); + } + + void from_ring_elem(ElementType &result, const ring_elem &a) const + { + mpfi_set(&result.re, &a.get_cci()->re); + mpfi_set(&result.im, &a.get_cci()->im); + } + + const ElementType &from_ring_elem_const(const ring_elem &a) const + { + return *a.get_cci(); + } + // 'init', 'init_set' functions + + void init(ElementType &result) const { + mpfi_init2(&result.re, mPrecision); + mpfi_init2(&result.im, mPrecision); + } + void init_set(ElementType &result, const ElementType &a) const + { + init(result); + mpfi_set(&result.re, &a.re); + mpfi_set(&result.im, &a.im); + } + + void set(ElementType &result, const ElementType &a) const + { + mpfi_set(&result.re, &a.re); + mpfi_set(&result.im, &a.im); + } + + void set(ElementType &result, const gmp_CCi a) const + { + mpfi_set(&result.re, a->re); + mpfi_set(&result.im, a->im); + } + + void set_zero(ElementType &result) const + { + mpfi_set_si(&result.re, 0); + mpfi_set_si(&result.im, 0); + } + + static void clear(ElementType &result) { + mpfi_clear(&result.re); + mpfi_clear(&result.im); + } + void copy(ElementType &result, const ElementType &a) const + { + mpfi_set(&result.re, &a.re); + mpfi_set(&result.im, &a.im); + } + + void set_from_long(ElementType &result, long a) const + { + mpfi_set_si(&result.re, a); + mpfi_set_si(&result.im, 0); + } + + void set_var(ElementType &result, int v) const + { + mpfi_set_si(&result.re, v); + mpfi_set_si(&result.im, 0); + } + + void set_from_mpz(ElementType &result, mpz_srcptr a) const + { + mpfi_set_z(&result.re, a); + mpfi_set_si(&result.im, 0); + } + + bool set_from_mpq(ElementType &result, mpq_srcptr a) const + { + mpfi_set_q(&result.re, a); + mpfi_set_si(&result.im, 0); + return true; + } + + bool set_from_double(ElementType &result, double a) const + { + mpfi_set_d(&result.re, a); + mpfi_set_si(&result.im, 0); + return true; + } + + bool set_from_BigReal(ElementType &result, gmp_RR a) const + { + mpfi_set_fr(&result.re, a); + mpfi_set_si(&result.im, 0); + return true; + } + + bool set_from_Interval(ElementType &result, gmp_RRi a) const + { + mpfi_set(&result.re, a); + mpfi_set_si(&result.im, 0); + return true; + } + + bool set_from_BigComplex(ElementType &result, gmp_CC a) const + { + mpfi_set_fr(&result.re, a->re); + mpfi_set_fr(&result.im, a->im); + return true; + } + + bool set_from_BigComplex(ElementType &result, const cc_struct * a) const + { + mpfi_set_fr(&result.re, &a->re); + mpfi_set_fr(&result.im, &a->im); + return true; + } + + bool set_from_complex_double(ElementType &result, double re, double im) const + { + mpfi_set_d(&result.re, re); + mpfi_set_d(&result.im, im); + return true; + } + + bool set_from_ComplexInterval(ElementType &result, gmp_CCi a) const + { + mpfi_set(&result.re, a->re); + mpfi_set(&result.im, a->im); + return true; + } + + bool set_from_ComplexInterval(ElementType &result, ElementType &a) const + { + mpfi_set(&result.re, &a.re); + mpfi_set(&result.im, &a.im); + return true; + } + + void set_from_BigReals(ElementType& result, gmp_RR re, gmp_RR im) const + { + mpfi_set_fr(&result.re, re); + mpfi_set_fr(&result.im, im); + } + void set_from_doubles(ElementType& result, double re, double im) const + { + mpfi_set_d(&result.re, re); + mpfi_set_d(&result.im, im); + } + + const ARingRRi::ElementType& realPartReference(const ElementType& a) const + { + return a.re; + } + const ARingRRi::ElementType& imaginaryPartReference( + const ElementType& a) const + { + return a.im; + } + void set_real_part(ElementType& c, ARingRRi::ElementType& a) const + { + mpfi_set(&c.re, &a); + } + void set_imaginary_part(ElementType& c, ARingRRi::ElementType& a) const + { + mpfi_set(&c.im, &a); + } + + // arithmetic + void negate(ElementType &result, const ElementType &a) const + { + mpfi_mul_si(&result.re, &a.re, -1); + mpfi_mul_si(&result.im, &a.im, -1); + } + + void invert(ElementType &result, const ElementType &a) const + // we silently assume that a != 0. If it is, result is set to a^0, i.e. 1 + { + mpfi_t norm, temp; + mpfi_init2(norm,get_precision()); + mpfi_init2(temp,get_precision()); + mpfi_set(norm,&a.re); + mpfi_mul(norm,norm,norm); + mpfi_set(temp,&a.im); + mpfi_mul(temp,temp,temp); + mpfi_add(norm,norm,temp); + mpfi_set(&result.re,&a.re); + mpfi_mul_si(&result.im,&a.im,-1); + mpfi_div(&result.re,&result.re,norm); + mpfi_div(&result.im,&result.im,norm); + } + + void add(ElementType &result, + const ElementType &a, + const ElementType &b) const + { + mpfi_add(&result.re, &a.re, &b.re); + mpfi_add(&result.im, &a.im, &b.im); + } + + + void addMultipleTo(ElementType &result, + const ElementType &a, + const ElementType &b) const + { + ElementType ab; + init(ab); + mult(ab,a,b); + add(result,result,ab); + } + + + void subtract(ElementType &result, + const ElementType &a, + const ElementType &b) const + { + mpfi_sub(&result.re, &a.re, &b.re); + mpfi_sub(&result.im, &a.im, &b.im); + } + + void subtract_multiple(ElementType &result, + const ElementType &a, + const ElementType &b) const + { + // result -= a*b + ElementType ab; + init(ab); + mult(ab, a, b); + subtract(result, result, ab); + clear(ab); + } + + void mult(ElementType &result, + const ElementType &a, + const ElementType &b) const + { + mpfi_t temp, retemp, imtemp; + mpfi_init2(temp,get_precision()); + mpfi_init2(retemp,get_precision()); + mpfi_init2(imtemp,get_precision()); + mpfi_mul(retemp,&a.re,&b.re); + mpfi_mul(temp,&a.im,&b.im); + mpfi_sub(retemp,retemp,temp); + mpfi_mul(temp,&a.re,&b.im); + mpfi_mul(imtemp,&a.im,&b.re); + mpfi_add(&result.im,imtemp,temp); + mpfi_set(&result.re,retemp); + } + + void divide(ElementType &result, + const ElementType &a, + const ElementType &b) const + { + // result -= a*b + ElementType b_inv; + init(b_inv); + invert(b_inv,b); + mult(result,a,b_inv); + clear(b_inv); + } + + void power(ElementType &result, const ElementType &a, int n) const + { + if (n >= 2) + { + ElementType b; + init(b); + if (n%2 == 0) + { + power(b,a,n/2); + mult(result,b,b); + } + else + { + power(b,a,n-1); + mult(result,a,b); + } + } + else if (n == 1) + { + mpfi_set(&result.re,&a.re); + mpfi_set(&result.im,&a.im); + } + else if (n == 0) + { + mpfi_set_si(&result.re,1); + mpfi_set_si(&result.im,0); + } + else if (n<0) + throw 20; + } + + /* Not entirely sure how to deal with this one. */ + void power_mpz(ElementType &result, const ElementType &a, mpz_srcptr n) const + { + if (mpz_cmp_si(n,2)>=0) + { + mpz_t r; + mpz_init(r); + mpz_fdiv_r_ui(r,n,2); + + ElementType b; + init(b); + + mpz_t m; + mpz_init(m); + + if (mpz_cmp_si(r,0) == 0) + { + mpz_cdiv_q_ui(m,n,2); + + power_mpz(b,a,m); + mult(result,b,b); + } + else + { + mpz_sub_ui(m,n,1); + + power_mpz(b,a,m); + mult(result,a,b); + } + mpz_clear(r); + mpz_clear(m); + } + else if (mpz_cmp_si(n,1)==0) + { + mpfi_set(&result.re,&a.re); + mpfi_set(&result.im,&a.im); + } + else if (mpz_cmp_si(n,0)==0) + { + mpfi_set_si(&result.re,1); + mpfi_set_si(&result.im,0); + } + else if (mpz_cmp_si(n,0)<0) + throw 20; + } + + void swap(ElementType &a, ElementType &b) const { + mpfi_swap(&a.re, &b.re); + mpfi_swap(&a.im, &b.im); + } + + void midpoint(ARingCCC::ElementType &a, const ElementType &b) const { + mpfi_mid(&a.re,&b.re); + mpfi_mid(&a.im,&b.im); + } + + void diameter(ARingRRi::ElementType &a, const ElementType &b) const { + mpfi_t temp; + mpfi_set(&a,&b.re); + mpfi_sqr(&a,&a); + mpfi_set(temp,&b.im); + mpfi_sqr(temp,temp); + mpfi_add(&a,&a,temp); + mpfi_sqrt(&a,&a); + } + + void elem_text_out(buffer &o, + const ElementType &a, + bool p_one, + bool p_plus, + bool p_parens) const; + + void syzygy(const ElementType &a, + const ElementType &b, + ElementType &x, + ElementType &y) const // remove? + // returns x,y s.y. x*a + y*b == 0. + // if possible, x is set to 1. + // no need to consider the case a==0 or b==0. + { + set_var(x, 0); // set x=1 + if (!is_zero(b)) + { + set(y, a); + negate(y, y); + divide(y, y, b); + } + } + + /* rewrite this (in rand.cpp or just copy over?) */ + void random(ElementType &result) const // redo? + { + mpfr_t val; + mpfr_init2(val, mPrecision); + randomMpfr(val); + mpfi_set_fr(&result.re,val); + + randomMpfr(val); + mpfi_put_fr(&result.re,val); + + randomMpfr(val); + mpfi_set_fr(&result.im,val); + + randomMpfr(val); + mpfi_put_fr(&result.im,val); + mpfr_clear(val); + } + + /* Needs to be redone. */ + void eval(const RingMap *map, + const ElementType &f, + int first_var, + ring_elem &result) const + { + gmp_CCi_struct g; + g.re = &f.re; + g.im = &f.im; + if (!map->get_ring()->from_ComplexInterval(&g, result)) + { + result = map->get_ring()->from_long(0); + ERROR("cannot coerce RRi value to ring type"); + } + } + +/* Not ready */ + void zeroize_tiny(gmp_RR epsilon, ElementType &a) const + { + throw 20; + //if (mpfr_cmpabs(&a, epsilon) < 0) set_zero(a); + } + /* Not ready */ + void increase_norm(gmp_RRmutable norm, const ElementType &a) const + { + throw 20; + /* if (mpfr_cmpabs(&a, norm) > 0) + { + set(*norm, a); + abs(*norm, *norm); + }*/ + } + + void abs(ElementType &result, const ElementType &a) const + { + mpfi_t temp; + mpfi_set(&result.re,&a.re); + mpfi_sqr(&result.re,&result.re); + mpfi_set(temp,&a.im); + mpfi_sqr(temp,temp); + mpfi_add(&result.re,&result.re,temp); + mpfi_sqrt(&result.re,&result.re); + mpfi_set_si(&result.im,0); + } + + void abs_squared(ElementType &result, const ElementType &a) const + { + abs(result,a); + mult(result, result, result); + } + + gmp_CC toBigComplex(const ElementType &a) const + { + gmp_CCmutable result = getmemstructtype(gmp_CCmutable); + result->re = getmemstructtype(gmp_RRmutable); + result->im = getmemstructtype(gmp_RRmutable); + mpfr_init2(result->re, get_precision()); + mpfr_init2(result->im, get_precision()); + mpfi_get_fr(result->re, &a.re); + mpfi_get_fr(result->im, &a.im); + return moveTo_gmpCC(result); + } + + private: + unsigned long mPrecision; +}; + +}; // end namespace M2 + +#endif + +// Local Variables: +// compile-command: "make -C $M2BUILDDIR/Macaulay2/e " +// indent-tabs-mode: nil +// End: diff --git a/M2/Macaulay2/e/aring-RR.cpp b/M2/Macaulay2/e/aring-RR.cpp index da5c39250c1..aa6f49d9624 100644 --- a/M2/Macaulay2/e/aring-RR.cpp +++ b/M2/Macaulay2/e/aring-RR.cpp @@ -9,22 +9,15 @@ void ARingRR::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { - ElementType &ap1 = const_cast(ap); - mpfr_t a; - mpfr_init(a); - mpfr_set_d(a, ap1, MPFR_RNDN); - M2_string s = (*gmp_tostringRRpointer)(a); - mpfr_clear(a); - bool prepend_plus = p_plus && (s->array[0] != '-'); - bool strip_last = - !p_one && ((s->len == 1 && s->array[0] == '1') || - (s->len == 2 && s->array[1] == '1' && s->array[0] == '-')); + (void) p_parens; - if (prepend_plus) o << "+"; - if (strip_last) - o.put((char *)s->array, s->len - 1); - else - o.put((char *)s->array, s->len); + if (p_plus && ap > 0) + o << "+"; + + if (!p_one && ap == -1) + o << "-"; + else if (p_one || ap != 1) + o << ap; } }; // end namespace M2 diff --git a/M2/Macaulay2/e/aring-RR.hpp b/M2/Macaulay2/e/aring-RR.hpp index 893fe61869a..6f01595ea6b 100644 --- a/M2/Macaulay2/e/aring-RR.hpp +++ b/M2/Macaulay2/e/aring-RR.hpp @@ -85,7 +85,7 @@ class ARingRR : public SimpleARing void set_zero(ElementType &result) const { result = 0.0; } static void clear(ElementType &result) { - // do nothing + (void) result; } void copy(ElementType &result, const ElementType &a) const { set(result, a); } @@ -94,7 +94,12 @@ class ARingRR : public SimpleARing result = static_cast(a); } - void set_var(ElementType &result, int v) const { result = 1.0; } + void set_var(ElementType &result, int v) const + { + (void) v; + result = 1.0; + } + void set_from_mpz(ElementType &result, mpz_srcptr a) const { result = mpz_get_d(a); @@ -226,6 +231,7 @@ class ARingRR : public SimpleARing int first_var, ring_elem &result) const { + (void) first_var; if (!map->get_ring()->from_double(f, result)) { result = map->get_ring()->from_long(0); diff --git a/M2/Macaulay2/e/aring-RRR.cpp b/M2/Macaulay2/e/aring-RRR.cpp index da50c16a427..0a8bc7e0b2f 100644 --- a/M2/Macaulay2/e/aring-RRR.cpp +++ b/M2/Macaulay2/e/aring-RRR.cpp @@ -9,18 +9,15 @@ void ARingRRR::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { - mpfr_ptr a = &const_cast(ap); - M2_string s = (*gmp_tostringRRpointer)(a); - bool prepend_plus = p_plus && (s->array[0] != '-'); - bool strip_last = - !p_one && ((s->len == 1 && s->array[0] == '1') || - (s->len == 2 && s->array[1] == '1' && s->array[0] == '-')); + (void) p_parens; - if (prepend_plus) o << "+"; - if (strip_last) - o.put((char *)s->array, s->len - 1); - else - o.put((char *)s->array, s->len); + if (p_plus && mpfr_cmp_si(&ap, 0) > 0) + o << "+"; + + if (!p_one && mpfr_cmp_si(&ap, -1) == 0) + o << "-"; + else if (p_one || mpfr_cmp_si(&ap, 1) != 0) + o << ≈ } }; // end namespace M2 diff --git a/M2/Macaulay2/e/aring-RRR.hpp b/M2/Macaulay2/e/aring-RRR.hpp index 2a3c9a0b226..82b2e94045d 100644 --- a/M2/Macaulay2/e/aring-RRR.hpp +++ b/M2/Macaulay2/e/aring-RRR.hpp @@ -116,6 +116,7 @@ class ARingRRR : public SimpleARing void set_var(ElementType &result, int v) const { + (void) v; mpfr_set_si(&result, 1, MPFR_RNDN); } @@ -244,6 +245,7 @@ class ARingRRR : public SimpleARing int first_var, ring_elem &result) const { + (void) first_var; if (!map->get_ring()->from_BigReal(&f, result)) { result = map->get_ring()->from_long(0); diff --git a/M2/Macaulay2/e/aring-RRi.cpp b/M2/Macaulay2/e/aring-RRi.cpp index 8dca4036952..1ad15d63440 100644 --- a/M2/Macaulay2/e/aring-RRi.cpp +++ b/M2/Macaulay2/e/aring-RRi.cpp @@ -9,16 +9,14 @@ void ARingRRi::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { - mpfi_ptr a = &const_cast(ap); - M2_string s1 = (*gmp_tostringRRpointer)(&(a->left)); - M2_string s2 = (*gmp_tostringRRpointer)(&(a->right)); + (void) p_parens; - if(p_plus) o << "+"; - o << "["; - o.put((char *)s1->array, s1->len); - o << ","; - o.put((char *)s2->array, s2->len); - o << "]"; + if(p_plus) + o << "+"; + + // TODO: how do we want to handle -1? intervals w/ matching endpoints? + if (p_one || mpfr_cmp_si(&ap.left, 1) != 0 || mpfr_cmp_si(&ap.right, 1) != 0) + o << ≈ } }; // end namespace M2 diff --git a/M2/Macaulay2/e/aring-RRi.hpp b/M2/Macaulay2/e/aring-RRi.hpp index 7c9c1bd9731..98fc487470f 100644 --- a/M2/Macaulay2/e/aring-RRi.hpp +++ b/M2/Macaulay2/e/aring-RRi.hpp @@ -97,7 +97,8 @@ class ARingRRi : public SimpleARing } // 'init', 'init_set' functions - void init(ElementType &result) const { mpfi_init2(&result, mPrecision); } + void init(ElementType &result) const { + mpfi_init2(&result, mPrecision); } void init_set(ElementType &result, const ElementType &a) const { init(result); @@ -326,14 +327,7 @@ class ARingRRi : public SimpleARing /* rewrite this (in rand.cpp or just copy over?) */ void random(ElementType &result) const // redo? { - mpfr_t val; - mpfr_init2(val, mPrecision); - randomMpfr(val); - mpfi_set_fr(&result,val); - - randomMpfr(val); - mpfi_put_fr(&result,val); - mpfr_clear(val); + rawSetRandomRRi(&result); } /* Needs to be redone. */ @@ -342,7 +336,8 @@ class ARingRRi : public SimpleARing int first_var, ring_elem &result) const { - if (!map->get_ring()->from_Interval(&f, result)) + (void) first_var; + if (!map->get_ring()->from_Interval(&f, result)) { result = map->get_ring()->from_long(0); ERROR("cannot coerce RRi value to ring type"); @@ -352,13 +347,17 @@ class ARingRRi : public SimpleARing /* Not ready */ void zeroize_tiny(gmp_RR epsilon, ElementType &a) const { - throw 20; + (void) epsilon; + (void) a; + throw 20; //if (mpfr_cmpabs(&a, epsilon) < 0) set_zero(a); } /* Not ready */ void increase_norm(gmp_RRmutable norm, const ElementType &a) const { - throw 20; + (void) norm; + (void) a; + throw 20; /* if (mpfr_cmpabs(&a, norm) > 0) { set(*norm, a); diff --git a/M2/Macaulay2/e/aring-gf-flint-big.hpp b/M2/Macaulay2/e/aring-gf-flint-big.hpp index facacbc8653..7a3615742f0 100644 --- a/M2/Macaulay2/e/aring-gf-flint-big.hpp +++ b/M2/Macaulay2/e/aring-gf-flint-big.hpp @@ -226,7 +226,13 @@ class ARingGFFlintBig : public RingInterface return true; } - bool set_from_BigReal(ElementType& result, gmp_RR a) const { return false; } + bool set_from_BigReal(ElementType& result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + void negate(ElementType& result, const ElementType& a) const { fq_nmod_neg(&result, &a, mContext); diff --git a/M2/Macaulay2/e/aring-gf-flint.hpp b/M2/Macaulay2/e/aring-gf-flint.hpp index 910c78e84f7..44f6b437f47 100644 --- a/M2/Macaulay2/e/aring-gf-flint.hpp +++ b/M2/Macaulay2/e/aring-gf-flint.hpp @@ -207,7 +207,13 @@ class ARingGFFlint : public RingInterface return true; } - bool set_from_BigReal(ElementType& result, gmp_RR a) const { return false; } + bool set_from_BigReal(ElementType& result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + void negate(ElementType& result, const ElementType& a) const { fq_zech_neg(&result, &a, mContext); diff --git a/M2/Macaulay2/e/aring-glue.hpp b/M2/Macaulay2/e/aring-glue.hpp index db26a5ed70b..ee6d5be323c 100644 --- a/M2/Macaulay2/e/aring-glue.hpp +++ b/M2/Macaulay2/e/aring-glue.hpp @@ -75,9 +75,15 @@ class ConcreteRing : public Ring const RingElement *getMinimalPolynomial() const { return nullptr; } const RingElement *getGenerator() const { return nullptr; } - const RingElement *getRepresentation(const ring_elem &a) const { return nullptr; } + const RingElement *getRepresentation(const ring_elem &a) const + { + (void) a; + return nullptr; + } + virtual long discreteLog(const ring_elem &a) const { + (void) a; throw exc::engine_error("cannot compute discrete logarithm in this ring"); } @@ -92,6 +98,7 @@ class ConcreteRing : public Ring virtual std::pair coerceToLongInteger(ring_elem a) const { + (void) a; return std::pair(false, 0); } @@ -139,6 +146,14 @@ class ConcreteRing : public Ring return ret; } + virtual bool from_ComplexInterval(gmp_CCi z, ring_elem &result) const + { + Element a(*R); + bool ret = get_from_ComplexInterval(*R, a , z); + if (ret) R->to_ring_elem(result, a); + return ret; + } + virtual bool from_BigComplex(gmp_CC q, ring_elem &result) const { Element a(*R); @@ -255,6 +270,7 @@ class ConcreteRing : public Ring virtual void remove(ring_elem &f) const { + (void) f; if (displayArithmeticCalls) fprintf(stderr, "calling remove\n"); /* currently, do nothing... */ } @@ -522,6 +538,7 @@ ConcreteRing *ConcreteRing::create( result->oneV = result->from_long(1); result->minus_oneV = result->from_long(-1); + return result; } @@ -640,6 +657,8 @@ bool ConcreteRing::promote(const Ring *R, return RP::promoter(R, S, fR, resultS); case M2::ring_CCC: return RP::promoter(R, S, fR, resultS); + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); default: return false; } @@ -656,6 +675,8 @@ bool ConcreteRing::promote(const Ring *R, return RP::promoter(R, S, fR, resultS); case M2::ring_CCC: return RP::promoter(R, S, fR, resultS); + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); default: return false; } @@ -672,6 +693,8 @@ bool ConcreteRing::promote(const Ring *R, return RP::promoter(R, S, fR, resultS); case M2::ring_CCC: return RP::promoter(R, S, fR, resultS); + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); default: return false; } @@ -684,6 +707,8 @@ bool ConcreteRing::promote(const Ring *R, return RP::promoter(R, S, fR, resultS); case M2::ring_RRi: return RP::promoter(R, S, fR, resultS); + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); default: return false; } @@ -694,6 +719,8 @@ bool ConcreteRing::promote(const Ring *R, return RP::promoter(R, S, fR, resultS); case M2::ring_CCC: return RP::promoter(R, S, fR, resultS); + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); default: return false; } @@ -704,9 +731,19 @@ bool ConcreteRing::promote(const Ring *R, return RP::promoter(R, S, fR, resultS); case M2::ring_CC: return RP::promoter(R, S, fR, resultS); + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); default: return false; } + case M2::ring_CCi: + switch (S->ringID()) + { + case M2::ring_CCi: + return RP::promoter(R, S, fR, resultS); + default: + return false; + } default: break; }; @@ -873,7 +910,6 @@ inline bool ConcreteRing::promote(const Ring *Rf, // Rf = Z/p[x]/F(x) ---> GF(p,n) // promotion: need to be able to know the value of 'x'. // lift: need to compute (primite_element)^e - ElementType a; bool retval = R->promote(Rf, f, a); R->to_ring_elem(result, a); @@ -967,6 +1003,7 @@ template ring_elem ConcreteRing::zeroize_tiny(gmp_RR epsilon, const ring_elem f) const { + (void) epsilon; return f; } @@ -974,7 +1011,8 @@ template void ConcreteRing::increase_maxnorm(gmp_RRmutable norm, const ring_elem f) const { - // do nothing by default + (void) norm; + (void) f; } template <> @@ -1103,6 +1141,12 @@ inline unsigned long ConcreteRing::get_precision() const return R->get_precision(); } +template <> +inline unsigned long ConcreteRing::get_precision() const +{ + return R->get_precision(); +} + template std::pair coerceToLongIntegerFcn(const RT &ring, ring_elem a) { diff --git a/M2/Macaulay2/e/aring-m2-gf.hpp b/M2/Macaulay2/e/aring-m2-gf.hpp index 0005eb665ab..990dbe081a3 100644 --- a/M2/Macaulay2/e/aring-m2-gf.hpp +++ b/M2/Macaulay2/e/aring-m2-gf.hpp @@ -121,6 +121,7 @@ class ARingGFM2 : public SimpleARing void getGenerator(elem &result_gen) const { result_gen = 1; } int get_repr(elem f) const { /*TODO: WRITE WRITE ;*/ + (void) f; throw exc::internal_error("get_repr not written"); } @@ -154,7 +155,7 @@ class ARingGFM2 : public SimpleARing void init_set(elem &result, elem a) const { result = a; } void set(elem &result, elem a) const { result = a; } void set_zero(elem &result) const { result = 0; } - static void clear(elem &result) { /* nothing */} + static void clear(elem &result) { (void) result; } void set_from_long(elem &result, long a) const { @@ -163,7 +164,12 @@ class ARingGFM2 : public SimpleARing result = mGF.fromZZTable(a1); } - void set_var(elem &result, int v) const { result = 1; } + void set_var(elem &result, int v) const + { + (void) v; + result = 1; + } + void set_from_mpz(elem &result, mpz_srcptr a) const { int b = static_cast(mpz_fdiv_ui(a, characteristic())); @@ -180,7 +186,13 @@ class ARingGFM2 : public SimpleARing return true; } - bool set_from_BigReal(elem &result, gmp_RR a) const { return false; } + bool set_from_BigReal(elem &result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + void negate(elem &result, elem a) const { if (a != 0) diff --git a/M2/Macaulay2/e/aring-qq-flint.cpp b/M2/Macaulay2/e/aring-qq-flint.cpp index c0d38ae9c0e..d39c85838b0 100644 --- a/M2/Macaulay2/e/aring-qq-flint.cpp +++ b/M2/Macaulay2/e/aring-qq-flint.cpp @@ -24,6 +24,8 @@ void ARingQQFlint::eval(const RingMap* map, ring_elem& result) const { mpq_t temp; + + (void) first_var; flint_mpq_init_set_readonly(temp, &f); bool ok = map->get_ring()->from_rational(temp, result); flint_mpq_clear_readonly(temp); @@ -43,6 +45,7 @@ void ARingQQFlint::elem_text_out(buffer& o, char s[1000]; char* str; + (void) p_parens; bool is_neg = (fmpq_sgn(&a) == -1); bool one = is_pm_one(a); diff --git a/M2/Macaulay2/e/aring-qq-flint.hpp b/M2/Macaulay2/e/aring-qq-flint.hpp index 02146584f16..70a3edcd349 100644 --- a/M2/Macaulay2/e/aring-qq-flint.hpp +++ b/M2/Macaulay2/e/aring-qq-flint.hpp @@ -116,8 +116,19 @@ class ARingQQFlint : public SimpleARing return true; } - bool set_from_BigReal(ElementType& result, gmp_RR a) const { return false; } - void set_var(ElementType& result, int v) const { fmpq_set_si(&result, 1, 1); } + bool set_from_BigReal(ElementType& result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + + void set_var(ElementType& result, int v) const + { + (void) v; + fmpq_set_si(&result, 1, 1); + } + /** @} */ /** @name arithmetic @@ -264,6 +275,9 @@ class ARingQQFlint : public SimpleARing bool lift(const Ring* Rg, const ElementType& f, ring_elem& result) const { + (void) Rg; + (void) f; + (void) result; return false; } diff --git a/M2/Macaulay2/e/aring-qq-gmp.cpp b/M2/Macaulay2/e/aring-qq-gmp.cpp index 6a21c43ac58..b82fedc07f3 100644 --- a/M2/Macaulay2/e/aring-qq-gmp.cpp +++ b/M2/Macaulay2/e/aring-qq-gmp.cpp @@ -20,6 +20,7 @@ void ARingQQGMP::eval(const RingMap* map, int first_var, ring_elem& result) const { + (void) first_var; bool ok = map->get_ring()->from_rational(&f, result); if (!ok) { @@ -37,6 +38,7 @@ void ARingQQGMP::elem_text_out(buffer& o, char s[1000]; char* str; + (void) p_parens; bool is_neg = (mpq_sgn(&a) == -1); bool one = is_pm_one(a); diff --git a/M2/Macaulay2/e/aring-qq-gmp.hpp b/M2/Macaulay2/e/aring-qq-gmp.hpp index df06889dd06..808f46eb25e 100644 --- a/M2/Macaulay2/e/aring-qq-gmp.hpp +++ b/M2/Macaulay2/e/aring-qq-gmp.hpp @@ -119,8 +119,19 @@ class ARingQQGMP : public SimpleARing return true; } - bool set_from_BigReal(ElementType& result, gmp_RR a) const { return false; } - void set_var(ElementType& result, int v) const { mpq_set_si(&result, 1, 1); } + bool set_from_BigReal(ElementType& result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + + void set_var(ElementType& result, int v) const + { + (void) v; + mpq_set_si(&result, 1, 1); + } + /** @} */ /** @name arithmetic diff --git a/M2/Macaulay2/e/aring-tower.cpp b/M2/Macaulay2/e/aring-tower.cpp index 6b05c61f42c..20d7d5ed60e 100644 --- a/M2/Macaulay2/e/aring-tower.cpp +++ b/M2/Macaulay2/e/aring-tower.cpp @@ -49,6 +49,8 @@ ARingTower *ARingTower::create(const ARingTower &R, const std::vector &new_names) { // TODO: write + (void) R; + (void) new_names; return nullptr; } diff --git a/M2/Macaulay2/e/aring-tower.hpp b/M2/Macaulay2/e/aring-tower.hpp index 384a5cd93fe..f2cf41f9a65 100644 --- a/M2/Macaulay2/e/aring-tower.hpp +++ b/M2/Macaulay2/e/aring-tower.hpp @@ -136,13 +136,28 @@ class ARingTower : public RingInterface // Routines to help in switch from coeffrings to aring // // these will be renamed or go away (hopefully) ///////// ///////////////////////////////////////////////////////// - void init_set(elem &result, elem a) const {} // TODO: write this - void set(elem &result, elem a) const {} // TODO: write this + void init_set(elem &result, elem a) const // TODO: write this + { + (void) result; + (void) a; + } + + void set(elem &result, elem a) const // TODO: write this + { + (void) result; + (void) a; + } + ///////////////////////////////// // ElementType informational //// ///////////////////////////////// - bool is_unit(ElementType f) const { return false; } // TODO: write this + bool is_unit(ElementType f) const // TODO: write this + { + (void) f; + return false; + } + bool is_zero(ElementType f) const { return f == nullptr; } bool is_equal(ElementType f, ElementType g) const { @@ -152,6 +167,8 @@ class ARingTower : public RingInterface int compare_elems(ElementType f, ElementType g) const { // TODO: write this + (void) f; + (void) g; return 0; } @@ -189,22 +206,34 @@ class ARingTower : public RingInterface void copy(elem &result, elem a) const { result = copy(mStartLevel, a); } void set_from_long(elem &result, long a) const { // TODO: write this + (void) result; + (void) a; } // v from 0..n_vars()-1, sets result to 0 if v is out of range void set_var(elem &result, int v) const { result = var(mStartLevel, v); } void set_from_mpz(elem &result, mpz_srcptr a) const { + (void) result; + (void) a; assert(false); } // TODO: write this bool set_from_mpq(elem &result, mpq_srcptr a) const { + (void) result; + (void) a; assert(false); return false; } // TODO: write this - bool set_from_BigReal(elem &result, gmp_RR a) const { return false; } + bool set_from_BigReal(elem &result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + // arithmetic void negate(elem &result, elem a) const { @@ -213,7 +242,12 @@ class ARingTower : public RingInterface } // we silently assume that a != 0. If it is, result is set to a^0, i.e. 1 - void invert(elem &result, elem a) const {} // TODO: write this + void invert(elem &result, elem a) const // TODO: write this + { + (void) result; + (void) a; + } + void add(elem &result, elem a, elem b) const { if (a == nullptr) @@ -234,13 +268,47 @@ class ARingTower : public RingInterface subtract_in_place(mStartLevel, result, b); } // TODO: write this - void subtract_multiple(elem &result, elem a, elem b) const {} // TODO: write - // this - void mult(elem &result, elem a, elem b) const {} // TODO: write this - void divide(elem &result, elem a, elem b) const {} // TODO: write this - void power(elem &result, elem a, int n) const {} // TODO: write this - void power_mpz(elem &result, elem a, mpz_srcptr n) const {} // TODO: write this - void swap(ElementType &a, ElementType &b) const {} // TODO: write this + void subtract_multiple(elem &result, elem a, elem b) const // TODO: write this + { + (void) result; + (void) a; + (void) b; + } + + void mult(elem &result, elem a, elem b) const // TODO: write this + { + (void) result; + (void) a; + (void) b; + } + + void divide(elem &result, elem a, elem b) const // TODO: write this + { + (void) result; + (void) a; + (void) b; + } + + void power(elem &result, elem a, int n) const // TODO: write this + { + (void) result; + (void) a; + (void) n; + } + + void power_mpz(elem &result, elem a, mpz_srcptr n) const // TODO: write this + { + (void) result; + (void) a; + (void) n; + } + + void swap(ElementType &a, ElementType &b) const // TODO: write this + { + (void) a; + (void) b; + } + void elem_text_out(buffer &o, ElementType a, bool p_one = true, @@ -258,14 +326,26 @@ class ARingTower : public RingInterface ElementType &x, ElementType &y) const { + (void) a; + (void) b; + (void) x; + (void) y; } // TODO: write this - void random(ElementType &result) const {} // TODO: write this + void random(ElementType &result) const // TODO: write this + { + (void) result; + } + void eval(const RingMap *map, const elem f, int first_var, ring_elem &result) const { + (void) map; + (void) f; + (void) first_var; + (void) result; } // TODO: write this // f *= b, where b is an element in mBaseRing diff --git a/M2/Macaulay2/e/aring-translate.hpp b/M2/Macaulay2/e/aring-translate.hpp index 367575f1b84..ada949a7580 100644 --- a/M2/Macaulay2/e/aring-translate.hpp +++ b/M2/Macaulay2/e/aring-translate.hpp @@ -7,11 +7,12 @@ // Contains functions which are "ring translational" // /////////////////////////////////////////////////////// -#include "aring-RRi.hpp" #include "aring-RR.hpp" #include "aring-CC.hpp" #include "aring-RRR.hpp" #include "aring-CCC.hpp" +#include "aring-RRi.hpp" +#include "aring-CCi.hpp" #include "aring-zz-gmp.hpp" #include "aring-zzp.hpp" #include "aring-zzp-ffpack.hpp" @@ -29,11 +30,23 @@ namespace M2 { template bool get_from_BigReal(const RT& R, typename RT::ElementType& a, gmp_RR b) { + (void) R; + (void) a; + (void) b; return false; } - + template bool get_from_Interval(const RT& R, typename RT::ElementType& a, gmp_RRi b) +{ + (void) R; + (void) a; + (void) b; + return false; +} + +template +bool get_from_ComplexInterval(const RT& R, typename RT::ElementType & a, gmp_CCi b) { return false; } @@ -41,11 +54,17 @@ bool get_from_Interval(const RT& R, typename RT::ElementType& a, gmp_RRi b) template bool get_from_BigComplex(const RT& R, typename RT::ElementType& a, gmp_CC b) { + (void) R; + (void) a; + (void) b; return false; } template bool get_from_double(const RT& R, typename RT::ElementType& a, double b) { + (void) R; + (void) a; + (void) b; return false; } template @@ -54,6 +73,10 @@ bool get_from_complex_double(const RT& R, double re, double im) { + (void) R; + (void) a; + (void) re; + (void) im; return false; } @@ -127,6 +150,42 @@ inline bool get_from_Interval(const ARingRRi& R, return R.set_from_Interval(a, b); } +inline bool get_from_ComplexInterval(const ARingCCi& R, + ARingCCi::ElementType& a, + gmp_CCi b) +{ + R.set(a, b); + return true; +} + +inline bool get_from_double(const ARingCCi& R, + ARingCCi::ElementType& a, + double b) +{ + return R.set_from_double(a, b); +} + +inline bool get_from_Interval(const ARingCCi& R, + ARingCCi::ElementType& a, + gmp_RRi b) +{ + return R.set_from_Interval(a, b); +} + +inline bool get_from_BigComplex(const ARingCCi& R, + ARingCCi::ElementType& a, + gmp_CC b) +{ + return R.set_from_BigComplex(a, b); +} + +inline bool get_from_BigReal(const ARingCCi& R, + ARingCCi::ElementType& a, + gmp_RR b) +{ + return R.set_from_BigReal(a, b); +} + inline bool get_from_double(const ARingRR& R, ARingRR::ElementType& a, double b) { return R.set_from_double(a, b); @@ -160,6 +219,14 @@ inline bool get_from_complex_double(const ARingCC& R, return R.set_from_complex_double(a, re, im); } +inline bool get_from_complex_double(const ARingCCi& R, + ARingCCi::ElementType& a, + double re, + double im) +{ + return R.set_from_complex_double(a, re, im); +} + // Promote an element of one ring to another. // Given a "natural and canonical" map R --> S (depends on the context) // and an element fR of R, set result_fS to be the image of fR under this map. @@ -170,6 +237,10 @@ bool mypromote(const RingR& R, const typename RingR::ElementType& fR, typename RingS::ElementType& result_fS) { + (void) R; + (void) S; + (void) fR; + (void) result_fS; return false; } template @@ -178,6 +249,10 @@ bool mylift(const RingR& R, typename RingR::ElementType& result_gR, const typename RingS::ElementType& gS) { + (void) R; + (void) S; + (void) result_gR; + (void) gS; return false; } @@ -187,6 +262,7 @@ inline bool mypromote(const ARingQQ& R, const ARingQQ::ElementType& fR, ARingRR::ElementType& fS) { + (void) R; return S.set_from_mpq(fS, &fR); } inline bool mypromote(const ARingQQ& R, @@ -194,6 +270,7 @@ inline bool mypromote(const ARingQQ& R, const ARingQQ::ElementType& fR, ARingRRR::ElementType& fS) { + (void) R; return S.set_from_mpq(fS, &fR); } inline bool mypromote(const ARingQQ& R, @@ -201,6 +278,7 @@ inline bool mypromote(const ARingQQ& R, const ARingQQ::ElementType& fR, ARingRRi::ElementType& fS) { + (void) R; return S.set_from_mpq(fS, &fR); } inline bool mypromote(const ARingQQ& R, @@ -208,6 +286,7 @@ inline bool mypromote(const ARingQQ& R, const ARingQQ::ElementType& fR, ARingCC::ElementType& fS) { + (void) R; return S.set_from_mpq(fS, &fR); } inline bool mypromote(const ARingQQ& R, @@ -215,6 +294,7 @@ inline bool mypromote(const ARingQQ& R, const ARingQQ::ElementType& fR, ARingCCC::ElementType& fS) { + (void) R; return S.set_from_mpq(fS, &fR); } ///////////////////////////////////////////////////// @@ -223,6 +303,7 @@ inline bool mypromote(const ARingRR& R, const ARingRR::ElementType& fR, ARingRR::ElementType& fS) { + (void) R; S.set_from_double(fS, fR); return true; } @@ -231,6 +312,7 @@ inline bool mypromote(const ARingRR& R, const ARingRR::ElementType& fR, ARingRRR::ElementType& fS) { + (void) R; S.set_from_double(fS, fR); return true; } @@ -239,6 +321,7 @@ inline bool mypromote(const ARingRR& R, const ARingRR::ElementType& fR, ARingCC::ElementType& fS) { + (void) R; S.set_from_doubles(fS, fR, 0); return true; } @@ -247,6 +330,7 @@ inline bool mypromote(const ARingRR& R, const ARingRR::ElementType& fR, ARingCCC::ElementType& fS) { + (void) R; S.set_from_doubles(fS, fR, 0); return true; } @@ -256,6 +340,7 @@ inline bool mypromote(const ARingRRR& R, const ARingRRR::ElementType& fR, ARingRRR::ElementType& fS) { + (void) R; S.set(fS, fR); return true; } @@ -264,6 +349,7 @@ inline bool mypromote(const ARingRRR& R, const ARingRRR::ElementType& fR, ARingRR::ElementType& fS) { + (void) R; auto fR1 = const_cast(fR); S.set_from_BigReal(fS, &fR1); return true; @@ -274,6 +360,7 @@ inline bool mypromote(const ARingRRR& R, const ARingRRR::ElementType& fR, ARingCCC::ElementType& fS) { + (void) R; S.set_from_RRR(fS, fR); return true; } @@ -282,6 +369,7 @@ inline bool mypromote(const ARingRRR& R, const ARingRRR::ElementType& fR, ARingCC::ElementType& fS) { + (void) R; auto fR1 = const_cast(fR); S.set_from_BigReal(fS, &fR1); return true; @@ -292,6 +380,7 @@ inline bool mypromote(const ARingRRi& R, const ARingRRi::ElementType& fR, ARingRRi::ElementType& fS) { + (void) R; S.set(fS, fR); return true; } @@ -300,6 +389,7 @@ inline bool mypromote(const ARingRR& R, const ARingRR::ElementType& fR, ARingRRi::ElementType& fS) { + (void) R; S.set_from_double(fS, fR); return true; } @@ -308,6 +398,7 @@ inline bool mypromote(const ARingRRR& R, const ARingRRR::ElementType& fR, ARingRRi::ElementType& fS) { + (void) R; S.set_from_BigReal(fS, &fR); return true; } @@ -317,6 +408,7 @@ inline bool mypromote(const ARingCC& R, const ARingCC::ElementType& fR, ARingCC::ElementType& fS) { + (void) R; S.set(fS, fR); return true; } @@ -325,6 +417,7 @@ inline bool mypromote(const ARingCC& R, const ARingCC::ElementType& fR, ARingCCC::ElementType& fS) { + (void) R; S.set_from_complex_double(fS, fR.re, fR.im); return true; } @@ -334,6 +427,7 @@ inline bool mypromote(const ARingCCC& R, const ARingCCC::ElementType& fR, ARingCC::ElementType& fS) { + (void) R; auto fR1 = const_cast(fR); S.set_from_BigReals(fS, &fR1.re, &fR1.im); return true; @@ -343,16 +437,69 @@ inline bool mypromote(const ARingCCC& R, const ARingCCC::ElementType& fR, ARingCCC::ElementType& fS) { + (void) R; S.set(fS, fR); return true; } ///////////////////////////////////////////////////// +inline bool mypromote(const ARingCCi& R, + const ARingCCi& S, + const ARingCCi::ElementType& fR, + ARingCCi::ElementType& fS) +{ + S.set(fS, fR); + return true; +} +inline bool mypromote(const ARingRR& R, + const ARingCCi& S, + const ARingRR::ElementType& fR, + ARingCCi::ElementType& fS) +{ + S.set_from_double(fS, fR); + return true; +} + +inline bool mypromote(const ARingRRi& R, + const ARingCCi& S, + const ARingRRi::ElementType& fR, + ARingCCi::ElementType& fS) +{ + S.set_from_Interval(fS, &fR); + return true; +} + +inline bool mypromote(const ARingRRR& R, + const ARingCCi& S, + const ARingRRR::ElementType& fR, + ARingCCi::ElementType& fS) +{ + S.set_from_BigReal(fS, &fR); + return true; +} +inline bool mypromote(const ARingCC& R, + const ARingCCi& S, + const ARingCC::ElementType& fR, + ARingCCi::ElementType& fS) +{ + S.set_from_complex_double(fS, fR.re, fR.im); + return true; +} +inline bool mypromote(const ARingCCC& R, + const ARingCCi& S, + const ARingCCC::ElementType& fR, + ARingCCi::ElementType& fS) +{ + S.set_from_BigComplex(fS, &fR); + return true; +} +///////////////////////////////////////////////////// ///////////////////////////////////////////////////// inline bool mylift(const ARingRRR& R, const ARingRR& S, ARingRRR::ElementType& result_gR, const ARingRR::ElementType& gS) { + (void) S; R.set_from_double(result_gR, gS); return true; } @@ -361,6 +508,7 @@ inline bool mylift(const ARingRRR& R, ARingRRR::ElementType& result_gR, const ARingRRR::ElementType& gS) { + (void) S; R.set(result_gR, gS); return true; } @@ -377,6 +525,7 @@ inline bool mylift(const ARingRRR& R, ARingRRR::ElementType& result_gR, const ARingCC::ElementType& gS) { + (void) S; R.set_from_double(result_gR, gS.re); return gS.im == 0; } @@ -386,6 +535,7 @@ inline bool mylift(const ARingRR& R, ARingRR::ElementType& result_gR, const ARingRR::ElementType& gS) { + (void) S; R.set_from_double(result_gR, gS); return true; } @@ -394,6 +544,7 @@ inline bool mylift(const ARingRR& R, ARingRR::ElementType& result_gR, const ARingRRR::ElementType& gS) { + (void) S; auto gS1 = const_cast(gS); R.set_from_BigReal(result_gR, &gS1); return true; @@ -412,6 +563,7 @@ inline bool mylift(const ARingRR& R, ARingRR::ElementType& result_gR, const ARingCC::ElementType& gS) { + (void) S; R.set_from_double(result_gR, gS.re); return gS.im == 0; } @@ -421,6 +573,7 @@ inline bool mylift(const ARingCCC& R, ARingCCC::ElementType& result_gR, const ARingCCC::ElementType& gS) { + (void) S; R.set(result_gR, gS); return true; } @@ -429,6 +582,7 @@ inline bool mylift(const ARingCCC& R, ARingCCC::ElementType& result_gR, const ARingCC::ElementType& gS) { + (void) S; R.set_from_complex_double(result_gR, gS.re, gS.im); return true; } @@ -437,6 +591,7 @@ inline bool mylift(const ARingCC& R, ARingCC::ElementType& result_gR, const ARingCCC::ElementType& gS) { + (void) S; auto gS1 = const_cast(gS); R.set_from_BigReals(result_gR, &gS1.re, &gS1.im); return true; @@ -446,6 +601,7 @@ inline bool mylift(const ARingCC& R, ARingCC::ElementType& result_gR, const ARingCC::ElementType& gS) { + (void) S; R.set(result_gR, gS); return true; } diff --git a/M2/Macaulay2/e/aring-zz-flint.cpp b/M2/Macaulay2/e/aring-zz-flint.cpp index 93d3e77d06c..0e9fffbd6a4 100644 --- a/M2/Macaulay2/e/aring-zz-flint.cpp +++ b/M2/Macaulay2/e/aring-zz-flint.cpp @@ -25,6 +25,8 @@ void ARingZZ::eval(const RingMap* map, ring_elem& result) const { mpz_t temp; + + (void) first_var; flint_mpz_init_set_readonly(temp, &f); result = map->get_ring()->from_int(temp); flint_mpz_clear_readonly(temp); @@ -38,6 +40,7 @@ void ARingZZ::elem_text_out(buffer& o, { char* str; + (void) p_parens; bool is_neg = (fmpz_cmp_si(&a, 0) == -1); bool is_one = (fmpz_cmp_si(&a, 1) == 0 || fmpz_cmp_si(&a, -1) == 0); diff --git a/M2/Macaulay2/e/aring-zz-flint.hpp b/M2/Macaulay2/e/aring-zz-flint.hpp index 4769c3c7fe2..e13f9f8a9dd 100644 --- a/M2/Macaulay2/e/aring-zz-flint.hpp +++ b/M2/Macaulay2/e/aring-zz-flint.hpp @@ -111,8 +111,19 @@ void set_from_mpz(ElementType& result, mpz_srcptr a) const return false; } - bool set_from_BigReal(ElementType& result, gmp_RR a) const { return false; } - void set_var(ElementType& result, int v) const { fmpz_set_si(&result, 1); } + bool set_from_BigReal(ElementType& result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + + void set_var(ElementType& result, int v) const + { + (void) v; + fmpz_set_si(&result, 1); + } + /** @} */ /** @name arithmetic @@ -243,11 +254,17 @@ void set_from_mpz(ElementType& result, mpz_srcptr a) const bool promote(const Ring* Rf, const ring_elem f, ElementType& result) const { + (void) Rf; + (void) f; + (void) result; return false; } bool lift(const Ring* Rg, const ElementType& f, ring_elem& result) const { + (void) Rg; + (void) f; + (void) result; return false; } diff --git a/M2/Macaulay2/e/aring-zz-gmp.cpp b/M2/Macaulay2/e/aring-zz-gmp.cpp index 8c2f686bbaf..5857e7ca609 100644 --- a/M2/Macaulay2/e/aring-zz-gmp.cpp +++ b/M2/Macaulay2/e/aring-zz-gmp.cpp @@ -13,6 +13,7 @@ void ARingZZGMP::eval(const RingMap* map, int first_var, ring_elem& result) const { + (void) first_var; mpz_ptr f1 = static_cast(const_cast(&f)); result = map->get_ring()->from_int(f1); } @@ -25,6 +26,7 @@ void ARingZZGMP::elem_text_out(buffer& o, { char* str; + (void) p_parens; bool is_neg = (mpz_cmp_si(&a, 0) == -1); bool is_one = (mpz_cmp_si(&a, 1) == 0 || mpz_cmp_si(&a, -1) == 0); diff --git a/M2/Macaulay2/e/aring-zz-gmp.hpp b/M2/Macaulay2/e/aring-zz-gmp.hpp index 70cec3ba488..e03bd418930 100644 --- a/M2/Macaulay2/e/aring-zz-gmp.hpp +++ b/M2/Macaulay2/e/aring-zz-gmp.hpp @@ -104,8 +104,19 @@ class ARingZZGMP : public SimpleARing return false; } - bool set_from_BigReal(ElementType& result, gmp_RR a) const { return false; } - void set_var(ElementType& result, int v) const { mpz_set_si(&result, 1); } + bool set_from_BigReal(ElementType& result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + + void set_var(ElementType& result, int v) const + { + (void) v; + mpz_set_si(&result, 1); + } + /** @} */ /** @name arithmetic @@ -237,11 +248,17 @@ class ARingZZGMP : public SimpleARing bool promote(const Ring* Rf, const ring_elem f, ElementType& result) const { + (void) Rf; + (void) f; + (void) result; return false; } bool lift(const Ring* Rg, const ElementType& f, ring_elem& result) const { + (void) Rg; + (void) f; + (void) result; return false; } diff --git a/M2/Macaulay2/e/aring-zzp-ffpack.cpp b/M2/Macaulay2/e/aring-zzp-ffpack.cpp index bc7cc5de3eb..2ae9e1551c1 100644 --- a/M2/Macaulay2/e/aring-zzp-ffpack.cpp +++ b/M2/Macaulay2/e/aring-zzp-ffpack.cpp @@ -22,6 +22,7 @@ void ARingZZpFFPACK::elem_text_out(buffer &o, bool print_plus, bool print_parens) const { + (void) print_parens; STT a = static_cast(elem); if (a == 1 and not print_one) return; if (a > 0 and print_plus) o << "+"; @@ -277,6 +278,7 @@ void ARingZZpFFPACK::eval(const RingMap *map, int first_var, // not used here. See ringmap.cpp ring_elem &result) const { + (void) first_var; // translate f to map->target() long a = static_cast(f); result = map->get_ring()->from_long(a); diff --git a/M2/Macaulay2/e/aring-zzp-ffpack.hpp b/M2/Macaulay2/e/aring-zzp-ffpack.hpp index 05c5f7017c6..18c97f814e1 100644 --- a/M2/Macaulay2/e/aring-zzp-ffpack.hpp +++ b/M2/Macaulay2/e/aring-zzp-ffpack.hpp @@ -13,6 +13,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #define bool_constant givaro_bool_constant #include #undef bool_constant @@ -144,7 +145,7 @@ class ARingZZpFFPACK : public SimpleARing void set(ElementType &result, ElementType a) const { result = a; } void init(ElementType &result) const; - static void clear(ElementType &result) {}; + static void clear(ElementType &result) { (void) result; }; void set_zero(ElementType &result) const; @@ -156,10 +157,21 @@ class ARingZZpFFPACK : public SimpleARing bool set_from_mpq(ElementType &result, mpq_srcptr a) const; - bool set_from_BigReal(ElementType &result, gmp_RR a) const { return false; } + bool set_from_BigReal(ElementType &result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + ElementType computeGenerator() const; - void set_var(ElementType &result, int v) const { result = getGenerator(); } + void set_var(ElementType &result, int v) const + { + (void) v; + result = getGenerator(); + } + /** @} */ /** @name arithmetic diff --git a/M2/Macaulay2/e/aring-zzp-flint.cpp b/M2/Macaulay2/e/aring-zzp-flint.cpp index 926bcf970c1..f2e4171200b 100644 --- a/M2/Macaulay2/e/aring-zzp-flint.cpp +++ b/M2/Macaulay2/e/aring-zzp-flint.cpp @@ -33,6 +33,7 @@ void ARingZZpFlint::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { + (void) p_parens; long n = coerceToLongInteger(a); if (n < 0) { @@ -49,6 +50,7 @@ void ARingZZpFlint::eval(const RingMap *map, int first_var, ring_elem &result) const { + (void) first_var; long a = coerceToLongInteger(f); result = map->get_ring()->from_long(a); } diff --git a/M2/Macaulay2/e/aring-zzp-flint.hpp b/M2/Macaulay2/e/aring-zzp-flint.hpp index a375d120c80..6a4e286b880 100644 --- a/M2/Macaulay2/e/aring-zzp-flint.hpp +++ b/M2/Macaulay2/e/aring-zzp-flint.hpp @@ -83,7 +83,7 @@ class ARingZZpFlint : public SimpleARing void init(ElementType &result) const { result = 0; } void init_set(ElementType &result, ElementType a) const { result = a; } - static void clear(ElementType &result) { /* nothing */} + static void clear(ElementType &result) { (void) result; } void set(ElementType &result, ElementType a) const { result = a; } void set_zero(ElementType &result) const { result = 0; } @@ -98,7 +98,12 @@ class ARingZZpFlint : public SimpleARing fmpz_clear(b); } - void set_var(ElementType &result, int v) const { result = 1; } + void set_var(ElementType &result, int v) const + { + (void) v; + result = 1; + } + void set_from_mpz(ElementType &result, mpz_srcptr a) const { result = mpz_fdiv_ui(a, mCharac); @@ -114,7 +119,13 @@ class ARingZZpFlint : public SimpleARing return true; } - bool set_from_BigReal(ElementType &result, gmp_RR a) const { return false; } + bool set_from_BigReal(ElementType &result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + // arithmetic void negate(ElementType &result, ElementType a) const { diff --git a/M2/Macaulay2/e/aring-zzp.cpp b/M2/Macaulay2/e/aring-zzp.cpp index 3b501eea0df..585d10fc900 100644 --- a/M2/Macaulay2/e/aring-zzp.cpp +++ b/M2/Macaulay2/e/aring-zzp.cpp @@ -77,6 +77,7 @@ void ARingZZp::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { + (void) p_parens; long n = coerceToLongInteger(a); if (n < 0) { @@ -93,6 +94,7 @@ void ARingZZp::eval(const RingMap *map, int first_var, ring_elem &result) const { + (void) first_var; long a = coerceToLongInteger(f); result = map->get_ring()->from_long(a); } diff --git a/M2/Macaulay2/e/aring-zzp.hpp b/M2/Macaulay2/e/aring-zzp.hpp index 5d70beb382c..2d7271b8ba8 100644 --- a/M2/Macaulay2/e/aring-zzp.hpp +++ b/M2/Macaulay2/e/aring-zzp.hpp @@ -109,7 +109,7 @@ class ARingZZp : public SimpleARing // 'init', 'init_set' functions void init(elem &result) const { result = 0; } - static void clear(elem &result) { /* nothing */} + static void clear(elem &result) { (void) result; } void set_zero(elem &result) const { result = 0; } void set_from_long(elem &result, long a) const @@ -119,7 +119,12 @@ class ARingZZp : public SimpleARing result = log_table[a]; } - void set_var(elem &result, int v) const { result = 1; } + void set_var(elem &result, int v) const + { + (void) v; + result = 1; + } + void set_from_mpz(elem &result, mpz_srcptr a) const { int b = static_cast(mpz_fdiv_ui(a, p)); @@ -136,7 +141,13 @@ class ARingZZp : public SimpleARing return true; } - bool set_from_BigReal(elem &result, gmp_RR a) const { return false; } + bool set_from_BigReal(elem &result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + // arithmetic void negate(elem &result, elem a) const { diff --git a/M2/Macaulay2/e/aring.hpp b/M2/Macaulay2/e/aring.hpp index a3d6add9256..1efbf154d2f 100644 --- a/M2/Macaulay2/e/aring.hpp +++ b/M2/Macaulay2/e/aring.hpp @@ -40,11 +40,12 @@ enum RingID { ring_GFM2, ring_GFFlintBig, ring_GFFlintZech, - ring_RRi, ring_RR, ring_CC, ring_RRR, ring_CCC, + ring_RRi, + ring_CCi, ring_tower_ZZp, ring_old ///< refers to all rings which are not ConcreteRing's. }; @@ -172,17 +173,39 @@ class DummyRing : public SimpleARing void getGenerator(elem &result) const { result = 0; } const PolynomialRing &originalRing() const { return *mOriginalRing; } long coerceToLongInteger(ElementType a) const { return a; } - void lift_to_original_ring(ring_elem &result, const ElementType &f) const {} - M2_arrayint fieldElementToM2Array(ElementType el) const { return nullptr; } - void to_ring_elem(ring_elem &result, const ElementType &a) const {} - void from_ring_elem(ElementType &result, const ring_elem &a) const {} + void lift_to_original_ring(ring_elem &result, const ElementType &f) const + { + (void) result; + (void) f; + } + M2_arrayint fieldElementToM2Array(ElementType el) const + { + (void) el; + return nullptr; + } + void to_ring_elem(ring_elem &result, const ElementType &a) const + { + (void) result; + (void) a; + } + void from_ring_elem(ElementType &result, const ring_elem &a) const + { + (void) result; + (void) a; + } bool promote(const Ring *Rf, const ring_elem f, ElementType &result) const { + (void) Rf; + (void) f; + (void) result; return false; } bool lift(const Ring *Rg, const ElementType f, ring_elem &result) const { + (void) Rg; + (void) f; + (void) result; return false; } @@ -191,6 +214,10 @@ class DummyRing : public SimpleARing int first_var, ring_elem &result) const { + (void) map; + (void) f; + (void) first_var; + (void) result; } void text_out(buffer &o) const { o << "GF(dummy)"; } @@ -198,68 +225,148 @@ class DummyRing : public SimpleARing const ElementType a, bool p_one, bool p_plus, - bool p_parens) const {}; + bool p_parens) const + { + (void) o; + (void) a; + (void) p_one; + (void) p_plus; + (void) p_parens; + } void init_set(elem &result, elem a) const { result = a; } void set(elem &result, elem a) const { result = a; } void set_from_long(elem &result, long a) const { result = a; } void init(elem &result) const { result = 0; } - void set_from_mpz(elem &result, mpz_srcptr a) const { result = 0; } - bool set_from_mpq(elem &result, mpq_srcptr a) const { return false; } - bool set_from_BigReal(elem &result, gmp_RR a) const { return false; } - void set_var(elem &result, int v) const { result = 1; } - bool is_unit(const ElementType f) const { return false; } - bool is_zero(const ElementType f) const { return true; } + void set_from_mpz(elem &result, mpz_srcptr a) const + { + (void) a; + result = 0; + } + bool set_from_mpq(elem &result, mpq_srcptr a) const + { + (void) result; + (void) a; + return false; + } + bool set_from_BigReal(elem &result, gmp_RR a) const + { + (void) result; + (void) a; + return false; + } + void set_var(elem &result, int v) const + { + (void) v; + result = 1; + } + bool is_unit(const ElementType f) const + { + (void) f; + return false; + } + bool is_zero(const ElementType f) const + { + (void) f; + return true; + } bool is_equal(const ElementType f, const ElementType g) const { + (void) f; + (void) g; return false; } int compare_elems(const ElementType f, const ElementType g) const { + (void) f; + (void) g; return 1; } static void clear(elem &result) { result = 0; } void set_zero(elem &result) const { result = 0; } void copy(elem &result, const elem a) const { result = a; } - void negate(elem &result, const elem a) const {}; - ; + void negate(elem &result, const elem a) const + { + (void) result; + (void) a; + } - void invert(elem &result, const elem a) const {}; - ; + void invert(elem &result, const elem a) const + { + (void) result; + (void) a; + } - void add(elem &result, const elem a, const elem b) const {}; - ; + void add(elem &result, const elem a, const elem b) const + { + (void) result; + (void) a; + (void) b; + } void subtract(ElementType &result, const ElementType a, - const ElementType b) const {}; - ; + const ElementType b) const + { + (void) result; + (void) a; + (void) b; + } - void subtract_multiple(elem &result, const elem a, const elem b) const {}; - ; + void subtract_multiple(elem &result, const elem a, const elem b) const + { + (void) result; + (void) a; + (void) b; + } - void mult(elem &result, const elem a, const elem b) const {}; - ; + void mult(elem &result, const elem a, const elem b) const + { + (void) result; + (void) a; + (void) b; + } ///@brief test doc - void divide(elem &result, const elem a, const elem b) const {}; - ; + void divide(elem &result, const elem a, const elem b) const + { + (void) result; + (void) a; + (void) b; + } - void power(elem &result, const elem a, const int n) const {}; - ; + void power(elem &result, const elem a, const int n) const + { + (void) result; + (void) a; + (void) n; + } - void power_mpz(elem &result, const elem a, mpz_srcptr n) const {}; - ; + void power_mpz(elem &result, const elem a, mpz_srcptr n) const + { + (void) result; + (void) a; + (void) n; + } void syzygy(const ElementType a, const ElementType b, ElementType &x, - ElementType &y) const {}; - ; - + ElementType &y) const + { + (void) a; + (void) b; + (void) x; + (void) y; + } void random(ElementType &result) const { result = 0; } - void swap(ElementType &a, ElementType &b) const { assert(false); }; + void swap(ElementType &a, ElementType &b) const + { + (void) a; + (void) b; + assert(false); + } }; }; // namespace M2 diff --git a/M2/Macaulay2/e/bibasis/tset.hpp b/M2/Macaulay2/e/bibasis/tset.hpp index a44d5129676..5f733df3202 100644 --- a/M2/Macaulay2/e/bibasis/tset.hpp +++ b/M2/Macaulay2/e/bibasis/tset.hpp @@ -41,7 +41,7 @@ namespace BIBasis std::size_t Size() const; const Triple* Find(const MonomType& monom) const; - Triple* const Back() const; + Triple* Back() const; void CollectNonMultiProlongations(Iterator& iterator, std::list*>& set); }; @@ -124,7 +124,7 @@ namespace BIBasis } template - Triple* const TSet::Back() const + Triple* TSet::Back() const { return TripleList.back(); } diff --git a/M2/Macaulay2/e/buffer.cpp b/M2/Macaulay2/e/buffer.cpp index b0205dd2742..8992468a7fa 100644 --- a/M2/Macaulay2/e/buffer.cpp +++ b/M2/Macaulay2/e/buffer.cpp @@ -1,7 +1,10 @@ #include "buffer.hpp" +#include "ringelem.hpp" #include #include #include +#include +#include void buffer::expand(int newcap) { @@ -58,7 +61,7 @@ void buffer::put(double n) { const int N = 100; char s[N]; - snprintf(s, N, "%lf", n); + snprintf(s, N, "%g", n); put(s, strlen(s)); } @@ -110,6 +113,77 @@ void buffer::put(unsigned long n, int width) put(s, strlen(s)); } +void buffer::put(mpfr_srcptr x) +{ + int n; + std::string s; + + n = mpfr_snprintf(nullptr, 0, "%Rg", x); + s.resize(n); + mpfr_snprintf(s.data(), s.size() + 1, "%Rg", x); + put(s); +} + +void buffer::put(mpfi_srcptr x) +{ + put('['); + put(&x->left); + put(','); + put(&x->right); + put(']'); +} + +void buffer::put(cc_doubles_srcptr x) +{ + if (x->re !=0 || (x->re == 0 && x->im == 0)) { + put(x->re); + if (x->im > 0) + put('+'); + } + + if (x->im != 0) { + if (x->im == -1) + put('-'); + else if (x->im != 1) + put(x->im); + put('i'); + } + +} + +void buffer::put(cc_srcptr x) +{ + if (mpfr_cmp_si(&x->re, 0) !=0 || + (mpfr_cmp_si(&x->re, 0) == 0 && mpfr_cmp_si(&x->im, 0) == 0)) { + put(&x->re); + if (mpfr_cmp_si(&x->im, 0) > 0) + put('+'); + } + + if (mpfr_cmp_si(&x->im, 0) != 0) { + if (mpfr_cmp_si(&x->im, -1) == 0) + put('-'); + else if (mpfr_cmp_si(&x->im, 1) != 0) + put(&x->im); + put('i'); + } +} + +void buffer::put(cci_srcptr x) +{ + if (!mpfi_is_zero(&x->re) || + mpfi_is_zero(&x->re) && mpfi_is_zero(&x->im)) { + put(&x->re); + if (!mpfi_is_zero(&x->im)) + put('+'); + } + + if (!mpfi_is_zero(&x->im)) { + put(&x->im); + put('i'); + } +} + // Local Variables: // compile-command: "make -C $M2BUILDDIR/Macaulay2/e " // indent-tabs-mode: nil diff --git a/M2/Macaulay2/e/buffer.hpp b/M2/Macaulay2/e/buffer.hpp index 2216c4a7552..4ed1decc785 100644 --- a/M2/Macaulay2/e/buffer.hpp +++ b/M2/Macaulay2/e/buffer.hpp @@ -3,13 +3,17 @@ #ifndef _buffer_hpp_ #define _buffer_hpp_ -#include "../d/M2mem.h" #include "newdelete.hpp" #include "engine-includes.hpp" #include const int BUFFER_INITIAL_CAPACITY = 100; +// forward declarations (from ringelem.hpp) +struct cc_struct; +struct cc_doubles_struct; +struct cci_struct; + struct indent { int n; @@ -64,6 +68,11 @@ class buffer : public our_new_delete void put(unsigned long long n); // Format the integer, place into buffer void put(unsigned long n, int width); // Format the integer, with given width field. + void put(mpfr_srcptr x); + void put(mpfi_srcptr x); + void put(cc_struct const *x); + void put(cc_doubles_struct const *x); + void put(cci_struct const *x); void put(std::string s) { put(s.data(), s.size()); } // To put an endline in: // o.put(newline); @@ -135,6 +144,31 @@ class buffer : public our_new_delete put(static_cast(c)); return *this; } + buffer &operator<<(mpfr_srcptr x) + { + put(x); + return *this; + } + buffer &operator<<(mpfi_srcptr x) + { + put(x); + return *this; + } + buffer &operator<<(cc_struct const *x) + { + put(x); + return *this; + } + buffer &operator<<(cc_doubles_struct const *x) + { + put(x); + return *this; + } + buffer &operator<<(cci_struct const *x) + { + put(x); + return *this; + } buffer &operator<<(indent s) { buffer &o = *this; diff --git a/M2/Macaulay2/e/coeffrings.cpp b/M2/Macaulay2/e/coeffrings.cpp index d2176810666..b5387a9f1ec 100644 --- a/M2/Macaulay2/e/coeffrings.cpp +++ b/M2/Macaulay2/e/coeffrings.cpp @@ -6,6 +6,7 @@ void CoefficientRingZZp::elem_text_out(buffer &o, bool p_plus, bool p_parens) const { + (void) p_parens; long n = coerceToLongInteger(a); if (n < 0) { diff --git a/M2/Macaulay2/e/coeffrings.hpp b/M2/Macaulay2/e/coeffrings.hpp index 6cbb0fe53fc..6985d30e5d3 100644 --- a/M2/Macaulay2/e/coeffrings.hpp +++ b/M2/Macaulay2/e/coeffrings.hpp @@ -84,8 +84,8 @@ class CoefficientRingZZp : public M2::SimpleARing } int to_int(int f) const { return exp_table[f]; } - void init(elem &result) const {} - static void clear(elem &result) { /* nothing */} + void init(elem &result) const { (void) result; } + static void clear(elem &result) { (void) result; } void init_set(elem &result, elem a) const { result = a; } void set_zero(elem &result) const { result = zero; } void set(elem &result, elem a) const { result = a; } @@ -228,9 +228,7 @@ class CoefficientRingR CoefficientRingR(const Ring *R0) : R(R0) {} void init_set(elem &result, elem a) const { result = a; } void init(elem &result) const { result = R->zero(); } - void clear(elem &result) const - { /* do nothing */ - } + void clear(elem &result) const { (void) result; } void set_zero(elem &result) const { result = R->zero(); } void set(elem &result, elem a) const { result = a; } diff --git a/M2/Macaulay2/e/comp-gb.cpp b/M2/Macaulay2/e/comp-gb.cpp index a8fe293aa4f..fe9271214cf 100644 --- a/M2/Macaulay2/e/comp-gb.cpp +++ b/M2/Macaulay2/e/comp-gb.cpp @@ -210,6 +210,7 @@ Computation /* or null */ *GBComputation::set_hilbert_function( // The default version returns an error saying that Hilbert functions cannot be // used. { + (void) h; ERROR("Hilbert function use is not implemented for this GB algorithm"); return nullptr; } @@ -217,6 +218,7 @@ Computation /* or null */ *GBComputation::set_hilbert_function( const Matrix /* or null */ *GBComputation::get_parallel_lead_terms( M2_arrayint w) { + (void) w; ERROR( "Cannot compute parallel lead terms for this kind of Groebner " "computation"); diff --git a/M2/Macaulay2/e/comp-res.cpp b/M2/Macaulay2/e/comp-res.cpp index 374374824fa..d2aaf4a7e97 100644 --- a/M2/Macaulay2/e/comp-res.cpp +++ b/M2/Macaulay2/e/comp-res.cpp @@ -241,6 +241,8 @@ void ResolutionComputation::betti_display(buffer &o, M2_arrayint ar) MutableMatrix /* or null */ *ResolutionComputation::get_matrix(int level, int degree) { + (void) level; + (void) degree; // the default version gives an error that it isn't defined ERROR("this function not defined for this resolution type"); return nullptr; diff --git a/M2/Macaulay2/e/complex.c b/M2/Macaulay2/e/complex.c index 566de295fba..dd6cf17664e 100644 --- a/M2/Macaulay2/e/complex.c +++ b/M2/Macaulay2/e/complex.c @@ -1,7 +1,8 @@ // Copyright 2007 Michael E. Stillman #include "complex.h" -#include "engine-includes.hpp" +#include "interface/m2-types.h" +#include "interface/m2-mem.h" #include void mpfc_init_set(gmp_CCmutable result, gmp_CCmutable a) diff --git a/M2/Macaulay2/e/complex.h b/M2/Macaulay2/e/complex.h index 4812d2168f4..aae494c8595 100644 --- a/M2/Macaulay2/e/complex.h +++ b/M2/Macaulay2/e/complex.h @@ -10,7 +10,8 @@ */ #if !defined(SAFEC_EXPORTS) -#include +//#include +#include "interface/m2-types.h" #endif #if defined(__cplusplus) diff --git a/M2/Macaulay2/e/debug.cpp b/M2/Macaulay2/e/debug.cpp index 6c79bc9ac4c..5a76ffbb95b 100644 --- a/M2/Macaulay2/e/debug.cpp +++ b/M2/Macaulay2/e/debug.cpp @@ -145,7 +145,7 @@ void dstash() void dRRR(gmp_RR a) { buffer o; - o << M2_tocharstar((*gmp_tostringRRpointer)(a)) << newline; + o << a << newline; emit(o.str()); } diff --git a/M2/Macaulay2/e/dmat.cpp b/M2/Macaulay2/e/dmat.cpp index de9fac860f6..2a4baca6a8e 100644 --- a/M2/Macaulay2/e/dmat.cpp +++ b/M2/Macaulay2/e/dmat.cpp @@ -187,7 +187,7 @@ bool solveLinear(const DMatZZpFFPACK& A, bool declare_A_is_invertible) // this parameter is unused { // std::cerr << "inside FFpackSolveLinear" << std::endl; - + (void) declare_A_is_invertible; size_t a_rows = A.numRows(); size_t a_cols = A.numColumns(); diff --git a/M2/Macaulay2/e/dpoly.cpp b/M2/Macaulay2/e/dpoly.cpp index e245c62ecea..f84ae008fe1 100644 --- a/M2/Macaulay2/e/dpoly.cpp +++ b/M2/Macaulay2/e/dpoly.cpp @@ -634,6 +634,7 @@ void DPoly::reset_degree_0(TowerPolynomial &f) } void DPoly::reset_degree_n(int level, TowerPolynomial &f) { + (void) level; int fdeg = f->deg; for (int j = fdeg; j >= 0; --j) if (f->arr.polys[j] != nullptr) @@ -1095,11 +1096,15 @@ void DPoly::remainder(int level, TowerPolynomial &f, const TowerPolynomial g) void DPoly::pseudo_remainder(int level, TowerPolynomial &f, const TowerPolynomial g) { + (void) level; + (void) f; if (g == nullptr) return; // TODO: write } TowerPolynomial DPoly::pseudo_division(int level, TowerPolynomial &f, const TowerPolynomial g) { + (void) level; + (void) f; if (g == nullptr) return nullptr; // TODO: write return nullptr; @@ -1107,6 +1112,9 @@ TowerPolynomial DPoly::pseudo_division(int level, TowerPolynomial &f, const Towe TowerPolynomial DPoly::resultant(int level, TowerPolynomial f, TowerPolynomial g) { // TODO: write + (void) level; + (void) f; + (void) g; return nullptr; } static void swap_poly(TowerPolynomial &f, TowerPolynomial &g) diff --git a/M2/Macaulay2/e/engine-includes.hpp b/M2/Macaulay2/e/engine-includes.hpp index 7d6a0bb7e37..18ddb11968e 100644 --- a/M2/Macaulay2/e/engine-includes.hpp +++ b/M2/Macaulay2/e/engine-includes.hpp @@ -10,7 +10,8 @@ #include #if !defined(SAFEC_EXPORTS) -#include +//#include +#include "interface/m2-types.h" #endif #if HAVE_STDINT_H @@ -26,8 +27,8 @@ #ifndef __cplusplus /* These are coming from newdelete.hpp, in C++ */ #include -#include "../d/M2mem.h" -#include "../d/debug.h" +//#include "../d/M2mem.h" +//#include "../d/debug.h" #endif // IWYU pragma: end_exports diff --git a/M2/Macaulay2/e/engine.cpp b/M2/Macaulay2/e/engine.cpp index c4d147f6d2c..79d1994e660 100644 --- a/M2/Macaulay2/e/engine.cpp +++ b/M2/Macaulay2/e/engine.cpp @@ -2,7 +2,8 @@ #include "interface/random.h" #include "aring-glue.hpp" // for initializeRationalRing -#include "engine-exports.h" // for M2_tostring, M2_string +#include "interface/m2-types.h" +//#include "engine-exports.h" // for M2_tostring, M2_string #include "error.h" // for error_message #include "hash.hpp" // for MutableEngineObject #include "mem.hpp" // for doubles, doubling_stash diff --git a/M2/Macaulay2/e/exptable.c b/M2/Macaulay2/e/exptable.c index d1b664d66ef..80f9ebf0274 100644 --- a/M2/Macaulay2/e/exptable.c +++ b/M2/Macaulay2/e/exptable.c @@ -4,6 +4,12 @@ * unsigned long int. */ /* The implementation uses table.{h,c}, which was written by David R. Hanson */ +#include "interface/m2-mem.h" + +/*these next lines added by MES, July 2002, to use our gc routines..*/ +#define NEW(p) ((p) = (void *) getmem((long)sizeof *(p))) +#define FREE(ptr) ((void)(freemem((ptr)), (ptr) = 0)) + struct exponent_table { int nvars; diff --git a/M2/Macaulay2/e/f4/f4-computation.cpp b/M2/Macaulay2/e/f4/f4-computation.cpp index cff1dbdd96f..7f648443f90 100644 --- a/M2/Macaulay2/e/f4/f4-computation.cpp +++ b/M2/Macaulay2/e/f4/f4-computation.cpp @@ -183,12 +183,14 @@ const Matrix /* or null */ *F4Computation::get_syzygies() const Matrix /* or null */ *F4Computation::get_initial(int nparts) { + (void) nparts; ERROR("Lead terms for `Algorithm => LinearAlgebra`: use `leadTerm gens gb` instead of `leadTerm gb`"); return nullptr; } const Matrix /* or null */ *F4Computation::matrix_remainder(const Matrix *m) { + (void) m; ERROR("No special algorithm for computing matrix remainder for `Algorithm => LinearAlgebra"); return nullptr; } @@ -198,6 +200,7 @@ M2_bool F4Computation::matrix_lift( const Matrix /* or null */ **result_remainder, const Matrix /* or null */ **result_quotient) { + (void) m; *result_remainder = nullptr; *result_quotient = nullptr; ERROR("No special algorithm for computing matrix remainder and lift for `Algorithm => LinearAlgebra"); @@ -209,6 +212,7 @@ int F4Computation::contains(const Matrix *m) // Otherwise return the index of the first column that // does not reduce to zero. { + (void) m; ERROR("No special algorithm for computing containment for `Algorithm => LinearAlgebra"); return 0; } @@ -224,6 +228,7 @@ void F4Computation::text_out(buffer &o) const /* This displays statistical information, and depends on the M2_gbTrace value */ { + (void) o; // TODO: what to display? } diff --git a/M2/Macaulay2/e/f4/f4-computation.hpp b/M2/Macaulay2/e/f4/f4-computation.hpp index d9e2b5a569c..8d9c780351c 100644 --- a/M2/Macaulay2/e/f4/f4-computation.hpp +++ b/M2/Macaulay2/e/f4/f4-computation.hpp @@ -4,7 +4,7 @@ #define _F4Computation_h_ #include "comp-gb.hpp" // for GBComputation -#include "engine-exports.h" // for M2_bool, M2_arrayint +#include "interface/m2-types.h" // for M2_bool, M2_arrayint #include "f4/f4.hpp" // for F4GB #include "interface/computation.h" // for ComputationStatusCode #include "polyring.hpp" // for PolynomialRing diff --git a/M2/Macaulay2/e/f4/f4-m2-interface.hpp b/M2/Macaulay2/e/f4/f4-m2-interface.hpp index 76a5635287d..7f93634ab1e 100644 --- a/M2/Macaulay2/e/f4/f4-m2-interface.hpp +++ b/M2/Macaulay2/e/f4/f4-m2-interface.hpp @@ -1,7 +1,7 @@ #ifndef __F4toM2interface_h_ #define __F4toM2interface_h_ -#include "engine-exports.h" // for M2_arrayint +#include "interface/m2-types.h" // for M2_arrayint #include "f4/f4-types.hpp" // for gb_array, GBF4Polynomial (ptr only), coefficient_m... #include "ringelem.hpp" // for vec diff --git a/M2/Macaulay2/e/f4/f4-monlookup.cpp b/M2/Macaulay2/e/f4/f4-monlookup.cpp index c0d1874d6af..e57fd0d5514 100644 --- a/M2/Macaulay2/e/f4/f4-monlookup.cpp +++ b/M2/Macaulay2/e/f4/f4-monlookup.cpp @@ -2,7 +2,7 @@ #include "f4/f4-monlookup.hpp" #include "buffer.hpp" // for buffer -#include "engine-exports.h" // for newline +#include "interface/m2-types.h" // for newline #include "f4/varpower-monomial.hpp" // for varpower_word, const_varpower_mo... #include "text-io.hpp" // for emit, emit_line diff --git a/M2/Macaulay2/e/f4/f4.cpp b/M2/Macaulay2/e/f4/f4.cpp index 67896099b8b..cb04cb0e6df 100644 --- a/M2/Macaulay2/e/f4/f4.cpp +++ b/M2/Macaulay2/e/f4/f4.cpp @@ -77,6 +77,12 @@ F4GB::F4GB(const VectorArithmetic* VA, mSPairSet(mMonomialInfo, mGroebnerBasis) #endif { + (void) collect_syz; + (void) n_rows_to_keep; + (void) weights0; + (void) strategy; + (void) use_max_degree; + (void) max_degree; // mLookupTable = new MonomialLookupTable(mMonomialInfo->n_vars()); // mSPairSet = new F4SPairSet(mMonomialInfo, mGroebnerBasis); mat = new coefficient_matrix; diff --git a/M2/Macaulay2/e/f4/f4.hpp b/M2/Macaulay2/e/f4/f4.hpp index 1a6c6774ea0..770c79e76c1 100644 --- a/M2/Macaulay2/e/f4/f4.hpp +++ b/M2/Macaulay2/e/f4/f4.hpp @@ -68,7 +68,7 @@ // Hilbert function use // -#include "engine-exports.h" // for M2_arrayint, M2_bool +#include "interface/m2-types.h" // for M2_arrayint, M2_bool #include "f4-types.hpp" // for gb_array, MonomialLookupTable #include "f4/moninfo.hpp" // for packed_monomial, MonomialInfo #include "f4/f4-spairs.hpp" // For F4SPairSet diff --git a/M2/Macaulay2/e/f4/moninfo.hpp b/M2/Macaulay2/e/f4/moninfo.hpp index 4ebdfcb422d..f0cfc63656e 100644 --- a/M2/Macaulay2/e/f4/moninfo.hpp +++ b/M2/Macaulay2/e/f4/moninfo.hpp @@ -3,7 +3,7 @@ #ifndef _moninfo_hpp_ #define _moninfo_hpp_ -#include "engine-exports.h" // for M2_arrayint, M2_arrayint_st... +#include "interface/m2-types.h" // for M2_arrayint, M2_arrayint_st... #include "f4/ntuple-monomial.hpp" // for ntuple_word, const_ntuple_m... #include "f4/varpower-monomial.hpp" // for varpower_word, index_varpow... #include "interface/monomial-ordering.h" // for MonomialOrdering @@ -96,7 +96,12 @@ class MonomialInfo : public our_new_delete int n_vars() const { return nvars; } int max_monomial_size() const { return nslots; } - int monomial_size(const_packed_monomial m) const { return nslots; } + int monomial_size(const_packed_monomial m) const + { + (void) m; + return nslots; + } + void show() const; int componentLocation() const { return mComponentLoc; } diff --git a/M2/Macaulay2/e/finalize.cpp b/M2/Macaulay2/e/finalize.cpp index 1f28cc936e0..df6e58cf561 100644 --- a/M2/Macaulay2/e/finalize.cpp +++ b/M2/Macaulay2/e/finalize.cpp @@ -41,6 +41,7 @@ static volatile std::atomic schorder_nremoved = 0; ////////////////////////////////////////////////////// extern "C" void remove_monideal(void *p, void *cd) { + (void) cd; #ifdef MEMDEBUG p = M2_debug_to_inner(p); #endif @@ -79,6 +80,7 @@ void intern_monideal(MonomialIdeal *G) // } void intern_polyring(const PolynomialRing *G) { + (void) G; // We are already setting a finalizer for rings in newdelete.hpp, // I believe this one is just unsetting that one. return; @@ -97,6 +99,7 @@ void intern_polyring(const PolynomialRing *G) ////////////////////////////////////////////////////// extern "C" void remove_gb(void *p, void *cd) { + (void) cd; #ifdef MEMDEBUG p = M2_debug_to_inner(p); #endif @@ -128,6 +131,7 @@ void intern_GB(GBComputation *G) ////////////////////////////////////////////////////// extern "C" void remove_res(void *p, void *cd) { + (void) cd; #ifdef MEMDEBUG p = M2_debug_to_inner(p); #endif @@ -153,6 +157,7 @@ void intern_res(ResolutionComputation *G) ////////////////////////////////////////////////////// extern "C" void remove_SchreyerOrder(void *p, void *cd) { + (void) cd; #ifdef MEMDEBUG p = M2_debug_to_inner(p); #endif @@ -181,6 +186,7 @@ void intern_SchreyerOrder(SchreyerOrder *G) extern "C" void remove_MutableMatrix(void *p, void *cd) { + (void) cd; #ifdef MEMDEBUG p = M2_debug_to_inner(p); #endif diff --git a/M2/Macaulay2/e/fplll-interface.cpp b/M2/Macaulay2/e/fplll-interface.cpp index 0ceb17bd42b..12c0ceaf867 100644 --- a/M2/Macaulay2/e/fplll-interface.cpp +++ b/M2/Macaulay2/e/fplll-interface.cpp @@ -8,6 +8,9 @@ bool fp_LLL(MutableMatrix *M, MutableMatrix *U, int strategy) { + (void) M; + (void) U; + (void) strategy; #ifndef HAVE_FPLLL ERROR("fplll is not available (configure M2 with fplll!)"); return 0; diff --git a/M2/Macaulay2/e/frac.cpp b/M2/Macaulay2/e/frac.cpp index fed6603d0eb..491b829adb1 100644 --- a/M2/Macaulay2/e/frac.cpp +++ b/M2/Macaulay2/e/frac.cpp @@ -403,7 +403,7 @@ ring_elem FractionField::copy(const ring_elem a) const return FRAC_RINGELEM(g); } -void FractionField::remove(ring_elem &a) const {} +void FractionField::remove(ring_elem &a) const { (void) a; } void FractionField::internal_negate_to(ring_elem &a) const { frac_elem *f = FRAC_VAL(a); @@ -711,6 +711,7 @@ ring_elem FractionField::get_terms(int nvars0, int, int) const { + (void) nvars0; return f; } diff --git a/M2/Macaulay2/e/gauss.cpp b/M2/Macaulay2/e/gauss.cpp index b4b094e3fcb..cb1045435f6 100644 --- a/M2/Macaulay2/e/gauss.cpp +++ b/M2/Macaulay2/e/gauss.cpp @@ -277,6 +277,7 @@ void GaussElimComputation::start_computation() const Matrix *GaussElimComputation::get_mingens() { return get_gb(); } const Matrix *GaussElimComputation::get_initial(int nparts) { + (void) nparts; MatrixConstructor mat(gens->rows(), 0); for (int i = 0; i < gens->n_rows(); i++) if (gb_list[i] != nullptr) diff --git a/M2/Macaulay2/e/gb-default.cpp b/M2/Macaulay2/e/gb-default.cpp index ef754c833f6..46bb1505855 100644 --- a/M2/Macaulay2/e/gb-default.cpp +++ b/M2/Macaulay2/e/gb-default.cpp @@ -61,6 +61,8 @@ gbA *gbA::create(const Matrix *m, int max_degree_limit, int max_reduction_count) { + (void) use_max_degree_limit; + (void) max_degree_limit; const PolynomialRing *origR = m->get_ring()->cast_to_PolynomialRing(); if (origR == nullptr) { @@ -1598,6 +1600,7 @@ int gbA::find_good_monomial_divisor_ZZ(mpz_srcptr c, int i, gap, newgap, egap; int n = 0; + (void) c; VECTOR(MonomialTableZZ::mon_term *) divisors; egap = degf - weightInfo_->exponents_weight(e, x); @@ -1836,6 +1839,8 @@ void gbA::remainder_ZZ(POLY &f, int degf, bool use_denom, ring_elem &denom) exponents_t EXP = ALLOCATE_EXPONENTS(exp_size); + (void) use_denom; + (void) denom; frem->next = nullptr; int count = 0; POLY h = f; @@ -2432,6 +2437,7 @@ void gbA::do_computation() np_i++; } state = STATE_HILB; + [[fallthrough]]; case STATE_HILB: // If we are using hilbert function tracking: @@ -2451,6 +2457,7 @@ void gbA::do_computation() hilb_new_elems = false; } state = STATE_NEWDEGREE; + [[fallthrough]]; case STATE_NEWDEGREE: // Get the spairs and generators for the next degree @@ -2535,6 +2542,7 @@ void gbA::do_computation() } } state = STATE_AUTOREDUCE; + [[fallthrough]]; // or state = STATE_NEWPAIRS case STATE_AUTOREDUCE: diff --git a/M2/Macaulay2/e/gb-f4/GBF4Computation.cpp b/M2/Macaulay2/e/gb-f4/GBF4Computation.cpp index 3e016d1bf42..3c61d72a405 100644 --- a/M2/Macaulay2/e/gb-f4/GBF4Computation.cpp +++ b/M2/Macaulay2/e/gb-f4/GBF4Computation.cpp @@ -21,6 +21,7 @@ GBF4Computation::GBF4Computation( mMacaulayMatrix(), mVariableWeights(variableWeights) { + (void) strategy; } void GBF4Computation::initializeWithMatrix(const Matrix* M) diff --git a/M2/Macaulay2/e/gb-f4/GBF4Interface.cpp b/M2/Macaulay2/e/gb-f4/GBF4Interface.cpp index 2cf0ee6fbeb..7d1032b2109 100644 --- a/M2/Macaulay2/e/gb-f4/GBF4Interface.cpp +++ b/M2/Macaulay2/e/gb-f4/GBF4Interface.cpp @@ -13,6 +13,7 @@ auto createGBF4Interface(const Matrix *inputMatrix, int numThreads ) -> GBComputation* { + (void) strategy; return newf4::createGBF4Interface(inputMatrix, variableWeights, newf4::Strategy::Normal, numThreads); } @@ -51,6 +52,7 @@ GBF4Interface::GBF4Interface(const PolynomialRing* originalRing, variableWeights, strategy)) { + (void) numThreads; mComputation->initializeWithMatrix(inputMatrix); mComputation->dumpBasisMonomials(); mComputation->showInput(); @@ -71,6 +73,7 @@ GBF4Interface::GBF4Interface(const PolynomialRing* originalRing, variableWeights, strategy)) { + (void) numThreads; mComputation->initializeWithBasicPolyList(basicPolyList); } diff --git a/M2/Macaulay2/e/gb-f4/GBF4Interface.hpp b/M2/Macaulay2/e/gb-f4/GBF4Interface.hpp index be55b99a304..149c461284c 100644 --- a/M2/Macaulay2/e/gb-f4/GBF4Interface.hpp +++ b/M2/Macaulay2/e/gb-f4/GBF4Interface.hpp @@ -69,7 +69,11 @@ class GBF4Interface : public GBComputation const PolynomialRing *get_ring() const override { return mOriginalRing; } - Computation /* or null */ *set_hilbert_function(const RingElement *h) override { return nullptr; } + Computation /* or null */ *set_hilbert_function(const RingElement *h) override + { + (void) h; + return nullptr; + } const Matrix /* or null */ *get_gb() override { return nullptr; } @@ -79,17 +83,37 @@ class GBF4Interface : public GBComputation const Matrix /* or null */ *get_syzygies() override { return nullptr; } - const Matrix /* or null */ *get_initial(int nparts) override { return nullptr; } + const Matrix /* or null */ *get_initial(int nparts) override { + (void) nparts; + return nullptr; + } - const Matrix /* or null */ *matrix_remainder(const Matrix *m) override { return nullptr; } + const Matrix /* or null */ *matrix_remainder(const Matrix *m) override + { + (void) m; + return nullptr; + } M2_bool matrix_lift(const Matrix *m, const Matrix /* or null */ **result_remainder, - const Matrix /* or null */ **result_quotient) override { return false; } - - int contains(const Matrix *m) override { return 0; } - - void text_out(buffer &o) const override { } + const Matrix /* or null */ **result_quotient) override + { + (void) m; + (void) result_remainder; + (void) result_quotient; + return false; + } + + int contains(const Matrix *m) override + { + (void) m; + return 0; + } + + void text_out(buffer &o) const override + { + (void) o; + } /* This displays statistical information, and depends on the M2_gbTrace value */ diff --git a/M2/Macaulay2/e/gb-f4/MonomialLookupTable.hpp b/M2/Macaulay2/e/gb-f4/MonomialLookupTable.hpp index 0caf7725a33..10f1b317e73 100644 --- a/M2/Macaulay2/e/gb-f4/MonomialLookupTable.hpp +++ b/M2/Macaulay2/e/gb-f4/MonomialLookupTable.hpp @@ -60,6 +60,7 @@ class MonomialLookupTable //TODO: to be added: mTable(table), mIter(iter) { + (void) table; } public: diff --git a/M2/Macaulay2/e/gb-f4/PolynomialList.cpp b/M2/Macaulay2/e/gb-f4/PolynomialList.cpp index 389244c50b4..1221512b6ad 100644 --- a/M2/Macaulay2/e/gb-f4/PolynomialList.cpp +++ b/M2/Macaulay2/e/gb-f4/PolynomialList.cpp @@ -6,6 +6,7 @@ namespace newf4 { void PolynomialListStreamCollector::idealBegin(size_t polyCount) { + (void) polyCount; mCurrentPoly = -1; } diff --git a/M2/Macaulay2/e/gb-homog2.cpp b/M2/Macaulay2/e/gb-homog2.cpp index 9621a86f980..1791fa708e2 100644 --- a/M2/Macaulay2/e/gb-homog2.cpp +++ b/M2/Macaulay2/e/gb-homog2.cpp @@ -117,6 +117,8 @@ GB_comp *GB_comp::create(const Matrix *m, M2_bool use_max_degree_limit, int max_degree_limit) { + (void) use_max_degree_limit; + (void) max_degree_limit; GB_comp *result = new GB_comp; result->initialize(m, collect_syz, n_rows_to_keep, gb_weights, strategy); return result; diff --git a/M2/Macaulay2/e/gb-sugarless.cpp b/M2/Macaulay2/e/gb-sugarless.cpp index 3407a09dbc5..20cfe17d684 100644 --- a/M2/Macaulay2/e/gb-sugarless.cpp +++ b/M2/Macaulay2/e/gb-sugarless.cpp @@ -110,6 +110,8 @@ GBinhom_comp *GBinhom_comp::create(const Matrix *m, M2_bool use_max_degree_limit, int max_degree_limit) { + (void) use_max_degree_limit; + (void) max_degree_limit; const PolynomialRing *P = m->get_ring()->cast_to_PolynomialRing(); if (P == nullptr || P->getCoefficients()->is_ZZ()) { @@ -889,6 +891,7 @@ void GBinhom_comp::text_out(buffer &o) const /* This displays statistical information, and depends on the M2_gbTrace value */ { + (void) o; stats(); } diff --git a/M2/Macaulay2/e/gb-toric.cpp b/M2/Macaulay2/e/gb-toric.cpp index 066d0d20b0f..932a344961c 100644 --- a/M2/Macaulay2/e/gb-toric.cpp +++ b/M2/Macaulay2/e/gb-toric.cpp @@ -665,6 +665,7 @@ binomialGB::binomialGB(const binomial_ring *R0, bool bigcell, bool homogprime) // use_bigcell(bigcell), is_homogeneous_prime(homogprime) { + (void) bigcell; } binomialGB::~binomialGB() @@ -997,6 +998,9 @@ binomialGB_comp *binomialGB_comp::create(const Matrix *m, M2_bool use_max_degree_limit, int max_degree_limit) { + (void) gb_weights; + (void) use_max_degree_limit; + (void) max_degree_limit; if (collect_syz || n_rows_to_keep > 0) { ERROR("Groebner basis Algorithm=>Toric cannot keep syzygies"); @@ -1210,6 +1214,7 @@ Matrix *binomialGB_comp::subringGB() Matrix *binomialGB_comp::reduce(const Matrix *m, Matrix *& /*lift*/) { + (void) m; ERROR("MES: not implemented yet"); return nullptr; } @@ -1249,6 +1254,7 @@ const Matrix *binomialGB_comp::get_syzygies() { return nullptr; } const Matrix /* or null */ *binomialGB_comp::matrix_remainder(const Matrix *m) // likely not planned to be implemented { + (void) m; return nullptr; } M2_bool binomialGB_comp::matrix_lift( @@ -1257,6 +1263,7 @@ M2_bool binomialGB_comp::matrix_lift( const Matrix /* or null */ **result_quotient) // not planned to be implemented { + (void) m; *result_remainder = nullptr; *result_quotient = nullptr; ERROR("rawGBMatrixLift not implemented for toric GB's"); diff --git a/M2/Macaulay2/e/gb-toric.hpp b/M2/Macaulay2/e/gb-toric.hpp index 51c86051ca7..d15efa73565 100644 --- a/M2/Macaulay2/e/gb-toric.hpp +++ b/M2/Macaulay2/e/gb-toric.hpp @@ -369,7 +369,11 @@ class binomialGB_comp : public GBComputation virtual const Matrix /* or null */ *get_initial(int nparts); - virtual void text_out(buffer &o) const { /* to do */} + virtual void text_out(buffer &o) const + { + (void) o; + /* to do */ + } /* This displays statistical information, and depends on the M2_gbTrace value */ diff --git a/M2/Macaulay2/e/gb-walk.cpp b/M2/Macaulay2/e/gb-walk.cpp index 25696952039..f2b36df4a17 100644 --- a/M2/Macaulay2/e/gb-walk.cpp +++ b/M2/Macaulay2/e/gb-walk.cpp @@ -61,6 +61,8 @@ GBWalker::GBWalker(MarkedGB *G0, long **order1, long **order2) GBWalker::GBWalker(const Matrix *gb_under_order1, const MonomialOrdering *order1) { + (void) gb_under_order1; + (void) order1; } GBWalker *GBWalker::create(const Matrix *gb_under_order1, @@ -167,6 +169,7 @@ void GBWalker::start_computation() } next_to_reduce = 0; state = STATE_reduce; + [[fallthrough]]; case STATE_reduce: while (next_to_reduce < 0) // TODO: consider the top of the loop { @@ -175,6 +178,7 @@ void GBWalker::start_computation() next_to_reduce++; } state = STATE_autoreduce; + break; case STATE_autoreduce: G->remove_gb(); delete G; @@ -195,6 +199,7 @@ const PolynomialRing *GBWalker::get_ring() const Computation /* or null */ *GBWalker::set_hilbert_function(const RingElement *h) { + (void) h; // MES: TO WRITE return nullptr; } @@ -225,18 +230,21 @@ const Matrix /* or null */ *GBWalker::get_syzygies() const Matrix /* or null */ *GBWalker::get_initial(int nparts) { + (void) nparts; // MES: TO WRITE return nullptr; } const Matrix /* or null */ *GBWalker::get_parallel_lead_terms(M2_arrayint w) { + (void) w; // MES: TO WRITE return nullptr; } const Matrix /* or null */ *GBWalker::matrix_remainder(const Matrix *m) { + (void) m; // MES: TO WRITE return nullptr; } @@ -245,6 +253,7 @@ M2_bool GBWalker::matrix_lift(const Matrix *m, const Matrix /* or null */ **result_remainder, const Matrix /* or null */ **result_quotient) { + (void) m; // MES: TO WRITE, should this be written? *result_remainder = nullptr; *result_quotient = nullptr; @@ -254,6 +263,7 @@ M2_bool GBWalker::matrix_lift(const Matrix *m, int GBWalker::contains(const Matrix *m) { + (void) m; // MES: TO WRITE return -1; } @@ -262,6 +272,7 @@ void GBWalker::text_out(buffer &o) const /* This displays statistical information, and depends on the M2_gbTrace value */ { + (void) o; // MES: TO WRITE } diff --git a/M2/Macaulay2/e/gbring.cpp b/M2/Macaulay2/e/gbring.cpp index d3bc1102abc..e99fd06d8ba 100644 --- a/M2/Macaulay2/e/gbring.cpp +++ b/M2/Macaulay2/e/gbring.cpp @@ -101,6 +101,7 @@ gbvector *GBRingPoly::mult_by_term1(const FreeModule *F, const int *monom, int comp) { + (void) F; // Remember: this multiplies w/o regard to any quotient elements gbvector head; gbvector *inresult = &head; @@ -259,6 +260,11 @@ gbvector *GBRingSolvable::mult_by_term1(const FreeModule *F, const int *monom, int comp) { + (void) F; + (void) f; + (void) u; + (void) monom; + (void) comp; // Remember: this multiplies w/o regard to any quotient elements #ifdef DEVELOPMENT #warning "implement GBRingSolvable::mult_by_term" @@ -439,6 +445,7 @@ gbvector *GBRing::gbvector_lead_term(int nparts, // If nparts < 0, only take the actual lead term (i.e. one monomial // in one component only). { + (void) F; #ifdef DEVELOPMENT #warning "Schreyer order question" #endif @@ -490,6 +497,7 @@ gbvector *GBRing::gbvector_parallel_lead_terms(M2_arrayint w, const gbvector *leadv, const gbvector *v) { + (void) F; if (v == nullptr) return nullptr; // loop through every term of v. Keep those t that satisfy: exp(leadv) - @@ -773,6 +781,7 @@ void GBRing::gbvector_text_out(buffer &o, const gbvector *v, int nterms) const { + (void) F; if (v == nullptr) { o << "0"; @@ -1646,6 +1655,7 @@ void GBRing::reduce_marked_lead_term_heap( monomial MONOM1 = ALLOCATE_MONOMIAL(monom_size); + (void) exp; find_reduction_coeffs(F, fcurrent_lead, marked_in_g, u, v); find_reduction_monomial(F, fcurrent_lead, marked_in_g, comp, MONOM1); diff --git a/M2/Macaulay2/e/hermite.cpp b/M2/Macaulay2/e/hermite.cpp index a79ffa2ed65..c023cbe8ae4 100644 --- a/M2/Macaulay2/e/hermite.cpp +++ b/M2/Macaulay2/e/hermite.cpp @@ -168,6 +168,7 @@ hm_elem *HermiteComputation::merge(hm_elem *f, hm_elem *g) return head.next; } // Now fall through to merge f into the result: + [[fallthrough]]; case -1: result->next = f; result = result->next; @@ -345,6 +346,7 @@ const Matrix /* or null */ *HermiteComputation::get_syzygies() const Matrix /* or null */ *HermiteComputation::get_initial(int nparts) { + (void) nparts; MatrixConstructor mat(gens->rows(), 0); for (hm_elem *p = GB_list; p != nullptr; p = p->next) { diff --git a/M2/Macaulay2/e/hilb.cpp b/M2/Macaulay2/e/hilb.cpp index 5245a273a61..5a907083764 100644 --- a/M2/Macaulay2/e/hilb.cpp +++ b/M2/Macaulay2/e/hilb.cpp @@ -12,7 +12,7 @@ #include "ExponentList.hpp" // for index_varpower, varpower, const_v... #include "ExponentVector.hpp" // for exponents -#include "M2mem.h" // for freemem +#include "interface/m2-mem.h" // for freemem #include "buffer.hpp" // for buffer #include "error.h" // for ERROR #include "freemod.hpp" // for FreeModule @@ -350,7 +350,7 @@ hilb_comp::hilb_comp(const PolynomialRing *RR, const Matrix *m) this_comp(0), n_components(m->n_rows()), current(nullptr), - part_table(S->n_vars(), mi_stash) + part_table(std::max(1, S->n_vars()), mi_stash) { assert(D == R->getMonoid()); one = R->getCoefficientRing()->from_long(1); diff --git a/M2/Macaulay2/e/imonorder.cpp b/M2/Macaulay2/e/imonorder.cpp index cbc82ec67cd..84f64686a25 100644 --- a/M2/Macaulay2/e/imonorder.cpp +++ b/M2/Macaulay2/e/imonorder.cpp @@ -341,7 +341,7 @@ MonomialOrder *monomialOrderMake(const MonomialOrdering *mo) return result; } -extern void monomialOrderFree(MonomialOrder *mo) {} +extern void monomialOrderFree(MonomialOrder *mo) { (void) mo; } static void MO_pack4(int nvars, const int *expon, int *slots) { int32_t i; diff --git a/M2/Macaulay2/e/interface/freemodule.h b/M2/Macaulay2/e/interface/freemodule.h index 039f7bc535a..27d23bb093f 100644 --- a/M2/Macaulay2/e/interface/freemodule.h +++ b/M2/Macaulay2/e/interface/freemodule.h @@ -97,7 +97,7 @@ const FreeModule /* or null */ *IM2_FreeModule_tensor(const FreeModule *F, const FreeModule *G); /* drg: connected rawTensor*/ /* The tensor product of two free modules over the same ring, or NULL. - * If F has (ordered basis {f_1,...,f_r}, and + * If F has (ordered) basis {f_1,...,f_r}, and * G has (ordered) basis {g_1, ..., g_s}, then * the result has (ordered) basis * {f_1 ** g_1, f_1 ** g_2, ..., f_1 ** g_s, diff --git a/M2/Macaulay2/e/interface/gmp-util.h b/M2/Macaulay2/e/interface/gmp-util.h index 2d535e1e714..289290aff03 100644 --- a/M2/Macaulay2/e/interface/gmp-util.h +++ b/M2/Macaulay2/e/interface/gmp-util.h @@ -63,6 +63,19 @@ inline void mpfi_reallocate_limbs (mpfi_ptr _z) // typedef CCmutable_struct* gmp_CCmutable; // typedef CC_struct* gmp_CC; +typedef struct { + mpfi_srcptr re; + mpfi_srcptr im; +} CCi_struct; + +typedef struct { + mpfi_ptr re; + mpfi_ptr im; +} CCimutable_struct; + +// typedef CCimutable_struct* gmp_CCimutable; +// typedef CCi_struct* gmp_CCi; + inline mpfr_srcptr moveTo_gmpRR (mpfr_ptr _z) { mpfr_reallocate_limbs(_z); @@ -84,6 +97,16 @@ inline void mpfi_reallocate_limbs (mpfi_ptr _z) return (gmp_CC) a; } +inline gmp_CCi moveTo_gmpCCi (gmp_CCimutable _z) +{ + CCimutable_struct* a = (CCimutable_struct*) _z; + mpfr_reallocate_limbs(&(a->re->left)); + mpfr_reallocate_limbs(&(a->re->right)); + mpfr_reallocate_limbs(&(a->im->left)); + mpfr_reallocate_limbs(&(a->im->right)); + return (gmp_CCi) a; +} + # if defined(__cplusplus) } # endif diff --git a/M2/Macaulay2/e/interface/groebner.cpp b/M2/Macaulay2/e/interface/groebner.cpp index 27f1c28dc81..1ce4d27f610 100644 --- a/M2/Macaulay2/e/interface/groebner.cpp +++ b/M2/Macaulay2/e/interface/groebner.cpp @@ -580,6 +580,9 @@ int IM2_Resolution_status(Computation *C, int *complete_up_through_this_degree, int *complete_up_through_this_level) { + (void) C; + (void) complete_up_through_this_degree; + (void) complete_up_through_this_level; #ifdef DEVELOPMENT #warning "IM2_Resolution_status to be written" #endif @@ -593,6 +596,10 @@ enum ComputationStatusCode IM2_Resolution_status_level( M2_bool minimize, int *complete_up_through_this_degree) { + (void) C; + (void) level; + (void) minimize; + (void) complete_up_through_this_degree; #ifdef DEVELOPMENT #warning "IM2_Resolution_status to be written" #endif @@ -888,6 +895,7 @@ ConstPolyList matrixToPolyList(const M2FreeAlgebraOrQuotient* A, const Matrix* input) { ConstPolyList result; + (void) A; result.reserve(input->n_cols() * input->n_rows()); for (int i=0; i < input->n_rows(); i++) { diff --git a/M2/Macaulay2/e/interface/m2-mem.cpp b/M2/Macaulay2/e/interface/m2-mem.cpp new file mode 100644 index 00000000000..ffef9b77641 --- /dev/null +++ b/M2/Macaulay2/e/interface/m2-mem.cpp @@ -0,0 +1,573 @@ +#include +#include +#include +#include + +#include "m2-mem.h" + +/* #ifdef MEMDEBUG */ +/* #include */ +/* #endif */ + +#ifndef NDEBUG +#define __thread +static __thread bool in_getmem = false; +static inline void enter_getmem() { + #if 0 + /* this is not always an error, because we may call GC_malloc from a finalizer */ + if (in_getmem) fatal("internal error: getmem called while getmem active"); + #endif + in_getmem = true; +} +static inline void exit_getmem() { + in_getmem = false; +} +#else +static inline void enter_getmem() {} +static inline void exit_getmem() {} +#endif + +/* trapchk: taken from d/debug.h *************************************/ + +void *trapaddr = (void *)1; +int trapcount = 0; +int trapset = 0; +size_t trapsize = (size_t)-1; + +void trap(void) {} /* I used to be concerned that this function would get optimized away, but it isn't static ... */ + +void *pointers[10]; /* during debugging we can put pointers here, visible to the garbage collector */ +void trapchk(void *p) { + trapcount++; + if (trapcount == trapset || p == trapaddr || p == (void *)~(intptr_t)trapaddr) trap(); +} +void trapchk_size(size_t n) { + trapcount++; + if (trapcount == trapset || trapsize == n) trap(); +} + +/*********************************************************************/ + +// taken from d/types.h +const int STDIN = 0; +const int STDOUT = 1; +const int STDERR = 2; +const int ERROR = -1; + +void outofmem(void) { + const char *msg = "\n\n *** out of memory, exiting ***\n"; + int r = write(STDERR,msg,strlen(msg)); + if (r == ERROR) exit(1); + exit(1); +} + +void outofmem2(size_t newsize) { + const char *msg = "\n\n *** out of memory trying to allocate %ld bytes, exiting ***\n"; + static char buf[sizeof(msg) + 100]; + snprintf(buf, sizeof(msg) + 100, + msg, (long)newsize); + int r = write(STDERR,buf,strlen(buf)); + if (r == ERROR) exit(1); + exit(1); +} + +char *getmem(size_t n) +{ + char *p; + TRAPCHK_SIZE(n); + enter_getmem(); +#ifdef MEMDEBUG + p = M2_debug_malloc(n); +#else + p = reinterpret_cast(GC_MALLOC(n)); /* GC_MALLOC clears its memory; we preserve that */ +#endif + if (p == NULL) outofmem2(n); +#ifndef NDEBUG + memset(p,0xbe,n); /* fill with 0xbebebebe ... */ + trapchk(p); +#endif + exit_getmem(); + return p; +} + +void freememlen(void *s, size_t old) { + (void)old; + TRAPCHK(s); +#ifdef MEMDEBUG + M2_debug_free(s); +#else + /* GC_FREE(s); */ +#endif +} + +void freemem(void *s) { + TRAPCHK(s); +#ifdef MEMDEBUG + M2_debug_free(s); +#else + /* GC_FREE(s); */ +#endif +} + +char *getmem_clear(size_t n) +{ + char *p; + enter_getmem(); +#ifdef MEMDEBUG + p = M2_debug_malloc(n); +#else + p = reinterpret_cast(GC_MALLOC(n)); +#endif + if (p == NULL) outofmem2(n); +#ifdef MEMDEBUG + memset(p,0,n); +#else + /* note: GC_MALLOC clears memory before returning. + If you switch to another memory allocator, you must clear it explicitly here */ +#endif + #ifndef NDEBUG + trapchk(p); + #endif + exit_getmem(); + return p; +} + +char *getmem_atomic(size_t n) +{ + char *p; + enter_getmem(); +#ifdef MEMDEBUG + p = M2_debug_malloc_atomic(n); +#else + p = reinterpret_cast(GC_MALLOC_ATOMIC(n)); +#endif + if (p == NULL) outofmem2(n); +#ifndef NDEBUG + memset(p,0xac,n); /* fill with 0xacacacac ... */ + trapchk(p); +#endif + exit_getmem(); + return p; +} + +// char *getmem_malloc(size_t n) +// { +// char *p; +// enter_getmem(); +// p = malloc(n); +// if (p == NULL) outofmem2(n); +// #ifndef NDEBUG +// memset(p,0xca,n); /* fill with 0xcacacaca */ +// trapchk(p); +// #endif +// exit_getmem(); +// return p; +// } + +char *getmem_atomic_clear(size_t n) +{ + char *p; + enter_getmem(); +#ifdef MEMDEBUG + p = M2_debug_malloc_atomic(n); +#else + p = reinterpret_cast(GC_MALLOC_ATOMIC(n)); +#endif + if (p == NULL) outofmem2(n); + memset(p,0,n); /* GC_MALLOC_ATOMIC does not clear memory */ +#ifndef NDEBUG + trapchk(p); +#endif + exit_getmem(); + return p; +} + +// char *getmoremem (char *s, size_t old, size_t newsize) { +// void *p; +// enter_getmem(); +// p = reinterpret_cast(GC_REALLOC(s,newsize)); +// if (p == NULL) outofmem2(newsize); +// # ifndef NDEBUG +// trapchk(p); +// # endif +// exit_getmem(); +// return reinterpret_cast(p); +// } + +// char *getmoremem1 (char *s, size_t newsize) { +// void *p; +// enter_getmem(); +// p = reinterpret_cast(GC_REALLOC(s,newsize)); +// if (p == NULL) outofmem2(newsize); +// # ifndef NDEBUG +// trapchk(p); +// # endif +// exit_getmem(); +// return reinterpret_cast(p); +// } + +// char *getmoremem_atomic (char *s, size_t old, size_t newsize) { +// void *p; +// enter_getmem(); +// #ifdef MEMDEBUG +// p = M2_debug_malloc_atomic(newsize); +// #else +// p = reinterpret_cast(GC_MALLOC_ATOMIC(newsize)); +// #endif +// size_t min = old 0) memset((char *)p+min,0xbe,excess); /* fill with 0xbebebebe */ +// } +// trapchk(p); +// # endif +// exit_getmem(); +// return reinterpret_cast(p); +// } + +/* Valgrind helper functions */ +#ifndef NVALGRIND + +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc)(size_t s){ + long result; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(result,fn,s); + VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); + return (void*)result; +} + +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_atomic)(size_t s){ + long result; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(result,fn,s); + VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); + return (void*)result; +} + +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_ignore_off_page)(size_t s){ + long result; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(result,fn,s); + VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); + return (void*)result; +} + +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_atomic_ignore_off_page)(size_t s){ + long result; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(result,fn,s); + VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); + return (void*)result; +} + +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_realloc)(void *p, size_t s){ + long result; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_WW(result,fn,(long)p,s); + VALGRIND_MAKE_MEM_DEFINED(&result,sizeof(result)); + return (void*)result; +} + +#endif /* NVALGRIND */ + +#ifdef MEMDEBUG + +/* note: the debugging facilities in this file partially conflict with + the debugging facilities gc.h provides, since when DEBUG is + defined, it records the location of the calls to GC_malloc. In + fact, we bypass their debugging entirely, by calling the functions + instead of the macros. +*/ + +/* + + Here is how you might use this. (Turn it on by configuring with --enable-memdebug.) + + (gdb) run + Starting program: /home/dan/local/src/M2/tmp/Macaulay2/bin/M2 -q --no-loaddata + [New Thread 16384 (LWP 5498)] + [Switching to Thread 16384 (LWP 5498)] + + Breakpoint 1, trap () at ../../../Macaulay2/d/debug.c:7 + + We always set a breakpoint in trap(). + + I want to look at the millionth memory allocation: + + (gdb) set trapset=1000000 + + (gdb) c + Continuing. + Macaulay 2, version 0.9.5 + --package Main installed + + Breakpoint 1, trap () at ../../../Macaulay2/d/debug.c:7 + (gdb) up + #1 0x0804d7d9 in trapchk (p=0x998cfd0) at ../../../Macaulay2/d/debug.c:11 + (gdb) up + #2 0x08050b88 in M2_debug_malloc_atomic (size=19) + at ../../../Macaulay2/d/memdebug.c:110 + (gdb) up + #3 0x0804df20 in getmem_atomic (n=19) at ../../../Macaulay2/d/M2mem.c:34 + (gdb) up + #4 0x0804dd86 in strings_join (x=0x83dde80, y=0x9cf54e0) + at ../../../Macaulay2/d/M2types.c:119 + (gdb) up + #5 0x08118521 in strings_plus_ (s_1=0x83dde80, t=0x9cf54e0) at strings.d:21 + (gdb) up + #6 0x0809ff29 in presentfun (e_33={type_ = 34, ptr_ = 0x9cf54e0}) + at actors4.d:820 + (gdb) up + #7 0x080d0441 in evaluate_apply_4 (f_12={type_ = 5, ptr_ = 0x83e08b0}, e_1= + {type_ = 34, ptr_ = 0x9cf54e0}) at evaluate.d:585 + + Let's say I'm suddenly interested in this pointer, f_12; + + I have a routine that will tell me the size of the memory area: + + (gdb) p M2_debug_size(f_12.ptr_) + $2 = 8 + + and a variable that tells how bytes are appended in front + + (gdb) p front + $3 = 16 + + and in the rear: + + (gdb) p rear + $4 = 16 + + So now look at memory, including the two fences: + + (gdb) x/10x f_12.ptr_-front + 0x83e08a0: 0x00001193 0x00000008 0xaaaaaaaa 0xaaaaaaaa + 0x83e08b0: 0x0809fed0 0x000f43e5 0xcccccccc 0xcccccccc + 0x83e08c0: 0x00000008 0x00001193 + + The words 0xaaaaaaaa are the (intact) fence words in front, and the + words 0xcccccccc are the fence words behind, while the memory is + active. They get changed to 0xa0a0a0a0 and to 0xc0c0c0c0 when the + memory is freed. + + The data bytes themselves are initialized to 0xbbbbbbbb upon + allocation, and changed to 0xb0b0b0b0 when the memory is freed. + + The two copies of 0x00001193 are the sequence number, and the two + copies of 0x00000008 are the size. They should agree. + +*/ + +#include +#include +#include +#include +#include +//#define MEMDEBUG_INTERNAL + +#define FREE_DELAY 10 +#define FENCE_INTS 2 +#define FRONT_FENCE 0xaaaaaaaa +#define FRONT_FENCE_GONE 0xa0a0a0a0 +#define BODY_PART 0xbbbbbbbb +#define BODY_PART_GONE 0xb0b0b0b0 +#define REAR_FENCE 0xcccccccc +#define REAR_FENCE_GONE 0xc0c0c0c0 + +struct FRONT { + int trapcount; + size_t size; + unsigned int fence[FENCE_INTS]; + }; + +struct REAR { + unsigned int fence[FENCE_INTS]; + size_t size; + int trapcount; + }; + + + + +void *delay_chain[FREE_DELAY]; +int delay_chain_index; + +size_t M2_debug_size(void *p) { + struct FRONT *f; + if (p == NULL) return 0; + f = (struct FRONT *)(p - sizeof(struct FRONT)); + return f->size; +} + +void *M2_debug_malloc(size_t size) { + struct FRONT *f; + char *p; + struct REAR *r; + int i; + int INTS_BODY = (size + sizeof(int) - 1)/sizeof(int); + f = (struct FRONT *)GC_MALLOC( sizeof(struct FRONT) + sizeof(int)*INTS_BODY + sizeof(struct REAR) ); + if (f == NULL) outofmem2(size); + p = (char *)f + sizeof(struct FRONT); + r = (struct REAR *)(p + sizeof(int)*INTS_BODY); + f->size = r->size = size; + for (i=0; ifence[i] = FRONT_FENCE; + for (i=0; ifence[i] = REAR_FENCE; + f->trapcount = r->trapcount = trapcount+1; + trapchk(p); /* trapchk increments trapcount before possibly calling trap() -- set your breakpoint in trap() */ + return p; + } + +void* M2_debug_malloc_uncollectable(size_t size) { + struct FRONT *f; + char *p; + struct REAR *r; + int i; + int INTS_BODY = (size + sizeof(int) - 1)/sizeof(int); + f = (struct FRONT *)GC_MALLOC_UNCOLLECTABLE( + sizeof(struct FRONT) + sizeof(int)*INTS_BODY + sizeof(struct REAR) + ); + if (f == NULL) outofmem2(size); + p = (char *)f + sizeof(struct FRONT); + r = (struct REAR *)(p + sizeof(int)*INTS_BODY); + f->size = r->size = size; + for (i=0; ifence[i] = FRONT_FENCE; + for (i=0; ifence[i] = REAR_FENCE; + f->trapcount = r->trapcount = trapcount+1; + trapchk(p); + return p; + } + +void* M2_debug_malloc_atomic(size_t size) { + struct FRONT *f; + char *p; + struct REAR *r; + int i; + int INTS_BODY = (size + sizeof(int) - 1)/sizeof(int); + f = (struct FRONT *)GC_MALLOC_ATOMIC( sizeof(struct FRONT) + sizeof(int)*INTS_BODY + sizeof(struct REAR) ); + if (f == NULL) outofmem2(size); + p = (void *)f + sizeof(struct FRONT); + r = (struct REAR *)(p + sizeof(int)*INTS_BODY); + f->size = r->size = size; + for (i=0; ifence[i] = FRONT_FENCE; + for (i=0; ifence[i] = REAR_FENCE; + f->trapcount = r->trapcount = trapcount+1; + trapchk(p); + return p; + } + +void* M2_debug_malloc_atomic_uncollectable(size_t size) { + struct FRONT *f; + char *p; + struct REAR *r; + int i; + int INTS_BODY = (size + sizeof(int) - 1)/sizeof(int); + f = (struct FRONT *)GC_MALLOC_ATOMIC_UNCOLLECTABLE( sizeof(struct FRONT) + sizeof(int)*INTS_BODY + sizeof(struct REAR) ); + if (f == NULL) outofmem2(size); + p = (void *)f + sizeof(struct FRONT); + r = (struct REAR *)(p + sizeof(int)*INTS_BODY); + f->size = r->size = size; + for (i=0; ifence[i] = FRONT_FENCE; + for (i=0; ifence[i] = REAR_FENCE; + f->trapcount = r->trapcount = trapcount+1; + trapchk(p); + return p; + } + +static void volatile smashed(void *p) { + if (0 == GC_base(p)) { + fprintf(stderr,"-- *** memdebug -- non-heap object encountered, %p, aborting\n",p); + } + else { + fprintf(stderr,"-- *** memdebug -- smashed object found, %p, aborting\n",p); + } + trap(); + abort(); + } + +void *M2_debug_to_outer(void *p) { + struct FRONT *f = p - sizeof(struct FRONT); + if (f->fence[0] != FRONT_FENCE) smashed(p); + return f; + } + + +void *M2_debug_to_inner(void *q) { + struct FRONT *f = q; + if (f->fence[0] != FRONT_FENCE) smashed(q); + return (void *)f + sizeof(struct FRONT); + } + +void M2_debug_info(void *p) { + struct FRONT *f; + struct REAR *r; + int INTS_BODY, i, smashed = 0; + size_t size; + if (p == NULL) return; + f = p - sizeof(struct FRONT); + size = f->size; + INTS_BODY = (size + sizeof(int) - 1)/sizeof(int); + r = (struct REAR *)(p + sizeof(int)*INTS_BODY); + for (i=0; ifence[i] != FRONT_FENCE) smashed = 1; + for (i=0; ifence[i] != REAR_FENCE ) smashed = 1; + fprintf(stderr,"addr %p, base %p, GC_base %p, trapcount %d, length %lu%s\n",p,f,GC_base(p),f->trapcount,size,smashed ? ", smashed" : ""); + } + +void M2_debug_free(void *p) { + struct FRONT *f; + struct REAR *r; + int INTS_BODY, i, _trapcount; + size_t size; + if (p == NULL) return; + f = (struct FRONT *)(p - sizeof(struct FRONT)); + size = f->size; + INTS_BODY = (size + sizeof(int) - 1)/sizeof(int); + r = p + sizeof(int)*INTS_BODY; + _trapcount = f->trapcount; + if (r->trapcount != _trapcount || r->size != size) smashed(p); + for (i=0; ifence[i] != FRONT_FENCE) smashed(p); + for (i=0; ifence[i] != REAR_FENCE ) smashed(p); + if (_trapcount == trapset) trap(); + trapchk(p); + for (i=0; ifence[i] = FRONT_FENCE_GONE; + for (i=0; ifence[i] = REAR_FENCE_GONE; +#if FREE_DELAY != 0 + if (delay_chain[delay_chain_index] != NULL) { + GC_FREE(delay_chain[delay_chain_index]); + } + delay_chain[delay_chain_index] = (void *)f; + delay_chain_index ++; + if (delay_chain_index == FREE_DELAY) delay_chain_index = 0; +#else + GC_FREE(f); +#endif + } + +void* M2_debug_realloc(void *old, size_t size) { + void *newsize = M2_debug_malloc(size); + size_t oldsize = M2_debug_size(old); + if (newsize == NULL) outofmem2(size); + memcpy(newsize,old,size < oldsize ? size : oldsize); + return newsize; + } + +#endif /* MEMDEBUG */ + +/* + Local Variables: + indent-tabs-mode: nil + End: +*/ diff --git a/M2/Macaulay2/e/interface/m2-mem.h b/M2/Macaulay2/e/interface/m2-mem.h new file mode 100644 index 00000000000..f3cd653c39d --- /dev/null +++ b/M2/Macaulay2/e/interface/m2-mem.h @@ -0,0 +1,123 @@ +#ifndef m2_mem_included +#define m2_mem_included + +#include + +// d/debug.h +#ifndef NDEBUG + +#if defined(__cplusplus) +extern "C" { +#endif + + extern void trap(void); + extern void *trapaddr; + extern int trapcount; + extern int trapset; + extern void trapchk(void *); + extern void trapchk_size(size_t); + extern int badBlock(); + + static __attribute__ ((unused)) void debug_version() {} + +#if defined(__cplusplus) +} +#endif + +#define TRAPCHK(p) trapchk(p) +#define TRAPCHK_SIZE(n) trapchk_size(n) + +#else + +#define TRAPCHK(p) (void)p +#define TRAPCHK_SIZE(n) + +#endif + +// d/M2mem.h + + +#ifdef NDEBUG +#define NVALGRIND 1 +#endif + +#include + + + + +#if defined(__cplusplus) +extern "C" { +#endif + +extern void outofmem2(size_t); +extern char *getmem(size_t); +extern void freemem(void *); +extern void freememlen(void *, size_t); +extern char *getmem_clear(size_t); +extern char *getmem_atomic(size_t); + //extern char *getmem_malloc(size_t); +extern char *getmem_atomic_clear(size_t); + //extern char *getmoremem(char *, size_t oldsize, size_t newsize); + //extern char *getmoremem1(char *, size_t newsize); + //extern char *getmoremem_atomic(char *, size_t oldsize, size_t newsize); + +/* Valgrind helper functions, + * NVALGRIND can be defined to explicitly disable this + * But also valgrind.h (included by memcheck.h) will define NVALGRIND + * if we are on a platform it doesn't understand + */ +#ifndef NVALGRIND +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc)(size_t); +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_atomic)(size_t); +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_ignore_off_page)(size_t); +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_malloc_atomic_ignore_off_page)(size_t); +void *I_WRAP_SONAME_FNNAME_ZU(libgcZdsoZd1,GC_realloc)(void*, size_t); +#endif /* NVALGRIND */ + +#ifdef MEMDEBUG + /* from d/memdebug.h */ + void M2_debug_free(void *); + void* M2_debug_malloc(size_t size); + void* M2_debug_malloc_atomic(size_t size); + void* M2_debug_malloc_atomic_uncollectable(size_t size); + void* M2_debug_realloc(void *,size_t size); + void* M2_debug_malloc_uncollectable(size_t size); + void *M2_debug_to_outer(void *p); + void *M2_debug_to_inner(void *f); +#endif + +#define sizeofarray(s,len) (sizeof(*(s)) + (len)*sizeof((s)->array[0])) +#define sizeofarraytype(S,len) sizeofarray((S)0,len) +#define sizeofstruct(s) sizeof(*(s)) +#define sizeofstructtype(S) sizeofstruct((S)0) + +#if defined(__cplusplus) +#define getmemarraytype(S,len) reinterpret_cast(getmem(sizeofarraytype(S,len))) +#define getmemstructtype(S) reinterpret_cast(getmem(sizeofstructtype(S))) +#define getmematomicarraytype(S,len) reinterpret_cast(getmem_atomic(sizeofarraytype(S,len))) +#define getmematomicstructtype(S) reinterpret_cast(getmem_atomic(sizeofstructtype(S))) +#define getmemvectortype(S,len) reinterpret_cast(getmem(sizeof(S)*len)) +#define getmematomicvectortype(S,len) reinterpret_cast(getmem_atomic(sizeof(S)*(len))) +#else +#define getmemarraytype(S,len) (S)(getmem(sizeofarraytype(S,len))) +#define getmemstructtype(S) (S)(getmem(sizeofstructtype(S))) +#define getmematomicarraytype(S,len) (S)(getmem_atomic(sizeofarraytype(S,len))) +#define getmematomicstructtype(S) (S)(getmem_atomic(sizeofstructtype(S))) +#define getmemvectortype(S,len) (S*)(getmem(sizeof(S)*len)) +#define getmematomicvectortype(S,len) (S*)(getmem_atomic(sizeof(S)*(len))) +#endif + + +#if defined(__cplusplus) +} +#endif + + +#endif + +/* + Local Variables: + indent-tabs-mode: nil + End: +*/ diff --git a/M2/Macaulay2/e/unit-tests/M2-replacement.c b/M2/Macaulay2/e/interface/m2-types.cpp similarity index 77% rename from M2/Macaulay2/e/unit-tests/M2-replacement.c rename to M2/Macaulay2/e/interface/m2-types.cpp index 6c612c9a661..6dd28fa6349 100644 --- a/M2/Macaulay2/e/unit-tests/M2-replacement.c +++ b/M2/Macaulay2/e/interface/m2-types.cpp @@ -1,10 +1,8 @@ -#include "engine-exports.h" -#include "M2mem-replacement.h" +#include "m2-types.h" +#include "m2-mem.h" #include #include -typedef struct RingElementrec *RingElement; - M2_arrayint M2_makearrayint(int n) { M2_arrayint z = (M2_arrayint)getmem_atomic(sizeofarray(z,n)); @@ -30,7 +28,7 @@ M2_string M2_join(M2_string x, M2_string y) //GC_CHECK_CLOBBER(p); return p; } -M2_string M2_tostring(M2_constcharstarOrNull s) +M2_string M2_tostring(const char* s) { int n = s ? strlen(s) : 0; M2_string p = getmematomicarraytype(M2_string,n); @@ -48,11 +46,6 @@ M2_string M2_tostringn(char *s, int n) return p; } -M2_string (*gmp_tonetCCparenpointer)(gmp_CC); -M2_string (*gmp_tonetCCpointer)(gmp_CC); -M2_string (*gmp_tostringRRpointer)(mpfr_srcptr); - - char newline[] = "\n"; int M2_numTBBThreads = 0; @@ -63,7 +56,6 @@ struct FUNCTION_CELL *thread_prepare_list; /* Local Variables: - compile-command: "make -C $M2BUILDDIR/Macaulay2/e/unit-tests check " indent-tabs-mode: nil End: */ diff --git a/M2/Macaulay2/e/interface/m2-types.h b/M2/Macaulay2/e/interface/m2-types.h new file mode 100644 index 00000000000..dd249307f9a --- /dev/null +++ b/M2/Macaulay2/e/interface/m2-types.h @@ -0,0 +1,185 @@ +#pragma once +// IWYU pragma: private, include "engine-includes.hpp" + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif +#include +#define hash_t uint64_t +#include + extern char newline[]; + +#ifdef HAVE_UNISTD_H + #include +#endif +#ifdef HAVE_MATH_H + #include +#endif +#ifdef HAVE_SYS_RESOURCE_H + #include +#endif + +#ifdef __cplusplus + #define BASECLASS : public our_new_delete + #include "newdelete.hpp" +#else + #define BASECLASS +#endif + + #include + +#if !defined(SAFEC_EXPORTS) +typedef char M2_bool; + +typedef struct M2_string_struct * M2_string; +struct M2_string_struct {int len;signed char array[];}; + +typedef struct M2_arrayint_struct * M2_arrayint; +typedef M2_arrayint M2_arrayintOrNull; +struct M2_arrayint_struct {int len;int array[];}; + +typedef struct M2_ArrayString_struct * M2_ArrayString; +typedef M2_ArrayString M2_ArrayStringOrNull; +struct M2_ArrayString_struct {int len;M2_string array[];}; + +struct MonomialOrdering; +struct Ring; +struct M2SLEvaluator; +struct M2SLProgram; +struct StraightLineProgram; +struct PathTracker; +struct M2PointArray; +struct MutableMatrix; +struct MutableComplex; +struct Matrix; +struct FreeModule; +struct Monoid; +struct MonomialIdeal; + +typedef mpz_ptr gmp_ZZmutable; +typedef mpz_srcptr gmp_ZZ; +typedef mpz_srcptr gmp_ZZorNull; + +typedef mpq_ptr gmp_QQmutable; +typedef mpq_srcptr gmp_QQ; +typedef mpq_srcptr gmp_QQorNull; + +typedef mpfr_srcptr gmp_RR; +typedef mpfr_srcptr gmp_RRorNull; +typedef mpfr_ptr gmp_RRmutable; + +typedef mpfi_ptr gmp_RRimutable; +typedef mpfi_srcptr gmp_RRi; +typedef mpfi_srcptr gmp_RRiorNull; + +typedef struct gmp_CC_struct * gmp_CC; +typedef gmp_CC gmp_CCorNull; +struct gmp_CC_struct BASECLASS {mpfr_srcptr re;mpfr_srcptr im;}; +typedef struct gmp_CCmutable_struct * gmp_CCmutable; +struct gmp_CCmutable_struct BASECLASS {mpfr_ptr re;mpfr_ptr im;}; + +typedef struct gmp_CCi_struct * gmp_CCi; +typedef gmp_CCi gmp_CCiorNull; +struct gmp_CCi_struct BASECLASS {mpfi_srcptr re;mpfi_srcptr im;}; +typedef struct gmp_CCimutable_struct * gmp_CCimutable; +struct gmp_CCimutable_struct BASECLASS {mpfi_ptr re;mpfi_ptr im;}; + +typedef struct gmp_arrayZZ_struct * gmp_arrayZZ; +struct gmp_arrayZZ_struct {int len;mpz_srcptr array[];}; + +typedef struct engine_RawMonomialArray_struct * engine_RawMonomialArray; +struct engine_RawMonomialArray_struct {int len;const struct EngineMonomial * array[];}; +typedef const struct RingElement * engine_RawRingElement; + +typedef struct engine_RawRingElementArray_struct * engine_RawRingElementArray; +typedef engine_RawRingElementArray engine_RawRingElementArrayOrNull; +struct engine_RawRingElementArray_struct {int len;const struct RingElement * array[];}; + +typedef struct engine_RawRingElementArrayArray_struct * engine_RawRingElementArrayArray; +typedef engine_RawRingElementArrayArray engine_RawRingElementArrayArrayOrNull; +struct engine_RawRingElementArrayArray_struct {int len;engine_RawRingElementArray array[];}; + +typedef struct engine_RawArrayPair_struct * engine_RawArrayPair; +typedef engine_RawArrayPair engine_RawArrayPairOrNull; +struct engine_RawArrayPair_struct BASECLASS {engine_RawMonomialArray monoms;engine_RawRingElementArray coeffs;}; + +typedef struct engine_RawArrayIntPair_struct * engine_RawArrayIntPair; +typedef engine_RawArrayIntPair engine_RawArrayIntPairOrNull; +struct engine_RawArrayIntPair_struct BASECLASS {M2_arrayint a;M2_arrayint b;}; + +typedef struct engine_RawMutableMatrixArray_struct * engine_RawMutableMatrixArray; +typedef engine_RawMutableMatrixArray engine_RawMutableMatrixArrayOrNull; +struct engine_RawMutableMatrixArray_struct {int len;struct MutableMatrix * array[];}; + +typedef struct engine_RawMatrixArray_struct * engine_RawMatrixArray; +typedef engine_RawMatrixArray engine_RawMatrixArrayOrNull; +struct engine_RawMatrixArray_struct {int len;const struct Matrix * array[];}; + +typedef struct engine_RawMatrixAndInt_struct * engine_RawMatrixAndInt; +struct engine_RawMatrixAndInt_struct BASECLASS {const struct Matrix * M;int i;}; + +typedef struct engine_RawMatrixPair_struct * engine_RawMatrixPair; +typedef engine_RawMatrixPair engine_RawMatrixPairOrNull; +struct engine_RawMatrixPair_struct BASECLASS {const struct Matrix * a;const struct Matrix * b;}; + +typedef struct engine_RawRingElementPair_struct * engine_RawRingElementPair; +typedef engine_RawRingElementPair engine_RawRingElementPairOrNull; +struct engine_RawRingElementPair_struct BASECLASS {const struct RingElement * a;const struct RingElement * b;}; + +typedef struct gmp_ZZpair_struct * gmp_ZZpair; +typedef gmp_ZZpair gmp_ZZpairOrNull; +struct gmp_ZZpair_struct BASECLASS {mpz_srcptr a;mpz_srcptr b;}; + +//typedef struct M2SLEvaluator * engine_RawSLEvaluator; +//typedef struct M2SLEvaluator * engine_RawSLEvaluatorOrNull; + +/* typedef const struct MonomialOrdering * engine_RawMonomialOrdering; */ +/* typedef const struct MonomialOrdering * engine_RawMonomialOrderingOrNull; */ + +typedef struct engine_RawMonomialOrderingArray_struct * engine_RawMonomialOrderingArray; +struct engine_RawMonomialOrderingArray_struct {int len;const struct MonomialOrdering * array[];}; + +/* +struct EngineMonomial; +typedef const struct MonomialOrdering * engine_RawMonomialOrdering; +typedef const struct MonomialOrdering * engine_RawMonomialOrderingOrNull; +typedef const struct Monoid * engine_RawMonoid; +typedef const struct Monoid * engine_RawMonoidOrNull; +typedef const struct Ring * engine_RawRing; +typedef const struct RingMap * engine_RawRingMap; +typedef const struct Ring * engine_RawRingOrNull; +typedef const struct RingElement * engine_RawRingElement; +typedef const struct RingElement * engine_RawRingElementOrNull; +typedef const struct MonomialIdeal * engine_RawMonomialIdeal; +typedef const struct MonomialIdeal * engine_RawMonomialIdealOrNull; +typedef const struct FreeModule * engine_RawFreeModule; +typedef const struct FreeModule * engine_RawFreeModuleOrNull; +typedef const struct Matrix * engine_RawMatrix; +typedef const struct Matrix * engine_RawMatrixOrNull; +*/ + +#if defined(__cplusplus) +extern "C" { +#endif + + M2_arrayint M2_makearrayint(int n); + char * M2_tocharstar(M2_string s); + M2_string M2_join(M2_string x, M2_string y); + M2_string M2_tostring(const char* s); + M2_string M2_tostringn(char *s, int n); + +#if defined(__cplusplus) +} +#endif + + +extern char newline[]; +extern int M2_numTBBThreads; +extern int M2_gbTrace; +extern int M2_numericalAlgebraicGeometryTrace; + +#else +typedef struct gmp_CC_struct * gmp_CC; + +#endif // !defined(SAFEC_EXPORTS) + diff --git a/M2/Macaulay2/e/interface/matrix.cpp b/M2/Macaulay2/e/interface/matrix.cpp index 374543f6d55..4617aae7cce 100644 --- a/M2/Macaulay2/e/interface/matrix.cpp +++ b/M2/Macaulay2/e/interface/matrix.cpp @@ -80,8 +80,65 @@ const RingElement /* or null */ *IM2_Matrix_get_entry(const Matrix *M, } } +/* Returns the entries of the matrix in a flat array in row major order + */ +engine_RawRingElementArrayArrayOrNull IM2_Matrix_get_entries(const Matrix *M) +{ + try + { + int ncols = M->n_cols(); + int nrows = M->n_rows(); + if(nrows < 0 || ncols < 0) + { + ERROR("internal error: matrix has a negative size %d by %d", + nrows, + ncols); + return nullptr; + } + engine_RawRingElementArrayArray entries = + getmemarraytype(engine_RawRingElementArrayArray, nrows); + entries->len = nrows; + RingElement *zero = + RingElement::make_raw(M->get_ring(), M->get_ring()->zero()); + for(int r = 0; r < nrows; r++) + { + engine_RawRingElementArray currRow = + getmemarraytype(engine_RawRingElementArray, ncols); + currRow->len = ncols; + std::fill_n(currRow->array, ncols, zero); + entries->array[r] = currRow; + } + //walk through the columns + for(int c = 0; c < ncols; c++) + { + const vec &column = M->elem(c); + for(const vecterm &term : column) + { + if(term.comp < 0 || term.comp >= nrows) + { + ERROR("internal error: matrix contains invalid entries:" + "row index %d out of range 0 .. %d", + term.comp, + nrows - 1); + //Ignoring the entry and continuing + continue; + } + entries->array[term.comp]->array[c] = + RingElement::make_raw(M->get_ring(), term.coeff); + } + } + return entries; + } catch (const exc::engine_error &e) + { + ERROR(e.what()); + return nullptr; + } + return nullptr; +} + const Matrix *IM2_Matrix_identity(const FreeModule *F, int preference) { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -92,6 +149,7 @@ const Matrix /* or null */ *IM2_Matrix_zero(const FreeModule *F, const FreeModule *G, int preference) { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -103,6 +161,7 @@ const Matrix /* or null */ *IM2_Matrix_make1(const FreeModule *target, const engine_RawRingElementArray M, int preference) { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -115,6 +174,7 @@ const Matrix /* or null */ *IM2_Matrix_make2(const FreeModule *target, const engine_RawRingElementArray M, int preference) { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -129,6 +189,7 @@ const Matrix /* or null */ *IM2_Matrix_make_sparse1( const engine_RawRingElementArray entries, int preference) { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -144,6 +205,7 @@ const Matrix /* or null */ *IM2_Matrix_make_sparse2( const engine_RawRingElementArray entries, int preference) { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -153,6 +215,7 @@ const Matrix /* or null */ *IM2_Matrix_make_sparse2( M2_bool IM2_Matrix_is_implemented_as_dense(const Matrix *M) /* Is the matrix M implemented as dense? */ { + (void) M; #ifdef DEVELOPMENT #warning not implemented yet #endif @@ -169,6 +232,7 @@ const Matrix /* or null */ *IM2_Matrix_remake2(const FreeModule *target, the expected rank. */ { + (void) preference; #ifdef DEVELOPMENT #warning prefer_dense not yet used #endif @@ -184,6 +248,7 @@ const Matrix /* or null */ *IM2_Matrix_remake1(const FreeModule *target, columns of the matrix. */ { + (void) preference; try { #ifdef DEVELOPMENT @@ -205,6 +270,7 @@ const Matrix /* or null */ *IM2_Matrix_random( int special_type, // 0: general, 1:upper triangular, others? int preference) { + (void) preference; #ifdef DEVELOPMENT #warning preference not yet used #endif @@ -687,6 +753,7 @@ const Matrix /* or null */ *rawMatrixSplitContent( const Matrix /* or null */ *IM2_Matrix_remove_content(const Matrix *M) { + (void) M; #ifdef DEVELOPMENT #warning \ "const Matrix /* or null */ * IM2_Matrix_remove_content(const Matrix *M) -- not implemented yet" @@ -857,6 +924,7 @@ M2_string rawSLEvaluatorToString(M2SLEvaluator *sle) unsigned int rawSLEvaluatorHash(M2SLEvaluator *) { return 0; } M2SLProgram /* or null */ *rawSLProgram(unsigned long nConstantsAndInputs) { + (void) nConstantsAndInputs; return new M2SLProgram(new SLProgram); } M2_string rawSLProgramToString(M2SLProgram *slp) diff --git a/M2/Macaulay2/e/interface/matrix.h b/M2/Macaulay2/e/interface/matrix.h index 1ef6d1cc426..61efe9d27de 100644 --- a/M2/Macaulay2/e/interface/matrix.h +++ b/M2/Macaulay2/e/interface/matrix.h @@ -60,6 +60,8 @@ const RingElement /* or null */ *IM2_Matrix_get_entry( int r, int c); /* drg: connected rawMatrixEntry, OK*/ +engine_RawRingElementArrayArrayOrNull IM2_Matrix_get_entries(const Matrix *M); + /*******************************************************************************/ const Matrix *IM2_Matrix_identity( const FreeModule *F, diff --git a/M2/Macaulay2/e/interface/monomial-ideal.cpp b/M2/Macaulay2/e/interface/monomial-ideal.cpp index bf76bfe3241..b5757d595c7 100644 --- a/M2/Macaulay2/e/interface/monomial-ideal.cpp +++ b/M2/Macaulay2/e/interface/monomial-ideal.cpp @@ -22,7 +22,7 @@ class PolynomialRing; class RingElement; -engine_RawMonomialIdealOrNull IM2_MonomialIdeal_make(const Matrix *m, int n) +const MonomialIdeal* /* or null */ IM2_MonomialIdeal_make(const Matrix *m, int n) { try { @@ -49,7 +49,7 @@ const Matrix /* or null */ *IM2_MonomialIdeal_to_matrix(const MonomialIdeal *I) } } -M2_string MonomialIdeal_to_string(const MonomialIdeal *I) +M2_string IM2_MonomialIdeal_to_string(const MonomialIdeal *I) { buffer o; try diff --git a/M2/Macaulay2/e/interface/monomial-ideal.h b/M2/Macaulay2/e/interface/monomial-ideal.h index 24cceedfef0..c93eda7d323 100644 --- a/M2/Macaulay2/e/interface/monomial-ideal.h +++ b/M2/Macaulay2/e/interface/monomial-ideal.h @@ -31,7 +31,7 @@ typedef struct RingElement RingElement; extern "C" { # endif -engine_RawMonomialIdealOrNull IM2_MonomialIdeal_make(const Matrix *m, int n); +const MonomialIdeal* /* or null */ IM2_MonomialIdeal_make(const Matrix *m, int n); /* drg: connected rawMonomialIdeal*/ /* Given a matrix 'm' over an allowed base ring (as above), create the monomial ideal consisting of all of the lead monomials of the columns @@ -43,7 +43,7 @@ const Matrix /* or null */ *IM2_MonomialIdeal_to_matrix(const MonomialIdeal *I); /* Return a one row matrix over the base ring of I consisting of the monomials in I */ -M2_string IM2_MonomialIdeal_to_string(const MonomialIdeal *I); /* TODO */ +M2_string IM2_MonomialIdeal_to_string(const MonomialIdeal *I); unsigned int rawMonomialIdealHash(const MonomialIdeal *I); /* connected to 'hash', sequential, as it is mutable */ diff --git a/M2/Macaulay2/e/interface/monomial-ordering.cpp b/M2/Macaulay2/e/interface/monomial-ordering.cpp index 117ed6085cb..a1609cb7da6 100644 --- a/M2/Macaulay2/e/interface/monomial-ordering.cpp +++ b/M2/Macaulay2/e/interface/monomial-ordering.cpp @@ -9,7 +9,7 @@ #include #include -#include "M2mem.h" // for getmemvectortype, getmematomicvectortype +#include "interface/m2-mem.h" // for getmemvectortype, getmematomicvectortype #include "error.h" #include "monordering.hpp" // TODO: where can this go? it only defines one class diff --git a/M2/Macaulay2/e/interface/mutable-matrix.cpp b/M2/Macaulay2/e/interface/mutable-matrix.cpp index 84a3ccf351c..4d64eeee721 100644 --- a/M2/Macaulay2/e/interface/mutable-matrix.cpp +++ b/M2/Macaulay2/e/interface/mutable-matrix.cpp @@ -72,6 +72,8 @@ unsigned int rawMutableMatrixHash(const MutableMatrix *M) { return M->hash(); } MutableMatrix /* or null */ *rawMutableMatrixPromote(const Ring *S, const MutableMatrix *f) { + (void) S; + (void) f; // Given a natural map i : R --> S // f is a matrix over R. // returns a matrix over S. @@ -92,6 +94,8 @@ MutableMatrix /* or null */ *rawMutableMatrixLift(int *success_return, // returns a matrix over R. // ERROR("MutableMatrix lift not implemented yet"); + (void) R; + (void) f; *success_return = 0; return nullptr; #if 0 @@ -196,6 +200,50 @@ IM2_MutableMatrix_get_entry(const MutableMatrix *M, int r, int c) return RingElement::make_raw(M->get_ring(), result); } +engine_RawRingElementArrayArrayOrNull +IM2_MutableMatrix_get_entries(const MutableMatrix *M) +{ + try + { + int ncols = M->n_cols(); + int nrows = M->n_rows(); + if(nrows < 0 || ncols < 0) + { + ERROR("internal error: matrix has a negative size %d by %d", + nrows, + ncols); + return nullptr; + } + engine_RawRingElementArrayArray entries = + getmemarraytype(engine_RawRingElementArrayArray, nrows); + entries->len = nrows; + for(int r = 0; r < nrows; r++) + { + engine_RawRingElementArray currRow = + getmemarraytype(engine_RawRingElementArray, ncols); + currRow->len = ncols; + entries->array[r] = currRow; + } + for(int r = 0; r < nrows; r++) + { + for(int c = 0; c < ncols; c++) + { + ring_elem result; + M->get_entry(r, c, result); + entries->array[r]->array[c] = + RingElement::make_raw(M->get_ring(), result); + } + } + return entries; + } catch (const exc::engine_error& e) + { + ERROR(e.what()); + return nullptr; + } + return nullptr; +} + + M2_bool IM2_MutableMatrix_set_entry(MutableMatrix *M, int r, int c, @@ -255,6 +303,7 @@ M2_bool IM2_MutableMatrix_row_operation(MutableMatrix *M, M2_bool opposite_mult) /* row(i) <- row(i) + r * row(j) */ { + (void) opposite_mult; const Ring *R = M->get_ring(); if (R != r->get_ring()) { @@ -278,6 +327,7 @@ M2_bool IM2_MutableMatrix_column_operation(MutableMatrix *M, M2_bool opposite_mult) /* column(i) <- column(i) + r * column(j) */ { + (void) opposite_mult; const Ring *R = M->get_ring(); if (R != r->get_ring()) { @@ -300,6 +350,7 @@ M2_bool IM2_MutableMatrix_row_scale(MutableMatrix *M, M2_bool opposite_mult) /* row(i) <- r * row(i) */ { + (void) opposite_mult; const Ring *R = M->get_ring(); if (R != r->get_ring()) { @@ -321,6 +372,7 @@ M2_bool IM2_MutableMatrix_column_scale(MutableMatrix *M, M2_bool opposite_mult) /* column(i) <- r * column(i) */ { + (void) opposite_mult; const Ring *R = M->get_ring(); if (R != r->get_ring()) { @@ -364,6 +416,7 @@ M2_bool IM2_MutableMatrix_column_2by2(MutableMatrix *M, column(c2) <- b1 * column(c1) + b2 * column(c2) */ { + (void) opposite_mult; const Ring *R = M->get_ring(); if (a1->get_ring() != R || a2->get_ring() != R || b1->get_ring() != R || b2->get_ring() != R) @@ -391,6 +444,7 @@ M2_bool IM2_MutableMatrix_row_2by2(MutableMatrix *M, row(r2) <- b1 * row(r1) + b2 * row(r2) */ { + (void) opposite_mult; const Ring *R = M->get_ring(); if (a1->get_ring() != R || a2->get_ring() != R || b1->get_ring() != R || b2->get_ring() != R) @@ -409,6 +463,9 @@ M2_bool IM2_MutableMatrix_row_2by2(MutableMatrix *M, M2_bool IM2_MutableMatrix_sort_columns(MutableMatrix *M, int lo, int hi) /* Returns false if M is not mutable, or lo, or hi are out of range */ { + (void) M; + (void) lo; + (void) hi; ERROR("not re-implemented yet"); return false; } @@ -567,6 +624,7 @@ M2_bool rawLLL(MutableMatrix *M, M2_bool IM2_SmithNormalForm(MutableMatrix *M) { + (void) M; #ifdef DEVELOPMENT #warning "implement smith" #endif @@ -576,6 +634,7 @@ M2_bool IM2_SmithNormalForm(MutableMatrix *M) M2_bool IM2_HermiteNormalForm(MutableMatrix *M) { + (void) M; #ifdef DEVELOPMENT #warning "implement hermite" #endif @@ -948,6 +1007,7 @@ engine_RawRingElementArrayOrNull rawLinAlgCharPoly(MutableMatrix *A) // returns an array whose coefficients give the characteristic polynomial of the // square matrix A { + (void) A; #if 1 #if 0 const Ring *R = A->get_ring(); @@ -981,6 +1041,7 @@ engine_RawRingElementArrayOrNull rawLinAlgMinPoly(MutableMatrix *A) // returns an array whose coefficients give the minimal polynomial of the square // matrix A { + (void) A; #if 1 #if 0 const Ring *R = A->get_ring(); @@ -1103,6 +1164,7 @@ gmp_RRorNull rawRingElementNorm(gmp_RR p, const RingElement *f) gmp_RRorNull rawMutableMatrixNorm(gmp_RR p, const MutableMatrix *M) { + (void) p; return M->norm(); } diff --git a/M2/Macaulay2/e/interface/mutable-matrix.h b/M2/Macaulay2/e/interface/mutable-matrix.h index 084adb369db..452ce76895a 100644 --- a/M2/Macaulay2/e/interface/mutable-matrix.h +++ b/M2/Macaulay2/e/interface/mutable-matrix.h @@ -88,6 +88,9 @@ const RingElement /* or null */ * IM2_MutableMatrix_get_entry(const MutableMatrix *M, int r, int c); /* drg: connected rawMatrixEntry, OK*/ +engine_RawRingElementArrayArrayOrNull +IM2_MutableMatrix_get_entries(const MutableMatrix *M); + /* Each of these routines returns false if there was an error. */ M2_bool IM2_MutableMatrix_set_entry(MutableMatrix *M, diff --git a/M2/Macaulay2/e/interface/polyroots.cpp b/M2/Macaulay2/e/interface/polyroots.cpp index 8b79368c545..41264e600b9 100644 --- a/M2/Macaulay2/e/interface/polyroots.cpp +++ b/M2/Macaulay2/e/interface/polyroots.cpp @@ -24,6 +24,7 @@ engine_RawRingElementArrayOrNull rawRoots(const RingElement *p, long prec, int unique) { + (void) unique; const Ring *R = p->get_ring(); const PolynomialRing *P = R->cast_to_PolynomialRing(); const Monoid *M = P->getMonoid(); diff --git a/M2/Macaulay2/e/interface/random.cpp b/M2/Macaulay2/e/interface/random.cpp index 6a12c64611f..c0620c1cf87 100644 --- a/M2/Macaulay2/e/interface/random.cpp +++ b/M2/Macaulay2/e/interface/random.cpp @@ -255,6 +255,38 @@ double randomDouble() return result; } +void rawSetRandomRRi(mpfi_ptr result) +{ + mpfr_t val; + + mpfr_init2(val, mpfi_get_prec(result)); + randomMpfr(val); + mpfi_set_fr(result,val); + randomMpfr(val); + mpfi_put_fr(result,val); + mpfr_clear(val); +} + +gmp_RRi rawRandomRRi(unsigned long precision) +{ + mpfi_ptr result; + + result = getmemstructtype(mpfi_ptr); + mpfi_init2(result, precision); + rawSetRandomRRi(result); + + return moveTo_gmpRRi(result); +} + +gmp_CCi rawRandomCCi(unsigned long precision) +{ + gmp_CCimutable result = getmemstructtype(gmp_CCimutable); + result->re = const_cast(rawRandomRRi(precision)); + result->im = const_cast(rawRandomRRi(precision)); + return reinterpret_cast(result); +} + + int system_randomint() { #if 0 diff --git a/M2/Macaulay2/e/interface/random.h b/M2/Macaulay2/e/interface/random.h index 11d8fd02ab4..bf862070c6b 100644 --- a/M2/Macaulay2/e/interface/random.h +++ b/M2/Macaulay2/e/interface/random.h @@ -58,6 +58,16 @@ gmp_CC rawRandomCC(unsigned long prec); void randomMpfr(mpfr_t result); +gmp_RRi rawRandomRRi(unsigned long prec); +void rawSetRandomRRi(mpfi_ptr result); +/* returns an interval with the given precision containing two random + * variates from the uniform distribution on [0, 1] */ + +gmp_CCi rawRandomCCi(unsigned long prec); +/* returns a complex interval with the given precision whose real and imaginary + * parts each contain two random variates from the uniform distribution on + * [0, 1] */ + double randomDouble(); int system_randomint(); diff --git a/M2/Macaulay2/e/interface/ring.cpp b/M2/Macaulay2/e/interface/ring.cpp index 273e9520a8a..a8dea7ee77e 100644 --- a/M2/Macaulay2/e/interface/ring.cpp +++ b/M2/Macaulay2/e/interface/ring.cpp @@ -30,6 +30,7 @@ #include "aring.hpp" #include "aring-glue.hpp" #include "aring-RRi.hpp" +#include "aring-CCi.hpp" #include "aring-RR.hpp" #include "aring-CC.hpp" #include "aring-RRR.hpp" @@ -93,6 +94,11 @@ const Ring /* or null */ *IM2_Ring_RRi(unsigned long prec) return M2::ConcreteRing::create(prec); } +const Ring /* or null */ *IM2_Ring_CCi(unsigned long prec) +{ + return M2::ConcreteRing::create(prec); +} + const Ring /* or null */ *IM2_Ring_RRR(unsigned long prec) { if (prec <= 53) return M2::ConcreteRing::create(); @@ -126,6 +132,8 @@ const Ring /* or null */ *IM2_Ring_polyring(const Ring *K, const Monoid *M) const Ring * /* or null */ rawDividedPowerRing(const Ring *K, const Monoid *M) { + (void) K; + (void) M; #if 0 //TODO: MES, this function has not yet been implemented, or even placed in ring.h try { diff --git a/M2/Macaulay2/e/interface/ring.h b/M2/Macaulay2/e/interface/ring.h index c8806d0c21f..f7606dec644 100644 --- a/M2/Macaulay2/e/interface/ring.h +++ b/M2/Macaulay2/e/interface/ring.h @@ -57,6 +57,9 @@ const Ring /* or null */ *rawGaloisField(const RingElement *f); const Ring /* or null */ *IM2_Ring_RRi(unsigned long prec); /* drg: connected rawRRi */ +const Ring /* or null */ *IM2_Ring_CCi(unsigned long prec); +/* drg: connected rawCCi */ + const Ring /* or null */ *IM2_Ring_RRR(unsigned long prec); /* drg: connected rawRRR */ diff --git a/M2/Macaulay2/e/interface/ringelement.cpp b/M2/Macaulay2/e/interface/ringelement.cpp index 6aed3d0b018..045d3af1f21 100644 --- a/M2/Macaulay2/e/interface/ringelement.cpp +++ b/M2/Macaulay2/e/interface/ringelement.cpp @@ -13,6 +13,10 @@ #include "aring-CC.hpp" #include "aring-CCC.hpp" +#include "aring-RR.hpp" +#include "aring-RRR.hpp" +#include "aring-RRi.hpp" +#include "aring-CCi.hpp" #include "aring-glue.hpp" #include "aring.hpp" #include "buffer.hpp" @@ -85,6 +89,14 @@ const RingElement *IM2_RingElement_from_Interval(const Ring *R, gmp_RRi z) return nullptr; } +const RingElement *IM2_RingElement_from_ComplexInterval(const Ring *R, gmp_CCi z) +{ + ring_elem f; + if (R->from_ComplexInterval(z, f)) return RingElement::make_raw(R, f); + ERROR("cannot create element of this ring from an element of CCi"); + return nullptr; +} + gmp_ZZorNull IM2_RingElement_to_Integer(const RingElement *a) /* If the ring of a is ZZ, or ZZ/p, this returns the underlying representation. Otherwise, NULL is returned, and an error is given */ @@ -136,7 +148,7 @@ gmp_RRorNull IM2_RingElement_to_BigReal(const RingElement *a) return moveTo_gmpRR(result); case M2::ring_RRR: R1 = - dynamic_cast *>(a->get_ring()); + dynamic_cast *>(R); result = getmemstructtype(gmp_RRmutable); mpfr_init2(result, R1->get_precision()); mpfr_set(result, a->get_value().get_mpfr(), MPFR_RNDN); @@ -160,9 +172,16 @@ gmp_RRiorNull IM2_RingElement_to_Interval(const RingElement *a) mpfi_init2(result, 53); mpfi_set_d(result, a->get_value().get_double()); return moveTo_gmpRRi(result); + case M2::ring_RRR: + R1 = + dynamic_cast *>(R); + result = getmemstructtype(gmp_RRimutable); + mpfi_init2(result, R1->get_precision()); + mpfi_set_fr(result, a->get_value().get_mpfr()); + return moveTo_gmpRRi(result); case M2::ring_RRi: R1 = - dynamic_cast *>(a->get_ring()); + dynamic_cast *>(R); result = getmemstructtype(gmp_RRimutable); mpfi_init2(result, R1->get_precision()); mpfi_set(result, a->get_value().get_mpfi()); @@ -196,6 +215,73 @@ gmp_CCorNull IM2_RingElement_to_BigComplex(const RingElement *a) return nullptr; } +gmp_CCiorNull IM2_RingElement_to_ComplexInterval(const RingElement *a) +{ + const Ring *R = a->get_ring(); + gmp_CCimutable result; + const M2::ConcreteRing *R1; + + switch (R->ringID()) + { + case M2::ring_RR: + result = getmemstructtype(gmp_CCimutable); + result->re = getmemstructtype(gmp_RRimutable); + result->im = getmemstructtype(gmp_RRimutable); + mpfi_init2(result->re, 53); + mpfi_init2(result->im, 53); + mpfi_set_d(result->re, a->get_value().get_double()); + mpfi_set_d(result->im, 0); + return moveTo_gmpCCi(result); + case M2::ring_RRR: + R1 = + dynamic_cast *>(R); + result = getmemstructtype(gmp_CCimutable); + result->re = getmemstructtype(gmp_RRimutable); + result->im = getmemstructtype(gmp_RRimutable); + mpfi_init2(result->re, R1->get_precision()); + mpfi_init2(result->im, R1->get_precision()); + mpfi_set_fr(result->re, a->get_value().get_mpfr()); + mpfi_set_d(result->im, 0); + return moveTo_gmpCCi(result); + case M2::ring_RRi: + R1 = + dynamic_cast *>(R); + result = getmemstructtype(gmp_CCimutable); + result->re = getmemstructtype(gmp_RRimutable); + result->im = getmemstructtype(gmp_RRimutable); + mpfi_init2(result->re, R1->get_precision()); + mpfi_init2(result->im, R1->get_precision()); + mpfi_set(result->re, a->get_value().get_mpfi()); + mpfi_set_d(result->im,0); + return moveTo_gmpCCi(result); + case M2::ring_CCC: + R1 = + dynamic_cast *>(R); + result = getmemstructtype(gmp_CCimutable); + result->re = getmemstructtype(gmp_RRimutable); + result->im = getmemstructtype(gmp_RRimutable); + mpfi_init2(result->re, R1->get_precision()); + mpfi_init2(result->im, R1->get_precision()); + mpfi_set_fr(result->re, &a->get_value().get_cc()->re); + mpfi_set_fr(result->im, &a->get_value().get_cc()->im); + return moveTo_gmpCCi(result); + case M2::ring_CCi: + R1 = + dynamic_cast *>(R); + result = getmemstructtype(gmp_CCimutable); + result->re = getmemstructtype(gmp_RRimutable); + result->im = getmemstructtype(gmp_RRimutable); + mpfi_init2(result->re, R1->get_precision()); + mpfi_init2(result->im, R1->get_precision()); + mpfi_set(result->re, &a->get_value().get_cci()->re); + mpfi_set(result->im, &a->get_value().get_cci()->im); + return moveTo_gmpCCi(result); + default: + ERROR("expected an element of CCi"); + return nullptr; + } +} + #if 0 int rawDiscreteLog(const RingElement *h) { diff --git a/M2/Macaulay2/e/interface/ringelement.h b/M2/Macaulay2/e/interface/ringelement.h index 593cb9ea631..c06905f2950 100644 --- a/M2/Macaulay2/e/interface/ringelement.h +++ b/M2/Macaulay2/e/interface/ringelement.h @@ -40,6 +40,8 @@ const RingElement *IM2_RingElement_from_BigReal(const Ring *R, gmp_RR z); const RingElement *IM2_RingElement_from_Interval(const Ring *R, gmp_RRi z); +const RingElement *IM2_RingElement_from_ComplexInterval(const Ring *R, gmp_CCi z); + gmp_ZZorNull IM2_RingElement_to_Integer(const RingElement *a); /* If the ring of a is ZZ, or ZZ/p, this returns the underlying representation. Otherwise, NULL is returned, and an error is given */ @@ -52,6 +54,8 @@ gmp_CCorNull IM2_RingElement_to_BigComplex(const RingElement *a); gmp_RRiorNull IM2_RingElement_to_Interval(const RingElement *a); +gmp_CCiorNull IM2_RingElement_to_ComplexInterval(const RingElement *a); + long rawDiscreteLog(const RingElement *h); const RingElement *rawMultiplicativeGenerator(const Ring *R); diff --git a/M2/Macaulay2/e/interreduce.cpp b/M2/Macaulay2/e/interreduce.cpp index afa2416dc56..57ce4d971c2 100644 --- a/M2/Macaulay2/e/interreduce.cpp +++ b/M2/Macaulay2/e/interreduce.cpp @@ -8,6 +8,9 @@ Interreducer::Interreducer(GBRing *R0, FreeModule *F0, VECTOR(gbvector *) & elems0) { + (void) R0; + (void) F0; + (void) elems0; } void Interreducer::showElem(int i, int nterms) diff --git a/M2/Macaulay2/e/lapack.cpp b/M2/Macaulay2/e/lapack.cpp index 3dab38859d2..548a6aba111 100644 --- a/M2/Macaulay2/e/lapack.cpp +++ b/M2/Macaulay2/e/lapack.cpp @@ -82,6 +82,7 @@ void fill_from_lapack_upper(const std::vector& lapack_numbers, // colum // lapack_numbers is in column major form // upper is in row major form { + (void) numcols; // At this point, upper should be a zero matrix of size (min(numrows, numcols) x numcols) assert(upper.numRows() == std::min(numrows, numcols)); assert(upper.numColumns() == numcols); @@ -107,6 +108,7 @@ void fill_from_lapack_upper(const std::vector& lapack_numbers, // colum // lapack_numbers is in column major form // upper is in row major form { + (void) numcols; // At this point, upper should be a zero matrix of size (min(numrows, numcols) x numcols) assert(upper.numRows() == std::min(numrows, numcols)); assert(upper.numColumns() == numcols); diff --git a/M2/Macaulay2/e/localring.cpp b/M2/Macaulay2/e/localring.cpp index fac73bcacbb..6fa068e6a13 100644 --- a/M2/Macaulay2/e/localring.cpp +++ b/M2/Macaulay2/e/localring.cpp @@ -226,6 +226,7 @@ ring_elem LocalRing::fraction(const ring_elem top, const ring_elem bottom) const // TODO: implement for MutableMatrix void LocalRing::lift_up(const Ring *R, const Matrix *m, Matrix *&result) const { + (void) R; const RingElement *a, *b, *d; MatrixConstructor mat(mRing->make_FreeModule(m->n_rows()), m->n_cols()); Matrix::column_iterator i(m), end(m); @@ -472,7 +473,7 @@ ring_elem LocalRing::copy(const ring_elem a) const return ring_elem(g); } -void LocalRing::remove(ring_elem &a) const {} +void LocalRing::remove(ring_elem &a) const { (void) a; } ring_elem LocalRing::negate(const ring_elem a) const { @@ -683,6 +684,8 @@ ring_elem LocalRing::get_coeff(const ring_elem f, const int *) const } ring_elem LocalRing::get_terms(int nvars0, const ring_elem f, int, int) const { + (void) nvars0; + (void) f; return f; } diff --git a/M2/Macaulay2/e/mat-arith.hpp b/M2/Macaulay2/e/mat-arith.hpp index 489af74b6a0..a46334f474b 100644 --- a/M2/Macaulay2/e/mat-arith.hpp +++ b/M2/Macaulay2/e/mat-arith.hpp @@ -412,6 +412,8 @@ template void transpose(const SMat& A, SMat& result) { // result should be the 0 matrix of the correct size. + (void) A; + (void) result; assert(&A != &result); // these cannot be aliased! assert(result.numRows() == A.numColumns()); assert(result.numColumns() == A.numRows()); diff --git a/M2/Macaulay2/e/mat-elem-ops.hpp b/M2/Macaulay2/e/mat-elem-ops.hpp index 4f2d56b27ad..69acfde3e9c 100644 --- a/M2/Macaulay2/e/mat-elem-ops.hpp +++ b/M2/Macaulay2/e/mat-elem-ops.hpp @@ -813,6 +813,7 @@ class MatElementaryOps > /* Delete rows i .. j from M */ { mat.delete_rows(i, j); } static void reduce_by_pivots(Mat& M) { + (void) M; throw exc::engine_error( "reduce_py_pivots not yet implemented for sparse mutable matrices"); // TODO: write this!! diff --git a/M2/Macaulay2/e/mat-linalg.hpp b/M2/Macaulay2/e/mat-linalg.hpp index fc58dc8a132..ff9ff94eedd 100644 --- a/M2/Macaulay2/e/mat-linalg.hpp +++ b/M2/Macaulay2/e/mat-linalg.hpp @@ -77,6 +77,7 @@ namespace MatrixOps { template size_t rank(const Mat& A) { + (void) A; throw exc::engine_error( "'rank' not implemented for this kind of matrix over this ring"); return 0; @@ -90,6 +91,8 @@ size_t rank(const Mat& A) template void determinant(const Mat& A, typename Mat::ElementType& result_det) { + (void) A; + (void) result_det; throw exc::engine_error( "'determinant' not implemented for this kind of matrix over this ring"); } @@ -111,6 +114,8 @@ void determinant(const Mat& A, typename Mat::ElementType& result_det) template bool inverse(const Mat& A, Mat& result_inv) { + (void) A; + (void) result_inv; throw exc::engine_error( "'invert' not implemented for this kind of matrix over this ring"); } @@ -127,6 +132,8 @@ bool inverse(const Mat& A, Mat& result_inv) template size_t rowReducedEchelonForm(const Mat& A, Mat& result_rref) { + (void) A; + (void) result_rref; throw exc::engine_error( "'rowReducedEchelonForm' not implemented for this kind of matrix over " "this ring"); @@ -148,6 +155,9 @@ size_t rowReducedEchelonForm(const Mat& A, Mat& result_rref) template void mult(const Mat& A, const Mat& B, Mat& result_product) { + (void) A; + (void) B; + (void) result_product; throw exc::engine_error( "'mult matrices' not implemented for this kind of matrix over this ring"); } @@ -168,6 +178,8 @@ void mult(const Mat& A, const Mat& B, Mat& result_product) template size_t nullSpace(const Mat& A, Mat& result_nullspace) { + (void) A; + (void) result_nullspace; throw exc::engine_error( "'nullSpace' not implemented for this kind of matrix over this ring"); } @@ -176,6 +188,9 @@ size_t nullSpace(const Mat& A, Mat& result_nullspace) template bool solveLinear(const Mat& A, const Mat& B, Mat& X) { + (void) A; + (void) B; + (void) X; throw exc::engine_error( "'solveLinear' not implemented for this kind of matrix over this ring"); } @@ -188,6 +203,9 @@ bool solveLinear(const Mat& A, const Mat& B, Mat& X) template bool solveInvertible(const Mat& A, const Mat& B, Mat& X) { + (void) A; + (void) B; + (void) X; throw exc::engine_error( "'solveInvertible' not implemented for this kind of matrix over this " "ring"); @@ -208,6 +226,8 @@ bool solveInvertible(const Mat& A, const Mat& B, Mat& X) template M2_arrayintOrNull rankProfile(const Mat& A, bool row_profile) { + (void) A; + (void) row_profile; throw exc::engine_error( "'rankProfile' not implemented for this kind of matrix over this ring"); } @@ -221,6 +241,9 @@ template void addMultipleTo(Mat& C, const Mat& A, const Mat& B) // C = C + A*B { + (void) C; + (void) A; + (void) B; throw exc::engine_error( "'addMultipleTo' not implemented for this kind of matrix over this ring"); } @@ -234,6 +257,9 @@ template void subtractMultipleTo(Mat& C, const Mat& A, const Mat& B) // C = C - A*B { + (void) C; + (void) A; + (void) B; throw exc::engine_error( "'subtractMultipleTo' not implemented for this kind of matrix over this " "ring"); @@ -242,6 +268,9 @@ void subtractMultipleTo(Mat& C, const Mat& A, const Mat& B) template M2_arrayintOrNull LU(const Mat& A, Mat& L, Mat& U) { + (void) A; + (void) L; + (void) U; throw exc::engine_error( "'LU' not implemented for this kind of matrix over this ring"); } @@ -249,6 +278,10 @@ M2_arrayintOrNull LU(const Mat& A, Mat& L, Mat& U) template M2_arrayintOrNull LUincremental(std::vector& P, Mat& LU, const Mat& v, int i) { + (void) P; + (void) LU; + (void) v; + (void) i; throw exc::engine_error( "'LUincremental' not implemented for this kind of matrix over this ring"); } @@ -256,6 +289,10 @@ M2_arrayintOrNull LUincremental(std::vector& P, Mat& LU, const Mat& v, i template void triangularSolve(Mat& Lv, Mat& x, int m, int strategy) { + (void) Lv; + (void) x; + (void) m; + (void) strategy; throw exc::engine_error( "'triangularSolve' not implemented for this kind of matrix over this " "ring"); @@ -264,6 +301,8 @@ void triangularSolve(Mat& Lv, Mat& x, int m, int strategy) template bool eigenvalues(const Mat& A, Mat2& eigenvals) { + (void) A; + (void) eigenvals; throw exc::engine_error( "'eigenvalues' not implemented for this kind of matrix over this ring"); } @@ -271,6 +310,8 @@ bool eigenvalues(const Mat& A, Mat2& eigenvals) template bool eigenvaluesHermitian(const Mat& A, Mat2& eigenvals) { + (void) A; + (void) eigenvals; throw exc::engine_error( "'eigenvalues' not implemented for this kind of matrix over this ring"); } @@ -278,6 +319,9 @@ bool eigenvaluesHermitian(const Mat& A, Mat2& eigenvals) template bool eigenvectors(const Mat& A, Mat2& eigenvals, Mat3& eigenvecs) { + (void) A; + (void) eigenvals; + (void) eigenvecs; throw exc::engine_error( "'eigenvectors' not implemented for this kind of matrix over this ring"); } @@ -285,6 +329,9 @@ bool eigenvectors(const Mat& A, Mat2& eigenvals, Mat3& eigenvecs) template bool eigenvectorsHermitian(const Mat& A, Mat2& eigenvals, Mat3& eigenvecs) { + (void) A; + (void) eigenvals; + (void) eigenvecs; throw exc::engine_error( "'eigenvectors' not implemented for this kind of matrix over this ring"); } @@ -292,6 +339,10 @@ bool eigenvectorsHermitian(const Mat& A, Mat2& eigenvals, Mat3& eigenvecs) template bool leastSquares(const Mat& A, const Mat& B, Mat& X, bool assume_full_rank) { + (void) A; + (void) B; + (void) X; + (void) assume_full_rank; throw exc::engine_error( "'leastSquares' not implemented for this kind of matrix over this ring"); } @@ -299,6 +350,11 @@ bool leastSquares(const Mat& A, const Mat& B, Mat& X, bool assume_full_rank) template bool SVD(const Mat& A, Mat2& Sigma, Mat& U, Mat& Vt, int strategy) { + (void) A; + (void) Sigma; + (void) U; + (void) Vt; + (void) strategy; throw exc::engine_error( "'SVD' not implemented for this kind of matrix over this ring"); } @@ -306,6 +362,10 @@ bool SVD(const Mat& A, Mat2& Sigma, Mat& U, Mat& Vt, int strategy) template bool QR(const Mat& A, Mat2& Q, Mat3& R, bool return_QR) { + (void) A; + (void) Q; + (void) R; + (void) return_QR; throw exc::engine_error( "'QR' not implemented for this kind of matrix over this ring"); } @@ -313,6 +373,8 @@ bool QR(const Mat& A, Mat2& Q, Mat3& R, bool return_QR) template void clean(gmp_RR epsilon, T& mat) { + (void) epsilon; + (void) mat; throw exc::engine_error( "'clean' not implemented for this kind of matrix over this ring"); } @@ -320,6 +382,8 @@ void clean(gmp_RR epsilon, T& mat) template void increase_norm(gmp_RRmutable nm, const T& mat) { + (void) nm; + (void) mat; throw exc::engine_error( "'norm' not implemented for this kind of matrix over this ring"); } @@ -577,30 +641,42 @@ void subtractMultipleTo(DMatZZpFFPACK& C, inline M2_arrayintOrNull LU(const DMatZZGMP& A, DMatZZGMP& L, DMatZZGMP& U) { + (void) A; + (void) L; + (void) U; throw exc::engine_error( "'LU' not implemented for this kind of matrix over this ring"); } inline M2_arrayintOrNull rankProfile(const DMatZZGMP& A, bool row_profile) { + (void) A; + (void) row_profile; throw exc::engine_error( "'rankProfile' not implemented for this kind of matrix over this ring"); } inline bool inverse(const DMatZZGMP& A, DMatZZGMP& result_inv) { + (void) A; + (void) result_inv; throw exc::engine_error( "'invert' not implemented for this kind of matrix over this ring"); } inline size_t nullSpace(const DMatZZGMP& A, DMatZZGMP& result_nullspace) { + (void) A; + (void) result_nullspace; throw exc::engine_error( "'nullSpace' not implemented for this kind of matrix over this ring"); } inline bool solveLinear(const DMatZZGMP& A, const DMatZZGMP& B, DMatZZGMP& X) { + (void) A; + (void) B; + (void) X; throw exc::engine_error( "'solveLinear' not implemented for this kind of matrix over this ring"); } @@ -609,6 +685,9 @@ inline bool solveInvertible(const DMatZZGMP& A, const DMatZZGMP& B, DMatZZGMP& X) { + (void) A; + (void) B; + (void) X; throw exc::engine_error( "'solveInvertible' not implemented for this kind of matrix over this " "ring"); @@ -720,6 +799,8 @@ inline bool solveLinear(const DMatZZ& A, const DMatZZ& B, DMatZZ& X) inline M2_arrayintOrNull rankProfile(const DMatZZ& A, bool row_profile) { + (void) A; + (void) row_profile; throw exc::engine_error( "'rankProfile' not implemented for this kind of matrix over this ring"); } @@ -1031,12 +1112,17 @@ inline bool solveLinear(const DMatQQFlint& A, // fmpq_mat_solve doesn't declare params const // DMatQQFlint& B1 = const_cast(B); // return fmpq_mat_solve(X.fmpq_mat(), B1.fmpq_mat(), A1.fmpq_mat()); + (void) A; + (void) B; + (void) X; return false; } inline M2_arrayintOrNull rankProfile(const DMatQQFlint& A, bool row_profile) { // TODO: WRITE ME + (void) A; + (void) row_profile; throw exc::engine_error( "'rankProfile' not implemented for this kind of matrix over this ring"); } @@ -1293,6 +1379,7 @@ inline bool leastSquares(const DMatRRR& A, DMatRRR& X, bool assume_full_rank) { + (void) assume_full_rank; return EigenM2::least_squares(&A, &B, &X); } @@ -1357,6 +1444,7 @@ inline bool leastSquares(const DMatCCC& A, DMatCCC& X, bool assume_full_rank) { + (void) assume_full_rank; return EigenM2::least_squares(&A, &B, &X); } diff --git a/M2/Macaulay2/e/mat.hpp b/M2/Macaulay2/e/mat.hpp index 65816d1d70a..d24b3d7e388 100644 --- a/M2/Macaulay2/e/mat.hpp +++ b/M2/Macaulay2/e/mat.hpp @@ -282,6 +282,7 @@ class MutableMatrix : public MutableEngineObject // engine_error is thrown. virtual engine_RawArrayIntPairOrNull LQUPFactorizationInPlace(bool transpose) { + (void) transpose; throw exc::engine_error("not implemented for this ring or matrix type"); } diff --git a/M2/Macaulay2/e/matrix-kbasis.cpp b/M2/Macaulay2/e/matrix-kbasis.cpp index 85899f63baf..4cf32ea0196 100644 --- a/M2/Macaulay2/e/matrix-kbasis.cpp +++ b/M2/Macaulay2/e/matrix-kbasis.cpp @@ -2,7 +2,7 @@ #include // for vector #include "ExponentVector.hpp" // for exponents, exponents_t -#include "M2mem.h" // for freemem +#include "interface/m2-mem.h" // for freemem #include "engine-includes.hpp" // for M2_arrayint, M2_arrayint_struct #include "error.h" // for ERROR #include "freemod.hpp" // for FreeModule diff --git a/M2/Macaulay2/e/matrix-stream.cpp b/M2/Macaulay2/e/matrix-stream.cpp index 18ce89de5ad..5b8b81c3205 100644 --- a/M2/Macaulay2/e/matrix-stream.cpp +++ b/M2/Macaulay2/e/matrix-stream.cpp @@ -19,13 +19,11 @@ MatrixStream::~MatrixStream() void MatrixStream::idealBegin(size_t polyCount) { - // We ignore polyCount - // Nothing to do + (void) polyCount; } void MatrixStream::appendPolynomialBegin(size_t termCount) { - // we ignore termCount - // Nothing to do + (void) termCount; } void MatrixStream::appendTermBegin(Component com) { diff --git a/M2/Macaulay2/e/matrix.hpp b/M2/Macaulay2/e/matrix.hpp index 29016a1eadf..b7d2fce93f8 100644 --- a/M2/Macaulay2/e/matrix.hpp +++ b/M2/Macaulay2/e/matrix.hpp @@ -264,7 +264,7 @@ class Matrix : public EngineObject const vecterm * v; public: column_iterator(const Matrix *M, int c) : v(M->elem(c)) {} - column_iterator(const Matrix *M) : v(nullptr) {} + column_iterator(const Matrix *M) : v(nullptr) { (void) M; } column_iterator& operator++() { v = v->next; return *this; } const vecterm* operator *() { return v; } diff --git a/M2/Macaulay2/e/monideal-minprimes.cpp b/M2/Macaulay2/e/monideal-minprimes.cpp index 9c97d940268..819583da542 100644 --- a/M2/Macaulay2/e/monideal-minprimes.cpp +++ b/M2/Macaulay2/e/monideal-minprimes.cpp @@ -98,6 +98,7 @@ static int alg1_reduce_exp(const int *m, const int *exp) void MinimalPrimes::alg1_grab_prime(int depth) { Bag *b = new Bag(0); + (void) depth; for (int i = 0; i < nvars; i++) if (exp[i + 1] > 0) exp2[i] = 1; @@ -166,6 +167,7 @@ MonomialIdeal *MinimalPrimes::alg1_min_primes(int maxcodim, int count) // We need to know how large to make it. So, we first add up all of the // degrees of the gens + (void) count; depth_limit = -maxcodim - 1; long len = 1; diff --git a/M2/Macaulay2/e/monoid.cpp b/M2/Macaulay2/e/monoid.cpp index a8e8d4ecad1..9a7e1597426 100644 --- a/M2/Macaulay2/e/monoid.cpp +++ b/M2/Macaulay2/e/monoid.cpp @@ -461,6 +461,7 @@ monomial Monoid::make_one() const } void Monoid::remove(monomial d) const { + (void) d; #if 0 // freemem(d); #endif diff --git a/M2/Macaulay2/e/monomial-sets.hpp b/M2/Macaulay2/e/monomial-sets.hpp index d1978938089..8e7d3b6b61e 100644 --- a/M2/Macaulay2/e/monomial-sets.hpp +++ b/M2/Macaulay2/e/monomial-sets.hpp @@ -67,6 +67,7 @@ class MonomialHashAndEqFixedSize // TODO: do something good here. std::size_t operator()(const int* m) const { + (void) m; return 0; } @@ -91,6 +92,7 @@ class MonomialHashAndEqVarSize // TODO: do something good here. std::size_t operator()(const int* m) const { + (void) m; return 0; } diff --git a/M2/Macaulay2/e/monsort.hpp b/M2/Macaulay2/e/monsort.hpp index 741a2702d70..03ae179241a 100644 --- a/M2/Macaulay2/e/monsort.hpp +++ b/M2/Macaulay2/e/monsort.hpp @@ -8,7 +8,7 @@ #include "newdelete.hpp" #if !defined(SAFEC_EXPORTS) -#include +#include "interface/m2-types.h" #endif template diff --git a/M2/Macaulay2/e/mutablecomplex.cpp b/M2/Macaulay2/e/mutablecomplex.cpp index 89294e94077..2921c969945 100644 --- a/M2/Macaulay2/e/mutablecomplex.cpp +++ b/M2/Macaulay2/e/mutablecomplex.cpp @@ -27,6 +27,7 @@ size_t MutableComplex::complexity(const iterator &i, const size_t flags) const // TODO: save row/column totals in a separate vector and only update when // pruning a unit { + (void) flags; ring_elem e; const size_t n = i.index(); const std::pair u = *i; @@ -66,6 +67,7 @@ size_t MutableComplex::complexity(const iterator &i, const size_t flags) const bool MutableComplex::next_unit(iterator &i, const size_t flags) const { + (void) flags; ring_elem e; for (; i < i.end(); ++i) { @@ -189,6 +191,8 @@ void MutableComplex::prune_complex(size_t nsteps, size_t flags) std::vector MutableComplex::prune_betti(size_t nsteps, size_t flags) { + (void) nsteps; + (void) flags; /* TODO: * separate all units into a square matrix (efficiently?) * start with the ones in sparcest row/column. @@ -200,6 +204,7 @@ std::vector MutableComplex::prune_betti(size_t nsteps, size_t flags) VECTOR(MutableMatrix *) MutableComplex::prune_morphisms(size_t nsteps, size_t flags) { + (void) nsteps; for (size_t n = 0; n < mMorphisms.size(); ++n) if (flags & FLAG_TRACE_MORPHISMS) if (mBetti[n] < mMorphisms[n]->n_cols()) diff --git a/M2/Macaulay2/e/mutablemat-defs.hpp b/M2/Macaulay2/e/mutablemat-defs.hpp index b41e74a8d01..d40012db093 100644 --- a/M2/Macaulay2/e/mutablemat-defs.hpp +++ b/M2/Macaulay2/e/mutablemat-defs.hpp @@ -23,11 +23,13 @@ bool isDense(const MT& mat); template bool isDense(const DMat& mat) { + (void) mat; return true; } template bool isDense(const SMat& mat) { + (void) mat; return false; } @@ -235,6 +237,7 @@ class MutableMat : public MutableMatrix result->mat.grab(m); return result; #endif + (void) prefer_dense; return clone(); } @@ -542,9 +545,24 @@ class MutableMat : public MutableMatrix // promote, lift, eval //////// /////////////////////////////// - virtual MutableMatrix* promote(const Ring* S) const { return 0; } - virtual MutableMatrix* lift(const Ring* R) const { return 0; } - virtual MutableMatrix* eval(const RingMap* F) const { return 0; } + virtual MutableMatrix* promote(const Ring* S) const + { + (void) S; + return 0; + } + + virtual MutableMatrix* lift(const Ring* R) const + { + (void) R; + return 0; + } + + virtual MutableMatrix* eval(const RingMap* F) const + { + (void) F; + return 0; + } + /////////////////////////////// // Matrix operations ////////// /////////////////////////////// diff --git a/M2/Macaulay2/e/mutablemat-imp.hpp b/M2/Macaulay2/e/mutablemat-imp.hpp index 574c14fdb0d..d700c9f1d6b 100644 --- a/M2/Macaulay2/e/mutablemat-imp.hpp +++ b/M2/Macaulay2/e/mutablemat-imp.hpp @@ -320,6 +320,7 @@ template engine_RawArrayIntPairOrNull MutableMat::LQUPFactorizationInPlace( bool transpose) { + (void) transpose; throw exc::engine_error( "LU decomposition currently not implemented for this ring and matrix " "type"); diff --git a/M2/Macaulay2/e/newdelete.hpp b/M2/Macaulay2/e/newdelete.hpp index e27dce59809..9d10cc02de3 100644 --- a/M2/Macaulay2/e/newdelete.hpp +++ b/M2/Macaulay2/e/newdelete.hpp @@ -1,8 +1,8 @@ #ifndef NEWDELETE_H #define NEWDELETE_H 1 -#include "M2mem.h" // for freemem, getmem, outofmem2 -#include "debug.h" // for TRAPCHK, TRAPCHK_SIZE +#include "interface/m2-mem.h" // for freemem, getmem, outofmem2 +//#include "debug.h" // for TRAPCHK, TRAPCHK_SIZE #include "M2/gc-include.h" // for GC_base, NULL, size_t, GC_REGISTER_FINALI... // IWYU pragma: begin_exports @@ -10,9 +10,9 @@ // IWYU pragma: end_exports #include -#ifdef MEMDEBUG -#include -#endif +// #ifdef MEMDEBUG +// #include +// #endif /** @brief a version of the STL vector, which allocates its backing memory with gc. @@ -99,19 +99,23 @@ struct our_new_delete static inline void *operator new(size_t size, void *existing_memory) { + (void) size; return existing_memory; } static inline void *operator new[](size_t size, void *existing_memory) { + (void) size; return existing_memory; } static inline void operator delete(void *obj, void *existing_memory) { + (void) existing_memory; TRAPCHK(obj); } static inline void operator delete[](void *obj, void *existing_memory) { + (void) existing_memory; TRAPCHK(obj); } @@ -149,6 +153,7 @@ class our_gc_cleanup: public our_new_delete static inline void cleanup(void* obj, void* displ) { + (void) displ; #ifdef MEMDEBUG obj = M2_debug_to_inner(obj); #endif diff --git a/M2/Macaulay2/e/poly.cpp b/M2/Macaulay2/e/poly.cpp index c3be6d673e6..b4e0186e6b8 100644 --- a/M2/Macaulay2/e/poly.cpp +++ b/M2/Macaulay2/e/poly.cpp @@ -259,6 +259,18 @@ bool PolyRing::from_Interval(gmp_RRi z, ring_elem &result) const return true; } +bool PolyRing::from_ComplexInterval(gmp_CCi z, ring_elem &result) const +{ + ring_elem a; + if (!K_->from_ComplexInterval(z, a)) + { + result = ZERO_RINGELEM; + return false; + } + result = fromCoefficient(a); + return true; +} + bool PolyRing::from_double(double z, ring_elem &result) const { ring_elem a; @@ -649,7 +661,7 @@ ring_elem PolyRing::copy(const ring_elem f) const return head.next; } -void PolyRing::remove(ring_elem &f) const {} +void PolyRing::remove(ring_elem &f) const { (void) f; } void PolyRing::internal_negate_to(ring_elem &f) const { Nterm *v = f; @@ -1834,6 +1846,9 @@ ring_elem *PolyRing::get_parts(const std::vector &wts, // (3) Sort the array, by increasing weight values. // (4) Make an array, copy the elems to it. + (void) wts; + (void) f; + (void) result_len; return nullptr; } diff --git a/M2/Macaulay2/e/poly.hpp b/M2/Macaulay2/e/poly.hpp index 359b4bf8633..9feb0a7f062 100644 --- a/M2/Macaulay2/e/poly.hpp +++ b/M2/Macaulay2/e/poly.hpp @@ -65,6 +65,7 @@ class PolyRing : public PolyRingFlat virtual bool from_BigComplex(gmp_CC z, ring_elem &result) const; virtual bool from_BigReal(gmp_RR z, ring_elem &result) const; virtual bool from_Interval(gmp_RRi z, ring_elem &result) const; + virtual bool from_ComplexInterval(gmp_CCi z, ring_elem &result) const; virtual bool from_double(double a, ring_elem &result) const; virtual bool from_complex_double(double re, double im, diff --git a/M2/Macaulay2/e/polyquotient.hpp b/M2/Macaulay2/e/polyquotient.hpp index 1bc73869912..90d39abf60d 100644 --- a/M2/Macaulay2/e/polyquotient.hpp +++ b/M2/Macaulay2/e/polyquotient.hpp @@ -75,6 +75,18 @@ class PolyRingQuotient : public PolyRingFlat normal_form(result); return ret; } + virtual bool from_Interval(gmp_RRi a, ring_elem &result) const + { + bool ret = numerR_->from_Interval(a, result); + normal_form(result); + return ret; + } + virtual bool from_ComplexInterval(gmp_CCi a, ring_elem &result) const + { + bool ret = numerR_->from_ComplexInterval(a, result); + normal_form(result); + return ret; + } virtual bool from_double(double a, ring_elem &result) const { bool ret = numerR_->from_double(a, result); diff --git a/M2/Macaulay2/e/qring.hpp b/M2/Macaulay2/e/qring.hpp index 6b125724aad..7f55389b7d5 100644 --- a/M2/Macaulay2/e/qring.hpp +++ b/M2/Macaulay2/e/qring.hpp @@ -45,13 +45,22 @@ class QRingInfo : public our_new_delete return quotient_gbvectors[i]; } - virtual void normal_form(ring_elem &f) const {} - virtual void gbvector_normal_form(const FreeModule *F, gbvector *&f) const {} + virtual void normal_form(ring_elem &f) const + { + (void) f; + } + virtual void gbvector_normal_form(const FreeModule *F, gbvector *&f) const + { + (void) F; + (void) f; + } virtual void gbvector_normal_form(const FreeModule *F, gbvector *&f, bool use_denom, ring_elem &denom) const { + (void) use_denom; + (void) denom; gbvector_normal_form(F, f); } diff --git a/M2/Macaulay2/e/reducedgb-ZZ.cpp b/M2/Macaulay2/e/reducedgb-ZZ.cpp index 1fd442e1f9e..e15efea358c 100644 --- a/M2/Macaulay2/e/reducedgb-ZZ.cpp +++ b/M2/Macaulay2/e/reducedgb-ZZ.cpp @@ -24,7 +24,7 @@ ReducedGB_ZZ::ReducedGB_ZZ(GBRing *R0, ringtableZZ = originalR->get_quotient_MonomialTableZZ(); } -void ReducedGB_ZZ::set_gb(VECTOR(POLY) & polys0) {} +void ReducedGB_ZZ::set_gb(VECTOR(POLY) & polys0) { (void) polys0; } struct ReducedGB_ZZ_sorter { GBRing *R; @@ -136,6 +136,8 @@ void ReducedGB_ZZ::remainder(POLY &f, bool use_denom, ring_elem &denom) gbvector *zero = nullptr; gbvector head; gbvector *frem = &head; + (void) use_denom; + (void) denom; frem->next = nullptr; POLY h = f; exponents_t EXP = ALLOCATE_EXPONENTS(R->exponent_byte_size()); @@ -179,6 +181,8 @@ void ReducedGB_ZZ::remainder(gbvector *&f, bool use_denom, ring_elem &denom) gbvector *zero = nullptr; gbvector head; gbvector *frem = &head; + (void) use_denom; + (void) denom; frem->next = nullptr; gbvector *h = f; exponents_t EXP = ALLOCATE_EXPONENTS(R->exponent_byte_size()); diff --git a/M2/Macaulay2/e/reducedgb-field.cpp b/M2/Macaulay2/e/reducedgb-field.cpp index e17cb19c298..a80fff35486 100644 --- a/M2/Macaulay2/e/reducedgb-field.cpp +++ b/M2/Macaulay2/e/reducedgb-field.cpp @@ -11,7 +11,7 @@ ReducedGB_Field::~ReducedGB_Field() Rideal = nullptr; } -void ReducedGB_Field::set_gb(VECTOR(POLY) & polys0) {} +void ReducedGB_Field::set_gb(VECTOR(POLY) & polys0) { (void) polys0; } struct ReducedGB_Field_sorter { GBRing *R; @@ -104,6 +104,7 @@ void ReducedGB_Field::remainder(POLY &f, bool use_denom, ring_elem &denom) frem->next = nullptr; POLY h = f; exponents_t EXP = ALLOCATE_EXPONENTS(R->exponent_byte_size()); + while (!R->gbvector_is_zero(h.f)) { R->gbvector_get_lead_exponents(F, h.f, EXP); @@ -140,11 +141,25 @@ void ReducedGB_Field::remainder(POLY &f, bool use_denom, ring_elem &denom) } } } + h.f = head.next; - // R->gbvector_remove_content(h.f, h.fsyz, use_denom, denom); - f.f = h.f; + + ring_elem denom1; + ring_elem one_elem = R->get_flattened_coefficients()->one(); + denom1 = R->get_flattened_coefficients()->one(); // is replaced on next line originalR->get_quotient_info()->gbvector_normal_form( - Fsyz, h.fsyz, use_denom, denom); + Fsyz, h.fsyz, true, denom1); + if (EQ != R->get_flattened_coefficients()->compare_elems(denom1, one_elem)) + { + // multiply h.f by denom1. + R->gbvector_mult_by_coeff_to(h.f, denom1); + } + if (use_denom) + { + R->get_flattened_coefficients()->mult_to(denom, denom1); + } + + f.f = h.f; f.fsyz = h.fsyz; } diff --git a/M2/Macaulay2/e/reducedgb-marked.cpp b/M2/Macaulay2/e/reducedgb-marked.cpp index 1f1b1a9c104..7c5efd71f75 100644 --- a/M2/Macaulay2/e/reducedgb-marked.cpp +++ b/M2/Macaulay2/e/reducedgb-marked.cpp @@ -18,7 +18,7 @@ MarkedGB::~MarkedGB() freemem(leadterms); } -void MarkedGB::set_gb(VECTOR(POLY) & polys0) {} +void MarkedGB::set_gb(VECTOR(POLY) & polys0) { (void) polys0; } struct MarkedGB_sorter { @@ -49,6 +49,7 @@ void MarkedGB::add_marked_elems(const VECTOR(gbvector *) & leadterms0, const VECTOR(POLY) & polys0, bool auto_reduced) { + (void) auto_reduced; // First sort these elements via increasing lex order (or monomial order?) // Next insert minimal elements into T, and polys const Monoid *M = originalR->getMonoid(); @@ -211,6 +212,8 @@ void MarkedGB::geo_remainder(gbvector *&f, bool use_denom, ring_elem &denom) { gbvector head; gbvector *frem = &head; + (void) use_denom; + (void) denom; frem->next = nullptr; gbvectorHeap fb(R, F); diff --git a/M2/Macaulay2/e/reducedgb.hpp b/M2/Macaulay2/e/reducedgb.hpp index 7d607ed97fa..0cbad89e480 100644 --- a/M2/Macaulay2/e/reducedgb.hpp +++ b/M2/Macaulay2/e/reducedgb.hpp @@ -98,6 +98,8 @@ class ReducedGB : public GBComputation virtual void minimalize(const VECTOR(POLY) & polys0, bool auto_reduce = true) { + (void) polys0; + (void) auto_reduce; } // I have to decide: does this ADD to the existing set? diff --git a/M2/Macaulay2/e/res-a0.cpp b/M2/Macaulay2/e/res-a0.cpp index ea36c7bc8a0..70e4c5a0366 100644 --- a/M2/Macaulay2/e/res-a0.cpp +++ b/M2/Macaulay2/e/res-a0.cpp @@ -759,7 +759,7 @@ int res2_comp::compare_res2_pairs(res2_pair *f, res2_pair *g) const if (EXP1[i] > EXP2[i]) return compare_use_descending; } } - // Fall through to COMPARE_ORDER + [[fallthrough]]; case COMPARE_ORDER: cmp = M->compare(f->syz->monom, g->syz->monom); if (cmp != 0) return compare_use_descending * cmp; diff --git a/M2/Macaulay2/e/res-a2.cpp b/M2/Macaulay2/e/res-a2.cpp index 253d8e9b01c..0d10d0e9402 100644 --- a/M2/Macaulay2/e/res-a2.cpp +++ b/M2/Macaulay2/e/res-a2.cpp @@ -73,7 +73,7 @@ void gb_emitter::flush() bool gb_emitter::is_done() { return (n_left == 0); } void gb_emitter::stats() const {} -void gb_emitter::text_out(buffer &o) const {} +void gb_emitter::text_out(buffer &o) const { (void) o; } typedef gb_node *gb_node_ptr; void gbres_comp::setup(const Matrix *m, int length, int origsyz, int strategy) diff --git a/M2/Macaulay2/e/ring-vecs.cpp b/M2/Macaulay2/e/ring-vecs.cpp index c0ab6c54dfe..33f8f3dcff2 100644 --- a/M2/Macaulay2/e/ring-vecs.cpp +++ b/M2/Macaulay2/e/ring-vecs.cpp @@ -314,6 +314,7 @@ void Ring::vec_text_out(buffer &o, vec Ring::vec_eval(const RingMap *map, const FreeModule *F, const vec v) const // v is a vector over 'this' { + (void) F; const Ring *targetRing = map->get_ring(); vecterm head; @@ -663,6 +664,8 @@ void Ring::vec_sort(vecterm *&f) const vec Ring::vec_lead_term(int nparts, const FreeModule *F, vec v) const { + (void) nparts; + (void) F; // May be over-ridden by subclasses. In particular, by polynomial classes. if (v == nullptr) return nullptr; return make_vec(v->comp, v->coeff); diff --git a/M2/Macaulay2/e/ring.cpp b/M2/Macaulay2/e/ring.cpp index 4b9b94b483e..a5d7d28306c 100644 --- a/M2/Macaulay2/e/ring.cpp +++ b/M2/Macaulay2/e/ring.cpp @@ -4,6 +4,7 @@ #include "ZZ.hpp" // for RingZZ #include "coeffrings.hpp" // for CoefficientRingR +#include "exceptions.hpp" // for exc::engine_error #include "freemod.hpp" // for FreeModule #include "monoid.hpp" // for Monoid #include "poly.hpp" // for PolyRing @@ -86,14 +87,16 @@ ring_elem Ring::get_non_unit() const void Ring::set_non_unit(ring_elem non_unit) const { - if (_isfield == 1) // i.e. declared to be a field - ERROR("a non unit was found in a ring declared to be a field"); + bool was_field = (_isfield == 1); const_cast(this)->_isfield = -1; const_cast(this)->_non_unit = non_unit; + if (was_field) + throw exc::engine_error("a non unit was found in a ring declared to be a field"); } ring_elem Ring::var(int v) const { + (void) v; // The default behavior is to just return 0. return zeroV; } @@ -232,23 +235,33 @@ ring_elem Ring::remainderAndQuotient(const ring_elem f, std::pair Ring::coerceToLongInteger(ring_elem a) const { + (void) a; return std::pair(false, 0); // the default is that it cannot be lifted. } bool Ring::from_BigComplex(gmp_CC z, ring_elem &result) const { + (void) z; result = from_long(0); return false; } bool Ring::from_BigReal(gmp_RR z, ring_elem &result) const { + (void) z; result = from_long(0); return false; } bool Ring::from_Interval(gmp_RRi z, ring_elem &result) const +{ + (void) z; + result = from_long(0); + return false; +} + +bool Ring::from_ComplexInterval(gmp_CCi z, ring_elem &result) const { result = from_long(0); return false; @@ -256,11 +269,14 @@ bool Ring::from_Interval(gmp_RRi z, ring_elem &result) const bool Ring::from_double(double a, ring_elem &result) const { + (void) a; result = from_long(0); return false; } bool Ring::from_complex_double(double re, double im, ring_elem &result) const { + (void) re; + (void) im; result = from_long(0); return false; } @@ -332,29 +348,41 @@ ring_elem Ring::split_off_content(ring_elem f, ring_elem &result) const void Ring::monomial_divisor(const ring_elem a, exponents_t exp) const { - // Do nothing + (void) a; + (void) exp; } ring_elem Ring::diff(ring_elem a, ring_elem b, int use_coeff) const { + (void) use_coeff; return mult(a, b); } -bool Ring::in_subring(int nslots, const ring_elem a) const { return true; } +bool Ring::in_subring(int nslots, const ring_elem a) const +{ + (void) nslots; + (void) a; + return true; +} + void Ring::degree_of_var(int n, const ring_elem a, int &lo, int &hi) const { + (void) n; + (void) a; lo = 0; hi = 0; } ring_elem Ring::divide_by_var(int n, int d, const ring_elem a) const { + (void) n; if (d == 0) return a; return from_long(0); } ring_elem Ring::divide_by_expvector(const_exponents exp, const ring_elem a) const { + (void) exp; return a; } @@ -379,6 +407,7 @@ bool Ring::is_homogeneous(const ring_elem) const { return true; } bool Ring::multi_degree(const ring_elem f, monomial d) const // returns true iff f is homogeneous { + (void) f; degree_monoid()->one(d); return true; } @@ -391,9 +420,15 @@ void Ring::degree_weights(const ring_elem, lo = hi = 0; } -int Ring::index_of_var(const ring_elem a) const { return -1; } +int Ring::index_of_var(const ring_elem a) const +{ + (void) a; + return -1; +} + M2_arrayint Ring::support(const ring_elem a) const { + (void) a; M2_arrayint result = M2_makearrayint(0); return result; } @@ -404,6 +439,7 @@ unsigned long Ring::get_precision() const { return 0; } ring_elem Ring::zeroize_tiny(gmp_RR epsilon, const ring_elem f) const // Default is to return f itself. { + (void) epsilon; return f; } @@ -412,6 +448,8 @@ void Ring::increase_maxnorm(gmp_RRmutable norm, const ring_elem f) const // replace norm. { // Default for rings not over RRR or CCC is to do nothing. + (void) norm; + (void) f; } /////////////////////////////////// diff --git a/M2/Macaulay2/e/ring.hpp b/M2/Macaulay2/e/ring.hpp index 06fc539b09d..e3c209db27d 100644 --- a/M2/Macaulay2/e/ring.hpp +++ b/M2/Macaulay2/e/ring.hpp @@ -28,6 +28,7 @@ class PolyRingFlat; class PolynomialRing; class RRR; class RRi; +class CCi; class RingMap; class RingZZ; class SchurRing2; @@ -276,6 +277,7 @@ class Ring : public MutableEngineObject // If a is zero, then r is set to -1. virtual long discreteLog(const ring_elem &a) const { + (void) a; throw exc::engine_error("cannot compute discrete logarithm in this ring"); } @@ -287,6 +289,7 @@ class Ring : public MutableEngineObject // for that call. virtual const RingElement *getRepresentation(const ring_elem &a) const { + (void) a; return nullptr; } @@ -294,6 +297,9 @@ class Ring : public MutableEngineObject size_t ncols, bool dense) const { + (void) nrows; + (void) ncols; + (void) dense; return nullptr; } @@ -328,6 +334,8 @@ class Ring : public MutableEngineObject virtual bool from_Interval(gmp_RRi a, ring_elem &result) const; // The default version calls from_long(0) and returns false. virtual bool from_BigComplex(gmp_CC z, ring_elem &result) const; + // The default version calls from_long(0) and returns false. + virtual bool from_ComplexInterval(gmp_CCi z, ring_elem &result) const; // Returns false if this ring cannot coerce a double to an element in this // ring virtual bool from_double(double a, ring_elem &result) const; diff --git a/M2/Macaulay2/e/ringelem.hpp b/M2/Macaulay2/e/ringelem.hpp index 1153ec1e319..cb863de1306 100644 --- a/M2/Macaulay2/e/ringelem.hpp +++ b/M2/Macaulay2/e/ringelem.hpp @@ -18,22 +18,30 @@ using RRimutable = mpfi_ptr; // The following is the data type used for complex numbers in aring-CCC // Perhaps we should have it be -typedef struct +struct cc_struct { __mpfr_struct re; __mpfr_struct im; -} cc_struct; +}; using cc_ptr = cc_struct *; using cc_srcptr = cc_struct const *; -typedef struct +struct cc_doubles_struct { double re; double im; -} cc_doubles_struct; +}; using cc_doubles_srcptr = cc_doubles_struct const *; using cc_doubles_ptr = cc_doubles_struct *; +struct cci_struct +{ + __mpfi_struct re; + __mpfi_struct im; +}; +using cci_ptr = cci_struct *; +using cci_srcptr = cci_struct const *; + struct Nterm; typedef Nterm *tpoly; class schur_poly; @@ -55,6 +63,7 @@ union ring_elem mpfi_srcptr mpfi_val; cc_doubles_srcptr cc_doubles_val; cc_srcptr cc_val; + cci_srcptr cci_val; const void *mPolyVal; public: ring_elem() : poly_val(nullptr) {} @@ -68,6 +77,7 @@ union ring_elem explicit ring_elem(mpfr_srcptr a) : mpfr_val(a) {} explicit ring_elem(mpfi_srcptr a) : mpfi_val(a) {} explicit ring_elem(cc_srcptr a) : cc_val(a) {} + explicit ring_elem(cci_srcptr a) : cci_val(a) {} explicit ring_elem(cc_doubles_srcptr a) : cc_doubles_val(a) {} explicit ring_elem(local_elem* a) : local_val(a) {} explicit ring_elem(const void* a) : mPolyVal(a) {} // non-commutative polynomials @@ -85,7 +95,9 @@ union ring_elem mpq_srcptr get_mpq() const { return mpq_val; } mpfr_srcptr get_mpfr() const { return mpfr_val; } mpfi_srcptr get_mpfi() const { return mpfi_val; } + cc_srcptr get_cc() const { return cc_val; } + cci_srcptr get_cci() const { return cci_val; } cc_doubles_srcptr get_cc_doubles() const { return cc_doubles_val; } const local_elem* get_local_elem() const { return local_val; } const schur_poly* get_schur_poly() const { return schur_poly_val; } diff --git a/M2/Macaulay2/e/sagbi.cpp b/M2/Macaulay2/e/sagbi.cpp index 6ca53607c3a..f44bfd649d4 100644 --- a/M2/Macaulay2/e/sagbi.cpp +++ b/M2/Macaulay2/e/sagbi.cpp @@ -83,7 +83,8 @@ ring_elem sagbi::subduct1(int numslots, MatrixConstructor matT(T->make_FreeModule(1), 1); MatrixConstructor matS(S->make_FreeModule(1), 1); bool breakFlag = false; - + + (void) numslots; while ((f != nullptr) && (breakFlag == false)) { // tensorRingg = S#"inclusionAmbient" liftg diff --git a/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.cpp b/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.cpp index 53185015c9e..a2fa6fcd13b 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.cpp @@ -58,6 +58,8 @@ TBBNodePtr DependencyGraph::createFillAndReduceNode(int lev, int sldeg) TBBNodePtr DependencyGraph::createRankNode(int lev, int sldeg) { + (void) lev; + (void) sldeg; return std::make_shared(mTBBGraph, [](const tbb::flow::continue_msg &msg) { @@ -89,9 +91,12 @@ TBBNodePtr DependencyGraph::createRankNode(int lev, int sldeg) TBBNodePtr DependencyGraph::createMinimalBettiNode(int lev, int sldeg) { + (void) lev; + (void) sldeg; return std::make_shared(mTBBGraph, [](const tbb::flow::continue_msg &msg) { + (void) msg; //std::lock_guard guard(mMutex); //std::cout << "minimal betti node lev=" << lev << " sldeg=" // << sldeg << " sum=" << lev + sldeg << std::endl; diff --git a/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.hpp b/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.hpp index e04622eda1b..3ff679faf35 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.hpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-dep-graph.hpp @@ -19,13 +19,15 @@ class F4Res; inline int getIndex(int lev, int sldeg, int nlevels, int nslanted_degrees) { - return lev + (sldeg * nlevels); + (void) nslanted_degrees; + return lev + (sldeg * nlevels); } // return value is (lev, sldeg) inline std::pair getPair(int index, int nlevels, int nslanted_degrees) { - return std::make_pair(index % nlevels, index / nlevels); + (void) nslanted_degrees; + return std::make_pair(index % nlevels, index / nlevels); } struct Node { diff --git a/M2/Macaulay2/e/schreyer-resolution/res-f4-computation.cpp b/M2/Macaulay2/e/schreyer-resolution/res-f4-computation.cpp index 5acfd37b27f..a0e74e8f78e 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-f4-computation.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-f4-computation.cpp @@ -10,6 +10,8 @@ #include "matrix.hpp" #include "exceptions.hpp" +#include + class ResolutionComputation; class MutableMatrix; @@ -35,6 +37,7 @@ ResolutionComputation* createF4Res(const Matrix* groebnerBasisMatrix, // We assume also that the matrix is homogeneous. // If any of these are incorrect, an error message is provided, and // null is returned. + (void) strategy; const PolynomialRing* origR = groebnerBasisMatrix->get_ring()->cast_to_PolynomialRing(); if (origR == nullptr) @@ -216,6 +219,12 @@ M2_arrayint F4ResComputation::minimal_betti(M2_arrayint slanted_degree_limit, int new_length_limit = (length_limit->len == 1 ? length_limit->array[0] : frame().maxLevel() - 1); + // std::cout << "---- show mComp ------------------------" << std::endl; + // mComp->show(0); + // std::cout << "stop, topdeg, newlength: " << stop_after_degree << " " + // << top_slanted_degree << " " << new_length_limit << std::endl; + // std::cout << "---- end show mComp ------------------------" << std::endl; + BettiDisplay B = frame().minimalBettiNumbers( stop_after_degree, top_slanted_degree, new_length_limit); return B.getBetti(); diff --git a/M2/Macaulay2/e/schreyer-resolution/res-f4-m2-interface.cpp b/M2/Macaulay2/e/schreyer-resolution/res-f4-m2-interface.cpp index 72809f84565..17b25aece3a 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-f4-m2-interface.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-f4-m2-interface.cpp @@ -12,7 +12,7 @@ #include "comp.hpp" // for Computation #include "mat-linalg.hpp" // for DMatLinAlg #include "dmat.hpp" // for DMat -#include "engine-exports.h" // for M2_arrayint +#include "interface/m2-types.h" // for M2_arrayint #include "error.h" // for ERROR #include "exceptions.hpp" // for engine_error #include "freemod.hpp" // for FreeModule diff --git a/M2/Macaulay2/e/schreyer-resolution/res-f4-monlookup.cpp b/M2/Macaulay2/e/schreyer-resolution/res-f4-monlookup.cpp index 8f0ff511de8..92d50523771 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-f4-monlookup.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-f4-monlookup.cpp @@ -3,7 +3,7 @@ #include "schreyer-resolution/res-f4-monlookup.hpp" #include "buffer.hpp" // for buffer -#include "engine-exports.h" // for newline +#include "interface/m2-types.h" // for newline #include "mem.hpp" // for stash #include "schreyer-resolution/res-monomial-types.hpp" // for index_res_v... #include "style.hpp" // for INTSIZE diff --git a/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.cpp b/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.cpp index df9b8a0ee33..f314a7b0004 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.cpp @@ -11,6 +11,8 @@ ResMonoidDense::ResMonoidDense(int nvars0, const std::vector& weightvecs, const MonomialOrderingType moType) { + (void) weightvecs; + (void) moType; nvars = nvars0; hashfcn = std::unique_ptr(new res_monomial_word[nvars]); for (int i = 0; i < nvars; i++) hashfcn[i] = rand(); diff --git a/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.hpp b/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.hpp index 48be26f5e25..85d2292519e 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.hpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-moninfo-dense.hpp @@ -63,7 +63,12 @@ class ResMonoidDense int n_vars() const { return nvars; } int max_monomial_size() const { return nslots; } - int monomial_size(res_const_packed_monomial m) const { return nslots; } + int monomial_size(res_const_packed_monomial m) const + { + (void) m; + return nslots; + } + void show() const; res_monomial_word hash_value(res_const_packed_monomial m) const diff --git a/M2/Macaulay2/e/schreyer-resolution/res-moninfo-sparse.cpp b/M2/Macaulay2/e/schreyer-resolution/res-moninfo-sparse.cpp index 3396eb4974f..5497bbba1e0 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-moninfo-sparse.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-moninfo-sparse.cpp @@ -3,7 +3,7 @@ #include "res-moninfo-sparse.hpp" #include // for fprintf, stderr #include // for rand -#include "engine-exports.h" // for M2_gbTrace +#include "interface/m2-types.h" // for M2_gbTrace #include "schreyer-resolution/res-monomial-types.hpp" // for res_monomial_word ResMonoidSparse::ResMonoidSparse(int nvars, diff --git a/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.cpp b/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.cpp index 5288ba25d14..8f734432bd8 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.cpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.cpp @@ -181,6 +181,8 @@ BettiDisplay SchreyerFrame::minimalBettiNumbers(bool stop_after_degree, // increase mComputationStatus if needed, mMinimalBetti, ... // computeFrame() } + // std::cout << "std::min(mMaxLength-1, length_limit): " << mMaxLength-1 << " " << length_limit << std::endl; + // length_limit = std::min(mMaxLength-1, length_limit); #if defined(WITH_TBB) // build the dependency graph @@ -204,6 +206,10 @@ BettiDisplay SchreyerFrame::minimalBettiNumbers(bool stop_after_degree, // Also note: if hideg is the highest degree that occurs in the frame, we do // not need to compute any matrices for these. + // std::cout << "res-schreyer-frame:208, mLoSlantedDegree, top_degree, " + // "length_limit, mMaxLength: " + // << mLoSlantedDegree << " " << top_degree << " " << length_limit << " " << mMaxLength << std::endl; + for (int deg = mLoSlantedDegree; deg <= top_degree - 1; deg++) for (int lev = 1; lev <= length_limit + 1; lev++) { @@ -244,8 +250,12 @@ BettiDisplay SchreyerFrame::minimalBettiNumbers(bool stop_after_degree, } BettiDisplay B(mBettiMinimal); // copy + // std::cout << "in res-schreyer-frame.cpp, minimalBettiNumbers A\n"; + // B.output(); B.resize(mLoSlantedDegree, top_degree, length_limit); - + // std::cout << "in res-schreyer-frame.cpp, minimalBettiNumbers B\n"; + // B.output(); + // std::cout << "mMaxLength, mLevels.size(): " << mMaxLength << " " << mFrame.mLevels.size() << std::endl; return B; } @@ -618,6 +628,7 @@ void SchreyerFrame::insertLevelZero(res_packed_monomial monom, int degree, int maxdeglevel0) { + (void) maxdeglevel0; auto& myframe = level(0); long idx = myframe.size(); myframe.emplace_back(FrameElement(monom, degree)); @@ -843,6 +854,8 @@ void SchreyerFrame::setBettiDisplays() } } + // std::cout << "--- minimal betti set ---\n"; + // mBettiMinimal.output(); #if 0 // Now set the todo list of pairs (degree, level) for minimalization. for (int slanted_degree = lo; slanted_degree < hi; slanted_degree++) @@ -965,6 +978,9 @@ void SchreyerFrame::computeRank(int slanted_degree, int lev) mBettiMinimal.entry(slanted_degree, lev) -= rk; if (slanted_degree <= mHiSlantedDegree and lev > 0) mBettiMinimal.entry(slanted_degree + 1, lev - 1) -= rk; + + // std::cout << "--- minimal betti after computeRank: " << slanted_degree << " " << lev << std::endl; + // mBettiMinimal.output(); } status = 3; } diff --git a/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.hpp b/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.hpp index ced3ee51dcf..1e4e7ba26b4 100644 --- a/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.hpp +++ b/M2/Macaulay2/e/schreyer-resolution/res-schreyer-frame.hpp @@ -22,7 +22,7 @@ #include "m2tbb.hpp" // for TBB headers #include "betti.hpp" // for BettiDisplay -#include "engine-exports.h" // for M2_arrayint +#include "interface/m2-types.h" // for M2_arrayint #include "schreyer-resolution/res-memblock.hpp" // for ResMemoryBlock #include "schreyer-resolution/res-moninfo.hpp" // for ResMonoid #include "schreyer-resolution/res-monomial-types.hpp" // for component_index diff --git a/M2/Macaulay2/e/schur.cpp b/M2/Macaulay2/e/schur.cpp index c93ad97e39a..ee83504dbab 100644 --- a/M2/Macaulay2/e/schur.cpp +++ b/M2/Macaulay2/e/schur.cpp @@ -95,12 +95,15 @@ SchurRing *SchurRing::create(const PolynomialRing *R) SchurRing *SchurRing::create(const Ring *A, int n) { + (void) A; + (void) n; ERROR("not implemented yet"); return nullptr; } SchurRing *SchurRing::createInfinite(const Ring *A) { + (void) A; ERROR("not implemented yet"); return nullptr; } diff --git a/M2/Macaulay2/e/schur2.cpp b/M2/Macaulay2/e/schur2.cpp index 8f0c0efa80a..7c37f8a6d1d 100644 --- a/M2/Macaulay2/e/schur2.cpp +++ b/M2/Macaulay2/e/schur2.cpp @@ -15,6 +15,8 @@ const int LARGE_NUMBER = 32000; void tableau2::initialize(int nvars, int maxwt0) { + (void) nvars; + (void) maxwt0; maxwt = SCHUR_MAX_WT; wt = 0; lambda = nullptr; @@ -318,12 +320,15 @@ ring_elem SchurRing2::copy(const ring_elem f) const ring_elem SchurRing2::invert(const ring_elem f) const { + (void) f; // This function is not relevant for this ring return zero(); } ring_elem SchurRing2::divide(const ring_elem f, const ring_elem g) const { + (void) f; + (void) g; // This function is not relevant for this ring return zero(); } @@ -333,6 +338,8 @@ void SchurRing2::syzygy(const ring_elem a, ring_elem &x, ring_elem &y) const { + (void) a; + (void) b; // This function is not relevant for this ring x = zero(); y = zero(); @@ -712,6 +719,8 @@ ring_elem SchurRing2::eval(const RingMap *map, const ring_elem f, int first_var) const { + (void) f; + (void) first_var; // Should we allow ring maps to other Schur rings? No others are that well // defined... // Use promote and lift for those instead? diff --git a/M2/Macaulay2/e/schur2.hpp b/M2/Macaulay2/e/schur2.hpp index 77a88208e01..91753b1ea69 100644 --- a/M2/Macaulay2/e/schur2.hpp +++ b/M2/Macaulay2/e/schur2.hpp @@ -173,7 +173,10 @@ class SchurRing2 : public Ring virtual int compare_elems(const ring_elem f, const ring_elem g) const; virtual ring_elem copy(const ring_elem f) const; - virtual void remove(ring_elem &f) const {} // let the GC do it! + virtual void remove(ring_elem &f) const + { + (void) f; + } // let the GC do it! virtual ring_elem negate(const ring_elem f) const; virtual ring_elem add(const ring_elem f, const ring_elem g) const; virtual ring_elem subtract(const ring_elem f, const ring_elem g) const; diff --git a/M2/Macaulay2/e/solvable.cpp b/M2/Macaulay2/e/solvable.cpp index 2ebf4fd2d52..e8277ce178e 100644 --- a/M2/Macaulay2/e/solvable.cpp +++ b/M2/Macaulay2/e/solvable.cpp @@ -56,6 +56,9 @@ ring_elem SolvableAlgebra::mult_by_term(const ring_elem f, // Computes c*m*f, BUT NOT doing normal form wrt a quotient ideal.. { // TODO + (void) f; + (void) c; + (void) m; #ifdef DEVELOPMENT #warning "implement SolvableAlgebra::mult_by_term" #endif diff --git a/M2/Macaulay2/e/spair.cpp b/M2/Macaulay2/e/spair.cpp index 8f5001c3098..7570379f252 100644 --- a/M2/Macaulay2/e/spair.cpp +++ b/M2/Macaulay2/e/spair.cpp @@ -269,6 +269,7 @@ void s_pair_heap::put_back(s_pair *&p) void s_pair_heap::stats() const {} void s_pair_heap::text_out(buffer &o) const { + (void) o; #ifdef DEVELOPMENT #warning "should we display anything in spair text_out, stats?" #endif diff --git a/M2/Macaulay2/e/table.c b/M2/Macaulay2/e/table.c index 35907450f6c..615f0f2bbdb 100644 --- a/M2/Macaulay2/e/table.c +++ b/M2/Macaulay2/e/table.c @@ -4,6 +4,12 @@ #include #include +#include "interface/m2-mem.h" + +/*these next lines added by MES, July 2002, to use our gc routines..*/ +#define NEW(p) ((p) = (void *) getmem((long)sizeof *(p))) +#define FREE(ptr) ((void)(freemem((ptr)), (ptr) = 0)) + #define T Table_T struct T { diff --git a/M2/Macaulay2/e/table.h b/M2/Macaulay2/e/table.h index 34386b979db..3ed59875332 100644 --- a/M2/Macaulay2/e/table.h +++ b/M2/Macaulay2/e/table.h @@ -5,13 +5,6 @@ #ifndef TABLE_INCLUDED #define TABLE_INCLUDED -/******************************************************/ -/*these next lines added by MES, July 2002, to use our gc routines..*/ -#include "engine-includes.hpp" -#define NEW(p) ((p) = (void *) getmem((long)sizeof *(p))) -#define FREE(ptr) ((void)(freemem((ptr)), (ptr) = 0)) -/******************************************************/ - #define T Table_T struct T; typedef struct T T; diff --git a/M2/Macaulay2/e/tower.cpp b/M2/Macaulay2/e/tower.cpp index 0069b796565..586b24a3005 100644 --- a/M2/Macaulay2/e/tower.cpp +++ b/M2/Macaulay2/e/tower.cpp @@ -54,6 +54,8 @@ Tower *Tower::create(int charac, M2_ArrayString names) Tower *Tower::create(const Tower *R, M2_ArrayString new_names) { // TODO: write + (void) R; + (void) new_names; return nullptr; } @@ -67,6 +69,7 @@ Tower *Tower::create(const Tower *R, VECTOR(ring_elem) & extensions) unsigned int Tower::computeHashValue(const ring_elem a) const { // TODO HASH + (void) a; return 3212415; } @@ -132,6 +135,7 @@ ring_elem Tower::var(int v) const bool Tower::is_unit(const ring_elem f) const { // Write this. Git issue #611. + (void) f; return false; } @@ -317,13 +321,19 @@ ring_elem Tower::eval(const RingMap *map, bool Tower::promote(const Ring *Rf, const ring_elem f, ring_elem &result) const { - // Write this. Git issue #611. + // Write this. Git issue #611 + (void) Rf; + (void) f; + (void) result; return false; } bool Tower::lift(const Ring *Rg, const ring_elem f, ring_elem &result) const { // Write this. Git issue #611. + (void) Rg; + (void) f; + (void) result; return false; } @@ -333,6 +343,10 @@ void Tower::syzygy(const ring_elem a, ring_elem &y) const { // Write this. Git issue #611. + (void) a; + (void) b; + (void) x; + (void) y; } ring_elem Tower::gcd(const ring_elem f, const ring_elem g) const diff --git a/M2/Macaulay2/e/unit-tests/M2mem-replacement.c b/M2/Macaulay2/e/unit-tests/M2mem-replacement.c deleted file mode 100644 index c5a0ba99a00..00000000000 --- a/M2/Macaulay2/e/unit-tests/M2mem-replacement.c +++ /dev/null @@ -1,166 +0,0 @@ -#include -#include -#include "../d/types.h" - -#include "M2mem-replacement.h" - -/* trapchk: taken from d/debug.h *************************************/ - -void *trapaddr = (void *)1; -int trapcount = 0; -int trapset = 0; -size_t trapsize = (size_t)-1; - -void trap(void) {} /* I used to be concerned that this function would get optimized away, but it isn't static ... */ - -void *pointers[10]; /* during debugging we can put pointers here, visible to the garbage collector */ -void trapchk(void *p) { - trapcount++; - if (trapcount == trapset || p == trapaddr || p == (void *)~(intptr_t)trapaddr) trap(); -} -void trapchk_size(size_t n) { - trapcount++; - if (trapcount == trapset || trapsize == n) trap(); -} - -/*********************************************************************/ - -void outofmem(void) { - const char *msg = "\n\n *** out of memory, exiting ***\n"; - int r = write(STDERR,msg,strlen(msg)); - if (r == ERROR) exit(1); - exit(1); -} - -void outofmem2(size_t new) { - const char *msg = "\n\n *** out of memory trying to allocate %ld bytes, exiting ***\n"; - static char buf[sizeof(msg) + 100]; - sprintf(buf,msg,(long)new); - int r = write(STDERR,buf,strlen(buf)); - if (r == ERROR) exit(1); - exit(1); -} - -char *getmem(size_t n) -{ - char *p; - p = GC_MALLOC(n); /* GC_MALLOC clears its memory, but getmem doesn't guarantee to */ - if (p == NULL) outofmem2(n); -#ifndef NDEBUG - memset(p,0xbe,n); /* fill with 0xbebebebe ... */ - trapchk(p); -#endif - return p; -} - -void freememlen(void *s, size_t old) { -# ifndef NDEBUG - trapchk(s); -# endif - GC_FREE(s); -} - -void freemem(void *s) { -# ifndef NDEBUG - trapchk(s); -# endif - GC_FREE(s); -} - -char *getmem_clear(size_t n) -{ - char *p; - p = GC_MALLOC(n); - if (p == NULL) outofmem2(n); - #if 0 - /* - note: GC_MALLOC clears memory before returning. - If you switch to another memory allocator, you must clear it explicitly with this: - */ - bzero(p,n); - #endif - #ifndef NDEBUG - trapchk(p); - #endif - return p; -} - -char *getmem_atomic(size_t n) -{ - char *p; - p = GC_MALLOC_ATOMIC(n); - if (p == NULL) outofmem2(n); -#ifndef NDEBUG - memset(p,0xac,n); /* fill with 0xacacacac ... */ - trapchk(p); -#endif - return p; -} - -char *getmem_malloc(size_t n) -{ - char *p; - p = malloc(n); - if (p == NULL) outofmem2(n); -#ifndef NDEBUG - memset(p,0xca,n); /* fill with 0xcacacaca */ - trapchk(p); -#endif - return p; -} - -char *getmem_atomic_clear(size_t n) -{ - char *p; - p = GC_MALLOC_ATOMIC(n); - if (p == NULL) outofmem2(n); - bzero(p,n); -#ifndef NDEBUG - trapchk(p); -#endif - return p; -} - -char *getmoremem (char *s, size_t old, size_t new) { - void *p; - p = GC_REALLOC(s,new); - if (p == NULL) outofmem2(new); -# ifndef NDEBUG - trapchk(p); -# endif - return p; - } - -char *getmoremem1 (char *s, size_t new) { - void *p; - p = GC_REALLOC(s,new); - if (p == NULL) outofmem2(new); -# ifndef NDEBUG - trapchk(p); -# endif - return p; - } - -char *getmoremem_atomic (char *s, size_t old, size_t new) { - void *p; - p = GC_MALLOC_ATOMIC(new); - size_t min = old 0) memset((char *)p+min,0xbe,excess); /* fill with 0xbebebebe */ - } - trapchk(p); -# endif - return p; - } - -/* Local Variables: - compile-command: "make -C $M2BUILDDIR/Macaulay2/e/unit-tests check " - indent-tabs-mode: nil - End: -*/ - diff --git a/M2/Macaulay2/e/unit-tests/M2mem-replacement.h b/M2/Macaulay2/e/unit-tests/M2mem-replacement.h deleted file mode 100644 index 3e9f7ee7fee..00000000000 --- a/M2/Macaulay2/e/unit-tests/M2mem-replacement.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef M2mem_included -#define M2mem_included - -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -extern void outofmem2(size_t); -extern char *getmem(size_t); -extern void freemem(void *); -extern void freememlen(void *, size_t); -extern char *getmem_clear(size_t); -extern char *getmem_atomic(size_t); -extern char *getmem_malloc(size_t); -extern char *getmem_atomic_clear(size_t); -extern char *getmoremem(char *, size_t oldsize, size_t newsize); -extern char *getmoremem1(char *, size_t newsize); -extern char *getmoremem_atomic(char *, size_t oldsize, size_t newsize); - -#define sizeofarray(s,len) (sizeof(*(s)) + (len)*sizeof((s)->array[0])) -#define sizeofarraytype(S,len) sizeofarray((S)0,len) -#define sizeofstruct(s) sizeof(*(s)) -#define sizeofstructtype(S) sizeofstruct((S)0) -#if defined(__cplusplus) -#define getmemarraytype(S,len) reinterpret_cast(getmem(sizeofarraytype(S,len))) -#define getmemstructtype(S) reinterpret_cast(getmem(sizeofstructtype(S))) -#define getmematomicarraytype(S,len) reinterpret_cast(getmem_atomic(sizeofarraytype(S,len))) -#define getmematomicstructtype(S) reinterpret_cast(getmem_atomic(sizeofstructtype(S))) -#define getmemvectortype(S,len) reinterpret_cast(getmem(sizeof(S)*len))) -#define getmematomicvectortype(S,len) reinterpret_cast(getmem_atomic(sizeof(S)*(len))) -#else -#define getmemarraytype(S,len) (S)(getmem(sizeofarraytype(S,len))) -#define getmemstructtype(S) (S)(getmem(sizeofstructtype(S))) -#define getmematomicarraytype(S,len) (S)(getmem_atomic(sizeofarraytype(S,len))) -#define getmematomicstructtype(S) (S)(getmem_atomic(sizeofstructtype(S))) -#define getmemvectortype(S,len) (S*)(getmem(sizeof(S)*len)) -#define getmematomicvectortype(S,len) (S*)(getmem_atomic(sizeof(S)*(len))) -#endif - -#if defined(__cplusplus) -} -#endif - -#endif - -/* Local Variables: - compile-command: "make -C $M2BUILDDIR/Macaulay2/e/unit-tests check " - indent-tabs-mode: nil - End: -*/ diff --git a/M2/Macaulay2/e/unit-tests/Makefile.files b/M2/Macaulay2/e/unit-tests/Makefile.files index d2df582189c..cc4a8b5e3f8 100644 --- a/M2/Macaulay2/e/unit-tests/Makefile.files +++ b/M2/Macaulay2/e/unit-tests/Makefile.files @@ -28,34 +28,20 @@ UNITTEST_CCFILES := \ PolyRingTest \ NewF4Test \ MonoidTest \ - MatrixIOTest + MatrixIOTest \ + RingRRRTest \ + RingCCCTest -# RingRRRTest \ -# RingCCCTest \ -# ARingGFTest \ - +# WeylAlgebraTest \ # TODO: add in this file -SHARED_UNITTEST_CFILES += \ - M2-replacement - -UNITTEST_CFILES += \ - $(SHARED_UNITTEST_CFILES) +# ARingGFTest UNITTEST_TARGET := testMain GTEST_DIR := $(BUILTLIBPATH)/include/gtest -E_FILES := $(addprefix ../, $(INTERFACE) $(COMMANDS) $(C_FILES)) -F4_FILES := $(addprefix ../f4/, $(F4_CCFILES)) -E_OBJECT_FILES := $(addsuffix .o, $(E_FILES) $(F4_FILES)) - -UNITTEST_FILES := $(UNITTEST_CCFILES) $(UNITTEST_CFILES) -UNITTEST_SHARED_FILES := $(SHARED_UNITTEST_CCFILES) $(SHARED_UNITTEST_CFILES) - -UNITTEST_OBJECT_FILES := $(addsuffix .o, $(UNITTEST_FILES)) ../../d/M2mem.o ../../d/memdebug.o ../../d/debug.o -UNITTEST_SHARED_OBJECT_FILES := $(addsuffix .o, $(UNITTEST_SHARED_FILES)) - - +UNITTEST_OBJECT_FILES := $(addsuffix .o, $(UNITTEST_CCFILES)) +UNITTEST_SHARED_OBJECT_FILES := $(addsuffix .o, $(SHARED_UNITTEST_CCFILES)) # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/e/unit-tests check" diff --git a/M2/Macaulay2/e/unit-tests/Makefile.in b/M2/Macaulay2/e/unit-tests/Makefile.in index 0f65bd49399..01e435b9fb8 100644 --- a/M2/Macaulay2/e/unit-tests/Makefile.in +++ b/M2/Macaulay2/e/unit-tests/Makefile.in @@ -4,102 +4,42 @@ VPATH = @srcdir@ SRCDIR = @srcdir@ DEPENDS = yes include ../Makefile.common -include @srcdir@/../Makefile.files +include ../Makefile.files include @srcdir@/Makefile.files export LD_LIBRARY_PATH := $(BUILTLIBPATH)/lib:$(LD_LIBRARY_PATH) CPPFLAGS := -I. -I@srcdir@ -I@srcdir@/.. $(CPPFLAGS) \ - -isystem $(GTEST_DIR) -isystem $(GTEST_DIR)/include \ - -I@srcdir@/../../d -I../../d \ - -I@srcdir@/../../c #-Wno-unused-local-typedefs # ffpack has some of these. -CXXFLAGS += -std=gnu++17 -Wno-sign-conversion #-Wno-unused-local-typedefs -coverage + -isystem $(GTEST_DIR) -isystem $(GTEST_DIR)/include +CXXFLAGS += -std=gnu++17 -Wno-sign-conversion -# note: on some machines, gcc can't find -lstdc++ LOADLIBES += @BUILTLIBS@ @LINALGLIBS@ @LIBS@ @FCLIBS@ -lgtest -pthread -.PHONY: place_into_lib runtests +.PHONY: runtests -all: $(UNITTEST_OBJECT_FILES) $(E_OBJECT_FILES) - echo @BUILTLIBS@ - echo @LIBS@ +all: $(UNITTEST_OBJECT_FILES) $(LIBENGINE) -# disabling a test temporarily, because it doesn't compile -#check: -# @ echo "makefile: Warning : skipping temporarily disabled test in e/unit-test" check: runtests fullCheck: $(UNITTEST_TARGET) valgrind --track-origins=yes ./$(UNITTEST_TARGET) -$(UNITTEST_TARGET) : $(UNITTEST_OBJECT_FILES) $(E_OBJECT_FILES) ../../system/supervisor.o +$(UNITTEST_TARGET) : $(UNITTEST_OBJECT_FILES) $(LIBENGINE) @CXX@ $(LDFLAGS) $^ $(LOADLIBES) -o $@ runtests: $(UNITTEST_TARGET) time ./$(UNITTEST_TARGET) -ARingRRRTest : ARingRRRTest.o $(UNITTEST_SHARED_OBJECT_FILES) $(E_OBJECT_FILES) - @CXX@ $(LDFLAGS) $^ $(LOADLIBES) -o $@ - -ARingRRiTest : ARingRRiTest.o $(UNITTEST_SHARED_OBJECT_FILES) $(E_OBJECT_FILES) - @CXX@ $(LDFLAGS) $^ $(LOADLIBES) -o $@ - -ARingGFTest : ARingGFTest.o $(UNITTEST_SHARED_OBJECT_FILES) $(E_OBJECT_FILES) ../../system/supervisor.o - @CXX@ $(LDFLAGS) $^ $(LOADLIBES) -o $@ - -NCGroebnerTest : NCGroebnerTest.o $(UNITTEST_SHARED_OBJECT_FILES) $(E_OBJECT_FILES) ../../system/supervisor.o - @CXX@ $(LDFLAGS) $^ $(LOADLIBES) -o $@ - -runNCGroebnerTest : NCGroebnerTest - time ./NCGroebnerTest - -ARingRRRZZpTest : \ - ARingZZpTest.o \ - ARingRRRTest.o \ - ARingZZpTest.o \ - RingZZTest.o \ - RingZZpTest.o \ - RingQQTest.o \ - RingRRRTest.o \ - RingCCCTest.o \ - $(UNITTEST_SHARED_OBJECT_FILES) $(E_OBJECT_FILES) - @CXX@ $(LDFLAGS) $^ $(LOADLIBES) -o $@ - -checkGivaro: GivaroTest - -GivaroTest: GivaroTest.o - @CXX@ $(LDFLAGS) $^ -lgivaro -lgmp -lgmpxx -o $@ - -checkRingRRR: ARingRRRTest - time ./ARingRRRTest - -checkRingRRi: ARingRRiTest - time ./ARingRRiTest - -#fails for whatever reasons -checkRingRRRZZp: ARingRRRZZpTest - time ./ARingRRRZZpTest - - -# fails on my 32-bit fedora 14 , gcc is 4.5.1. -failing: checkRingRRRZZp - - -%.s : %.c; $(COMPILE.cc) -S $< $(OUTPUT_OPTION) - MORE_OPTIONS = -Wno-cast-qual -COMPILE.c += $(MORE_OPTIONS) COMPILE.cc += $(MORE_OPTIONS) - Makefile: Makefile.in; cd ../../..; ./config.status Macaulay2/e/unit-tests/Makefile clean::; rm -f *.o *.rpo *.dep dep-*.tmp LOGFILE testMain distclean: clean; rm -f Makefile ifeq "$(DEPENDS)" "yes" -include $(UNITTEST_CFILES:=.dep) $(UNITTEST_CCFILES:=.dep) +include $(UNITTEST_CCFILES:=.dep) endif # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/e/unit-tests check" # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/e/unit-tests/README b/M2/Macaulay2/e/unit-tests/README index ebcf7f67be6..3005b015f15 100644 --- a/M2/Macaulay2/e/unit-tests/README +++ b/M2/Macaulay2/e/unit-tests/README @@ -1,14 +1,11 @@ This directory contains unit tests for engine. We are using googletest (gtest) currently, but only in a fairly naive manner. -However, gtest does not play well with libgc, at least I could not get them to work together. -We create our own executable here (or, executables), and have a number of files which avoid the -use of libgc, as well as avoid needing to link in d directory -(M2mem-replacement, M2-replacement, M2-cpp-replacement). - -Run tests (and compiles them) using: make check - -Currently, the source files are compiled during 'make' - -We are experimenting with using catch2 instead of gtest, and also with trying to build the -unit test executable via cmake. +gtest plays well with libgc, I believe, although it didn't use to. + +The one thing we need to link in besides the M2 engine library file, is M2-cpp-replacement.cpp, which defines the system_interrupted() +function (to do nothing), which normally is supplied by the Macaulay2 executable. + +Autotools build: + Run tests (and compiles them) using: make -k check + Currently, the source files are compiled during 'make' diff --git a/M2/Macaulay2/e/util.hpp b/M2/Macaulay2/e/util.hpp index b18eae24ecd..0cfbe945e99 100644 --- a/M2/Macaulay2/e/util.hpp +++ b/M2/Macaulay2/e/util.hpp @@ -5,8 +5,8 @@ #include // for vector #include // for ostream -#include "M2mem.h" // for getmemarraytype -#include "engine-includes.hpp" // for M2_* types +#include "interface/m2-mem.h" // for getmemarraytype +#include "interface/m2-types.h" // for M2_* types /** * Utilities for converting between M2 types and standard C++ types diff --git a/M2/Macaulay2/editors/.gitignore b/M2/Macaulay2/editors/.gitignore index 7804d6a6d43..9464ef20e76 100644 --- a/M2/Macaulay2/editors/.gitignore +++ b/M2/Macaulay2/editors/.gitignore @@ -1,7 +1,6 @@ emacs/M2-emacs-hlp.txt emacs/M2-emacs.m2 emacs/M2-symbols.el -Macaulay2Web/M2-symbols.ts prism/macaulay2.js prism/node_modules/ prism/package-lock.json diff --git a/M2/Macaulay2/editors/Macaulay2Web/M2-symbols.ts.in b/M2/Macaulay2/editors/Macaulay2Web/M2-symbols.ts.in deleted file mode 100644 index 37cb8fd06e1..00000000000 --- a/M2/Macaulay2/editors/Macaulay2Web/M2-symbols.ts.in +++ /dev/null @@ -1,3 +0,0 @@ -// @M2BANNER@ - -export default [@M2SYMBOLS@]; diff --git a/M2/Macaulay2/editors/Makefile.in b/M2/Macaulay2/editors/Makefile.in index b7dcd8a7913..f2e7dac941b 100644 --- a/M2/Macaulay2/editors/Makefile.in +++ b/M2/Macaulay2/editors/Makefile.in @@ -1,17 +1,17 @@ # @configure_input@ include ../../include/config.Makefile ARGS = --script -.NOTPARALLEL: MADEFILES = M2-symbols.el M2-emacs-help.txt M2-emacs.m2 SRCFILES = M2-init.el M2-mode.el M2.el README.md -all: @pre_emacsdir@ $(addprefix @pre_emacsdir@/, $(SRCFILES) $(MADEFILES)) +all: $(addprefix @pre_emacsdir@/, $(SRCFILES) $(MADEFILES)) verify: $(addprefix @pre_emacsdir@/, $(SRCFILES) $(MADEFILES)) $(addprefix emacs/, $(SRCFILES) $(MADEFILES)); ls -lrt $^ Makefile: @srcdir@/Makefile.in; cd ../..; ./config.status Macaulay2/editors/Makefile @pre_emacsdir@ :; @INSTALL@ -d "$@" -@pre_emacsdir@/% : $(addprefix emacs/, %) ; @INSTALL_DATA@ $^ @pre_emacsdir@ +@pre_emacsdir@/% : emacs/% | @pre_emacsdir@ + @INSTALL_DATA@ $< $(@D) emacs/M2-symbols.el prism/macaulay2.js : \ @srcdir@/make-M2-symbols.m2 @srcdir@/../m2/exports.m2 \ @@ -35,7 +35,7 @@ prism/%: @srcdir@/prism/% prism cp $< $@ clean: - rm -rf @pre_emacsdir@/* emacs prism pygments vim Macaulay2Web + rm -rf @pre_emacsdir@/* emacs prism pygments vim else clean: diff --git a/M2/Macaulay2/editors/README.md b/M2/Macaulay2/editors/README.md index 402d3a6a06a..11b7407aa85 100644 --- a/M2/Macaulay2/editors/README.md +++ b/M2/Macaulay2/editors/README.md @@ -8,7 +8,6 @@ completion of Macaulay2 code. Each subdirectory contains one or more template files for a particular application. -* [`Macaulay2Web`](Macaulay2Web): Symbols for the Macaulay2 web app. * [`emacs`](https://github.com/Macaulay2/M2-emacs): Symbols for GNU Emacs. * [`prism`](prism): Symbols for the Prism Javascript library. These are used for syntax highlighting the online Macaulay2 documentation. @@ -22,3 +21,4 @@ been moved to their own repositories: * highlight.js: https://github.com/d-torrance/highlightjs-macaulay2 * Linguist: https://github.com/Macaulay2/language-macaulay2 * TextMate: https://github.com/Macaulay2/vscode-macaulay2 +* Macaulay2Web (symbols for autocompletion): https://github.com/pzinn/Macaulay2Web/ diff --git a/M2/Macaulay2/editors/emacs b/M2/Macaulay2/editors/emacs index 524968452e9..a95ab17170b 160000 --- a/M2/Macaulay2/editors/emacs +++ b/M2/Macaulay2/editors/emacs @@ -1 +1 @@ -Subproject commit 524968452e95d010769ece30092edaa09d1e814f +Subproject commit a95ab17170bf6234b77fa8ccdb2431b7fb9e9dd9 diff --git a/M2/Macaulay2/editors/make-M2-symbols.m2 b/M2/Macaulay2/editors/make-M2-symbols.m2 index 27e2a784f0f..5506980b329 100644 --- a/M2/Macaulay2/editors/make-M2-symbols.m2 +++ b/M2/Macaulay2/editors/make-M2-symbols.m2 @@ -21,9 +21,6 @@ generateGrammar("vim/m2.vim.dict", x -> demark(" ", x)) -- TODO: is this necessa generateGrammar("pygments/macaulay2.py", x -> demark("," | newline | " ", format \ x)) --- Macaulay2Web: Write M2-symbols.ts -generateGrammar("Macaulay2Web/M2-symbols.ts", x -> demark(",", format \ x)) - -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/emacs M2-symbols " -- End: diff --git a/M2/Macaulay2/editors/vim/m2.vim.dict b/M2/Macaulay2/editors/vim/m2.vim.dict index 29cfeb57f7e..074253117e4 100644 --- a/M2/Macaulay2/editors/vim/m2.vim.dict +++ b/M2/Macaulay2/editors/vim/m2.vim.dict @@ -1,6 +1,6 @@ -"" Auto-generated for Macaulay2-1.25.11. Do not modify this file manually. +"" Auto-generated for Macaulay2-1.26.05. Do not modify this file manually. " Vim dictionary file " Language: Macaulay2 -A1BrouwerDegrees AInfinity ANCHOR AbstractSimplicialComplexes AbstractToricVarieties Acknowledgement AdditionalPaths Adjacent AdjointIdeal AdjunctionForSurfaces AffineVariety AfterEval AfterNoPrint AfterPrint AlgebraicSplines Algorithm Alignment AllCodimensions AllMarkovBases AnalyzeSheafOnP1 Analyzer AngleBarList Array Ascending AssociativeAlgebras AssociativeExpression AtomicInt Authors AuxiliaryFiles BGG BIBasis BKZ BLOCKQUOTE BODY BOLD BR BUTTON Bag Bareiss Base BaseFunction BaseRow BasicList BasisElementLimit Bayer BeforePrint BeginningMacaulay2 Benchmark BernsteinSato Bertini BesselJ BesselY Beta BettiCharacters BettiTally Binary BinaryOperation Binomial BinomialEdgeIdeals Binomials Body BoijSoederberg Book3264Examples Boolean BooleanGB Boxes Brackets Browse Bruns CC CDATA CODE COMMENT CacheExampleOutput CacheTable CallLimit CatalanConstant Caveat CellularResolutions Center Certification ChainComplex ChainComplexExtras ChainComplexMap ChainComplexOperations ChangeMatrix CharacteristicClasses CheckDocumentation Chordal Citation Classic ClosestFit CodimensionLimit CodingTheory CoefficientRing Cofactor CohenEngine CohenTopLevel CoherentSheaf CohomCalg CoincidentRootLoci Command CompiledFunction CompiledFunctionBody CompiledFunctionClosure Complement CompleteIntersection CompleteIntersectionResolutions ComplexField Complexes ConductorElement Configuration ConformalBlocks ConnectionMatrices Consequences Constant Constants Contributors ConvexInterface ConwayPolynomials Core CorrespondenceScrolls CotangentSchubert CpMackeyFunctors Cremona Cyclotomic DD DGAlgebras DIV DL DT Database Date DebuggingMode DecomposableSparseSystems Decompose Default Degree DegreeGroup DegreeLift DegreeLimit DegreeMap DegreeOrder DegreeRank Degrees Dense Density Depth Descending Descent Describe Description DeterminantalRepresentations Dictionary DiffAlg Digamma DirectSum Dispatch Divide DivideConquer DividedPowers Dmodules DocumentTag Down Dynamic EM EXAMPLE EagonResolution EdgeIdeals EigenSolver EisenbudHunekeVasconcelos Eliminate Elimination EliminationMatrices EllipticCurves EllipticIntegrals Email End Engine EngineRing EngineTests EnumerationCurves Equation EquivariantGB EulerConstant ExampleFiles ExampleItem ExampleSystems Exclude Expression Ext ExtLongExactSequence ExteriorExtensions ExteriorIdeals ExteriorModules FGLM Fano FastMinors FastNonminimal File FileName FilePosition FindOne FiniteFittingIdeals First FirstPackage FlatMonoid Flexible FollowLinks ForeignFunctions FormalGroupLaws Format FourTiTwo FourierMotzkin FractionField FrobeniusThresholds Function FunctionApplication FunctionBody FunctionClosure FunctionFieldDesingularization GBDegrees GCstats GF GKMVarieties GLex GRevLex GTZ GaloisField GameTheory Gamma GeneralOrderedMonoid GenerateAssertions Generic GenericInitialIdeal GeometricDecomposability Givens Global GlobalAssignHook GlobalDictionary GlobalHookStore GlobalReleaseHook GlobalSectionLimit Gorenstein GradedLieAlgebras GradedModule GradedModuleMap GraphicalModels GraphicalModelsMLE Graphics Graphs Grassmannian GroebnerBasis GroebnerBasisOptions GroebnerStrata GroebnerWalk GroupLex GroupRevLex HEAD HEADER1 HEADER2 HEADER3 HEADER4 HEADER5 HEADER6 HH HR HREF HTML Hadamard HardDegreeLimit HashTable HeaderType Heading Headline Heft Height Hermite Hermitian HigherCIOperators HighestWeights Hilbert HodgeIntegrals Holder HolonomicSystems Hom HomePage Homogeneous Homogeneous2 HomologicalAlgebraPackage HomotopyLieAlgebra HorizontalSpace Hybrid HyperplaneArrangements Hypertext HypertextContainer HypertextParagraph HypertextVoid IFRAME IMG INDENT INPUT ITALIC Ideal IgnoreExampleErrors ImmutableType IncidenceCorrespondenceCohomology Increment IndeterminateNumber Index IndexedVariable IndexedVariableTable InexactField InexactFieldFamily InexactNumber InfiniteNumber InfoDirSection Inhomogeneous Inputs InstallPrefix IntegerProgramming IntegralClosure IntermediateMarkUpType Intersection InvariantRing InverseMethod InverseSystems Inverses Invertible InvolutiveBases Isomorphism Iterate Iterator JSON JSONRPC Jacobian Jets Join K3Carpets K3Surfaces KBD Keep KeepFiles KeepZeroes Key Keyword Keywords Kronecker KustinMiller LABEL LATER LI LINK LITERAL LLL LLLBases LUdecomposition LatticePolytopes Layout Left LengthLimit Lex LexIdeals LieAlgebraRepresentations Limit Linear LinearAlgebra LinearTruncations List LoadDocumentation Local LocalDictionary LocalRings LongPolynomial LowerBound M0nbar M2CODE MCMApproximations MENU META Macaulay2Doc Maintainer MakeDocumentation MakeHTML MakeInfo MakePDF Manipulator MapExpression MapleInterface MarkUpType Markov MatchingFields Matrix MatrixExpression MatrixFactorizations MatrixSchubert Matroids MaxReductionCount MaximalRank MergeTeX MethodFunction MethodFunctionBinary MethodFunctionSingle MethodFunctionWithOptions MinimalGenerators MinimalMatrix MinimalPrimes Minimize MinimumVersion Minus Miura MixedMultiplicity Module ModuleDeformations MonodromySolver Monoid MonoidElement Monomial MonomialAlgebras MonomialIdeal MonomialIntegerPrograms MonomialOrbits MonomialOrder MonomialSize Monomials Msolve MultiGradedRationalMap MultigradedBGG MultigradedBettiTally MultigradedImplicitization MultiplicitySequence MultiplierIdeals MultiplierIdealsDim2 MultiprojectiveVarieties MutableHashTable MutableList MutableMatrix NAGtypes NCAlgebra NCLex NNParser NTL Name Nauty NautyGraphs Net NetFile NewFromMethod NewMethod NewOfFromMethod NewOfMethod NoPrint NoetherNormalization NoetherianOperators NonPrincipalTestIdeals NonminimalComplexes NormalToricVarieties Normaliz NotANumber Nothing Number NumberedVerticalList NumericSolutions NumericalAlgebraicGeometry NumericalCertification NumericalImplicitization NumericalLinearAlgebra NumericalSchubertCalculus NumericalSemigroups OIGroebnerBases OL OO OldChainComplexes OldPolyhedra OldToricVectorBundles OneExpression OnlineLookup OpenMath Option OptionTable OptionalComponentsPresent Options Order OrderedMonoid Oscillators OutputDictionary Outputs PARA PHCpack POSIX PRE Package PackageCitations PackageDictionary PackageExports PackageImports PackageTemplate PairLimit PairsRemaining ParallelF4 ParallelizeByDegree Parametrization Parenthesize Parser Parsing Partition PathSignatures PencilsOfQuadrics Permanents Permutations PhylogeneticTrees PieriMaps PlaneCurveLinearSeries PlaneCurveSingularities Points Polyhedra Polymake PolynomialRing PolyominoIdeals Posets Position PositivityToricBundles Postfix Power Precision Prefix PrimaryDecomposition PrimaryTag PrimitiveElement Print Probability Product ProductOrder Program ProgramRun Proj Projective ProjectiveHilbertPolynomial ProjectiveVariety Prune PruneComplex Pseudocode PseudocodeClosure PseudomonomialPrimaryDecomposition Pullback PushForward Python QQ QQParser QRDecomposition QthPower QuadraticIdealExamplesByRoos Quasidegrees QuaternaryQuartics QuillenSuslin Quotient QuotientRing RHom RInterface RR RRi Radical RadicalCodim1 RaiseError RandomCanonicalCurves RandomComplexes RandomCurves RandomCurvesOverVerySmallFiniteFields RandomGenus14Curves RandomIdeals RandomMonomialIdeals RandomObjects RandomPlaneCurves RandomPoints RandomSpaceCurves Range RationalMaps RationalPoints RationalPoints2 ReactionNetworks RealFP RealField RealQP RealQP1 RealRR RealRoots RealXD Reduce ReesAlgebra References ReflexivePolytopesDB Regularity RelativeCanonicalResolution Reload RemakeAllDocumentation RerunExamples ResLengthThree ResidualIntersections Resolution ResolutionsOfStanleyReisnerRings Result Resultants RevLex Reverse Right Ring RingElement RingFamily RingMap RowExpression RunDirectory RunExamples RunExternalM2 SAMP SCMAlgebras SCRIPT SCSCP SLPexpressions SLnEquivariantMatrices SMALL SPACE SPAN SRdeformations STRONG STYLE SUB SUBSECTION SUP SVD SVDComplexes SYNOPSIS SagbiGbDetection Saturation SaturationMap Schubert Schubert2 SchurComplexes SchurFunctors SchurRings SchurVeronese ScriptedFunctor SectionRing SeeAlso SegreClasses SelfInitializingType SemidefiniteProgramming Seminormalization SeparateExec Sequence Serialization Set SheafExpression SheafMap SheafOfRings ShimoyamaYokoyama SimpleDoc SimplicialComplexes SimplicialDecomposability SimplicialPosets SimplifyFractions SizeLimit SkewCommutative SlackIdeals Sort SortStrategy SourceCode SourceRing SpaceCurves SparseMonomialVectorExpression SparseResultants SparseVectorExpression Spec SpechtModule SpecialFanoFourfolds SpectralSequences Standard StartWithOneMinor StatGraphs StatePolytope StopBeforeComputation StopIteration StopWithMinimalGenerators Strategy Strict String StronglyStableIdeals Style SubalgebraBases Subnodes SubringLimit Subscript Sugarless Sum SumOfTwists SumsOfSquares SuperLinearAlgebra Superscript SwitchingFields Symbol SymbolBody SymbolicPowers SymmetricPolynomials Syzygies SyzygyLimit SyzygyMatrix SyzygyRows TABLE TD TEST TEX TH TITLE TO TO2 TOH TR TSpreadIdeals TT Table Tableaux Tally TangentCone Task TateOnProducts TeXmacs TensorComplexes TensorProduct TerraciniLoci Test TestIdeals TestInput Text ThinSincereQuivers Thing ThreadedGB Threads Threshold Time Topcom Tor TorAlgebra Toric ToricHigherDirectImages ToricInvariants ToricTopology ToricVectorBundles Torsion TorsionFree TotalPairs TriangularSets Triangulations Tries Trim Triplets Tropical TropicalToric Truncate Truncations Type TypicalValue UL URL Undo Unique Units Unmixed Up UpdateOnly UpperTriangular Usage UseCachedExampleOutput UseHilbertFunction UseSyzygies UserMode VAR VNumber Valuations Variable VariableBaseName Variables Varieties Variety Vasconcelos Vector VectorExpression VectorFields VectorGraphics Verbose Verbosity Verify VersalDeformations Version VerticalList VerticalSpace VirtualResolutions VirtualTally VisibleList Visualize WebApp Weights WeilDivisors WeylAlgebra WeylAlgebras WeylGroups WhitneyStratifications Wrap WrapperType XML ZZ ZZParser ZeroExpression about abs accumulate acos acosh acot acoth addCancelTask addDependencyTask addEndFunction addHook addStartTask adjoint agm alarm all allowableThreads ambient analyticSpread ancestor ancestors and andP ann annihilator antipode any append applicationDirectory applicationDirectorySuffix apply applyKeys applyPairs applyTable applyValues apropos arXiv argument ascii asin asinh ass assert associatedGradedRing associatedPrimes atEndOfFile atan atan2 atanh autoload backtrace baseFilename baseName baseRing baseRings basis beginDocumentation benchmark betti between binomial blockMatrixForm borel break breakpoint cache cacheValue cancelTask canonicalBundle capture catch ceiling centerString chainComplex changeBase changeDirectory char charAnalyzer characters check checkDegrees chi cite class clean clearAll clearEcho clearOutput close closeIn closeOut code codim coefficient coefficientRing coefficients cohomology coimage coker cokernel collectGarbage columnAdd columnMult columnPermute columnRankProfile columnSwap columnate combine commandInterpreter commandLine commonRing commonest comodule compactMatrixForm compareExchange complement complete components compose compositions compress concatenate conductor cone conjugate connectionCount constParser content continue contract conwayPolynomial copy copyDirectory copyFile copyright cos cosh cot cotangentSheaf cotangentSurjection coth cover coverMap coverageSummary cpuTime createTask csc csch current currentColumnNumber currentDirectory currentFileDirectory currentFileName currentLayout currentPackage currentPosition currentRowNumber currentString currentTime dd deadParser debug debugError debugLevel debuggingMode decompose deepSplice default defaultPrecision degree degreeGroup degreeLength degrees degreesMonoid degreesRing delete demark denominator depth describe det determinant diagonalMatrix diameter dictionary dictionaryPath diff difference dim directProduct directSum disassemble discriminant dismiss distinguished divideByVariable do doc docExample docTemplate document drop dual eagonNorthcott echoOff echoOn edit eigenvalues eigenvectors eint elapsedTime elapsedTiming elements eliminate else embeddedToAbstract end endPackage endl engineDebugLevel entries environment erase erf erfc error errorDepth euler eulerSequence eulers even examples exchange exec exit exp expectedReesIdeal expm1 exponents export exportFrom exportMutable expression extend exteriorPower factor false fileDictionaries fileExecutable fileExists fileExitHooks fileLength fileMode fileReadable fileTime fileWritable fillMatrix findFiles findHeft findProgram findSynonyms first firstkey fittingIdeal flagLookup flatten flattenRing flip floor flush fold for forceGB fork format formation fpLLL frac fraction frames from fromDividedPowers fromDual functionBody futureParser gb gbRemove gbSnapshot gbTrace gcd gcdCoefficients gcdLLL genera generateAssertions generator generators genericMatrix genericSkewMatrix genericSymmetricMatrix gens genus get getChangeMatrix getGlobalSymbol getIOThreadMode getNetFile getNonUnit getPrimeWithRootOfUnity getSymbol getWWW getc getenv gfanInterface global globalAssign globalAssignFunction globalAssignment globalAssignmentHooks globalReleaseFunction gradedModule gradedModuleMap gramm graphIdeal graphRing groebnerBasis groupID handleInterrupts hash hashTable headlines heft height help hermite hh hilbertFunction hilbertPolynomial hilbertSeries hold homeDirectory homogenize homology homomorphism hooks horizontalJoin html httpHeaders hypertext icFracP icFractions icMap icPIdeal id ideal idealSheaf idealSheafSequence idealizer identity if ii image imaginaryPart importFrom in incomparable independentSets indeterminate index indexComponents indices inducedMap inducesWellDefinedMap infinity info infoHelp input insert installAssignmentMethod installHilbertFunction installMethod installMinprimes installPackage installedPackages instance instances integralClosure integrate interpreterDepth intersect intersectInP intersection interval inverse inverseErf inversePermutation inverseRegularizedBeta inverseRegularizedGamma inverseSystem irreducibleCharacteristicSeries irreducibleDecomposition isANumber isAffineRing isBorel isCanceled isCommutative isConstant isDirectSum isDirectory isEmpty isExact isField isFinite isFinitePrimeField isFreeModule isGlobalSymbol isHomogeneous isIdeal isInfinite isInjective isInputFile isIsomorphic isIsomorphism isLLL isLinearType isListener isLocallyFree isMember isModule isMonomialIdeal isMutable isNormal isOpen isOutputFile isPolynomialRing isPrimary isPrime isPrimitive isProjective isPseudoprime isQuotientModule isQuotientOf isQuotientRing isReady isReal isReduction isRegularFile isRing isSkewCommutative isSmooth isSorted isSquareFree isStandardGradedPolynomialRing isSubmodule isSubquotient isSubset isSupportedInZeroLocus isSurjective isTable isUnit isVeryAmple isWellDefined isWeylAlgebra isc isomorphism iterator jacobian jacobianDual join ker kernel kernelLLL kernelOfLocalization keys kill koszul last lastMatch lcm leadCoefficient leadComponent leadMonomial leadTerm left length letterParser lift liftable limitFiles limitProcesses lineNumber lines linkFile list listForm listLocalSymbols listSymbols listUserSymbols lngamma load loadDepth loadPackage loadedFiles loadedPackages local localDictionaries localize locate log log1p lookup lookupCount makeDirectory makeDocumentTag makePackageIndex makeS2 map markedGB match mathML matrix max maxAllowableThreads maxExponent maxPosition member memoize memoizeClear memoizeValues merge mergePairs method methodOptions methods midpoint min minExponent minPosition minPres mingens mingle minimalBetti minimalPresentation minimalPresentationMap minimalPresentationMapInv minimalPrimes minimalReduction minimize minimizeFilename minors minprimes minus mkdir mod module modulo monoid monomialCurveIdeal monomialIdeal monomialSubideal monomials moveFile multidegree multidoc multigraded multiplicity mutable mutableIdentity mutableMatrix nanosleep needs needsPackage net netList new newClass newCoordinateSystem newNetFile newPackage newRing newline next nextPrime nextkey nil nonspaceAnalyzer norm normalCone not notImplemented notify null nullParser nullSpace nullaryMethods nullhomotopy numColumns numRows numTBBThreads number numcols numerator numeric numericInterval numgens numrows odd oeis of ofClass on oo ooo oooo openDatabase openDatabaseOut openFiles openIn openInOut openListener openOut openOutAppend operatorAttributes optP optionalSignParser options or orP order override pack package packageTemplate pad pager pairs parallelApply parent part partition partitions parts path pdim peek permanents permutations pfaffian pfaffians pi pivots plus poincare poincareN polarize poly position positions power powermod precision prefixDirectory prefixPath preimage prepend presentation pretty primaryComponent primaryDecomposition print printString printWidth printerr printingAccuracy printingLeadLimit printingPrecision printingSeparator printingTimeLimit printingTrailLimit processID product profile profileSummary programPaths projectiveHilbertPolynomial promote protect prune pruningMap pseudoRemainder pseudocode pullback pullbackMaps pushForward pushout pushoutMaps quit quotient quotientRemainder radical radicalContainment random randomKRationalPoint randomMutableMatrix randomSubset rank rays read readDirectory readPackage readlink realPart realpath recursionDepth recursionLimit reduceHilbert reducedRowEchelonForm reductionNumber reesAlgebra reesAlgebraIdeal reesIdeal regSeqInIdeal regex regexQuote registerFinalizer regularity regularizedBeta regularizedGamma relations relativizeFilename remainder remove removeDirectory removeFile removeLowestDimension reorganize replace res reshape resolution restart resultant return returnCode reverse right ring ringFromFractions rootPath rootURI roots rotate round rowAdd rowMult rowPermute rowRankProfile rowSwap rsort run runHooks runLengthEncode runProgram same saturate scan scanKeys scanLines scanPairs scanValues schedule schreyerOrder scriptCommandLine searchPath sec sech seeParsing select selectInSubring selectKeys selectPairs selectValues selectVariables separate separateRegexp sequence serialNumber set setEcho setGroupID setIOExclusive setIOSynchronized setIOUnSynchronized setRandomSeed setup setupEmacs setupLift setupPromote sheaf sheafExt sheafHom sheafMap shield show showClassStructure showHtml showStructure showTex showUserStructure sign simpleDocFrob sin singularLocus sinh size size2 sleep smithNormalForm solve someTerms sort sortColumns source span specialFiber specialFiberIdeal splice splitWWW sqrt stack stacksProject stacktrace standardForm standardPairs stashValue status stderr stdio step stopIfError store style sub sublists submatrix submatrixByDegrees subquotient subscript subsets substitute substring subtable sum super superscript support switch sylvesterMatrix symbol symbolBody symlinkDirectory symlinkFile symmetricAlgebra symmetricAlgebraIdeal symmetricKernel symmetricPower synonym syz syzygyScheme table take tally tan tangentCone tangentSheaf tanh target taskResult temporaryFileName tensor tensorAssociativity terminalParser terms testExample testHunekeQuestion tests tex texMath then threadLocal threadVariable throw time times timing to toAbsolutePath toCC toDividedPowers toDual toExternalString toField toList toLower toRR toRRi toSequence toString toUpper top topCoefficients topComponents topLevelMode trace transpose trim true truncate truncateOutput try tutorial typicalValues ultimate unbag uncurry undocumented uniform uninstallAllPackages uninstallPackage union unique uniquePermutations unsequence unstack urlEncode use userSymbols utf8 utf8check utf8substring validate value values variety vars vector versalEmbedding version viewHelp wait wedgeProduct weightRange when whichGm while width wikipedia wrap xor yonedaSheafExtension youngest zero zeta \ No newline at end of file +A1BrouwerDegrees AInfinity ANCHOR AbstractSimplicialComplexes AbstractToricVarieties Acknowledgement AdditionalPaths Adjacent AdjointIdeal AdjunctionForSurfaces AffineVariety AfterEval AfterNoPrint AfterPrint AlgebraicSplines Algorithm Alignment AllCodimensions AllMarkovBases AnalyzeSheafOnP1 Analyzer AngleBarList Array Ascending AssociativeAlgebras AssociativeExpression AtomicInt Authors AuxiliaryFiles BGG BIBasis BKZ BLOCKQUOTE BODY BOLD BR BUTTON Bag Bareiss Base BaseFunction BaseRow BasicList BasisElementLimit Bayer BeforePrint BeginningMacaulay2 Benchmark BernsteinSato Bertini BesselJ BesselY Beta BettiCharacters BettiTally Binary BinaryOperation Binomial BinomialEdgeIdeals Binomials Body BoijSoederberg Book3264Examples Boolean BooleanGB Boundary Boxes Brackets Browse Bruns CC CCi CDATA CODE COMMENT CacheExampleOutput CacheTable CallLimit CatalanConstant Caveat CellularResolutions Center Certification ChainComplexExtras ChainComplexOperations ChangeMatrix CharacteristicClasses CheckDocumentation Chordal Citation Classic ClosestFit CodimensionLimit CodingTheory CoefficientRing Cofactor CohenEngine CohenTopLevel CoherentSheaf CohomCalg CoincidentRootLoci Command CompiledFunction CompiledFunctionBody CompiledFunctionClosure Complement CompleteIntersection CompleteIntersectionResolutions Complex ComplexField ComplexMap Complexes Concentration ConductorElement Configuration ConformalBlocks ConnectionMatrices Consequences Constant Constants Contributors ConvexInterface ConwayPolynomials Core CorrespondenceScrolls CotangentSchubert CpMackeyFunctors Cremona Cycle Cyclotomic DD DGAlgebras DIV DL DT Database Date DebuggingMode DecomposableSparseSystems Decompose Default Degree DegreeGroup DegreeLift DegreeLimit DegreeMap DegreeOrder DegreeRank Degrees Dense Density Depth Descending Descent Describe Description DeterminantalRepresentations Dictionary DiffAlg Digamma DirectSum Direction Dispatch Divide DivideConquer DividedPowers Dmodules DocumentTag Down Dynamic EM EXAMPLE EagonResolution EdgeIdeals EigenSolver EisenbudHunekeVasconcelos Eliminate Elimination EliminationMatrices EliminationTemplates EllipticCurves EllipticIntegrals Email End Engine EngineRing EngineTests EnumerationCurves Equation EquivariantGB Error EulerConstant ExampleFiles ExampleItem ExampleSystems Exclude Expression Ext ExteriorExtensions ExteriorIdeals ExteriorModules FGLM Fano FastMinors FastNonminimal File FileName FilePosition FindOne FiniteFittingIdeals First FirstPackage FlatMonoid Flexible FollowLinks ForeignFunctions FormalGroupLaws Format FourTiTwo FourierMotzkin FractionField FreeToExact FrobeniusThresholds Function FunctionApplication FunctionBody FunctionClosure FunctionFieldDesingularization GBDegrees GCstats GF GKMVarieties GLex GRevLex GTZ GaloisField GameTheory Gamma GeneralOrderedMonoid GenerateAssertions Generic GenericInitialIdeal GeometricDecomposability Givens Global GlobalAssignHook GlobalDictionary GlobalHookStore GlobalReleaseHook GlobalSectionLimit Gorenstein GradedLieAlgebras GradedModule GradedModuleMap GraphicalModels GraphicalModelsMLE Graphics Graphs Grassmannian GroebnerBasis GroebnerBasisOptions GroebnerStrata GroebnerWalk GroupLex GroupRevLex HEAD HEADER1 HEADER2 HEADER3 HEADER4 HEADER5 HEADER6 HH HR HREF HTML Hadamard HardDegreeLimit HashTable HeaderType Heading Headline Heft Height Hermite Hermitian HigherCIOperators HighestWeights Hilbert HodgeIntegrals Holder HolonomicSystems Hom HomePage Homogeneous Homogeneous2 Homogenization HomologicalAlgebraPackage HomotopyLieAlgebra HorizontalSpace Hybrid HyperplaneArrangements Hypertext HypertextContainer HypertextParagraph HypertextVoid IFRAME IMG INDENT INPUT ITALIC Ideal IgnoreExampleErrors ImmutableType IncidenceCorrespondenceCohomology Increment IndeterminateNumber Index IndexedVariable IndexedVariableTable InexactField InexactFieldFamily InexactNumber InfiniteNumber InfoDirSection Inhomogeneous Inputs InstallPrefix IntegerProgramming IntegralClosure IntermediateMarkUpType InternalDegree Intersection InvariantRing InverseMethod InverseSystems Inverses Invertible InvolutiveBases Isomorphism Iterate Iterator JSON JSONRPC Jacobian Jets Join K3Carpets K3Surfaces KBD Keep KeepFiles KeepZeroes Key Keyword Keywords Kronecker KustinMiller LABEL LATER LI LINK LITERAL LLL LLLBases LUdecomposition LatticePolytopes Layout Left LengthLimit Lex LexIdeals LieAlgebraRepresentations Limit Linear LinearAlgebra LinearTruncations List LoadDocumentation Local LocalDictionary LocalRings LongPolynomial LowerBound M0nbar M2CODE MCMApproximations MENU META MRDI Macaulay2Doc MacaulayPosets Maintainer MakeDocumentation MakeHTML MakeInfo MakePDF Manipulator MapExpression MapleInterface MarkUpType Markov MatchingFields Matrix MatrixExpression MatrixFactorizations MatrixSchubert Matroids MaxReductionCount MaximalRank MergeTeX MethodFunction MethodFunctionBinary MethodFunctionSingle MethodFunctionWithOptions MinimalGenerators MinimalMatrix MinimalPrimes Minimize MinimumVersion Minus Miura MixedMultiplicity Module ModuleDeformations MonodromySolver Monoid MonoidElement Monomial MonomialAlgebras MonomialIdeal MonomialIntegerPrograms MonomialOrbits MonomialOrder MonomialSize Monomials Msolve MultiGradedRationalMap MultigradedBGG MultigradedBettiTally MultigradedImplicitization MultiplicitySequence MultiplierIdeals MultiplierIdealsDim2 MultiprojectiveVarieties MutableHashTable MutableList MutableMatrix Mutex NAGtypes NCAlgebra NCLex NNParser NTL Name Nauty NautyGraphs Net NetFile NewFromMethod NewMethod NewOfFromMethod NewOfMethod NoPrint NoetherNormalization NoetherianOperators NonPrincipalTestIdeals Nonminimal NonminimalWithGB NormalToricVarieties Normaliz NotANumber Nothing Number NumberedVerticalList NumericSolutions NumericalAlgebraicGeometry NumericalCertification NumericalImplicitization NumericalLinearAlgebra NumericalSchubertCalculus NumericalSemigroups OIGroebnerBases OL OO OldChainComplexes OneExpression OnlineLookup OpenMath Option OptionTable OptionalComponentsPresent Options Order OrderedMonoid Oscillators OutputDictionary Outputs OverField OverZZ PARA PHCpack POSIX PRE Package PackageCitations PackageDictionary PackageExports PackageImports PackageTemplate Padic PairLimit PairsRemaining ParallelF4 ParallelizeByDegree Parametrization Parenthesize Parser Parsing Partition PathSignatures PencilsOfQuadrics Permanents Permutations PhylogeneticTrees PieriMaps PlaneCurveLinearSeries PlaneCurveSingularities Points Polyhedra Polymake PolynomialRing PolyominoIdeals Posets Position PositivityToricBundles Postfix Power Precision Prefix PrimaryDecomposition PrimaryTag PrimitiveElement Print Probability Product ProductOrder Program ProgramRun Proj Projective ProjectiveHilbertPolynomial ProjectiveVariety Prune PruningMap Pseudocode PseudocodeClosure PseudomonomialPrimaryDecomposition Pullback PushForward Python QQ QQParser QRDecomposition QthPower QuadraticIdealExamplesByRoos Quasidegrees QuaternaryQuartics QuillenSuslin Quotient QuotientRing RInterface RR RRi Radical RadicalCodim1 RaiseError RandomCanonicalCurves RandomComplexes RandomCurves RandomCurvesOverVerySmallFiniteFields RandomGenus14Curves RandomIdeals RandomMonomialIdeals RandomObjects RandomPlaneCurves RandomPoints RandomSpaceCurves Range RationalMaps RationalPoints RationalPoints2 ReactionNetworks RealFP RealField RealQP RealQP1 RealRR RealRoots RealXD Reduce ReesAlgebra References ReflexivePolytopesDB Regularity RelativeCanonicalResolution Reload RemakeAllDocumentation RerunExamples ResLengthThree ResidualIntersections Resolution ResolutionsOfStanleyReisnerRings Result Resultants RevLex Reverse Right Ring RingElement RingFamily RingMap RowExpression RunDirectory RunExamples RunExternalM2 SAMP SCMAlgebras SCRIPT SCSCP SLPexpressions SLnEquivariantMatrices SMALL SPACE SPAN SRdeformations STRONG STYLE SUB SUBSECTION SUP SVD SVDComplexes SYNOPSIS SagbiGbDetection Saturation SaturationMap Schubert Schubert2 SchurComplexes SchurFunctors SchurRings SchurVeronese ScriptedFunctor SectionRing SeeAlso SegreClasses SelfInitializingType SemidefiniteProgramming Seminormalization SeparateExec Sequence Serialization Set SheafExpression SheafMap SheafOfRings ShimoyamaYokoyama SimpleDoc SimplicialComplexes SimplicialDecomposability SimplicialModules SimplicialPosets SimplifyFractions SizeLimit SkewCommutative SlackIdeals Sort SortStrategy SourceCode SourceRing SpaceCurves SparseMonomialVectorExpression SparseResultants SparseVectorExpression Spec SpechtModule SpecialFanoFourfolds SpectralSequences Standard StartWithOneMinor StatGraphs StatePolytope StopBeforeComputation StopIteration StopWithMinimalGenerators Strategy Strict String StronglyStableIdeals Style SubalgebraBases Subnodes SubringLimit Subscript Sugarless Sum SumOfTwists SumsOfSquares SuperLinearAlgebra Superscript SwitchingFields Symbol SymbolBody SymbolicPowers SymmetricPolynomials Syzygies SyzygyLimit SyzygyMatrix SyzygyRows TABLE TD TEST TEX TH TITLE TO TO2 TOH TR TSpreadIdeals TT Table Tableaux Tally TangentCone Task TateOnProducts TeXmacs TensorComplexes TensorProduct TerraciniLoci Test TestIdeals TestInput Text ThinSincereQuivers Thing ThreadedGB Threads Threshold Time Topcom Tor TorAlgebra Toric ToricHigherDirectImages ToricInvariants ToricTopology ToricVectorBundles Torsion TorsionFree TotalPairs TriangularSets Triangulations Tries Trim Triplets Tropical TropicalToric Truncate Truncations Type TypicalValue UL URL Undo Unique UnitTest Units Unmixed Up UpdateOnly UpperTriangular Usage UseCachedExampleOutput UseHilbertFunction UseSyzygies UseTarget UserMode VAR VNumber Valuations Variable VariableBaseName Variables Varieties Variety Vasconcelos Vector VectorExpression VectorFields VectorGraphics Verbose Verbosity Verify VersalDeformations Version VerticalList VerticalSpace VirtualResolutions VirtualTally VisibleList Visualize WebApp Weights WeilDivisors WeylAlgebra WeylAlgebras WeylGroups WhitneyStratifications WittVectors Wrap WrapperType XML ZZ ZZParser ZeroExpression about abs accumulate acos acosh acot acoth addCancelTask addDependencyTask addEndFunction addHook addStartTask adjoint agm alarm all allowableThreads ambient analyticSpread ancestor ancestors and andP ann annihilator antipode any append applicationDirectory applicationDirectorySuffix apply applyKeys applyPairs applyTable applyValues apropos arXiv argument ascii asin asinh ass assert associatedGradedRing associatedPrimes atEndOfFile atan atan2 atanh augmentationMap autoload backtrace baseFilename baseName baseRing baseRings basis beginDocumentation benchmark betti between binomial blockMatrixForm borel break breakpoint cache cacheValue cancelTask canonicalBundle canonicalMap canonicalTruncation capture catch ceiling centerString changeBase changeDirectory char charAnalyzer characters check checkDegrees chi cite class clean clearAll clearEcho clearOutput close closeIn closeOut code codim coefficient coefficientRing coefficients cohomology coimage coker cokernel collectGarbage columnAdd columnMult columnPermute columnRankProfile columnSwap columnate combine commandInterpreter commandLine commonRing commonest comodule compactMatrixForm compareExchange complement complete complex component components compose compositions compress concatenate concentration conductor cone conjugate connectingExtMap connectingMap connectingTorMap connectionCount constParser constantStrand content continue contract conwayPolynomial copy copyDirectory copyFile copyright cos cosh cot cotangentSheaf coth cover coverMap coverageSummary cpuTime createTask csc csch current currentColumnNumber currentDirectory currentFileDirectory currentFileName currentLayout currentPackage currentPosition currentRowNumber currentString currentTime cylinder dd deadParser debug debugError debugLevel debuggingMode decompose deepSplice default defaultPrecision degree degreeGroup degreeLength degrees degreesMonoid degreesRing delete demark denominator depth describe det determinant diagonalMatrix diameter dictionary dictionaryPath diff difference dim directProduct directSum disassemble discriminant dismiss distinguished divideByVariable do doc docExample docTemplate document drop dual eagonNorthcott eagonNorthcottComplex echoOff echoOn edit eigenvalues eigenvectors eint elapsedTime elapsedTiming elements eliminate else end endPackage endl engineDebugLevel entries environment epicResolutionMap erase erf erfc error errorDepth euler eulers even examples except exchange exec exit exp expectedReesIdeal expm1 exponents export exportFrom exportMutable expression extend exteriorPower factor false fileDictionaries fileExecutable fileExists fileExitHooks fileLength fileMode fileReadable fileTime fileWritable fillMatrix findFiles findHeft findProgram findSynonyms first firstkey fittingIdeal flagLookup flatten flattenRing flip floor flush fold for forceGB fork format formation fpLLL frac fraction frames freeResolution from fromDividedPowers fromDual functionBody futureParser gb gbRemove gbSnapshot gbTrace gcd gcdCoefficients gcdLLL genera generateAssertions generator generators genericMatrix genericSkewMatrix genericSymmetricMatrix gens genus get getChangeMatrix getGlobalSymbol getIOThreadMode getNetFile getNonUnit getPrimeWithRootOfUnity getSymbol getWWW getc getenv gfanInterface global globalAssign globalAssignFunction globalAssignment globalAssignmentHooks globalReleaseFunction gradedModule gramm graphIdeal graphRing groebnerBasis groupID handleInterrupts hash hashTable headlines heft height help hermite hh hilbertFunction hilbertPolynomial hilbertSeries hold homeDirectory homogenize homology homomorphism homotopyMap hooks horizontalJoin horseshoeResolution html httpHeaders hypertext icFracP icFractions icMap icPIdeal id ideal idealSheaf idealizer identity if ii image imaginaryPart importFrom in incomparable independentSets indeterminate index indexComponents indices inducedMap inducesWellDefinedMap infinity info infoHelp input insert installAssignmentMethod installHilbertFunction installMethod installMinprimes installPackage installedPackages instance instances integralClosure integrate interpreterDepth intersect intersectInP intersection interval inverse inverseErf inversePermutation inverseRegularizedBeta inverseRegularizedGamma inverseSystem irreducibleCharacteristicSeries irreducibleDecomposition isANumber isAffineRing isBorel isCanceled isCommutative isComplexMorphism isConstant isDirectSum isDirectory isEmpty isExact isField isFinite isFinitePrimeField isFree isFreeModule isGlobalSymbol isHomogeneous isIdeal isInfinite isInjective isInputFile isIsomorphic isIsomorphism isLLL isLinearType isListener isLocallyFree isMember isModule isMonomialIdeal isMutable isNormal isNullHomotopic isNullHomotopyOf isOpen isOutputFile isPolynomialRing isPrimary isPrime isPrimitive isProjective isPseudoprime isQuasiIsomorphism isQuotientModule isQuotientOf isQuotientRing isReady isReal isReduction isRegularFile isRing isScalar isShortExactSequence isSkewCommutative isSmooth isSorted isSquareFree isStandardGradedPolynomialRing isSubmodule isSubquotient isSubset isSupportedInZeroLocus isSurjective isTable isUnit isVeryAmple isWellDefined isWeylAlgebra isc isomorphism iterator jacobian jacobianDual join ker kernel kernelLLL kernelOfLocalization keys kill koszul koszulComplex last lastMatch lcm leadCoefficient leadComponent leadMonomial leadTerm left length letterParser lift liftMapAlongQuasiIsomorphism liftable limitFiles limitProcesses lineNumber lines linkFile list listForm listLocalSymbols listSymbols listUserSymbols lngamma load loadDepth loadPackage loadedFiles loadedPackages local localDictionaries localize locate lock log log1p longExactSequence lookup lookupCount lowerLeft lowerRight makeDirectory makeDocumentTag makePackageIndex makeS2 map markedGB match mathML matrix max maxAllowableThreads maxExponent maxPosition member memoize memoizeClear memoizeValues merge mergePairs method methodOptions methods midpoint min minExponent minPosition minPres mingens mingle minimalBetti minimalPresentation minimalPresentationMap minimalPresentationMapInv minimalPrimes minimalReduction minimize minimizeFilename minimizingMap minors minprimes minus mkdir mod module modulo monoid monomialCurveIdeal monomialIdeal monomialSubideal monomials moveFile multidegree multidoc multigraded multiplicity mutable mutableIdentity mutableMatrix naiveTruncation nanosleep needs needsPackage net netList new newClass newCoordinateSystem newNetFile newPackage newRing newline next nextPrime nextkey nil nonspaceAnalyzer norm normalCone not notImplemented notify null nullHomotopy nullParser nullSpace nullaryMethods nullhomotopy numColumns numRows numTBBThreads number numcols numerator numeric numericInterval numgens numrows odd oeis of ofClass on oo ooo oooo openDatabase openDatabaseOut openFiles openIn openInOut openListener openOut openOutAppend operatorAttributes optP optionalSignParser options or orP order override pack package packageTemplate pad pager pairs parallelApply parent parse part partition partitions parts path pdim peek permanents permutations pfaffian pfaffians pi pivots plus poincare poincareN polarize poly polylog position positions power powermod precision prefixDirectory prefixPath preimage prepend presentation pretty primaryComponent primaryDecomposition print printString printWidth printerr printingAccuracy printingLeadLimit printingPrecision printingSeparator printingTimeLimit printingTrailLimit processID product profile profileSummary programPaths projectiveHilbertPolynomial promote protect prune pruneComplex pruneDiff pruneUnit pruningMap pseudoRemainder pseudocode pullback pullbackMaps pushForward pushout pushoutMaps quit quotient quotientRemainder radical radicalContainment random randomComplexMap randomElement randomKRationalPoint randomMutableMatrix randomSubset rank rays read readDirectory readPackage readlink realPart realpath recursionDepth recursionLimit reduceHilbert reducedRowEchelonForm reductionNumber reesAlgebra reesAlgebraIdeal reesIdeal regSeqInIdeal regex regexQuote registerFinalizer regularity regularizedBeta regularizedGamma relations relativizeFilename remainder remove removeDirectory removeFile removeLowestDimension reorganize replace res reshape resolution resolutionMap restart resultant return returnCode reverse right ring ringFromFractions rootPath rootURI roots rotate round rowAdd rowMult rowPermute rowRankProfile rowSwap rsort run runHooks runLengthEncode runProgram same saturate scan scanKeys scanLines scanPairs scanValues schedule schreyerOrder scriptCommandLine searchPath sec sech seeParsing select selectInSubring selectKeys selectPairs selectValues selectVariables separate separateRegexp sequence serialNumber set setEcho setGroupID setIOExclusive setIOSynchronized setIOUnSynchronized setRandomSeed setup setupEmacs setupLift setupPromote sheaf sheafExt sheafHom shield show showClassStructure showHtml showStructure showTex showUserStructure shuffle sign simpleDocFrob sin singularLocus sinh size size2 sleep smithNormalForm solve someTerms sort sortColumns source span specialFiber specialFiberIdeal splice splitWWW sqrt stack stacksProject stacktrace standardForm standardPairs stashValue status stderr stdio step stopIfError store style sub sublists submatrix submatrixByDegrees subquotient subscript subsets substitute substring subtable sum super superscript support switch sylvesterMatrix symbol symbolBody symlinkDirectory symlinkFile symmetricAlgebra symmetricAlgebraIdeal symmetricKernel symmetricPower synonym syz table take tally tan tangentCone tangentSheaf tanh target taskResult temporaryFileName tensor tensorAssociativity tensorCommutativity terminalParser terms testExample testHunekeQuestion tests tex texMath then threadLocal threadVariable throw time times timing to toAbsolutePath toCC toCCi toChainComplex toDividedPowers toDual toExternalString toField toList toLower toMutableComplex toRR toRRi toSequence toString toUpper top topCoefficients topComponents topLevelMode torSymmetry trace transpose trap trim true truncate truncateOutput try tryLock tutorial typicalValues ultimate unbag uncurry undocumented uniform uninstallAllPackages uninstallPackage union unique uniquePermutations unlock unsequence unstack upperLeft upperRight urlEncode use userSymbols utf8 utf8check utf8substring validate value values variety vars vector versalEmbedding version viewHelp wait wedgeProduct weightRange when whichGm while width wikipedia wrap xor yonedaExtension yonedaMap yonedaProduct youngest zero zeta \ No newline at end of file diff --git a/M2/Macaulay2/editors/vim/m2.vim.syntax b/M2/Macaulay2/editors/vim/m2.vim.syntax index e227151f214..ae03bf19a57 100644 --- a/M2/Macaulay2/editors/vim/m2.vim.syntax +++ b/M2/Macaulay2/editors/vim/m2.vim.syntax @@ -1,4 +1,4 @@ -"" Auto-generated for Macaulay2-1.25.11. Do not modify this file manually. +"" Auto-generated for Macaulay2-1.26.05. Do not modify this file manually. " Vim syntax file " Language: Macaulay2 @@ -18,19 +18,19 @@ syn case match syn keyword m2Boolean true false syn keyword m2Keyword contained - \ SPACE TEST and break breakpoint catch continue do elapsedTime elapsedTiming else for from global if in list local new not of or profile return shield step symbol then threadLocal threadVariable throw time timing to try when while xor + \ SPACE TEST and break breakpoint catch continue do elapsedTime elapsedTiming else except for from global if in list local new not of or profile return shield step symbol then threadLocal threadVariable throw time timing to trap try when while xor syn keyword m2Datatype contained - \ ANCHOR Adjacent AffineVariety Analyzer AngleBarList Array AssociativeExpression AtomicInt BLOCKQUOTE BODY BOLD BR BUTTON Bag BasicList BettiTally BinaryOperation Boolean CC CDATA CODE COMMENT CacheTable ChainComplex ChainComplexMap CoherentSheaf Command CompiledFunction CompiledFunctionBody CompiledFunctionClosure ComplexField Constant DD DIV DL DT Database Descent Describe Dictionary DirectSum Divide DocumentTag EM Eliminate EngineRing Equation ExampleItem Expression File FilePosition FractionField Function FunctionApplication FunctionBody FunctionClosure GaloisField GeneralOrderedMonoid GlobalDictionary GradedModule GradedModuleMap GroebnerBasis GroebnerBasisOptions HEAD HEADER1 HEADER2 HEADER3 HEADER4 HEADER5 HEADER6 HR HREF HTML HashTable HeaderType Holder Hybrid Hypertext HypertextContainer HypertextParagraph HypertextVoid IFRAME IMG INDENT INPUT ITALIC Ideal ImmutableType IndeterminateNumber IndexedVariable IndexedVariableTable InexactField InexactFieldFamily InexactNumber InfiniteNumber IntermediateMarkUpType Iterator KBD Keyword LABEL LATER LI LINK LITERAL List LocalDictionary LowerBound MENU META Manipulator MapExpression MarkUpType Matrix MatrixExpression MethodFunction MethodFunctionBinary MethodFunctionSingle MethodFunctionWithOptions Minus Module Monoid MonoidElement MonomialIdeal MultigradedBettiTally MutableHashTable MutableList MutableMatrix Net NetFile Nothing Number NumberedVerticalList OL OneExpression Option OptionTable OrderedMonoid PARA PRE Package Parenthesize Parser Partition PolynomialRing Power Product ProductOrder Program ProgramRun ProjectiveHilbertPolynomial ProjectiveVariety Pseudocode PseudocodeClosure QQ QuotientRing RR RRi RealField Resolution Ring RingElement RingFamily RingMap RowExpression SAMP SCRIPT SMALL SPAN STRONG STYLE SUB SUBSECTION SUP ScriptedFunctor SelfInitializingType Sequence Set SheafExpression SheafMap SheafOfRings SparseMonomialVectorExpression SparseVectorExpression String Subscript Sum SumOfTwists Superscript Symbol SymbolBody TABLE TD TEX TH TITLE TO TO2 TOH TR TT Table Tally Task TensorProduct TestInput Thing Time Type UL URL VAR Variety Vector VectorExpression VerticalList VirtualTally VisibleList WrapperType ZZ ZeroExpression + \ ANCHOR Adjacent AffineVariety Analyzer AngleBarList Array AssociativeExpression AtomicInt BLOCKQUOTE BODY BOLD BR BUTTON Bag BasicList BettiTally BinaryOperation Boolean CC CCi CDATA CODE COMMENT CacheTable CoherentSheaf Command CompiledFunction CompiledFunctionBody CompiledFunctionClosure Complex ComplexField ComplexMap Constant DD DIV DL DT Database Descent Describe Dictionary DirectSum Divide DocumentTag EM Eliminate EngineRing Equation Error ExampleItem Expression File FilePosition FractionField Function FunctionApplication FunctionBody FunctionClosure GaloisField GeneralOrderedMonoid GlobalDictionary GradedModule GradedModuleMap GroebnerBasis GroebnerBasisOptions HEAD HEADER1 HEADER2 HEADER3 HEADER4 HEADER5 HEADER6 HR HREF HTML HashTable HeaderType Holder Hybrid Hypertext HypertextContainer HypertextParagraph HypertextVoid IFRAME IMG INDENT INPUT ITALIC Ideal ImmutableType IndeterminateNumber IndexedVariable IndexedVariableTable InexactField InexactFieldFamily InexactNumber InfiniteNumber IntermediateMarkUpType Iterator KBD Keyword LABEL LATER LI LINK LITERAL List LocalDictionary LowerBound MENU META Manipulator MapExpression MarkUpType Matrix MatrixExpression MethodFunction MethodFunctionBinary MethodFunctionSingle MethodFunctionWithOptions Minus Module Monoid MonoidElement MonomialIdeal MultigradedBettiTally MutableHashTable MutableList MutableMatrix Mutex Net NetFile Nothing Number NumberedVerticalList OL OneExpression Option OptionTable OrderedMonoid PARA PRE Package Parenthesize Parser Partition PolynomialRing Power Product ProductOrder Program ProgramRun ProjectiveHilbertPolynomial ProjectiveVariety Pseudocode PseudocodeClosure QQ QuotientRing RR RRi RealField Resolution Ring RingElement RingFamily RingMap RowExpression SAMP SCRIPT SMALL SPAN STRONG STYLE SUB SUBSECTION SUP ScriptedFunctor SelfInitializingType Sequence Set SheafExpression SheafMap SheafOfRings SparseMonomialVectorExpression SparseVectorExpression String Subscript Sum SumOfTwists Superscript Symbol SymbolBody TABLE TD TEX TH TITLE TO TO2 TOH TR TT Table Tally Task TensorProduct TestInput Thing Time Type UL URL VAR Variety Vector VectorExpression VerticalList VirtualTally VisibleList WrapperType ZZ ZeroExpression syn keyword m2Function container - \ BesselJ BesselY Beta Digamma EXAMPLE End Fano GCstats GF Gamma Grassmannian Hom LLL LUdecomposition M2CODE NNParser Proj QQParser QRDecomposition SVD SYNOPSIS Schubert Spec ZZParser about abs accumulate acos acosh acot acoth addCancelTask addDependencyTask addEndFunction addHook addStartTask adjoint agm alarm all ambient analyticSpread ancestor ancestors andP ann annihilator antipode any append applicationDirectory apply applyKeys applyPairs applyTable applyValues apropos arXiv ascii asin asinh ass assert associatedGradedRing associatedPrimes atEndOfFile atan atan2 atanh autoload baseFilename baseName baseRing basis beginDocumentation benchmark betti between binomial borel cacheValue cancelTask canonicalBundle capture ceiling centerString chainComplex changeBase changeDirectory char charAnalyzer characters check checkDegrees chi class clean clearEcho code codim coefficient coefficientRing coefficients cohomology coimage coker cokernel collectGarbage columnAdd columnMult columnPermute columnRankProfile columnSwap columnate combine commandInterpreter commonRing commonest comodule compareExchange complement complete components compose compositions compress concatenate conductor cone conjugate connectionCount constParser content contract conwayPolynomial copy copyDirectory copyFile cos cosh cot cotangentSheaf coth cover coverMap cpuTime createTask csc csch currentColumnNumber currentDirectory currentPosition currentRowNumber currentTime deadParser debug debugError decompose deepSplice default degree degreeGroup degreeLength degrees degreesMonoid degreesRing delete demark denominator depth describe det determinant diagonalMatrix diameter dictionary diff difference dim directProduct directSum disassemble discriminant dismiss distinguished divideByVariable doc document drop dual eagonNorthcott echoOff echoOn eigenvalues eigenvectors eint elements eliminate endPackage entries erase erf erfc error euler eulers even examples exchange exec exp expectedReesIdeal expm1 exponents export exportFrom exportMutable expression extend exteriorPower factor fileExecutable fileExists fileLength fileMode fileReadable fileTime fileWritable fillMatrix findFiles findHeft findProgram findSynonyms first firstkey fittingIdeal flagLookup flatten flattenRing flip floor fold forceGB fork format formation frac fraction frames fromDividedPowers fromDual functionBody futureParser gb gbRemove gbSnapshot gcd gcdCoefficients gcdLLL genera generateAssertions generator generators genericMatrix genericSkewMatrix genericSymmetricMatrix gens genus get getChangeMatrix getGlobalSymbol getIOThreadMode getNetFile getNonUnit getPrimeWithRootOfUnity getSymbol getWWW getc getenv globalAssign globalAssignFunction globalAssignment globalReleaseFunction gradedModule gradedModuleMap gramm graphIdeal graphRing groebnerBasis groupID hash hashTable headlines heft height hermite hilbertFunction hilbertPolynomial hilbertSeries hold homogenize homology homomorphism hooks horizontalJoin html httpHeaders hypertext icFracP icFractions icMap icPIdeal ideal idealSheaf idealizer identity image imaginaryPart importFrom independentSets index indices inducedMap inducesWellDefinedMap info input insert installAssignmentMethod installHilbertFunction installMethod installMinprimes installPackage installedPackages instance instances integralClosure integrate intersect intersectInP intersection interval inverse inverseErf inversePermutation inverseRegularizedBeta inverseRegularizedGamma inverseSystem irreducibleCharacteristicSeries irreducibleDecomposition isANumber isAffineRing isBorel isCanceled isCommutative isConstant isDirectSum isDirectory isEmpty isExact isField isFinite isFinitePrimeField isFreeModule isGlobalSymbol isHomogeneous isIdeal isInfinite isInjective isInputFile isIsomorphic isIsomorphism isLLL isLinearType isListener isLocallyFree isMember isModule isMonomialIdeal isMutable isNormal isOpen isOutputFile isPolynomialRing isPrimary isPrime isPrimitive isProjective isPseudoprime isQuotientModule isQuotientOf isQuotientRing isReady isReal isReduction isRegularFile isRing isSkewCommutative isSmooth isSorted isSquareFree isStandardGradedPolynomialRing isSubmodule isSubquotient isSubset isSupportedInZeroLocus isSurjective isTable isUnit isVeryAmple isWellDefined isWeylAlgebra isc isomorphism iterator jacobian jacobianDual join ker kernel kernelLLL kernelOfLocalization keys kill koszul last lcm leadCoefficient leadComponent leadMonomial leadTerm left length letterParser lift liftable limitFiles limitProcesses lines linkFile listForm listSymbols lngamma load loadPackage localDictionaries localize locate log log1p lookup lookupCount makeDirectory makeDocumentTag makePackageIndex makeS2 map markedGB match mathML matrix max maxPosition member memoize memoizeClear memoizeValues merge mergePairs method methodOptions methods midpoint min minPosition minPres mingens mingle minimalBetti minimalPresentation minimalPrimes minimalReduction minimize minimizeFilename minors minprimes minus mkdir mod module modulo monoid monomialCurveIdeal monomialIdeal monomialSubideal monomials moveFile multidegree multidoc multigraded multiplicity mutable mutableIdentity mutableMatrix nanosleep needs needsPackage net netList newClass newCoordinateSystem newNetFile newPackage newRing next nextPrime nextkey nonspaceAnalyzer norm normalCone notImplemented nullParser nullSpace nullhomotopy numColumns numRows number numcols numerator numeric numericInterval numgens numrows odd oeis ofClass on openDatabase openDatabaseOut openFiles openIn openInOut openListener openOut openOutAppend optP optionalSignParser options orP override pack package packageTemplate pad pager pairs parallelApply parent part partition partitions parts pdim peek permanents permutations pfaffian pfaffians pivots plus poincare poincareN polarize poly position positions power powermod precision preimage prepend presentation pretty primaryComponent primaryDecomposition print printString printerr processID product projectiveHilbertPolynomial promote protect prune pseudoRemainder pseudocode pullback pushForward pushout quotient quotientRemainder radical radicalContainment random randomKRationalPoint randomMutableMatrix randomSubset rank rays read readDirectory readPackage readlink realPart realpath recursionDepth reduceHilbert reducedRowEchelonForm reductionNumber reesAlgebra reesAlgebraIdeal reesIdeal regSeqInIdeal regex regexQuote registerFinalizer regularity regularizedBeta regularizedGamma relations relativizeFilename remainder remove removeDirectory removeFile removeLowestDimension reorganize replace res reshape resolution resultant reverse right ring ringFromFractions roots rotate round rowAdd rowMult rowPermute rowRankProfile rowSwap rsort run runHooks runLengthEncode runProgram same saturate scan scanKeys scanLines scanPairs scanValues schedule schreyerOrder searchPath sec sech seeParsing select selectInSubring selectKeys selectPairs selectValues selectVariables separate separateRegexp sequence serialNumber set setEcho setGroupID setIOExclusive setIOSynchronized setIOUnSynchronized setRandomSeed setup setupEmacs setupLift setupPromote sheaf sheafHom sheafMap show showHtml showTex sign simpleDocFrob sin singularLocus sinh size size2 sleep smithNormalForm solve someTerms sort sortColumns source span specialFiber specialFiberIdeal splice splitWWW sqrt stack stacksProject stacktrace standardForm standardPairs stashValue status store style sub sublists submatrix submatrixByDegrees subquotient subsets substitute substring subtable sum super support switch sylvesterMatrix symbolBody symlinkDirectory symlinkFile symmetricAlgebra symmetricAlgebraIdeal symmetricKernel symmetricPower synonym syz syzygyScheme table take tally tan tangentCone tangentSheaf tanh target taskResult temporaryFileName tensor tensorAssociativity terminalParser terms testHunekeQuestion tests tex texMath times toAbsolutePath toCC toDividedPowers toDual toExternalString toField toList toLower toRR toRRi toSequence toString toUpper top topCoefficients topComponents trace transpose trim truncate truncateOutput tutorial ultimate unbag uncurry undocumented uniform uninstallAllPackages uninstallPackage union unique uniquePermutations unsequence unstack urlEncode use userSymbols utf8 utf8check utf8substring validate value values variety vars vector versalEmbedding wait wedgeProduct weightRange whichGm width wikipedia wrap youngest zero zeta + \ BesselJ BesselY Beta Digamma EXAMPLE End Fano GCstats GF Gamma Grassmannian Hom LLL LUdecomposition M2CODE NNParser Proj QQParser QRDecomposition SVD SYNOPSIS Schubert Spec ZZParser about abs accumulate acos acosh acot acoth addCancelTask addDependencyTask addEndFunction addHook addStartTask adjoint agm alarm all ambient analyticSpread ancestor ancestors andP ann annihilator antipode any append applicationDirectory apply applyKeys applyPairs applyTable applyValues apropos arXiv ascii asin asinh ass assert associatedGradedRing associatedPrimes atEndOfFile atan atan2 atanh augmentationMap autoload baseFilename baseName baseRing basis beginDocumentation benchmark betti between binomial borel cacheValue cancelTask canonicalBundle canonicalMap canonicalTruncation capture ceiling centerString changeBase changeDirectory char charAnalyzer characters check checkDegrees chi class clean clearEcho code codim coefficient coefficientRing coefficients cohomology coimage coker cokernel collectGarbage columnAdd columnMult columnPermute columnRankProfile columnSwap columnate combine commandInterpreter commonRing commonest comodule compareExchange complement complete complex component components compose compositions compress concatenate concentration conductor cone conjugate connectingExtMap connectingMap connectingTorMap connectionCount constParser constantStrand content contract conwayPolynomial copy copyDirectory copyFile cos cosh cot cotangentSheaf coth cover coverMap cpuTime createTask csc csch currentColumnNumber currentDirectory currentPosition currentRowNumber currentTime cylinder deadParser debug debugError decompose deepSplice default degree degreeGroup degreeLength degrees degreesMonoid degreesRing delete demark denominator depth describe det determinant diagonalMatrix diameter dictionary diff difference dim directProduct directSum disassemble discriminant dismiss distinguished divideByVariable doc document drop dual eagonNorthcott eagonNorthcottComplex echoOff echoOn eigenvalues eigenvectors eint elements eliminate endPackage entries epicResolutionMap erase erf erfc error euler eulers even examples exchange exec exp expectedReesIdeal expm1 exponents export exportFrom exportMutable expression extend exteriorPower factor fileExecutable fileExists fileLength fileMode fileReadable fileTime fileWritable fillMatrix findFiles findHeft findProgram findSynonyms first firstkey fittingIdeal flagLookup flatten flattenRing flip floor fold forceGB fork format formation frac fraction frames freeResolution fromDividedPowers fromDual functionBody futureParser gb gbRemove gbSnapshot gcd gcdCoefficients gcdLLL genera generateAssertions generator generators genericMatrix genericSkewMatrix genericSymmetricMatrix gens genus get getChangeMatrix getGlobalSymbol getIOThreadMode getNetFile getNonUnit getPrimeWithRootOfUnity getSymbol getWWW getc getenv globalAssign globalAssignFunction globalAssignment globalReleaseFunction gradedModule gramm graphIdeal graphRing groebnerBasis groupID hash hashTable headlines heft height hermite hilbertFunction hilbertPolynomial hilbertSeries hold homogenize homology homomorphism homotopyMap hooks horizontalJoin horseshoeResolution html httpHeaders hypertext icFracP icFractions icMap icPIdeal ideal idealSheaf idealizer identity image imaginaryPart importFrom independentSets index indices inducedMap inducesWellDefinedMap info input insert installAssignmentMethod installHilbertFunction installMethod installMinprimes installPackage installedPackages instance instances integralClosure integrate intersect intersectInP intersection interval inverse inverseErf inversePermutation inverseRegularizedBeta inverseRegularizedGamma inverseSystem irreducibleCharacteristicSeries irreducibleDecomposition isANumber isAffineRing isBorel isCanceled isCommutative isComplexMorphism isConstant isDirectSum isDirectory isEmpty isExact isField isFinite isFinitePrimeField isFree isFreeModule isGlobalSymbol isHomogeneous isIdeal isInfinite isInjective isInputFile isIsomorphic isIsomorphism isLLL isLinearType isListener isLocallyFree isMember isModule isMonomialIdeal isMutable isNormal isNullHomotopic isNullHomotopyOf isOpen isOutputFile isPolynomialRing isPrimary isPrime isPrimitive isProjective isPseudoprime isQuasiIsomorphism isQuotientModule isQuotientOf isQuotientRing isReady isReal isReduction isRegularFile isRing isScalar isShortExactSequence isSkewCommutative isSmooth isSorted isSquareFree isStandardGradedPolynomialRing isSubmodule isSubquotient isSubset isSupportedInZeroLocus isSurjective isTable isUnit isVeryAmple isWellDefined isWeylAlgebra isc isomorphism iterator jacobian jacobianDual join ker kernel kernelLLL kernelOfLocalization keys kill koszul koszulComplex last lcm leadCoefficient leadComponent leadMonomial leadTerm left length letterParser lift liftMapAlongQuasiIsomorphism liftable limitFiles limitProcesses lines linkFile listForm listSymbols lngamma load loadPackage localDictionaries localize locate lock log log1p longExactSequence lookup lookupCount lowerLeft lowerRight makeDirectory makeDocumentTag makePackageIndex makeS2 map markedGB match mathML matrix max maxPosition member memoize memoizeClear memoizeValues merge mergePairs method methodOptions methods midpoint min minPosition minPres mingens mingle minimalBetti minimalPresentation minimalPrimes minimalReduction minimize minimizeFilename minors minprimes minus mkdir mod module modulo monoid monomialCurveIdeal monomialIdeal monomialSubideal monomials moveFile multidegree multidoc multigraded multiplicity mutable mutableIdentity mutableMatrix naiveTruncation nanosleep needs needsPackage net netList newClass newCoordinateSystem newNetFile newPackage newRing next nextPrime nextkey nonspaceAnalyzer norm normalCone notImplemented nullHomotopy nullParser nullSpace nullhomotopy numColumns numRows number numcols numerator numeric numericInterval numgens numrows odd oeis ofClass on openDatabase openDatabaseOut openFiles openIn openInOut openListener openOut openOutAppend optP optionalSignParser options orP override pack package packageTemplate pad pager pairs parallelApply parent parse part partition partitions parts pdim peek permanents permutations pfaffian pfaffians pivots plus poincare poincareN polarize poly polylog position positions power powermod precision preimage prepend presentation pretty primaryComponent primaryDecomposition print printString printerr processID product projectiveHilbertPolynomial promote protect prune pruneComplex pruneDiff pruneUnit pseudoRemainder pseudocode pullback pushForward pushout quotient quotientRemainder radical radicalContainment random randomComplexMap randomElement randomKRationalPoint randomMutableMatrix randomSubset rank rays read readDirectory readPackage readlink realPart realpath recursionDepth reduceHilbert reducedRowEchelonForm reductionNumber reesAlgebra reesAlgebraIdeal reesIdeal regSeqInIdeal regex regexQuote registerFinalizer regularity regularizedBeta regularizedGamma relations relativizeFilename remainder remove removeDirectory removeFile removeLowestDimension reorganize replace res reshape resolution resolutionMap resultant reverse right ring ringFromFractions roots rotate round rowAdd rowMult rowPermute rowRankProfile rowSwap rsort run runHooks runLengthEncode runProgram same saturate scan scanKeys scanLines scanPairs scanValues schedule schreyerOrder searchPath sec sech seeParsing select selectInSubring selectKeys selectPairs selectValues selectVariables separate separateRegexp sequence serialNumber set setEcho setGroupID setIOExclusive setIOSynchronized setIOUnSynchronized setRandomSeed setup setupEmacs setupLift setupPromote sheaf sheafHom show showHtml showTex shuffle sign simpleDocFrob sin singularLocus sinh size size2 sleep smithNormalForm solve someTerms sort sortColumns source span specialFiber specialFiberIdeal splice splitWWW sqrt stack stacksProject stacktrace standardForm standardPairs stashValue status store style sub sublists submatrix submatrixByDegrees subquotient subsets substitute substring subtable sum super support switch sylvesterMatrix symbolBody symlinkDirectory symlinkFile symmetricAlgebra symmetricAlgebraIdeal symmetricKernel symmetricPower synonym syz table take tally tan tangentCone tangentSheaf tanh target taskResult temporaryFileName tensor tensorAssociativity tensorCommutativity terminalParser terms testHunekeQuestion tests tex texMath times toAbsolutePath toCC toCCi toChainComplex toDividedPowers toDual toExternalString toField toList toLower toMutableComplex toRR toRRi toSequence toString toUpper top topCoefficients topComponents torSymmetry trace transpose trim truncate truncateOutput tryLock tutorial ultimate unbag uncurry undocumented uniform uninstallAllPackages uninstallPackage union unique uniquePermutations unlock unsequence unstack upperLeft upperRight urlEncode use userSymbols utf8 utf8check utf8substring validate value values variety vars vector versalEmbedding wait wedgeProduct weightRange whichGm width wikipedia wrap yonedaExtension yonedaMap yonedaProduct youngest zero zeta syn keyword m2Constant container - \ A1BrouwerDegrees AbstractSimplicialComplexes AbstractToricVarieties Acknowledgement AdditionalPaths AdjointIdeal AdjunctionForSurfaces AfterEval AfterNoPrint AfterPrint AInfinity AlgebraicSplines Algorithm Alignment AllCodimensions AllMarkovBases allowableThreads AnalyzeSheafOnP1 applicationDirectorySuffix argument Ascending AssociativeAlgebras Authors AuxiliaryFiles backtrace Bareiss Base BaseFunction baseRings BaseRow BasisElementLimit Bayer BeforePrint BeginningMacaulay2 Benchmark BernsteinSato Bertini BettiCharacters BGG BIBasis Binary Binomial BinomialEdgeIdeals Binomials BKZ blockMatrixForm Body BoijSoederberg Book3264Examples BooleanGB Boxes Brackets Browse Bruns cache CacheExampleOutput CallLimit CannedExample CatalanConstant Caveat CellularResolutions Center Certification ChainComplexExtras ChainComplexOperations ChangeMatrix CharacteristicClasses CheckDocumentation Chordal Citation cite Classic clearAll clearOutput close closeIn closeOut ClosestFit Code CodimensionLimit CodingTheory CoefficientRing Cofactor CohenEngine CohenTopLevel CohomCalg CoincidentRootLoci commandLine compactMatrixForm Complement CompleteIntersection CompleteIntersectionResolutions Complexes ConductorElement Configuration ConformalBlocks ConnectionMatrices Consequences Constants Contributors ConvexInterface ConwayPolynomials copyright Core CorrespondenceScrolls CotangentSchubert cotangentSurjection coverageSummary CpMackeyFunctors Cremona currentFileDirectory currentFileName currentLayout currentPackage Cyclotomic Date dd DebuggingMode debuggingMode debugLevel DecomposableSparseSystems Decompose Default defaultPrecision Degree DegreeGroup DegreeLift DegreeLimit DegreeMap DegreeOrder DegreeRank Degrees Dense Density Depth Descending Description DeterminantalRepresentations DGAlgebras dictionaryPath DiffAlg Dispatch DivideConquer DividedPowers Dmodules docExample docTemplate Down Dynamic EagonResolution EdgeIdeals edit EigenSolver EisenbudHunekeVasconcelos Elimination EliminationMatrices EllipticCurves EllipticIntegrals Email embeddedToAbstract end endl Engine engineDebugLevel EngineTests EnumerationCurves environment EquivariantGB errorDepth EulerConstant eulerSequence Example ExampleFiles ExampleSystems Exclude exit Ext ExteriorExtensions ExteriorIdeals ExteriorModules ExtLongExactSequence false FastMinors FastNonminimal FGLM fileDictionaries fileExitHooks FileName FindOne FiniteFittingIdeals First FirstPackage FlatMonoid Flexible flush FollowLinks ForeignFunctions FormalGroupLaws Format FourierMotzkin FourTiTwo fpLLL FrobeniusThresholds FunctionFieldDesingularization GameTheory GBDegrees gbTrace GenerateAssertions Generic GenericInitialIdeal GeometricDecomposability gfanInterface Givens GKMVarieties GLex Global GlobalAssignHook globalAssignmentHooks GlobalHookStore GlobalReleaseHook GlobalSectionLimit Gorenstein GradedLieAlgebras GraphicalModels GraphicalModelsMLE Graphics Graphs GRevLex GroebnerStrata GroebnerWalk GroupLex GroupRevLex GTZ Hadamard handleInterrupts HardDegreeLimit Heading Headline Heft Height help Hermite Hermitian HH hh HigherCIOperators HighestWeights Hilbert HodgeIntegrals HolonomicSystems homeDirectory HomePage Homogeneous Homogeneous2 HomologicalAlgebraPackage HomotopyLieAlgebra HorizontalSpace HyperplaneArrangements id idealSheafSequence IgnoreExampleErrors ii IncidenceCorrespondenceCohomology incomparable Increment indeterminate Index indexComponents infinity InfoDirSection infoHelp Inhomogeneous Inputs InstallPrefix IntegerProgramming IntegralClosure interpreterDepth Intersection InvariantRing InverseMethod Inverses InverseSystems Invertible InvolutiveBases Isomorphism Item Iterate Jacobian Jets Join JSON JSONRPC K3Carpets K3Surfaces Keep KeepFiles KeepZeroes Key Keywords Kronecker KustinMiller lastMatch LatticePolytopes Layout Left LengthLimit Lex LexIdeals LieAlgebraRepresentations Limit Linear LinearAlgebra LinearTruncations lineNumber listLocalSymbols listUserSymbols LLLBases loadDepth LoadDocumentation loadedFiles loadedPackages Local LocalRings LongPolynomial M0nbar Macaulay2Doc Maintainer MakeDocumentation MakeHTML MakeInfo MakePDF MapleInterface Markov MatchingFields MatrixFactorizations MatrixSchubert Matroids maxAllowableThreads maxExponent MaximalRank MaxReductionCount MCMApproximations MergeTeX minExponent MinimalGenerators MinimalMatrix minimalPresentationMap minimalPresentationMapInv MinimalPrimes Minimize MinimumVersion Miura MixedMultiplicity ModuleDeformations MonodromySolver Monomial MonomialAlgebras MonomialIntegerPrograms MonomialOrbits MonomialOrder Monomials MonomialSize Msolve MultigradedBGG MultigradedImplicitization MultiGradedRationalMap MultiplicitySequence MultiplierIdeals MultiplierIdealsDim2 MultiprojectiveVarieties NAGtypes Name Nauty NautyGraphs NCAlgebra NCLex NewFromMethod newline NewMethod NewOfFromMethod NewOfMethod nil Node NoetherianOperators NoetherNormalization NonminimalComplexes NonPrincipalTestIdeals NoPrint Normaliz NormalToricVarieties NotANumber notify NTL null nullaryMethods NumericalAlgebraicGeometry NumericalCertification NumericalImplicitization NumericalLinearAlgebra NumericalSchubertCalculus NumericalSemigroups NumericSolutions numTBBThreads OIGroebnerBases OldChainComplexes OldPolyhedra OldToricVectorBundles OnlineLookup OO oo ooo oooo OpenMath operatorAttributes OptionalComponentsPresent Options Order order Oscillators OutputDictionary Outputs PackageCitations PackageDictionary PackageExports PackageImports PackageTemplate PairLimit PairsRemaining ParallelF4 ParallelizeByDegree Parametrization Parsing path PathSignatures PencilsOfQuadrics Permanents Permutations PHCpack PhylogeneticTrees pi PieriMaps PlaneCurveLinearSeries PlaneCurveSingularities Points Polyhedra Polymake PolyominoIdeals Posets Position PositivityToricBundles POSIX Postfix Pre Precision Prefix prefixDirectory prefixPath PrimaryDecomposition PrimaryTag PrimitiveElement Print printingAccuracy printingLeadLimit printingPrecision printingSeparator printingTimeLimit printingTrailLimit printWidth Probability profileSummary programPaths Projective Prune PruneComplex pruningMap PseudomonomialPrimaryDecomposition Pullback pullbackMaps PushForward pushoutMaps Python QthPower QuadraticIdealExamplesByRoos Quasidegrees QuaternaryQuartics QuillenSuslin quit Quotient Radical RadicalCodim1 RaiseError RandomCanonicalCurves RandomComplexes RandomCurves RandomCurvesOverVerySmallFiniteFields RandomGenus14Curves RandomIdeals RandomMonomialIdeals RandomObjects RandomPlaneCurves RandomPoints RandomSpaceCurves Range RationalMaps RationalPoints RationalPoints2 ReactionNetworks RealFP RealQP RealQP1 RealRoots RealRR RealXD recursionLimit Reduce ReesAlgebra References ReflexivePolytopesDB Regularity RelativeCanonicalResolution Reload RemakeAllDocumentation RerunExamples ResidualIntersections ResLengthThree ResolutionsOfStanleyReisnerRings restart Result Resultants returnCode Reverse RevLex RHom Right RInterface rootPath rootURI RunDirectory RunExamples RunExternalM2 SagbiGbDetection Saturation SaturationMap Schubert2 SchurComplexes SchurFunctors SchurRings SchurVeronese SCMAlgebras scriptCommandLine SCSCP SectionRing SeeAlso SegreClasses SemidefiniteProgramming Seminormalization SeparateExec Serialization sheafExt ShimoyamaYokoyama showClassStructure showStructure showUserStructure SimpleDoc SimplicialComplexes SimplicialDecomposability SimplicialPosets SimplifyFractions SizeLimit SkewCommutative SlackIdeals SLnEquivariantMatrices SLPexpressions Sort SortStrategy SourceCode SourceRing SpaceCurves SparseResultants SpechtModule SpecialFanoFourfolds SpectralSequences SRdeformations Standard StartWithOneMinor StatePolytope StatGraphs stderr stdio StopBeforeComputation stopIfError StopIteration StopWithMinimalGenerators Strategy Strict StronglyStableIdeals Style SubalgebraBases Subnodes SubringLimit subscript Sugarless SumsOfSquares SuperLinearAlgebra superscript SVDComplexes SwitchingFields SymbolicPowers SymmetricPolynomials Synopsis Syzygies SyzygyLimit SyzygyMatrix SyzygyRows Tableaux TangentCone TateOnProducts TensorComplexes TerraciniLoci Test testExample TestIdeals TeXmacs Text ThinSincereQuivers ThreadedGB Threads Threshold Topcom topLevelMode Tor TorAlgebra Toric ToricHigherDirectImages ToricInvariants ToricTopology ToricVectorBundles Torsion TorsionFree TotalPairs Tree TriangularSets Triangulations Tries Trim Triplets Tropical TropicalToric true Truncate Truncations TSpreadIdeals TypicalValue typicalValues Undo Unique Units Unmixed Up UpdateOnly UpperTriangular Usage UseCachedExampleOutput UseHilbertFunction UserMode UseSyzygies Valuations Variable VariableBaseName Variables Varieties Vasconcelos VectorFields VectorGraphics Verbose Verbosity Verify VersalDeformations Version version VerticalSpace viewHelp VirtualResolutions Visualize VNumber WebApp Weights WeilDivisors WeylAlgebra WeylAlgebras WeylGroups WhitneyStratifications Wrap XML yonedaSheafExtension + \ A1BrouwerDegrees AbstractSimplicialComplexes AbstractToricVarieties Acknowledgement AdditionalPaths AdjointIdeal AdjunctionForSurfaces AfterEval AfterNoPrint AfterPrint AInfinity AlgebraicSplines Algorithm Alignment AllCodimensions AllMarkovBases allowableThreads AnalyzeSheafOnP1 applicationDirectorySuffix argument Ascending AssociativeAlgebras Authors AuxiliaryFiles backtrace Bareiss Base BaseFunction baseRings BaseRow BasisElementLimit Bayer BeforePrint BeginningMacaulay2 Benchmark BernsteinSato Bertini BettiCharacters BGG BIBasis Binary Binomial BinomialEdgeIdeals Binomials BKZ blockMatrixForm Body BoijSoederberg Book3264Examples BooleanGB Boundary Boxes Brackets Browse Bruns cache CacheExampleOutput CallLimit CannedExample CatalanConstant Caveat CellularResolutions Center Certification ChainComplexExtras ChainComplexOperations ChangeMatrix CharacteristicClasses CheckDocumentation Chordal Citation cite Classic clearAll clearOutput close closeIn closeOut ClosestFit Code CodimensionLimit CodingTheory CoefficientRing Cofactor CohenEngine CohenTopLevel CohomCalg CoincidentRootLoci commandLine compactMatrixForm Complement CompleteIntersection CompleteIntersectionResolutions Complexes Concentration ConductorElement Configuration ConformalBlocks ConnectionMatrices Consequences Constants Contributors ConvexInterface ConwayPolynomials copyright Core CorrespondenceScrolls CotangentSchubert coverageSummary CpMackeyFunctors Cremona currentFileDirectory currentFileName currentLayout currentPackage Cycle Cyclotomic Date dd DebuggingMode debuggingMode debugLevel DecomposableSparseSystems Decompose Default defaultPrecision Degree DegreeGroup DegreeLift DegreeLimit DegreeMap DegreeOrder DegreeRank Degrees Dense Density Depth Descending Description DeterminantalRepresentations DGAlgebras dictionaryPath DiffAlg Direction Dispatch DivideConquer DividedPowers Dmodules docExample docTemplate Down Dynamic EagonResolution EdgeIdeals edit EigenSolver EisenbudHunekeVasconcelos Elimination EliminationMatrices EliminationTemplates EllipticCurves EllipticIntegrals Email end endl Engine engineDebugLevel EngineTests EnumerationCurves environment EquivariantGB errorDepth EulerConstant Example ExampleFiles ExampleSystems Exclude exit Ext ExteriorExtensions ExteriorIdeals ExteriorModules false FastMinors FastNonminimal FGLM fileDictionaries fileExitHooks FileName FindOne FiniteFittingIdeals First FirstPackage FlatMonoid Flexible flush FollowLinks ForeignFunctions FormalGroupLaws Format FourierMotzkin FourTiTwo fpLLL FreeToExact FrobeniusThresholds FunctionFieldDesingularization GameTheory GBDegrees gbTrace GenerateAssertions Generic GenericInitialIdeal GeometricDecomposability gfanInterface Givens GKMVarieties GLex Global GlobalAssignHook globalAssignmentHooks GlobalHookStore GlobalReleaseHook GlobalSectionLimit Gorenstein GradedLieAlgebras GraphicalModels GraphicalModelsMLE Graphics Graphs GRevLex GroebnerStrata GroebnerWalk GroupLex GroupRevLex GTZ Hadamard handleInterrupts HardDegreeLimit Heading Headline Heft Height help Hermite Hermitian HH hh HigherCIOperators HighestWeights Hilbert HodgeIntegrals HolonomicSystems homeDirectory HomePage Homogeneous Homogeneous2 Homogenization HomologicalAlgebraPackage HomotopyLieAlgebra HorizontalSpace HyperplaneArrangements id IgnoreExampleErrors ii IncidenceCorrespondenceCohomology incomparable Increment indeterminate Index indexComponents infinity InfoDirSection infoHelp Inhomogeneous Inputs InstallPrefix IntegerProgramming IntegralClosure InternalDegree interpreterDepth Intersection InvariantRing InverseMethod Inverses InverseSystems Invertible InvolutiveBases Isomorphism Item Iterate Jacobian Jets Join JSON JSONRPC K3Carpets K3Surfaces Keep KeepFiles KeepZeroes Key Keywords Kronecker KustinMiller lastMatch LatticePolytopes Layout Left LengthLimit Lex LexIdeals LieAlgebraRepresentations Limit Linear LinearAlgebra LinearTruncations lineNumber listLocalSymbols listUserSymbols LLLBases loadDepth LoadDocumentation loadedFiles loadedPackages Local LocalRings LongPolynomial M0nbar Macaulay2Doc MacaulayPosets Maintainer MakeDocumentation MakeHTML MakeInfo MakePDF MapleInterface Markov MatchingFields MatrixFactorizations MatrixSchubert Matroids maxAllowableThreads maxExponent MaximalRank MaxReductionCount MCMApproximations MergeTeX minExponent MinimalGenerators MinimalMatrix minimalPresentationMap minimalPresentationMapInv MinimalPrimes Minimize minimizingMap MinimumVersion Miura MixedMultiplicity ModuleDeformations MonodromySolver Monomial MonomialAlgebras MonomialIntegerPrograms MonomialOrbits MonomialOrder Monomials MonomialSize MRDI Msolve MultigradedBGG MultigradedImplicitization MultiGradedRationalMap MultiplicitySequence MultiplierIdeals MultiplierIdealsDim2 MultiprojectiveVarieties NAGtypes Name Nauty NautyGraphs NCAlgebra NCLex NewFromMethod newline NewMethod NewOfFromMethod NewOfMethod nil Node NoetherianOperators NoetherNormalization Nonminimal NonminimalWithGB NonPrincipalTestIdeals NoPrint Normaliz NormalToricVarieties NotANumber notify NTL null nullaryMethods NumericalAlgebraicGeometry NumericalCertification NumericalImplicitization NumericalLinearAlgebra NumericalSchubertCalculus NumericalSemigroups NumericSolutions numTBBThreads OIGroebnerBases OldChainComplexes OnlineLookup OO oo ooo oooo OpenMath operatorAttributes OptionalComponentsPresent Options Order order Oscillators OutputDictionary Outputs OverField OverZZ PackageCitations PackageDictionary PackageExports PackageImports PackageTemplate Padic PairLimit PairsRemaining ParallelF4 ParallelizeByDegree Parametrization Parsing path PathSignatures PencilsOfQuadrics Permanents Permutations PHCpack PhylogeneticTrees pi PieriMaps PlaneCurveLinearSeries PlaneCurveSingularities Points Polyhedra Polymake PolyominoIdeals Posets Position PositivityToricBundles POSIX Postfix Pre Precision Prefix prefixDirectory prefixPath PrimaryDecomposition PrimaryTag PrimitiveElement Print printingAccuracy printingLeadLimit printingPrecision printingSeparator printingTimeLimit printingTrailLimit printWidth Probability profileSummary programPaths Projective Prune PruningMap pruningMap PseudomonomialPrimaryDecomposition Pullback pullbackMaps PushForward pushoutMaps Python QthPower QuadraticIdealExamplesByRoos Quasidegrees QuaternaryQuartics QuillenSuslin quit Quotient Radical RadicalCodim1 RaiseError RandomCanonicalCurves RandomComplexes RandomCurves RandomCurvesOverVerySmallFiniteFields RandomGenus14Curves RandomIdeals RandomMonomialIdeals RandomObjects RandomPlaneCurves RandomPoints RandomSpaceCurves Range RationalMaps RationalPoints RationalPoints2 ReactionNetworks RealFP RealQP RealQP1 RealRoots RealRR RealXD recursionLimit Reduce ReesAlgebra References ReflexivePolytopesDB Regularity RelativeCanonicalResolution Reload RemakeAllDocumentation RerunExamples ResidualIntersections ResLengthThree ResolutionsOfStanleyReisnerRings restart Result Resultants returnCode Reverse RevLex Right RInterface rootPath rootURI RunDirectory RunExamples RunExternalM2 SagbiGbDetection Saturation SaturationMap Schubert2 SchurComplexes SchurFunctors SchurRings SchurVeronese SCMAlgebras scriptCommandLine SCSCP SectionRing SeeAlso SegreClasses SemidefiniteProgramming Seminormalization SeparateExec Serialization sheafExt ShimoyamaYokoyama showClassStructure showStructure showUserStructure SimpleDoc SimplicialComplexes SimplicialDecomposability SimplicialModules SimplicialPosets SimplifyFractions SizeLimit SkewCommutative SlackIdeals SLnEquivariantMatrices SLPexpressions Sort SortStrategy SourceCode SourceRing SpaceCurves SparseResultants SpechtModule SpecialFanoFourfolds SpectralSequences SRdeformations Standard StartWithOneMinor StatePolytope StatGraphs stderr stdio StopBeforeComputation stopIfError StopIteration StopWithMinimalGenerators Strategy Strict StronglyStableIdeals Style SubalgebraBases Subnodes SubringLimit subscript Sugarless SumsOfSquares SuperLinearAlgebra superscript SVDComplexes SwitchingFields SymbolicPowers SymmetricPolynomials Synopsis Syzygies SyzygyLimit SyzygyMatrix SyzygyRows Tableaux TangentCone TateOnProducts TensorComplexes TerraciniLoci Test testExample TestIdeals TeXmacs Text ThinSincereQuivers ThreadedGB Threads Threshold Topcom topLevelMode Tor TorAlgebra Toric ToricHigherDirectImages ToricInvariants ToricTopology ToricVectorBundles Torsion TorsionFree TotalPairs Tree TriangularSets Triangulations Tries Trim Triplets Tropical TropicalToric true Truncate Truncations TSpreadIdeals TypicalValue typicalValues Undo Unique Units UnitTest Unmixed Up UpdateOnly UpperTriangular Usage UseCachedExampleOutput UseHilbertFunction UserMode UseSyzygies UseTarget Valuations Variable VariableBaseName Variables Varieties Vasconcelos VectorFields VectorGraphics Verbose Verbosity Verify VersalDeformations Version version VerticalSpace viewHelp VirtualResolutions Visualize VNumber WebApp Weights WeilDivisors WeylAlgebra WeylAlgebras WeylGroups WhitneyStratifications WittVectors Wrap XML syn keyword m2Symbol contained - \ A1BrouwerDegrees AInfinity ANCHOR AbstractSimplicialComplexes AbstractToricVarieties Acknowledgement AdditionalPaths Adjacent AdjointIdeal AdjunctionForSurfaces AffineVariety AfterEval AfterNoPrint AfterPrint AlgebraicSplines Algorithm Alignment AllCodimensions AllMarkovBases AnalyzeSheafOnP1 Analyzer AngleBarList Array Ascending AssociativeAlgebras AssociativeExpression AtomicInt Authors AuxiliaryFiles BGG BIBasis BKZ BLOCKQUOTE BODY BOLD BR BUTTON Bag Bareiss Base BaseFunction BaseRow BasicList BasisElementLimit Bayer BeforePrint BeginningMacaulay2 Benchmark BernsteinSato Bertini BesselJ BesselY Beta BettiCharacters BettiTally Binary BinaryOperation Binomial BinomialEdgeIdeals Binomials Body BoijSoederberg Book3264Examples Boolean BooleanGB Boxes Brackets Browse Bruns CC CDATA CODE COMMENT CacheExampleOutput CacheTable CallLimit CatalanConstant Caveat CellularResolutions Center Certification ChainComplex ChainComplexExtras ChainComplexMap ChainComplexOperations ChangeMatrix CharacteristicClasses CheckDocumentation Chordal Citation Classic ClosestFit CodimensionLimit CodingTheory CoefficientRing Cofactor CohenEngine CohenTopLevel CoherentSheaf CohomCalg CoincidentRootLoci Command CompiledFunction CompiledFunctionBody CompiledFunctionClosure Complement CompleteIntersection CompleteIntersectionResolutions ComplexField Complexes ConductorElement Configuration ConformalBlocks ConnectionMatrices Consequences Constant Constants Contributors ConvexInterface ConwayPolynomials Core CorrespondenceScrolls CotangentSchubert CpMackeyFunctors Cremona Cyclotomic DD DGAlgebras DIV DL DT Database Date DebuggingMode DecomposableSparseSystems Decompose Default Degree DegreeGroup DegreeLift DegreeLimit DegreeMap DegreeOrder DegreeRank Degrees Dense Density Depth Descending Descent Describe Description DeterminantalRepresentations Dictionary DiffAlg Digamma DirectSum Dispatch Divide DivideConquer DividedPowers Dmodules DocumentTag Down Dynamic EM EXAMPLE EagonResolution EdgeIdeals EigenSolver EisenbudHunekeVasconcelos Eliminate Elimination EliminationMatrices EllipticCurves EllipticIntegrals Email End Engine EngineRing EngineTests EnumerationCurves Equation EquivariantGB EulerConstant ExampleFiles ExampleItem ExampleSystems Exclude Expression Ext ExtLongExactSequence ExteriorExtensions ExteriorIdeals ExteriorModules FGLM Fano FastMinors FastNonminimal File FileName FilePosition FindOne FiniteFittingIdeals First FirstPackage FlatMonoid Flexible FollowLinks ForeignFunctions FormalGroupLaws Format FourTiTwo FourierMotzkin FractionField FrobeniusThresholds Function FunctionApplication FunctionBody FunctionClosure FunctionFieldDesingularization GBDegrees GCstats GF GKMVarieties GLex GRevLex GTZ GaloisField GameTheory Gamma GeneralOrderedMonoid GenerateAssertions Generic GenericInitialIdeal GeometricDecomposability Givens Global GlobalAssignHook GlobalDictionary GlobalHookStore GlobalReleaseHook GlobalSectionLimit Gorenstein GradedLieAlgebras GradedModule GradedModuleMap GraphicalModels GraphicalModelsMLE Graphics Graphs Grassmannian GroebnerBasis GroebnerBasisOptions GroebnerStrata GroebnerWalk GroupLex GroupRevLex HEAD HEADER1 HEADER2 HEADER3 HEADER4 HEADER5 HEADER6 HH HR HREF HTML Hadamard HardDegreeLimit HashTable HeaderType Heading Headline Heft Height Hermite Hermitian HigherCIOperators HighestWeights Hilbert HodgeIntegrals Holder HolonomicSystems Hom HomePage Homogeneous Homogeneous2 HomologicalAlgebraPackage HomotopyLieAlgebra HorizontalSpace Hybrid HyperplaneArrangements Hypertext HypertextContainer HypertextParagraph HypertextVoid IFRAME IMG INDENT INPUT ITALIC Ideal IgnoreExampleErrors ImmutableType IncidenceCorrespondenceCohomology Increment IndeterminateNumber Index IndexedVariable IndexedVariableTable InexactField InexactFieldFamily InexactNumber InfiniteNumber InfoDirSection Inhomogeneous Inputs InstallPrefix IntegerProgramming IntegralClosure IntermediateMarkUpType Intersection InvariantRing InverseMethod InverseSystems Inverses Invertible InvolutiveBases Isomorphism Iterate Iterator JSON JSONRPC Jacobian Jets Join K3Carpets K3Surfaces KBD Keep KeepFiles KeepZeroes Key Keyword Keywords Kronecker KustinMiller LABEL LATER LI LINK LITERAL LLL LLLBases LUdecomposition LatticePolytopes Layout Left LengthLimit Lex LexIdeals LieAlgebraRepresentations Limit Linear LinearAlgebra LinearTruncations List LoadDocumentation Local LocalDictionary LocalRings LongPolynomial LowerBound M0nbar M2CODE MCMApproximations MENU META Macaulay2Doc Maintainer MakeDocumentation MakeHTML MakeInfo MakePDF Manipulator MapExpression MapleInterface MarkUpType Markov MatchingFields Matrix MatrixExpression MatrixFactorizations MatrixSchubert Matroids MaxReductionCount MaximalRank MergeTeX MethodFunction MethodFunctionBinary MethodFunctionSingle MethodFunctionWithOptions MinimalGenerators MinimalMatrix MinimalPrimes Minimize MinimumVersion Minus Miura MixedMultiplicity Module ModuleDeformations MonodromySolver Monoid MonoidElement Monomial MonomialAlgebras MonomialIdeal MonomialIntegerPrograms MonomialOrbits MonomialOrder MonomialSize Monomials Msolve MultiGradedRationalMap MultigradedBGG MultigradedBettiTally MultigradedImplicitization MultiplicitySequence MultiplierIdeals MultiplierIdealsDim2 MultiprojectiveVarieties MutableHashTable MutableList MutableMatrix NAGtypes NCAlgebra NCLex NNParser NTL Name Nauty NautyGraphs Net NetFile NewFromMethod NewMethod NewOfFromMethod NewOfMethod NoPrint NoetherNormalization NoetherianOperators NonPrincipalTestIdeals NonminimalComplexes NormalToricVarieties Normaliz NotANumber Nothing Number NumberedVerticalList NumericSolutions NumericalAlgebraicGeometry NumericalCertification NumericalImplicitization NumericalLinearAlgebra NumericalSchubertCalculus NumericalSemigroups OIGroebnerBases OL OO OldChainComplexes OldPolyhedra OldToricVectorBundles OneExpression OnlineLookup OpenMath Option OptionTable OptionalComponentsPresent Options Order OrderedMonoid Oscillators OutputDictionary Outputs PARA PHCpack POSIX PRE Package PackageCitations PackageDictionary PackageExports PackageImports PackageTemplate PairLimit PairsRemaining ParallelF4 ParallelizeByDegree Parametrization Parenthesize Parser Parsing Partition PathSignatures PencilsOfQuadrics Permanents Permutations PhylogeneticTrees PieriMaps PlaneCurveLinearSeries PlaneCurveSingularities Points Polyhedra Polymake PolynomialRing PolyominoIdeals Posets Position PositivityToricBundles Postfix Power Precision Prefix PrimaryDecomposition PrimaryTag PrimitiveElement Print Probability Product ProductOrder Program ProgramRun Proj Projective ProjectiveHilbertPolynomial ProjectiveVariety Prune PruneComplex Pseudocode PseudocodeClosure PseudomonomialPrimaryDecomposition Pullback PushForward Python QQ QQParser QRDecomposition QthPower QuadraticIdealExamplesByRoos Quasidegrees QuaternaryQuartics QuillenSuslin Quotient QuotientRing RHom RInterface RR RRi Radical RadicalCodim1 RaiseError RandomCanonicalCurves RandomComplexes RandomCurves RandomCurvesOverVerySmallFiniteFields RandomGenus14Curves RandomIdeals RandomMonomialIdeals RandomObjects RandomPlaneCurves RandomPoints RandomSpaceCurves Range RationalMaps RationalPoints RationalPoints2 ReactionNetworks RealFP RealField RealQP RealQP1 RealRR RealRoots RealXD Reduce ReesAlgebra References ReflexivePolytopesDB Regularity RelativeCanonicalResolution Reload RemakeAllDocumentation RerunExamples ResLengthThree ResidualIntersections Resolution ResolutionsOfStanleyReisnerRings Result Resultants RevLex Reverse Right Ring RingElement RingFamily RingMap RowExpression RunDirectory RunExamples RunExternalM2 SAMP SCMAlgebras SCRIPT SCSCP SLPexpressions SLnEquivariantMatrices SMALL SPACE SPAN SRdeformations STRONG STYLE SUB SUBSECTION SUP SVD SVDComplexes SYNOPSIS SagbiGbDetection Saturation SaturationMap Schubert Schubert2 SchurComplexes SchurFunctors SchurRings SchurVeronese ScriptedFunctor SectionRing SeeAlso SegreClasses SelfInitializingType SemidefiniteProgramming Seminormalization SeparateExec Sequence Serialization Set SheafExpression SheafMap SheafOfRings ShimoyamaYokoyama SimpleDoc SimplicialComplexes SimplicialDecomposability SimplicialPosets SimplifyFractions SizeLimit SkewCommutative SlackIdeals Sort SortStrategy SourceCode SourceRing SpaceCurves SparseMonomialVectorExpression SparseResultants SparseVectorExpression Spec SpechtModule SpecialFanoFourfolds SpectralSequences Standard StartWithOneMinor StatGraphs StatePolytope StopBeforeComputation StopIteration StopWithMinimalGenerators Strategy Strict String StronglyStableIdeals Style SubalgebraBases Subnodes SubringLimit Subscript Sugarless Sum SumOfTwists SumsOfSquares SuperLinearAlgebra Superscript SwitchingFields Symbol SymbolBody SymbolicPowers SymmetricPolynomials Syzygies SyzygyLimit SyzygyMatrix SyzygyRows TABLE TD TEST TEX TH TITLE TO TO2 TOH TR TSpreadIdeals TT Table Tableaux Tally TangentCone Task TateOnProducts TeXmacs TensorComplexes TensorProduct TerraciniLoci Test TestIdeals TestInput Text ThinSincereQuivers Thing ThreadedGB Threads Threshold Time Topcom Tor TorAlgebra Toric ToricHigherDirectImages ToricInvariants ToricTopology ToricVectorBundles Torsion TorsionFree TotalPairs TriangularSets Triangulations Tries Trim Triplets Tropical TropicalToric Truncate Truncations Type TypicalValue UL URL Undo Unique Units Unmixed Up UpdateOnly UpperTriangular Usage UseCachedExampleOutput UseHilbertFunction UseSyzygies UserMode VAR VNumber Valuations Variable VariableBaseName Variables Varieties Variety Vasconcelos Vector VectorExpression VectorFields VectorGraphics Verbose Verbosity Verify VersalDeformations Version VerticalList VerticalSpace VirtualResolutions VirtualTally VisibleList Visualize WebApp Weights WeilDivisors WeylAlgebra WeylAlgebras WeylGroups WhitneyStratifications Wrap WrapperType XML ZZ ZZParser ZeroExpression about abs accumulate acos acosh acot acoth addCancelTask addDependencyTask addEndFunction addHook addStartTask adjoint agm alarm all allowableThreads ambient analyticSpread ancestor ancestors and andP ann annihilator antipode any append applicationDirectory applicationDirectorySuffix apply applyKeys applyPairs applyTable applyValues apropos arXiv argument ascii asin asinh ass assert associatedGradedRing associatedPrimes atEndOfFile atan atan2 atanh autoload backtrace baseFilename baseName baseRing baseRings basis beginDocumentation benchmark betti between binomial blockMatrixForm borel break breakpoint cache cacheValue cancelTask canonicalBundle capture catch ceiling centerString chainComplex changeBase changeDirectory char charAnalyzer characters check checkDegrees chi cite class clean clearAll clearEcho clearOutput close closeIn closeOut code codim coefficient coefficientRing coefficients cohomology coimage coker cokernel collectGarbage columnAdd columnMult columnPermute columnRankProfile columnSwap columnate combine commandInterpreter commandLine commonRing commonest comodule compactMatrixForm compareExchange complement complete components compose compositions compress concatenate conductor cone conjugate connectionCount constParser content continue contract conwayPolynomial copy copyDirectory copyFile copyright cos cosh cot cotangentSheaf cotangentSurjection coth cover coverMap coverageSummary cpuTime createTask csc csch current currentColumnNumber currentDirectory currentFileDirectory currentFileName currentLayout currentPackage currentPosition currentRowNumber currentString currentTime dd deadParser debug debugError debugLevel debuggingMode decompose deepSplice default defaultPrecision degree degreeGroup degreeLength degrees degreesMonoid degreesRing delete demark denominator depth describe det determinant diagonalMatrix diameter dictionary dictionaryPath diff difference dim directProduct directSum disassemble discriminant dismiss distinguished divideByVariable do doc docExample docTemplate document drop dual eagonNorthcott echoOff echoOn edit eigenvalues eigenvectors eint elapsedTime elapsedTiming elements eliminate else embeddedToAbstract end endPackage endl engineDebugLevel entries environment erase erf erfc error errorDepth euler eulerSequence eulers even examples exchange exec exit exp expectedReesIdeal expm1 exponents export exportFrom exportMutable expression extend exteriorPower factor false fileDictionaries fileExecutable fileExists fileExitHooks fileLength fileMode fileReadable fileTime fileWritable fillMatrix findFiles findHeft findProgram findSynonyms first firstkey fittingIdeal flagLookup flatten flattenRing flip floor flush fold for forceGB fork format formation fpLLL frac fraction frames from fromDividedPowers fromDual functionBody futureParser gb gbRemove gbSnapshot gbTrace gcd gcdCoefficients gcdLLL genera generateAssertions generator generators genericMatrix genericSkewMatrix genericSymmetricMatrix gens genus get getChangeMatrix getGlobalSymbol getIOThreadMode getNetFile getNonUnit getPrimeWithRootOfUnity getSymbol getWWW getc getenv gfanInterface global globalAssign globalAssignFunction globalAssignment globalAssignmentHooks globalReleaseFunction gradedModule gradedModuleMap gramm graphIdeal graphRing groebnerBasis groupID handleInterrupts hash hashTable headlines heft height help hermite hh hilbertFunction hilbertPolynomial hilbertSeries hold homeDirectory homogenize homology homomorphism hooks horizontalJoin html httpHeaders hypertext icFracP icFractions icMap icPIdeal id ideal idealSheaf idealSheafSequence idealizer identity if ii image imaginaryPart importFrom in incomparable independentSets indeterminate index indexComponents indices inducedMap inducesWellDefinedMap infinity info infoHelp input insert installAssignmentMethod installHilbertFunction installMethod installMinprimes installPackage installedPackages instance instances integralClosure integrate interpreterDepth intersect intersectInP intersection interval inverse inverseErf inversePermutation inverseRegularizedBeta inverseRegularizedGamma inverseSystem irreducibleCharacteristicSeries irreducibleDecomposition isANumber isAffineRing isBorel isCanceled isCommutative isConstant isDirectSum isDirectory isEmpty isExact isField isFinite isFinitePrimeField isFreeModule isGlobalSymbol isHomogeneous isIdeal isInfinite isInjective isInputFile isIsomorphic isIsomorphism isLLL isLinearType isListener isLocallyFree isMember isModule isMonomialIdeal isMutable isNormal isOpen isOutputFile isPolynomialRing isPrimary isPrime isPrimitive isProjective isPseudoprime isQuotientModule isQuotientOf isQuotientRing isReady isReal isReduction isRegularFile isRing isSkewCommutative isSmooth isSorted isSquareFree isStandardGradedPolynomialRing isSubmodule isSubquotient isSubset isSupportedInZeroLocus isSurjective isTable isUnit isVeryAmple isWellDefined isWeylAlgebra isc isomorphism iterator jacobian jacobianDual join ker kernel kernelLLL kernelOfLocalization keys kill koszul last lastMatch lcm leadCoefficient leadComponent leadMonomial leadTerm left length letterParser lift liftable limitFiles limitProcesses lineNumber lines linkFile list listForm listLocalSymbols listSymbols listUserSymbols lngamma load loadDepth loadPackage loadedFiles loadedPackages local localDictionaries localize locate log log1p lookup lookupCount makeDirectory makeDocumentTag makePackageIndex makeS2 map markedGB match mathML matrix max maxAllowableThreads maxExponent maxPosition member memoize memoizeClear memoizeValues merge mergePairs method methodOptions methods midpoint min minExponent minPosition minPres mingens mingle minimalBetti minimalPresentation minimalPresentationMap minimalPresentationMapInv minimalPrimes minimalReduction minimize minimizeFilename minors minprimes minus mkdir mod module modulo monoid monomialCurveIdeal monomialIdeal monomialSubideal monomials moveFile multidegree multidoc multigraded multiplicity mutable mutableIdentity mutableMatrix nanosleep needs needsPackage net netList new newClass newCoordinateSystem newNetFile newPackage newRing newline next nextPrime nextkey nil nonspaceAnalyzer norm normalCone not notImplemented notify null nullParser nullSpace nullaryMethods nullhomotopy numColumns numRows numTBBThreads number numcols numerator numeric numericInterval numgens numrows odd oeis of ofClass on oo ooo oooo openDatabase openDatabaseOut openFiles openIn openInOut openListener openOut openOutAppend operatorAttributes optP optionalSignParser options or orP order override pack package packageTemplate pad pager pairs parallelApply parent part partition partitions parts path pdim peek permanents permutations pfaffian pfaffians pi pivots plus poincare poincareN polarize poly position positions power powermod precision prefixDirectory prefixPath preimage prepend presentation pretty primaryComponent primaryDecomposition print printString printWidth printerr printingAccuracy printingLeadLimit printingPrecision printingSeparator printingTimeLimit printingTrailLimit processID product profile profileSummary programPaths projectiveHilbertPolynomial promote protect prune pruningMap pseudoRemainder pseudocode pullback pullbackMaps pushForward pushout pushoutMaps quit quotient quotientRemainder radical radicalContainment random randomKRationalPoint randomMutableMatrix randomSubset rank rays read readDirectory readPackage readlink realPart realpath recursionDepth recursionLimit reduceHilbert reducedRowEchelonForm reductionNumber reesAlgebra reesAlgebraIdeal reesIdeal regSeqInIdeal regex regexQuote registerFinalizer regularity regularizedBeta regularizedGamma relations relativizeFilename remainder remove removeDirectory removeFile removeLowestDimension reorganize replace res reshape resolution restart resultant return returnCode reverse right ring ringFromFractions rootPath rootURI roots rotate round rowAdd rowMult rowPermute rowRankProfile rowSwap rsort run runHooks runLengthEncode runProgram same saturate scan scanKeys scanLines scanPairs scanValues schedule schreyerOrder scriptCommandLine searchPath sec sech seeParsing select selectInSubring selectKeys selectPairs selectValues selectVariables separate separateRegexp sequence serialNumber set setEcho setGroupID setIOExclusive setIOSynchronized setIOUnSynchronized setRandomSeed setup setupEmacs setupLift setupPromote sheaf sheafExt sheafHom sheafMap shield show showClassStructure showHtml showStructure showTex showUserStructure sign simpleDocFrob sin singularLocus sinh size size2 sleep smithNormalForm solve someTerms sort sortColumns source span specialFiber specialFiberIdeal splice splitWWW sqrt stack stacksProject stacktrace standardForm standardPairs stashValue status stderr stdio step stopIfError store style sub sublists submatrix submatrixByDegrees subquotient subscript subsets substitute substring subtable sum super superscript support switch sylvesterMatrix symbol symbolBody symlinkDirectory symlinkFile symmetricAlgebra symmetricAlgebraIdeal symmetricKernel symmetricPower synonym syz syzygyScheme table take tally tan tangentCone tangentSheaf tanh target taskResult temporaryFileName tensor tensorAssociativity terminalParser terms testExample testHunekeQuestion tests tex texMath then threadLocal threadVariable throw time times timing to toAbsolutePath toCC toDividedPowers toDual toExternalString toField toList toLower toRR toRRi toSequence toString toUpper top topCoefficients topComponents topLevelMode trace transpose trim true truncate truncateOutput try tutorial typicalValues ultimate unbag uncurry undocumented uniform uninstallAllPackages uninstallPackage union unique uniquePermutations unsequence unstack urlEncode use userSymbols utf8 utf8check utf8substring validate value values variety vars vector versalEmbedding version viewHelp wait wedgeProduct weightRange when whichGm while width wikipedia wrap xor yonedaSheafExtension youngest zero zeta + \ A1BrouwerDegrees AInfinity ANCHOR AbstractSimplicialComplexes AbstractToricVarieties Acknowledgement AdditionalPaths Adjacent AdjointIdeal AdjunctionForSurfaces AffineVariety AfterEval AfterNoPrint AfterPrint AlgebraicSplines Algorithm Alignment AllCodimensions AllMarkovBases AnalyzeSheafOnP1 Analyzer AngleBarList Array Ascending AssociativeAlgebras AssociativeExpression AtomicInt Authors AuxiliaryFiles BGG BIBasis BKZ BLOCKQUOTE BODY BOLD BR BUTTON Bag Bareiss Base BaseFunction BaseRow BasicList BasisElementLimit Bayer BeforePrint BeginningMacaulay2 Benchmark BernsteinSato Bertini BesselJ BesselY Beta BettiCharacters BettiTally Binary BinaryOperation Binomial BinomialEdgeIdeals Binomials Body BoijSoederberg Book3264Examples Boolean BooleanGB Boundary Boxes Brackets Browse Bruns CC CCi CDATA CODE COMMENT CacheExampleOutput CacheTable CallLimit CatalanConstant Caveat CellularResolutions Center Certification ChainComplexExtras ChainComplexOperations ChangeMatrix CharacteristicClasses CheckDocumentation Chordal Citation Classic ClosestFit CodimensionLimit CodingTheory CoefficientRing Cofactor CohenEngine CohenTopLevel CoherentSheaf CohomCalg CoincidentRootLoci Command CompiledFunction CompiledFunctionBody CompiledFunctionClosure Complement CompleteIntersection CompleteIntersectionResolutions Complex ComplexField ComplexMap Complexes Concentration ConductorElement Configuration ConformalBlocks ConnectionMatrices Consequences Constant Constants Contributors ConvexInterface ConwayPolynomials Core CorrespondenceScrolls CotangentSchubert CpMackeyFunctors Cremona Cycle Cyclotomic DD DGAlgebras DIV DL DT Database Date DebuggingMode DecomposableSparseSystems Decompose Default Degree DegreeGroup DegreeLift DegreeLimit DegreeMap DegreeOrder DegreeRank Degrees Dense Density Depth Descending Descent Describe Description DeterminantalRepresentations Dictionary DiffAlg Digamma DirectSum Direction Dispatch Divide DivideConquer DividedPowers Dmodules DocumentTag Down Dynamic EM EXAMPLE EagonResolution EdgeIdeals EigenSolver EisenbudHunekeVasconcelos Eliminate Elimination EliminationMatrices EliminationTemplates EllipticCurves EllipticIntegrals Email End Engine EngineRing EngineTests EnumerationCurves Equation EquivariantGB Error EulerConstant ExampleFiles ExampleItem ExampleSystems Exclude Expression Ext ExteriorExtensions ExteriorIdeals ExteriorModules FGLM Fano FastMinors FastNonminimal File FileName FilePosition FindOne FiniteFittingIdeals First FirstPackage FlatMonoid Flexible FollowLinks ForeignFunctions FormalGroupLaws Format FourTiTwo FourierMotzkin FractionField FreeToExact FrobeniusThresholds Function FunctionApplication FunctionBody FunctionClosure FunctionFieldDesingularization GBDegrees GCstats GF GKMVarieties GLex GRevLex GTZ GaloisField GameTheory Gamma GeneralOrderedMonoid GenerateAssertions Generic GenericInitialIdeal GeometricDecomposability Givens Global GlobalAssignHook GlobalDictionary GlobalHookStore GlobalReleaseHook GlobalSectionLimit Gorenstein GradedLieAlgebras GradedModule GradedModuleMap GraphicalModels GraphicalModelsMLE Graphics Graphs Grassmannian GroebnerBasis GroebnerBasisOptions GroebnerStrata GroebnerWalk GroupLex GroupRevLex HEAD HEADER1 HEADER2 HEADER3 HEADER4 HEADER5 HEADER6 HH HR HREF HTML Hadamard HardDegreeLimit HashTable HeaderType Heading Headline Heft Height Hermite Hermitian HigherCIOperators HighestWeights Hilbert HodgeIntegrals Holder HolonomicSystems Hom HomePage Homogeneous Homogeneous2 Homogenization HomologicalAlgebraPackage HomotopyLieAlgebra HorizontalSpace Hybrid HyperplaneArrangements Hypertext HypertextContainer HypertextParagraph HypertextVoid IFRAME IMG INDENT INPUT ITALIC Ideal IgnoreExampleErrors ImmutableType IncidenceCorrespondenceCohomology Increment IndeterminateNumber Index IndexedVariable IndexedVariableTable InexactField InexactFieldFamily InexactNumber InfiniteNumber InfoDirSection Inhomogeneous Inputs InstallPrefix IntegerProgramming IntegralClosure IntermediateMarkUpType InternalDegree Intersection InvariantRing InverseMethod InverseSystems Inverses Invertible InvolutiveBases Isomorphism Iterate Iterator JSON JSONRPC Jacobian Jets Join K3Carpets K3Surfaces KBD Keep KeepFiles KeepZeroes Key Keyword Keywords Kronecker KustinMiller LABEL LATER LI LINK LITERAL LLL LLLBases LUdecomposition LatticePolytopes Layout Left LengthLimit Lex LexIdeals LieAlgebraRepresentations Limit Linear LinearAlgebra LinearTruncations List LoadDocumentation Local LocalDictionary LocalRings LongPolynomial LowerBound M0nbar M2CODE MCMApproximations MENU META MRDI Macaulay2Doc MacaulayPosets Maintainer MakeDocumentation MakeHTML MakeInfo MakePDF Manipulator MapExpression MapleInterface MarkUpType Markov MatchingFields Matrix MatrixExpression MatrixFactorizations MatrixSchubert Matroids MaxReductionCount MaximalRank MergeTeX MethodFunction MethodFunctionBinary MethodFunctionSingle MethodFunctionWithOptions MinimalGenerators MinimalMatrix MinimalPrimes Minimize MinimumVersion Minus Miura MixedMultiplicity Module ModuleDeformations MonodromySolver Monoid MonoidElement Monomial MonomialAlgebras MonomialIdeal MonomialIntegerPrograms MonomialOrbits MonomialOrder MonomialSize Monomials Msolve MultiGradedRationalMap MultigradedBGG MultigradedBettiTally MultigradedImplicitization MultiplicitySequence MultiplierIdeals MultiplierIdealsDim2 MultiprojectiveVarieties MutableHashTable MutableList MutableMatrix Mutex NAGtypes NCAlgebra NCLex NNParser NTL Name Nauty NautyGraphs Net NetFile NewFromMethod NewMethod NewOfFromMethod NewOfMethod NoPrint NoetherNormalization NoetherianOperators NonPrincipalTestIdeals Nonminimal NonminimalWithGB NormalToricVarieties Normaliz NotANumber Nothing Number NumberedVerticalList NumericSolutions NumericalAlgebraicGeometry NumericalCertification NumericalImplicitization NumericalLinearAlgebra NumericalSchubertCalculus NumericalSemigroups OIGroebnerBases OL OO OldChainComplexes OneExpression OnlineLookup OpenMath Option OptionTable OptionalComponentsPresent Options Order OrderedMonoid Oscillators OutputDictionary Outputs OverField OverZZ PARA PHCpack POSIX PRE Package PackageCitations PackageDictionary PackageExports PackageImports PackageTemplate Padic PairLimit PairsRemaining ParallelF4 ParallelizeByDegree Parametrization Parenthesize Parser Parsing Partition PathSignatures PencilsOfQuadrics Permanents Permutations PhylogeneticTrees PieriMaps PlaneCurveLinearSeries PlaneCurveSingularities Points Polyhedra Polymake PolynomialRing PolyominoIdeals Posets Position PositivityToricBundles Postfix Power Precision Prefix PrimaryDecomposition PrimaryTag PrimitiveElement Print Probability Product ProductOrder Program ProgramRun Proj Projective ProjectiveHilbertPolynomial ProjectiveVariety Prune PruningMap Pseudocode PseudocodeClosure PseudomonomialPrimaryDecomposition Pullback PushForward Python QQ QQParser QRDecomposition QthPower QuadraticIdealExamplesByRoos Quasidegrees QuaternaryQuartics QuillenSuslin Quotient QuotientRing RInterface RR RRi Radical RadicalCodim1 RaiseError RandomCanonicalCurves RandomComplexes RandomCurves RandomCurvesOverVerySmallFiniteFields RandomGenus14Curves RandomIdeals RandomMonomialIdeals RandomObjects RandomPlaneCurves RandomPoints RandomSpaceCurves Range RationalMaps RationalPoints RationalPoints2 ReactionNetworks RealFP RealField RealQP RealQP1 RealRR RealRoots RealXD Reduce ReesAlgebra References ReflexivePolytopesDB Regularity RelativeCanonicalResolution Reload RemakeAllDocumentation RerunExamples ResLengthThree ResidualIntersections Resolution ResolutionsOfStanleyReisnerRings Result Resultants RevLex Reverse Right Ring RingElement RingFamily RingMap RowExpression RunDirectory RunExamples RunExternalM2 SAMP SCMAlgebras SCRIPT SCSCP SLPexpressions SLnEquivariantMatrices SMALL SPACE SPAN SRdeformations STRONG STYLE SUB SUBSECTION SUP SVD SVDComplexes SYNOPSIS SagbiGbDetection Saturation SaturationMap Schubert Schubert2 SchurComplexes SchurFunctors SchurRings SchurVeronese ScriptedFunctor SectionRing SeeAlso SegreClasses SelfInitializingType SemidefiniteProgramming Seminormalization SeparateExec Sequence Serialization Set SheafExpression SheafMap SheafOfRings ShimoyamaYokoyama SimpleDoc SimplicialComplexes SimplicialDecomposability SimplicialModules SimplicialPosets SimplifyFractions SizeLimit SkewCommutative SlackIdeals Sort SortStrategy SourceCode SourceRing SpaceCurves SparseMonomialVectorExpression SparseResultants SparseVectorExpression Spec SpechtModule SpecialFanoFourfolds SpectralSequences Standard StartWithOneMinor StatGraphs StatePolytope StopBeforeComputation StopIteration StopWithMinimalGenerators Strategy Strict String StronglyStableIdeals Style SubalgebraBases Subnodes SubringLimit Subscript Sugarless Sum SumOfTwists SumsOfSquares SuperLinearAlgebra Superscript SwitchingFields Symbol SymbolBody SymbolicPowers SymmetricPolynomials Syzygies SyzygyLimit SyzygyMatrix SyzygyRows TABLE TD TEST TEX TH TITLE TO TO2 TOH TR TSpreadIdeals TT Table Tableaux Tally TangentCone Task TateOnProducts TeXmacs TensorComplexes TensorProduct TerraciniLoci Test TestIdeals TestInput Text ThinSincereQuivers Thing ThreadedGB Threads Threshold Time Topcom Tor TorAlgebra Toric ToricHigherDirectImages ToricInvariants ToricTopology ToricVectorBundles Torsion TorsionFree TotalPairs TriangularSets Triangulations Tries Trim Triplets Tropical TropicalToric Truncate Truncations Type TypicalValue UL URL Undo Unique UnitTest Units Unmixed Up UpdateOnly UpperTriangular Usage UseCachedExampleOutput UseHilbertFunction UseSyzygies UseTarget UserMode VAR VNumber Valuations Variable VariableBaseName Variables Varieties Variety Vasconcelos Vector VectorExpression VectorFields VectorGraphics Verbose Verbosity Verify VersalDeformations Version VerticalList VerticalSpace VirtualResolutions VirtualTally VisibleList Visualize WebApp Weights WeilDivisors WeylAlgebra WeylAlgebras WeylGroups WhitneyStratifications WittVectors Wrap WrapperType XML ZZ ZZParser ZeroExpression about abs accumulate acos acosh acot acoth addCancelTask addDependencyTask addEndFunction addHook addStartTask adjoint agm alarm all allowableThreads ambient analyticSpread ancestor ancestors and andP ann annihilator antipode any append applicationDirectory applicationDirectorySuffix apply applyKeys applyPairs applyTable applyValues apropos arXiv argument ascii asin asinh ass assert associatedGradedRing associatedPrimes atEndOfFile atan atan2 atanh augmentationMap autoload backtrace baseFilename baseName baseRing baseRings basis beginDocumentation benchmark betti between binomial blockMatrixForm borel break breakpoint cache cacheValue cancelTask canonicalBundle canonicalMap canonicalTruncation capture catch ceiling centerString changeBase changeDirectory char charAnalyzer characters check checkDegrees chi cite class clean clearAll clearEcho clearOutput close closeIn closeOut code codim coefficient coefficientRing coefficients cohomology coimage coker cokernel collectGarbage columnAdd columnMult columnPermute columnRankProfile columnSwap columnate combine commandInterpreter commandLine commonRing commonest comodule compactMatrixForm compareExchange complement complete complex component components compose compositions compress concatenate concentration conductor cone conjugate connectingExtMap connectingMap connectingTorMap connectionCount constParser constantStrand content continue contract conwayPolynomial copy copyDirectory copyFile copyright cos cosh cot cotangentSheaf coth cover coverMap coverageSummary cpuTime createTask csc csch current currentColumnNumber currentDirectory currentFileDirectory currentFileName currentLayout currentPackage currentPosition currentRowNumber currentString currentTime cylinder dd deadParser debug debugError debugLevel debuggingMode decompose deepSplice default defaultPrecision degree degreeGroup degreeLength degrees degreesMonoid degreesRing delete demark denominator depth describe det determinant diagonalMatrix diameter dictionary dictionaryPath diff difference dim directProduct directSum disassemble discriminant dismiss distinguished divideByVariable do doc docExample docTemplate document drop dual eagonNorthcott eagonNorthcottComplex echoOff echoOn edit eigenvalues eigenvectors eint elapsedTime elapsedTiming elements eliminate else end endPackage endl engineDebugLevel entries environment epicResolutionMap erase erf erfc error errorDepth euler eulers even examples except exchange exec exit exp expectedReesIdeal expm1 exponents export exportFrom exportMutable expression extend exteriorPower factor false fileDictionaries fileExecutable fileExists fileExitHooks fileLength fileMode fileReadable fileTime fileWritable fillMatrix findFiles findHeft findProgram findSynonyms first firstkey fittingIdeal flagLookup flatten flattenRing flip floor flush fold for forceGB fork format formation fpLLL frac fraction frames freeResolution from fromDividedPowers fromDual functionBody futureParser gb gbRemove gbSnapshot gbTrace gcd gcdCoefficients gcdLLL genera generateAssertions generator generators genericMatrix genericSkewMatrix genericSymmetricMatrix gens genus get getChangeMatrix getGlobalSymbol getIOThreadMode getNetFile getNonUnit getPrimeWithRootOfUnity getSymbol getWWW getc getenv gfanInterface global globalAssign globalAssignFunction globalAssignment globalAssignmentHooks globalReleaseFunction gradedModule gramm graphIdeal graphRing groebnerBasis groupID handleInterrupts hash hashTable headlines heft height help hermite hh hilbertFunction hilbertPolynomial hilbertSeries hold homeDirectory homogenize homology homomorphism homotopyMap hooks horizontalJoin horseshoeResolution html httpHeaders hypertext icFracP icFractions icMap icPIdeal id ideal idealSheaf idealizer identity if ii image imaginaryPart importFrom in incomparable independentSets indeterminate index indexComponents indices inducedMap inducesWellDefinedMap infinity info infoHelp input insert installAssignmentMethod installHilbertFunction installMethod installMinprimes installPackage installedPackages instance instances integralClosure integrate interpreterDepth intersect intersectInP intersection interval inverse inverseErf inversePermutation inverseRegularizedBeta inverseRegularizedGamma inverseSystem irreducibleCharacteristicSeries irreducibleDecomposition isANumber isAffineRing isBorel isCanceled isCommutative isComplexMorphism isConstant isDirectSum isDirectory isEmpty isExact isField isFinite isFinitePrimeField isFree isFreeModule isGlobalSymbol isHomogeneous isIdeal isInfinite isInjective isInputFile isIsomorphic isIsomorphism isLLL isLinearType isListener isLocallyFree isMember isModule isMonomialIdeal isMutable isNormal isNullHomotopic isNullHomotopyOf isOpen isOutputFile isPolynomialRing isPrimary isPrime isPrimitive isProjective isPseudoprime isQuasiIsomorphism isQuotientModule isQuotientOf isQuotientRing isReady isReal isReduction isRegularFile isRing isScalar isShortExactSequence isSkewCommutative isSmooth isSorted isSquareFree isStandardGradedPolynomialRing isSubmodule isSubquotient isSubset isSupportedInZeroLocus isSurjective isTable isUnit isVeryAmple isWellDefined isWeylAlgebra isc isomorphism iterator jacobian jacobianDual join ker kernel kernelLLL kernelOfLocalization keys kill koszul koszulComplex last lastMatch lcm leadCoefficient leadComponent leadMonomial leadTerm left length letterParser lift liftMapAlongQuasiIsomorphism liftable limitFiles limitProcesses lineNumber lines linkFile list listForm listLocalSymbols listSymbols listUserSymbols lngamma load loadDepth loadPackage loadedFiles loadedPackages local localDictionaries localize locate lock log log1p longExactSequence lookup lookupCount lowerLeft lowerRight makeDirectory makeDocumentTag makePackageIndex makeS2 map markedGB match mathML matrix max maxAllowableThreads maxExponent maxPosition member memoize memoizeClear memoizeValues merge mergePairs method methodOptions methods midpoint min minExponent minPosition minPres mingens mingle minimalBetti minimalPresentation minimalPresentationMap minimalPresentationMapInv minimalPrimes minimalReduction minimize minimizeFilename minimizingMap minors minprimes minus mkdir mod module modulo monoid monomialCurveIdeal monomialIdeal monomialSubideal monomials moveFile multidegree multidoc multigraded multiplicity mutable mutableIdentity mutableMatrix naiveTruncation nanosleep needs needsPackage net netList new newClass newCoordinateSystem newNetFile newPackage newRing newline next nextPrime nextkey nil nonspaceAnalyzer norm normalCone not notImplemented notify null nullHomotopy nullParser nullSpace nullaryMethods nullhomotopy numColumns numRows numTBBThreads number numcols numerator numeric numericInterval numgens numrows odd oeis of ofClass on oo ooo oooo openDatabase openDatabaseOut openFiles openIn openInOut openListener openOut openOutAppend operatorAttributes optP optionalSignParser options or orP order override pack package packageTemplate pad pager pairs parallelApply parent parse part partition partitions parts path pdim peek permanents permutations pfaffian pfaffians pi pivots plus poincare poincareN polarize poly polylog position positions power powermod precision prefixDirectory prefixPath preimage prepend presentation pretty primaryComponent primaryDecomposition print printString printWidth printerr printingAccuracy printingLeadLimit printingPrecision printingSeparator printingTimeLimit printingTrailLimit processID product profile profileSummary programPaths projectiveHilbertPolynomial promote protect prune pruneComplex pruneDiff pruneUnit pruningMap pseudoRemainder pseudocode pullback pullbackMaps pushForward pushout pushoutMaps quit quotient quotientRemainder radical radicalContainment random randomComplexMap randomElement randomKRationalPoint randomMutableMatrix randomSubset rank rays read readDirectory readPackage readlink realPart realpath recursionDepth recursionLimit reduceHilbert reducedRowEchelonForm reductionNumber reesAlgebra reesAlgebraIdeal reesIdeal regSeqInIdeal regex regexQuote registerFinalizer regularity regularizedBeta regularizedGamma relations relativizeFilename remainder remove removeDirectory removeFile removeLowestDimension reorganize replace res reshape resolution resolutionMap restart resultant return returnCode reverse right ring ringFromFractions rootPath rootURI roots rotate round rowAdd rowMult rowPermute rowRankProfile rowSwap rsort run runHooks runLengthEncode runProgram same saturate scan scanKeys scanLines scanPairs scanValues schedule schreyerOrder scriptCommandLine searchPath sec sech seeParsing select selectInSubring selectKeys selectPairs selectValues selectVariables separate separateRegexp sequence serialNumber set setEcho setGroupID setIOExclusive setIOSynchronized setIOUnSynchronized setRandomSeed setup setupEmacs setupLift setupPromote sheaf sheafExt sheafHom shield show showClassStructure showHtml showStructure showTex showUserStructure shuffle sign simpleDocFrob sin singularLocus sinh size size2 sleep smithNormalForm solve someTerms sort sortColumns source span specialFiber specialFiberIdeal splice splitWWW sqrt stack stacksProject stacktrace standardForm standardPairs stashValue status stderr stdio step stopIfError store style sub sublists submatrix submatrixByDegrees subquotient subscript subsets substitute substring subtable sum super superscript support switch sylvesterMatrix symbol symbolBody symlinkDirectory symlinkFile symmetricAlgebra symmetricAlgebraIdeal symmetricKernel symmetricPower synonym syz table take tally tan tangentCone tangentSheaf tanh target taskResult temporaryFileName tensor tensorAssociativity tensorCommutativity terminalParser terms testExample testHunekeQuestion tests tex texMath then threadLocal threadVariable throw time times timing to toAbsolutePath toCC toCCi toChainComplex toDividedPowers toDual toExternalString toField toList toLower toMutableComplex toRR toRRi toSequence toString toUpper top topCoefficients topComponents topLevelMode torSymmetry trace transpose trap trim true truncate truncateOutput try tryLock tutorial typicalValues ultimate unbag uncurry undocumented uniform uninstallAllPackages uninstallPackage union unique uniquePermutations unlock unsequence unstack upperLeft upperRight urlEncode use userSymbols utf8 utf8check utf8substring validate value values variety vars vector versalEmbedding version viewHelp wait wedgeProduct weightRange when whichGm while width wikipedia wrap xor yonedaExtension yonedaMap yonedaProduct youngest zero zeta let b:current_syntax = "m2" diff --git a/M2/Macaulay2/html-check-links/Makefile.in b/M2/Macaulay2/html-check-links/Makefile.in index 692d5b6950a..a4dd55d8b4b 100644 --- a/M2/Macaulay2/html-check-links/Makefile.in +++ b/M2/Macaulay2/html-check-links/Makefile.in @@ -75,4 +75,3 @@ distclean : clean # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/html-check-links run-html-check-links" # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/m2/CMakeLists.txt b/M2/Macaulay2/m2/CMakeLists.txt index 53141264e66..f7b191d3dcc 100644 --- a/M2/Macaulay2/m2/CMakeLists.txt +++ b/M2/Macaulay2/m2/CMakeLists.txt @@ -3,6 +3,7 @@ set(CORE_DIR ${M2_DIST_PREFIX}/${M2_INSTALL_DATADIR}/Core) set(CORE_TESTS ${CORE_DIR}/tests) +set(M2 ${M2_DIST_PREFIX}/${M2_INSTALL_BINDIR}/M2) set(M2_ARGS -q --silent --stop -e errorDepth=0 --no-preload) # List of Core sources @@ -33,7 +34,7 @@ add_custom_target(M2-core ALL DEPENDS ${CORE_DIR}/tvalues.m2 ${TAGS}) add_custom_command(OUTPUT ${CORE_DIR}/tvalues.m2 COMMENT "Generating Macaulay2/Core/tvalues.m2" COMMAND rm -f ${CORE_DIR}/tvalues.m2 # delete old tvalues.m2 - COMMAND M2-binary ${M2_ARGS} -e "exit 0" # regenerate tvalues.m2 + COMMAND ${M2} ${M2_ARGS} -e "exit 0" # regenerate tvalues.m2 WORKING_DIRECTORY ${CORE_DIR} DEPENDS M2-binary VERBATIM) diff --git a/M2/Macaulay2/m2/Hom.m2 b/M2/Macaulay2/m2/Hom.m2 index 43c6301b7e9..7c21f275555 100644 --- a/M2/Macaulay2/m2/Hom.m2 +++ b/M2/Macaulay2/m2/Hom.m2 @@ -28,6 +28,7 @@ Hom(Module, Module) := Module => opts -> (M, N) -> ( e := opts.DegreeLimit; if e === {} then e = null; if e === 0 then e = degree 0_M; + opts = opts ++ { DegreeLimit => e }; -- M.cache is a hashless (hence ageless) CacheTable, but -- M.cache.cache is a MutableHashTable, hence has an age. Y := youngest(M.cache.cache, N.cache.cache); diff --git a/M2/Macaulay2/m2/Makefile.in b/M2/Macaulay2/m2/Makefile.in index 33048553d4f..94df836059c 100644 --- a/M2/Macaulay2/m2/Makefile.in +++ b/M2/Macaulay2/m2/Makefile.in @@ -4,7 +4,7 @@ all: initialize phase1 include ../../include/config.Makefile VPATH = @srcdir@ -srcdir = $(shell cd @srcdir@; /bin/pwd) +srcdir = $(shell cd @srcdir@; env pwd) USER ?= unknown-user @@ -36,31 +36,9 @@ clean::;rm -f @pre_packagesdir@/Core/tvalues.m2 all: tags tags: Makefile @srcdir@/TAGS @srcdir@/TAGS.doc clean::; rm -f TAGS @srcdir@/TAGS @srcdir@/TAGS.doc -@srcdir@/TAGS: \ - $(DUMPEDM2SRCFILES) \ - $(shell @FIND@ @srcdir@/../packages -name "*.m2" -a -not -name .\#\* | sort) \ - ../editors/emacs/make-M2-emacs-help.m2 \ - ../editors/make-M2-symbols.m2 \ - @srcdir@/../*/COPYRIGHT \ - @srcdir@/basictests/*.m2 \ - @srcdir@/../packages/*.m2 \ - @srcdir@/../packages/*/*.m2 \ - @srcdir@/../tests/*/*.m2 \ - @srcdir@/../packages/SimpleDoc/doc.txt \ - loadsequence \ - Makefile.in \ - ../editors/Makefile.in \ - ../tests/Makefile.in \ - ../tests/Makefile.test.in \ - ../tests/slow/Makefile.in \ - ../tests/engine/Makefile.in \ - ../packages/Makefile.in \ - ../../Makefile.in +@srcdir@/TAGS: $(DUMPEDM2FILES) @pre_packagesdir@/Core/tvalues.m2 : making $@ - @ set -e ; \ - cd @srcdir@ && \ - for i in $(patsubst @srcdir@/%, "%", $^) ; \ - do [ -f $$i ] || (echo "file not found: $$i" >&2; exit 1) ; echo ; echo $$i,0 ; done >TAGS + @ETAGS@ -o $@ $^ || true @srcdir@/TAGS.doc: Makefile.in $(shell @FIND@ @srcdir@/../packages/Macaulay2Doc -not \( -name .svn -prune \) -type d) : making $@ : $^ @@ -81,7 +59,7 @@ gdb: .gdb-directories .gdbinit .gdb-files clean::; rm -f .gdb-directories .gdb-directories: ../../libraries/* Makefile echo '# -*- sh -*-' >$@ - /bin/echo 'echo -- loading .gdb-directories\n' >>$@ + env echo 'echo -- loading .gdb-directories\n' >>$@ for i in \ ../../libraries/*/build/*/. \ ../../libraries/ntl/build/*/src/. \ @@ -92,15 +70,15 @@ clean::; rm -f .gdb-directories then echo directory $$i >>$@ ;\ fi ;\ done - /bin/echo 'echo -- loaded .gdb-directories\n' >>$@ + env echo 'echo -- loaded .gdb-directories\n' >>$@ .gdbinit.$(USER):; touch $@ clean::; rm -f .gdbinit .gdb-files: Makefile echo '# -*- sh -*-' >$@ - /bin/echo 'echo -- loading .gdb-files\n' >>$@ + env echo 'echo -- loading .gdb-files\n' >>$@ echo 'set environment LD_LIBRARY_PATH $(BUILTLIBPATH)/lib:$(LD_LIBRARY_PATH)' >>$@ echo 'file @pre_exec_prefix@/bin/M2@EXE@' >>$@ - /bin/echo 'echo -- loaded .gdb-files\n' >>$@ + env echo 'echo -- loaded .gdb-files\n' >>$@ clean::; rm -f .gdb-files profile: gmon.out; LD_LIBRARY_PATH="$(BUILTLIBPATH)/lib:$(LD_LIBRARY_PATH)" gprof @pre_exec_prefix@/bin/M2@EXE@ >profile clean::; rm -f profile @@ -114,4 +92,3 @@ distclean: clean; rm -f Makefile version.m2 # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/m2/basictests/Makefile.in b/M2/Macaulay2/m2/basictests/Makefile.in index 84d31f3b1e6..baa794c55b6 100644 --- a/M2/Macaulay2/m2/basictests/Makefile.in +++ b/M2/Macaulay2/m2/basictests/Makefile.in @@ -68,4 +68,3 @@ check-return-code :; if echo 'error "foo"' | @pre_exec_prefix@/bin/M2@EXE@ $(ARG # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/packages/Macaulay2Doc/basictests " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/m2/basictests/hashcodes.m2 b/M2/Macaulay2/m2/basictests/hashcodes.m2 index 66fb0a74eee..89e5fbe57af 100644 --- a/M2/Macaulay2/m2/basictests/hashcodes.m2 +++ b/M2/Macaulay2/m2/basictests/hashcodes.m2 @@ -5,6 +5,7 @@ assert ( hash {("a","bcd"),("bcd","a")} == 10180479327404092074 ) -- it would also be nice to keep the following hash codes the same assert( (hash 123) === 123 ) +assert( hash 2^100 === 3405506911546692669 ) assert( (hash "asdf") === 3003444 ) assert( (hash {1,2,3}) === 5292466133541383614 ) assert( (hash (1,2,3)) === 568179786079386623 ) @@ -18,9 +19,7 @@ assert( (hash Nothing) === 1000069 ) assert( (hash (1 => 2)) === 1729140528276943882 ) assert( hash Nothing == 1000069 ) assert( (hash Boolean) === 1000035 ) - --- these might change if our floating point implementation changes, but let's check anyway: -assert( hash 1.23p200 === 18446744072207201388 -* 64-bit *- or hash 1.23p200 == 18446744073069319069 -* 32-bit *- ) +assert( hash 1.23p200 === 10359908877735906505 ) -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/packages/Macaulay2Doc/basictests hashcodes.okay" diff --git a/M2/Macaulay2/m2/classes.m2 b/M2/Macaulay2/m2/classes.m2 index 22d93f2c20e..3b244c6f0c7 100644 --- a/M2/Macaulay2/m2/classes.m2 +++ b/M2/Macaulay2/m2/classes.m2 @@ -9,7 +9,7 @@ ancestors = T -> unique join({T}, while (T = parent T) =!= Thing list T, {Thing ancestors' = T -> unique join({T}, while (T = class T) =!= Type list T, {Type}) -- TODO: make this TT toString X later? -synonym = X -> if X.?synonym then X.synonym else "object of class " | toString X +synonym = X -> X.synonym ?? "object of class " | toString X -- TODO: find a more permanent solution plurals = new MutableHashTable from { "body" => "bodies", @@ -22,13 +22,14 @@ pluralize = s -> demark_" " append( drop(ws := separate_" " s, -1), if plurals#?(last ws) then plurals#(last ws) else last ws | "s") -pluralsynonym = T -> if T.?synonym then pluralize T.synonym else "objects of class " | toString T +pluralsynonym = T -> pluralize T.synonym ?? "objects of class " | toString T Time.synonym = "timing result" Boolean.synonym = "Boolean value" MutableHashTable.synonym = "mutable hash table" HashTable.synonym = "hash table" CacheTable.synonym = "cache table" +Error.synonym = "error" Function.synonym = "function" FunctionClosure.synonym = "function closure" CompiledFunction.synonym = "compiled function" @@ -84,18 +85,17 @@ Command Thing := (x,y) -> x#0 y -- Now some extra stuff: -apply(Thing, Command) := VisibleList => (v,f) -> apply(v, i -> f i) -Command \ VisibleList := +Command \ VisibleList := VisibleList => (f,v) -> apply(v,f#0) Function \ VisibleList := VisibleList => (f,v) -> apply(v,f) -Command \ String := +Command \ String := Sequence => (f,s) -> apply(s,f#0) Function \ String := Sequence => (f,s) -> apply(s,f) -Command \\ Thing := +Command \\ Thing := Function \\ Thing := VisibleList => (f,v) -> f v - List / Command := + List / Command := List => (v,f) -> apply(v,f#0) List / Function := List => (v,f) -> apply(v,f) -- just because of conflict with List / Thing! -VisibleList / Command := +VisibleList / Command := VisibleList => (v,f) -> apply(v,f#0) VisibleList / Function := VisibleList => (v,f) -> apply(v,f) - String / Command := + String / Command := Sequence => (s,f) -> apply(s,f#0) String / Function := Sequence => (s,f) -> apply(s,f) VisibleList // Command := -- here to make documentation easier VisibleList // Function := -- here to make documentation easier diff --git a/M2/Macaulay2/m2/complexes.m2 b/M2/Macaulay2/m2/complexes.m2 index e0d2ac495ff..71791370653 100644 --- a/M2/Macaulay2/m2/complexes.m2 +++ b/M2/Macaulay2/m2/complexes.m2 @@ -4,7 +4,7 @@ needs "matrix1.m2" -- for Ideal needs "modules.m2" -- for Module -- whether to use Complexes or OldChainComplexes -HomologicalAlgebraPackage = "OldChainComplexes" +HomologicalAlgebraPackage = "Complexes" ----------------------------------------------------------------------------- -- Local utilities @@ -79,11 +79,11 @@ Ext(ZZ, Module, Module) := Module => o -> (i, M, N) -> missingPackage "either Co Ext(ZZ, Ring, Matrix) := Ext(ZZ, Ideal, Matrix) := Matrix => o -> (i, M, g) -> Ext^i(module M, g, o) -Ext(ZZ, Module, Matrix) := Matrix => o -> (i, M, g) -> missingPackage "OldChainComplexes" +Ext(ZZ, Module, Matrix) := Matrix => o -> (i, M, g) -> missingPackage "either Complexes or OldChainComplexes" Ext(ZZ, Matrix, Ring) := Ext(ZZ, Matrix, Ideal) := Matrix => o -> (i, f, N) -> Ext^i(f, module N, o) -Ext(ZZ, Matrix, Module) := Matrix => o -> (i, f, N) -> missingPackage "OldChainComplexes" +Ext(ZZ, Matrix, Module) := Matrix => o -> (i, f, N) -> missingPackage "either Complexes or OldChainComplexes" -- TODO: Ext(R,S) should work as well, e.g. via pushForward Ext(Ring, Ring) := Ext(Ring, Ideal) := Ext(Ring, Module) := diff --git a/M2/Macaulay2/m2/debugging.m2 b/M2/Macaulay2/m2/debugging.m2 index 93aee660d0e..a8f3406c312 100644 --- a/M2/Macaulay2/m2/debugging.m2 +++ b/M2/Macaulay2/m2/debugging.m2 @@ -3,23 +3,8 @@ needs "nets.m2" needs "methods.m2" -processArgs := args -> concatenate ( - args = sequence args; - apply(args, x -> - if class x === String then x - else if class x === Symbol then ("'", toString x, "'") - else silentRobustString(40,3,x) - ), - apply(args, x -> if class x === Symbol then ("\n", toString locate x, ": here is the first use of '",toString x, "'") else "") - ) -olderror := error -error = args -> ( - -- this is the body of the "error" function, which prints out error messages - olderror processArgs args) -protect symbol error - warningMessage0 = (args,deb) -> ( - args = processArgs args; + args = processErrorArgs args; h := hash args % 10000; if debugWarningHashcode === h then error args @@ -166,14 +151,7 @@ localSymbols Pseudocode := localSymbols Symbol := localSymbols Dictionary := localSymbols Function := ls - --- make this work eventually: --- localSymbols() := () -> if current === null then ls() else ls current --- meanwhile: (see also method123()) --- nullaryMethods # (1 : localSymbols) = () -> if current =!= null then ls current else error "not in debugger (i.e., current not set)" --- also meanwhile: -installMethod(localSymbols, () -> if current =!= null then ls current else error "not in debugger (i.e., current not set)") - +localSymbols() := () -> if current =!= null then ls current else error "not in debugger (i.e., current not set)" localSymbols(Type,Symbol) := localSymbols(Type,Dictionary) := localSymbols(Type,Function) := @@ -243,12 +221,8 @@ FilePosition.synonym = "file position" -- TODO: add FilePosition(String, ZZ, ZZ) and FilePosition(String) toExternalString FilePosition := toString FilePosition := -net FilePosition := p -> concatenate( - if match(" ", p#0) then format p#0 else p#0, - ":",toString p#1,":",toString p#2, - if #p==4 then (":(",toString p#3,")") - else if #p>=5 then ("-",toString p#3,":",toString p#4) - ) +net FilePosition := simpleToString -- tostringFilePosition in debugging.dd + String | FilePosition := (s, p) -> s | toString p FilePosition | String := (p, s) -> toString p | s @@ -259,13 +233,14 @@ currentPosition = () -> new FilePosition from { currentFileName, currentRowNumbe -- locate ----------------------------------------------------------------------------- -locate' = locate -- defined in d/actors4.d +locate' = locate -- defined in d/debugging.dd locate = method(Dispatch => Thing, TypicalValue => FilePosition) locate Nothing := locate FunctionBody:= locate Function := locate Pseudocode := locate Sequence := +locate Error := locate Symbol := FilePosition => locate' locate Command := FilePosition => C -> locate'(C#0) locate List := List => x -> apply(x, locate) diff --git a/M2/Macaulay2/m2/document.m2 b/M2/Macaulay2/m2/document.m2 index 61d4d501607..505ad952ab2 100644 --- a/M2/Macaulay2/m2/document.m2 +++ b/M2/Macaulay2/m2/document.m2 @@ -255,7 +255,6 @@ fSeq := new HashTable from splice { (2, symbol (*) ) => s -> (toString s#1, " ", toString s#0), -- postfix operator (2, symbol ^* ) => s -> (toString s#1, " ", toString s#0), -- postfix operator (2, symbol _* ) => s -> (toString s#1, " ", toString s#0), -- postfix operator - (2, symbol ~ ) => s -> (toString s#1, " ", toString s#0), -- postfix operator (2, symbol ^~ ) => s -> (toString s#1, " ", toString s#0), -- postfix operator (2, symbol _~ ) => s -> (toString s#1, " ", toString s#0), -- postfix operator (2, symbol ! ) => s -> (toString s#1, " ", toString s#0), -- postfix operator diff --git a/M2/Macaulay2/m2/engine.m2 b/M2/Macaulay2/m2/engine.m2 index 717dd4b4e2c..859ee169c95 100644 --- a/M2/Macaulay2/m2/engine.m2 +++ b/M2/Macaulay2/m2/engine.m2 @@ -255,6 +255,7 @@ raw Number := x -> x_((class x).RawRing) raw InexactNumber := x -> x_((ring x).RawRing) Number _ RawRing := (n,R) -> rawFromNumber(R,n) RawRingElement _ RawRing := (x,R) -> rawPromote(R,x) +raw Constant := raw @@ numeric RawRingElement == RawRingElement := (x,y) -> x === y @@ -353,7 +354,8 @@ target RawMatrix := o -> rawTarget o source RawMatrix := o -> rawSource o transposeSequence := t -> pack(#t, mingle t) isHomogeneous RawMatrix := rawIsHomogeneous -entries RawMutableMatrix := entries RawMatrix := m -> table(rawNumberOfRows m,rawNumberOfColumns m,(i,j)->rawMatrixEntry(m,i,j)) +entries RawMutableMatrix := +entries RawMatrix := rawMatrixEntries ZZ * RawMatrix := (n,f) -> ( R := rawRing rawTarget f; diff --git a/M2/Macaulay2/m2/enginering.m2 b/M2/Macaulay2/m2/enginering.m2 index 48dfda69d5b..3a1ef19a813 100644 --- a/M2/Macaulay2/m2/enginering.m2 +++ b/M2/Macaulay2/m2/enginering.m2 @@ -274,9 +274,12 @@ expression EngineRing := R -> if hasAttribute(R,ReverseDictionary) then expressi toString EngineRing := toString @@ expression net EngineRing := net @@ expression texMath EngineRing := texMath @@ expression +mathML EngineRing := mathML @@ expression ZZ _ EngineRing := RR _ EngineRing := +CC _ EngineRing := +CCi _ EngineRing := RRi _ EngineRing := RingElement => (i,R) -> new R from i_(R.RawRing) new RingElement from RawRingElement := (R, f) -> ( @@ -315,6 +318,20 @@ coefficientRing FractionField := F -> coefficientRing last F.baseRings -- freduce := (f) -> (numerator f)/(denominator f) +isPromotable(FractionField,FractionField) := (F,G) -> lookup(promote,F,G) =!= null or ( + R:=baseRing F; S:=baseRing G; + l:=lookup(promote,R,S); + l=!=null and (setupPromote(a->fraction(promote(numerator a,S),promote(denominator a,S)),F,G); true) + ) + +lift(RingElement, RingElement) := opts -> (f, R) -> ( + if (instance(class f,FractionField) and instance(R,FractionField) and (R1:=baseRing R; lookup(lift,baseRing class f,R1) =!= null)) then ( + setupLift(a->fraction(lift(numerator a,R1),lift(denominator a,R1)),class f,R); + return lift(f,R,opts); + ); + if opts.Verify then error ("cannot lift from "|toString class f|" to "|toString R) + ) + factoryAlmostGood = R -> ( if instance(R,QuotientRing) then factoryAlmostGood ambient R else if instance(R,PolynomialRing) then factoryAlmostGood coefficientRing R @@ -364,10 +381,6 @@ frac EngineRing := R -> if isField R then R else if R.?frac then R.frac else ( if R.?indexSymbols then F.indexSymbols = applyValues(R.indexSymbols, r -> promote(r,F)); if R.?indexStrings then F.indexStrings = applyValues(R.indexStrings, r -> promote(r,F)); if R.?numallvars then F.numallvars=R.numallvars; - scan(R.baseRings, S -> if S.?frac and not isPromotable(S.frac,F) then ( - setupPromote(a->fraction(promote(numerator a,R),promote(denominator a,R)),S.frac,F); - setupLift(a->fraction(lift(numerator a,S),lift(denominator a,S)),F,S.frac); - )); F) -- methods for all ring elements @@ -375,13 +388,23 @@ frac EngineRing := R -> if isField R then R else if R.?frac then R.frac else ( clean(RR,Number) := (epsilon,f) -> if abs f < epsilon then f-f else f clean(RR,RingElement) := (epsilon,f) -> new ring f from clean(epsilon,raw f) -norm(List) := z -> max(abs\z) -norm(RR,Number) := (p,z) -> p-p+abs z -norm(RR,RingElement) := (p,f) -> new RR from norm(p,raw f) -norm(InfiniteNumber,Number) := -norm(InfiniteNumber,RingElement) := (p,f) -> norm(numeric(precision f, p), f) -norm(RingElement) := (f) -> norm(numeric(precision f,infinity), f) -norm Number := abs +norm List := +norm Number := +norm RingElement := norm_infinity +norm(Number, List) := (p, z) -> norm(p, matrix {z}) +norm(Number, Number) := (p, z) -> abs z +norm(Number, RingElement) := (p, f) -> ( + R := ring f; + -- l^infinity norm for polynomials over RR or CC in engine + if p == infinity and any(R.baseRings, + S -> instance(S, RealField) or instance(S, ComplexField)) + then ( + if precision p > precision R + then p = numeric(precision R, infinity); + norm(p, raw f)) + else ( + (mons, coeffs) := coefficients f; + norm(p, lift(coeffs, coefficientRing R)))) degreeLength Ring := R -> R.degreeLength @@ -465,10 +488,10 @@ ZZ ? RingElement := (m,y) -> m_(class y) ? y RingElement ^ ZZ := RingElement => (x,i) -> new ring x from (raw x)^i -toString RingElement := x -> toString expression x -toExternalString RingElement := x -> toExternalFormat expression x -net RingElement := x -> net expression x -texMath RingElement := x -> texMath expression x +toString RingElement := toString @@ expression +toExternalString RingElement := toExternalFormat @@ expression +net RingElement := net @@ expression +texMath RingElement := texMath @@ expression someTerms(RingElement,ZZ,ZZ) := RingElement => (f,i,n) -> new ring f from rawGetTerms(numgens ring f,raw f,i,n+i-1) diff --git a/M2/Macaulay2/m2/examples.m2 b/M2/Macaulay2/m2/examples.m2 index 202502d0a98..ca2246ac7b5 100644 --- a/M2/Macaulay2/m2/examples.m2 +++ b/M2/Macaulay2/m2/examples.m2 @@ -243,7 +243,7 @@ captureExampleOutput = (desc, inputs, pkg, inf, outf, errf, data, inputhash, cha if isCapturable(inputs, pkg) then ( desc = concatenate(desc, 62 - #desc); stderr << commentize pad("capturing " | desc, 72) << flush; -- the timing info will appear at the end - (err, output) := capture(inputs, UserMode => false); + (err, output) := capture(inputs, UserMode => false, PackageExports => pkg); alarm 0; -- cancel any alarms that were set if err then printerr "capture failed; retrying ..." else (outf << M2outputHash << inputhash << endl << output << close; diff --git a/M2/Macaulay2/m2/exports.m2 b/M2/Macaulay2/m2/exports.m2 index 56e7ec34d30..2e35f433c19 100644 --- a/M2/Macaulay2/m2/exports.m2 +++ b/M2/Macaulay2/m2/exports.m2 @@ -96,6 +96,8 @@ export { "Boxes", "CC", "CC'", + "CCi", + "CCi'", "CacheExampleOutput", "CacheTable", "CallLimit", @@ -156,6 +158,7 @@ export { "End", "Engine", "EngineRing", + "Error", "Equation", "EulerConstant", "ExampleFiles", @@ -287,6 +290,7 @@ export { "MutableHashTable", "MutableList", "MutableMatrix", + "Mutex", "midpoint", "NCLex", "Name", @@ -353,6 +357,7 @@ export { "Reload", "RemakeAllDocumentation", "RerunExamples", + "Resolution", "Result", "RevLex", "Reverse", @@ -663,6 +668,7 @@ export { "eulers", "even", "examples", + "except", "exchange", "exec", "exit", @@ -888,10 +894,13 @@ export { "local", "localDictionaries", "locate", + "lock", "log", "log1p", "lookup", "lookupCount", + "lowerLeft", + "lowerRight", "makeDirectory", "makeDocumentTag", "makePackageIndex", @@ -992,6 +1001,7 @@ export { "pairs", "parallelApply", "parent", + "parse", "part", "partition", "partitions", @@ -1009,6 +1019,7 @@ export { "poincare", "poincareN", "polarize", + "polylog", "position", "positions", "power", @@ -1045,6 +1056,7 @@ export { "quotientRemainder", "quotientRemainder'", "random", + "randomElement", "randomKRationalPoint", "randomMutableMatrix", "randomSubset", @@ -1137,6 +1149,7 @@ export { "showStructure", "showTex", "showUserStructure", + "shuffle", "sign", "sin", "singularLocus", @@ -1210,23 +1223,26 @@ export { "to", "toAbsolutePath", "toCC", + "toCCi", "toExternalString", "toField", "toList", "toLower", "toRR", - "toRRi", + "toRRi", "toSequence", "toString", "toUpper", "topCoefficients", "trace", "transpose", + "trap", "trim", "true", "truncate", "truncateOutput", "try", + "tryLock", "tutorial", "typicalValues", "uniquePermutations", @@ -1239,8 +1255,11 @@ export { "uninstallPackage", "union", "unique", + "unlock", "unsequence", "unstack", + "upperLeft", + "upperRight", "urlEncode", "use", "userSymbols", diff --git a/M2/Macaulay2/m2/expressions.m2 b/M2/Macaulay2/m2/expressions.m2 index b7cf129f53d..25fc1e7ff27 100644 --- a/M2/Macaulay2/m2/expressions.m2 +++ b/M2/Macaulay2/m2/expressions.m2 @@ -933,8 +933,8 @@ toCompactString Divide := x -> toCompactParen x#0 | "/" | toCompactParen x#1 net MatrixExpression := x -> ( (opts,m) := matrixOpts x; + if opts.zero =!= null then return "0"; blk := opts.Blocks =!= null; -- whether to display blocks - if all(m,r->all(r,i->class i===ZeroExpression)) then return "0"; net1 := if compactMatrixForm then toCompactString else net; vbox0 := if opts.Degrees === null then 0 else 1; (hbox,vbox) := if blk then (drop(accumulate(plus,0,opts.Blocks#0),-1),prepend(vbox0,accumulate(plus,vbox0,opts.Blocks#1))) else (false,{vbox0,vbox0+#m#0}); @@ -1135,7 +1135,7 @@ texMath Table := m -> ( texMath MatrixExpression := x -> ( (opts,m) := matrixOpts x; - if all(m,r->all(r,i->class i===ZeroExpression)) then return "0"; + if opts.zero =!= null then return "0"; blk := opts.Blocks =!= null; -- whether to display blocks if blk then ( j := 0; h := 0; ); m = applyTable(m,texMath); diff --git a/M2/Macaulay2/m2/files.m2 b/M2/Macaulay2/m2/files.m2 index 7cda1010301..b4076c0373d 100644 --- a/M2/Macaulay2/m2/files.m2 +++ b/M2/Macaulay2/m2/files.m2 @@ -484,8 +484,8 @@ prelim := () -> ( promptUser = true; if prefixDirectory === null then error "can't determine Macaulay 2 prefix (prefixDirectory not set)"; ) -installMethod(setupEmacs, () -> ( prelim(); mungeEmacs(); )) -installMethod(setup, () -> ( +setupEmacs() := () -> ( prelim(); mungeEmacs(); ) +setup() := () -> ( prelim(); dotprofileFix = concatenate(shHeader, apply(shellfixes, (var,dir,rest) -> fix(var,dir,rest,bashtempl))); dotloginFix = concatenate(shHeader,apply(shellfixes, (var,dir,rest) -> fix(var,dir,rest,cshtempl))); @@ -505,7 +505,7 @@ installMethod(setup, () -> ( -- csh and tcsh: mungeFile("~/.login",startToken,endToken,M2loginRead) or -- emacs: - mungeEmacs(); )) + mungeEmacs(); ) scanLines = method() ifbrk := x -> if x =!= null then break x diff --git a/M2/Macaulay2/m2/format.m2 b/M2/Macaulay2/m2/format.m2 index aa42cae2b4f..525c790caaa 100644 --- a/M2/Macaulay2/m2/format.m2 +++ b/M2/Macaulay2/m2/format.m2 @@ -31,7 +31,16 @@ setupRenderer = (parser, joiner, T) -> ( -- Default joiners: (TODO: move to string.m2?) -- concatenate -- horizontalJoin -wrapHorizontalJoin := x -> wrap horizontalJoin x +net BR := info BR := x -> stack() +net HR := info HR := x -> concatenate(printWidth:"-") + +horizontalJoin' := x -> ( -- horizontalJoin except for BRs and HRs + netBR := net BR{}; netHR := net HR{}; + x' := sublists(toList x, y -> y=!=netBR and y=!=netHR, toList, y -> {y}); + stack(horizontalJoin\x') + ) + +wrapHorizontalJoin := x -> wrap horizontalJoin' x -- Main types: (see hypertext.m2) -- Hypertext > HypertextParagraph, HypertextContainer @@ -52,7 +61,7 @@ scan({net, info, html, markdown, tex}, parser -> -- Rendering by horizontal join of inputs scan({net, info}, - parser -> setupRenderer(parser, horizontalJoin, Hypertext)) + parser -> setupRenderer(parser, horizontalJoin', Hypertext)) scan({net, info}, parser -> setupRenderer(parser, wrapHorizontalJoin, HypertextParagraph)) @@ -172,19 +181,16 @@ info HEADER1 := Hop(info,"*") info HEADER2 := Hop(info,"=") info HEADER3 := Hop(info,"-") -net HR := -info HR := x -> concatenate(printWidth:"-") - net PRE := net TT := net CODE := net SAMP := info TT := info SAMP := -info CODE := x -> horizontalJoin apply(noopts x,net) +info CODE := x -> horizontalJoin' apply(noopts x,net) net KBD := -info KBD := x -> formatNoEscaping toString horizontalJoin apply(noopts x,net) +info KBD := x -> formatNoEscaping toString horizontalJoin' apply(noopts x,net) info PRE := x -> wrap(printWidth, "-", concatenate apply(noopts x,toString @@ info)) diff --git a/M2/Macaulay2/m2/gb.m2 b/M2/Macaulay2/m2/gb.m2 index e5cb5e49b7b..474a78636fc 100644 --- a/M2/Macaulay2/m2/gb.m2 +++ b/M2/Macaulay2/m2/gb.m2 @@ -301,12 +301,15 @@ degreeToHeft = (R, d) -> ( -- gb ----------------------------------------------------------------------------- +-- TODO: find better name and export this +fullgens = M -> M.cache#"full gens" ??= ( + if M.?relations then generators M | M.relations else generators M) + gb = method(TypicalValue => GroebnerBasis, Options => gbDefaults) gb Ideal := GroebnerBasis => opts -> I -> gb (module I, opts) gb Module := GroebnerBasis => opts -> M -> ( - if M.?relations then ( - M.cache#"full gens" ??= generators M | relations M; - gb(M.cache#"full gens", opts, SyzygyRows => numgens source generators M)) + if M.?relations + then gb(fullgens M, opts, SyzygyRows => numgens source generators M) else gb(generators M, opts)) gb Matrix := GroebnerBasis => opts -> m -> ( checkArgGB m; diff --git a/M2/Macaulay2/m2/genmat.m2 b/M2/Macaulay2/m2/genmat.m2 index 548e8e1a08f..d65a13189d4 100644 --- a/M2/Macaulay2/m2/genmat.m2 +++ b/M2/Macaulay2/m2/genmat.m2 @@ -120,8 +120,6 @@ random(Module, Module) := Matrix => opts -> (F,G) -> ( R := ring F; if R =!= ring G then error "modules over different rings"; if opts.MaximalRank then return (randomMR opts)(F,G); - p := char R; - if p === 0 then p = opts.Height; degreesTable := table(degrees F, degrees G, (i,j) -> toList apply(j,i,difference)); degreesTally := tally flatten degreesTable; @@ -144,7 +142,7 @@ random(Module, Module) := Matrix => opts -> (F,G) -> ( deg -> ( numused := 0; if deg === 0 then ( - n := apply(degreesTally#deg, x -> random p); + n := apply(degreesTally#deg, x -> random(R.BaseRing, opts)); () -> ( r := n#numused; numused = numused + 1; @@ -158,7 +156,7 @@ random(Module, Module) := Matrix => opts -> (F,G) -> ( n = first entries ( m * matrix (R, table( k, degreesTally#deg, - (i,j)->random p))); + (i,j)->random(R.BaseRing, opts)))); () -> ( r := n#numused; numused = numused + 1; diff --git a/M2/Macaulay2/m2/help.m2 b/M2/Macaulay2/m2/help.m2 index e19f1764d5a..5ce44c176df 100644 --- a/M2/Macaulay2/m2/help.m2 +++ b/M2/Macaulay2/m2/help.m2 @@ -316,6 +316,15 @@ getSource(Symbol, Package) := (S, pkg) -> ( ) -- Handling operators + +-- for each unicode operator, we give a pair: +-- * unicode name +-- * TeX name (for emacs TeX input mode -- null if not available) +unicodeOperators := hashTable { + symbol · => ("middle dot", "\\cdot"), + symbol ⊠ => ("squared times", "\\boxtimes"), + symbol â§¢ => ("shuffle product",)} + -- e.g. symbol + getOperator := key -> if operator#?key then ( op := toString key; @@ -326,6 +335,8 @@ getOperator := key -> if operator#?key then ( "The user may install ", TO "Macaulay2Doc :: binary methods", " for handling such expressions with code such as"}, PRE if key === symbol SPACE then " X Y := (x,y) -> ..." + else if (getParsing key)#0 <= (getParsing symbol :=)#0 + then " (X "|op|" Y) := (x,y) -> ..." else " X "|op|" Y := (x,y) -> ...", PARA {"where ", TT "X", " is the class of ", TT "x", " and ", TT "Y", " is the class of ", TT "y", "."}}, if key === symbol ?? then { -- can't install binary methods @@ -344,7 +355,15 @@ getOperator := key -> if operator#?key then ( PARA {"This operator may be used as a binary operator in an expression like ", TT ("x" | op | "y"), ". ", "The user may ", TO2{ "Macaulay2Doc :: :=", "install a method" }, " for handling such expressions with code such as"}, PRE (" X "|op|" (x,y) -> ..."), - PARA {"where ", TT "X", " is the class of ", TT "x", "."}} + PARA {"where ", TT "X", " is the class of ", TT "x", "."}}, + if unicodeOperators#?key then { + PARA {"To insert this character in Emacs, you may press ", KBD "C-x 8 RET", " or ", KBD "M-x insert-char", + " and then enter ", format unicodeOperators#key#0, " in the minibuffer."}, + if (texcmd := unicodeOperators#key#1) =!= null + then PARA {"Alternatively, you may press ", KBD "C-x RET C-\\", " or ", KBD "M-x set-input-method", + " and then enter \"TeX\" in the minibuffer. Afterwards, typing \"", texcmd, + "\" will input the character. You may then toggle the input method using ", KBD "C-\\", " or ", + KBD "M-x toggle-input-method"}}, )) -- TODO: expand this @@ -635,6 +654,7 @@ about Type := about Symbol := about ScriptedFunctor := about Function := o -> f -> about("\\b" | toString f | "\\b", o) +about Keyword := o -> f -> about("(?:^| )" | regexQuote toString f | "(?:$| )") about String := o -> re -> lastabout = ( packagesSeen := new MutableHashTable; NumberedVerticalList sort join( diff --git a/M2/Macaulay2/m2/hilbert.m2 b/M2/Macaulay2/m2/hilbert.m2 index 8dea49f1121..0a65867e982 100644 --- a/M2/Macaulay2/m2/hilbert.m2 +++ b/M2/Macaulay2/m2/hilbert.m2 @@ -84,8 +84,21 @@ poincare Module := M -> ( if (P := computation M) =!= null then return P; error("no applicable strategy for computing poincare over ", toString ring M)) +-- Use that the Poincare polynomial of a subquotient module M is the difference of the Poincare polynomials of two quotients. +-- This avoids having to find a presentation of M (unless that has already been done). addHook((poincare, Module), Strategy => Default, M -> ( - new degreesRing ring M from rawHilbert raw leadTerm gb presentation M)) + hf := if hasMinPres M then + rawHilbert raw leadTerm gb relations minimalPresentation M + -- We cannot just call "poincare minimalPresentation M", because there are cases (such as M free) + -- where both M and minimalPresentation M are cached as having a minimal presentation; + -- so that would lead to an infinite loop. + else if not M.?generators then + rawHilbert raw leadTerm gb relations M + else if M.cache.?presentation then + rawHilbert raw leadTerm gb M.cache.presentation + else (rawHilbert raw leadTerm gb relations M) - (rawHilbert raw leadTerm gb M); + new degreesRing ring M from hf + )) -- manually installs the numerator of the reduced Hilbert series for the module storefuns#poincare = method() @@ -360,26 +373,40 @@ hilbertSeries ProjectiveHilbertPolynomial := opts -> P -> ( -- hilbertFunction ----------------------------------------------------------------------------- -hilbertFunction = method() +hilbertFunction = method(Options => { Strategy => Default }) hilbertFunction(ZZ, Ring) := hilbertFunction(ZZ, Ideal) := -hilbertFunction(ZZ, Module) := (d, M) -> hilbertFunction({d}, M) +hilbertFunction(ZZ, Module) := opts -> (d, M) -> hilbertFunction({d}, M, opts) -hilbertFunction(List, Ring) := (L, R) -> hilbertFunction(L, module R) -hilbertFunction(List, Ideal) := -hilbertFunction(List, Module) := (L, M) -> ( - -- computes the Hilbert series to a sufficiently high order and - -- returns the desired coefficient, thus it is cached by hilbertSeries +hilbertFunction(List, Ring) := opts -> (L, R) -> hilbertFunction(L, module R, opts) +hilbertFunction(List, Ideal) := opts -> (L, I) -> hilbertFunction(L, comodule I, opts) +hilbertFunction(List, Module) := opts -> (L, M) -> ( R := ring M; if not all(L, i -> instance(i, ZZ)) then error "hilbertFunction: expected degree to be an integer or list of integers"; if #L =!= degreeLength R then error "hilbertFunction: degree length mismatch"; if heft R === null then error "hilbertFunction: ring has no heft vector"; -- - HF := runHooks((hilbertFunction, List, Module), (L, M)); + HF := runHooks((hilbertFunction, List, Module), (opts, L, M), Strategy => opts.Strategy); if HF =!= null then return HF; error("no applicable strategy for computing Hilbert function over ", toString R)) -addHook((hilbertFunction, List, Module), Strategy => Default, (L, M) -> ( +-- When a module is given as a subquotient, M = N1/N2 with N2 < N1 < free module F, +-- Strategy => Base uses that hilbertFunction(d, M) = hilbertFunction(d, F/N2) - hilbertFunction(d, F/N1). +-- Also, we do this by finding a basis for F/N2 and F/N1 in degree d, rather than computing the whole Hilbert series. +-- This may or may not be faster than the default strategy, but it should be at least as fast as "rank source basis(d, M)" +-- in essentially all cases, and faster than that when M was defined as a subquotient module. +-- If a presentation or minimal presentation for M has already been computed, we use that. +addHook((hilbertFunction, List, Module), Strategy => Base, (opts, L, M) -> ( + if hasMinPres M then numColumns basis(L, minimalPresentation M) + else if not M.?generators then numColumns basis(L, M) + else if M.cache.?presentation then numColumns basis(L, cokernel presentation M) + else ( + numColumns basis(L, super M) - + numColumns basis(L, cokernel fullgens M)))) + +-- computes the Hilbert series to a sufficiently high order and +-- returns the desired coefficient, thus it is cached by hilbertSeries +addHook((hilbertFunction, List, Module), Strategy => Default, (opts, L, M) -> ( h := heft ring M; f := hilbertSeries(M, Order => 1 + sum(h, L, times)); U := monoid ring f; @@ -387,4 +414,4 @@ addHook((hilbertFunction, List, Module), Strategy => Default, (L, M) -> ( hilbertFunction Ring := hilbertFunction Ideal := -hilbertFunction Module := M -> d -> hilbertFunction(d, M) +hilbertFunction Module := opts -> M -> d -> hilbertFunction(d, M, opts) diff --git a/M2/Macaulay2/m2/html.m2 b/M2/Macaulay2/m2/html.m2 index 826de9cb8bb..6fe479ad0a3 100644 --- a/M2/Macaulay2/m2/html.m2 +++ b/M2/Macaulay2/m2/html.m2 @@ -26,7 +26,12 @@ KaTeX := () -> ( "\\QQ": "\\mathbb{Q}", "\\RR": "\\mathbb{R}", "\\CC": "\\mathbb{C}", - "\\PP": "\\mathbb{P}" + "\\PP": "\\mathbb{P}", + "\\OO": "\\mathcal{O}", + "\\Hom": "\\operatorname{Hom}", + "\\End": "\\operatorname{End}", + "\\Ext": "\\operatorname{Ext}", + "\\Tor": "\\operatorname{Tor}" }, delimiters = [ { left: "$$", right: "$$", display: true}, { left: "\\[", right: "\\]", display: true}, diff --git a/M2/Macaulay2/m2/hypertext.m2 b/M2/Macaulay2/m2/hypertext.m2 index 173a838a044..3cace746506 100644 --- a/M2/Macaulay2/m2/hypertext.m2 +++ b/M2/Macaulay2/m2/hypertext.m2 @@ -31,7 +31,7 @@ toExternalString Hypertext := s -> concatenate(toString class s, toExternalStrin new Hypertext from VisibleList := (M,x) -> x -- needed because otherwise next line takes over new Hypertext from Thing := (M,x) -> {x} -new Hypertext from Net := (M,x) -> {toString x} +new Hypertext from Net := (M,x) -> between(BR(),unstack x) Hypertext#AfterPrint = x -> null diff --git a/M2/Macaulay2/m2/installPackage.m2 b/M2/Macaulay2/m2/installPackage.m2 index 1a6066fadfc..539fe0e7c0a 100644 --- a/M2/Macaulay2/m2/installPackage.m2 +++ b/M2/Macaulay2/m2/installPackage.m2 @@ -719,7 +719,7 @@ installPackage Package := opts -> pkg -> ( verboseLog("copying auxiliary source files from ", auxiliaryFilesDirectory); makeDirectory(installPrefix | srcDirectory); copyDirectory(auxiliaryFilesDirectory, installPrefix | srcDirectory, - Exclude => {"^CVS$", "^\\.svn$", "Makefile"}, + Exclude => {"^CVS$", "^\\.svn$", "Makefile", "^\\.gitignore$"}, UpdateOnly => true, Verbose => opts.Verbose or debugLevel > 0)) else if pkgopts.AuxiliaryFiles diff --git a/M2/Macaulay2/m2/integers.m2 b/M2/Macaulay2/m2/integers.m2 index 8b777c81c33..982552c9343 100644 --- a/M2/Macaulay2/m2/integers.m2 +++ b/M2/Macaulay2/m2/integers.m2 @@ -44,7 +44,7 @@ ZZ.random = opts -> ZZ -> rawRandomZZ opts.Height texMath ZZ := toString gcd = method(Binary => true) -installMethod(gcd, () -> 0) +gcd() := () -> 0 gcd(ZZ,ZZ) := ZZ => gcd0 gcd(ZZ,QQ) := QQ => (x,y) -> gcd(x * denominator y, numerator y) / denominator y gcd(QQ,ZZ) := QQ => (y,x) -> gcd(x * denominator y, numerator y) / denominator y @@ -54,7 +54,7 @@ gcd(QQ,QQ) := QQ => (x,y) -> ( gcd ZZ := gcd QQ := identity abs = method() -abs ZZ := abs RR := abs RRi := abs CC := abs QQ := abs0 +abs Number := abs0 abs Constant := abs @@ numeric sign = method() @@ -62,7 +62,7 @@ sign Number := sign0 sign Constant := sign @@ numeric lcm = method(Binary => true) -installMethod(lcm, () -> 1) +lcm() := () -> 1 lcm(ZZ,ZZ) := (f,g) -> ( d := gcd(f, g); if d == 0 then 0 @@ -101,7 +101,7 @@ Function or Function := (f, g) -> s -> f s or g s Function xor Function := (f, g) -> s -> f s xor g s not Function := f -> s -> not f s -ZZ~ := bitnotfun +~ ZZ := bitnotfun changeBase = method() changeBase(ZZ, ZZ) := String => @@ -139,24 +139,6 @@ tonelliShanks = (n, p) -> ( t = (t * c) % p; r = (r * b) % p)) ------------------------------------------------------------------------------ --- AtomicInt ------------------------------------------------------------------------------ - -AtomicInt.synonym = "atomic integer" - -scan({symbol +=, symbol -=, symbol &=, symbol |=, symbol ^^=}, - op -> typicalValues#(op, AtomicInt) = ZZ) - -store = method() -store(AtomicInt, ZZ) := atomicStore - -exchange = method() -exchange(AtomicInt, ZZ) := atomicExchange - -compareExchange = method() -compareExchange(AtomicInt, ZZ, ZZ) := atomicCompareExchange - -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/intersect.m2 b/M2/Macaulay2/m2/intersect.m2 index 197adf05efc..e4af54ca29d 100644 --- a/M2/Macaulay2/m2/intersect.m2 +++ b/M2/Macaulay2/m2/intersect.m2 @@ -98,7 +98,7 @@ algorithms#(intersect, Module, Module) = new MutableHashTable from { or not same apply(L, N -> N.?relations and (N.relations == M.relations or image N.relations == image M.relations)) then error "intersect: all modules must be submodules of the same module"; -- - relns := directSum apply(L, N -> if N.?relations then generators N | N.relations else generators N); + relns := directSum apply(L, fullgens); g := map(R^(#L), R^1, table(#L, 1, x -> 1)) ** id_(ambient M); h := modulo(g, relns); -- diff --git a/M2/Macaulay2/m2/intervals.m2 b/M2/Macaulay2/m2/intervals.m2 index be644a91189..accca66ba3d 100644 --- a/M2/Macaulay2/m2/intervals.m2 +++ b/M2/Macaulay2/m2/intervals.m2 @@ -4,55 +4,89 @@ needs "reals.m2" interval = method(Options => {Precision => -1}) -for A in {ZZ,QQ,RR} do -interval A := opts -> N -> ( - if opts.Precision < 0 then toRRi(N) - else toRRi(opts.Precision,N,N)) - -for A in {ZZ,QQ,RR} do -for B in {ZZ,QQ,RR} do -interval(A,B) := opts -> (N,M) -> ( +interval Number := opts -> N -> ( + p := if opts.Precision < 0 then defaultPrecision else opts.Precision; + interval(numeric(p, N), opts)) +interval RR := opts -> N -> ( + if opts.Precision < 0 or opts.Precision == precision N then toRRi N + else toRRi(opts.Precision, N, N)) +interval RRi := opts -> N -> ( + if opts.Precision < 0 or opts.Precision == precision N then N + else toRRi(opts.Precision, left N, right N)) +interval CC := opts -> N -> ( + if opts.Precision < 0 or opts.Precision == precision N then toCCi N + else toCCi(opts.Precision, realPart N, imaginaryPart N)) +interval CCi := opts -> N -> ( + if opts.Precision < 0 or opts.Precision == precision N then N + else toCCi(opts.Precision, realPart N, imaginaryPart N)) + +interval(Number, Number) := opts -> (N, M) -> ( + p := opts.Precision; + if p < 0 then p = min(precision N, precision M); + if isInfinite p then p = defaultPrecision; + interval(numeric(p, N), numeric(p, M), opts)) +interval(RR, RR) := opts -> (N,M) -> ( if opts.Precision < 0 then toRRi(N,M) else toRRi(opts.Precision,N,M)) +interval(RR, RRi) := opts -> (N,M) -> interval(toRRi N, M, opts) +interval(RRi, RR) := opts -> (N,M) -> interval(N, toRRi M, opts) +interval(RRi,RRi) := opts -> (N,M) -> ( + if opts.Precision < 0 then toCCi(N,M) + else toCCi(opts.Precision,N,M)) + +interval(RR,CC) := opts -> (N, M) -> interval(toCC N, M, opts) +interval(CC,RR) := opts -> (N, M) -> interval(N, toCC M, opts) +interval(CC,CC) := opts -> (N,M) -> ( + if opts.Precision < 0 + then toCCi( + interval(realPart N, realPart M), + interval(imaginaryPart N, imaginaryPart M)) + else toCCi(opts.Precision, + interval(realPart N, realPart M, opts), + interval(imaginaryPart N, imaginaryPart M, opts))) + +-- the following don't make sense +interval(RRi, CC) := +interval(CC, RRi) := +interval(CCi, Number) := +interval(Number, CCi) := opts -> (N, M) -> error "interval not well-defined" interval(Array) := opts -> A -> ( if (length(A) == 0) or (length(A)>2) then error("expected length 2") else if length(A) == 1 then interval(opts,A_0) - else interval(opts,A_0,A_1)) - -spanRRi = method(Options => {Precision => -1}) + else interval(opts,A_0,A_1)) + +span = method(Dispatch => Thing, Options => {Precision => -1}, Binary => true) +span Number := opts -> N -> interval(N, opts) +span(Number, Number) := opts -> (N,M) -> span( + interval(N, opts), interval(M, opts), opts) +span(RRi, RRi) := opts -> (N,M) -> ( + if isEmpty(N) then interval(M, opts) + else if isEmpty(M) then interval(N, opts) + else interval(min(left N, left M), max(right N, right M), opts)) +span(RRi, CCi) := opts -> (N, M) -> span(toCCi N, M, opts) +span(CCi, RRi) := opts -> (N, M) -> span(N, toCCi M, opts) +span(CCi, CCi) := opts -> (N, M) -> ( + if isEmpty N then interval(M, opts) + else if isEmpty M then interval(N, opts) + else interval( + span(realPart M, realPart N, opts), + span(imaginaryPart M, imaginaryPart N, opts), + opts)) for A in {ZZ,QQ,RR} do -for B in {ZZ,QQ,RR} do -spanRRi(A,B) := opts -> (N,M) -> ( - if opts.Precision < 0 then toRRi(min(M,N),max(M,N)) - else toRRi(opts.Precision,min(M,N),max(M,N))) - -for A in {ZZ,QQ,RR} do ( -spanRRi(RRi,A) := opts -> (N,M) -> ( - if isEmpty(N) then interval(opts,M) - else if opts.Precision < 0 then toRRi(min(left N,M),max(right N,M)) - else toRRi(opts.Precision,min(left N,M),max(right N,M))); -spanRRi(A,RRi) := opts -> (N,M) -> span(opts,M,N)) - -spanRRi(RRi,RRi) := opts -> (N,M) -> ( - if isEmpty(N) then interval(opts,left M, right M) - else if isEmpty(M) then interval(opts, left N, right N) - else if opts.Precision < 0 then toRRi(min(left N,left M),max(right N,right M)) - else toRRi(opts.Precision,min(left N,left M),max(right N,right M))) - -span = method(Dispatch => Thing, Options => true) - -span ZZ := span QQ := span RR := {Precision => -1} >> opts -> N -> interval(N,opts) - -span RRi := {Precision => -1} >> opts -> N -> interval(left N,right N,opts) +isMember(A,RRi) := (N,M) -> subsetRRi(N,M); +isMember(CC,CCi) := (N,M) -> subsetRRi(realPart N,realPart M) and subsetRRi(imaginaryPart N, imaginaryPart M); +isMember(CC,RRi) := (N,M) -> subsetRRi(realPart N,realPart M) and subsetRRi(imaginaryPart N, imaginaryPart M); -span List := span Sequence := {Precision => -1} >> opts -> L -> fold(L, (N, M) -> spanRRi(N, M, opts)) +isSubset(RRi,RRi) := (N,M) -> subsetRRi(N,M); +isSubset(CCi,RRi) := (N,M) -> subsetRRi(realPart N,realPart M) and subsetRRi(imaginaryPart N,imaginaryPart M); for A in {ZZ,QQ,RR} do -isMember(A,RRi) := (N,M) -> subsetRRi(N,M); +isMember(A,CCi) := (N,M) -> subsetRRi(realPart N,realPart M) and subsetRRi(imaginaryPart N,imaginaryPart M); -isSubset(RRi,RRi) := (N,M) -> subsetRRi(N,M); +for A in {RRi,CCi} do +isSubset(A,CCi) := (N,M) -> subsetRRi(realPart N,realPart M) and subsetRRi(imaginaryPart N,imaginaryPart M); -- intersect is an associative binary method, so it works on arbitrary lists and sequences intersect RRi := RRi => { Precision => -1 } >> opts -> identity @@ -60,6 +94,53 @@ intersect(RRi, RRi) := RRi => { Precision => -1 } >> opts -> (N, M) -> ( if opts.Precision < 0 then intersectRRi(N,M) else intersectRRi(opts.Precision,N,M)) -isEmpty RRi := Boolean => isEmptyRRi +intersect CCi := CCi => { Precision => -1 } >> opts -> identity +intersect(CCi, RRi) := CCi => { Precision => -1 } >> opts -> (N, M) -> ( + if opts.Precision < 0 then interval(intersectRRi(realPart N,M), imaginaryPart N) + else interval(intersectRRi(opts.Precision,realPart N,M), imaginaryPart N)) +intersect(RRi, CCi) := CCi => { Precision => -1 } >> opts -> (N, M) -> intersect(M, N) +intersect(CCi, CCi) := CCi => { Precision => -1 } >> opts -> (N, M) -> ( + if opts.Precision < 0 then interval(intersectRRi(realPart N,realPart M), intersectRRi(imaginaryPart N, imaginaryPart M)) + else interval(intersectRRi(opts.Precision,realPart N, realPart M), intersectRRi(opts.Precision, imaginaryPart N, imaginaryPart M))) + +CCi ? CCi := (x, y) -> ( + if (r := realPart x ? realPart y) =!= symbol == then r + else imaginaryPart x ? imaginaryPart y) +CCi ? Number := (x, y) -> x ? toCCi numeric y +Number ? CCi := (x, y) -> toCCi numeric x ? y + +-- need to define these or we get "comparison not implemented" from the +-- interpeter +ZZ ? CCi := +QQ ? CCi := +RR ? CCi := +RRi ? CCi := +CC ? CCi := (x, y) -> toCCi x ? y -toExternalString RRi:= x -> "interval" | toExternalString (left x, right x) +isEmpty RRi := Boolean => isEmptyRRi +isEmpty CCi := x -> isEmptyRRi realPart x or isEmptyRRi imaginaryPart x + +midpoint = method() +midpoint Number := identity +midpoint RRi := midpoint CCi := midpoint0 + +midpoint Ring := R -> ( + if instance(R, RealIntervalField) then RR_(precision R) + else if instance(R, ComplexIntervalField) then CC_(precision R) + else R) + +intervalPolyHelper := (func, f) -> ( + R := midpoint ring f; + if R === ring f then f + else sum(listForm f, (m, c) -> func c * product(#m, i -> R_i^(m#i)))) + +midpoint RingElement := f -> intervalPolyHelper(midpoint, f) +left RingElement := f -> intervalPolyHelper(left, f) +right RingElement := f -> intervalPolyHelper(right, f) +lowerLeft RingElement := f -> intervalPolyHelper(lowerLeft, f) +lowerRight RingElement := f -> intervalPolyHelper(lowerRight, f) +upperLeft RingElement := f -> intervalPolyHelper(upperLeft, f) +upperRight RingElement := f -> intervalPolyHelper(upperRight, f) + +toExternalString RRi := x -> "interval" | toExternalString (left x, right x) +toExternalString CCi := x -> "interval" | toExternalString (left realPart x+(left imaginaryPart x)*ii,right realPart x+(right imaginaryPart x)*ii) diff --git a/M2/Macaulay2/m2/lists.m2 b/M2/Macaulay2/m2/lists.m2 index fea04763e3e..d9dedda348c 100644 --- a/M2/Macaulay2/m2/lists.m2 +++ b/M2/Macaulay2/m2/lists.m2 @@ -33,7 +33,8 @@ List / RingElement := List / Number := List => (v,b) -> apply(v,x->x / b) List // RingElement := List // Number := List => (v,b) -> apply(v,x->x // b) List % RingElement := List % Number := List => (v,b) -> apply(v,x->x % b) -VisibleList _ List := VisibleList => (x,y) -> apply(splice y, i -> x#i) +VisibleList _ List := VisibleList => (L, ind) -> ( + new class L from apply(splice ind, i -> L#i)) Sequence .. Sequence := Sequence => (v,w) -> ( n := #v; @@ -228,15 +229,21 @@ flexibleOperators = sort flexibleOperators fixedOperators = sort fixedOperators allOperators = sort allOperators -random List := opts -> s -> ( - n := #s; - if n <= 1 then return s; - s = new MutableList from s; - for i from 1 to n-1 do ( - j := random (i+1); - t := s#i ; s#i = s#j ; s#j = t; - ); - new List from s) +randomElement = method() +randomElement List := x -> ( + if #x == 0 then error "expected a nonempty list"; + x#(random(#x))) + +-- TODO: change to call randomElement instead (M2 1.26.11 or 1.27.05) +seenRandomWarning := false; +random List := opts -> ( + x -> ( + if not seenRandomWarning then ( + printerr( + "the behavior of random(List) will change soon; ", + "use shuffle(List) instead"); + seenRandomWarning = true); + shuffle x)) randomSubset = method() -- Knuth Algorithm S, Art of Computer Programming, Section 3.4.2 @@ -256,6 +263,15 @@ randomSubset VisibleList := x -> x_(randomSubset(#x)) randomSubset(Set, ZZ) := (x, n) -> set randomSubset(toList x, n) randomSubset Set := x -> set randomSubset toList x +shuffle = method() +shuffle MutableList := s -> ( + for i from 1 to #s-1 do ( + j := random (i+1); + (s#i, s#j) = (s#j, s#i)); + s) +shuffle List := s -> toList shuffle new MutableList from s +shuffle(List, ZZ) := shuffle @@ randomSubset + ----------------------------------------------------------------------------- -- sublists ----------------------------------------------------------------------------- @@ -339,25 +355,6 @@ pack(ZZ, BasicList) := List => pack' pack(String, ZZ) := pack(BasicList, ZZ) := List => (L, n) -> pack'(n, L) ------------------------------------------------------------------------------ - -parallelApplyRaw = (L, f) -> - -- 'reverse's to minimize thread switching in 'taskResult's: - reverse (taskResult \ reverse apply(L, e -> schedule(f, e))); -parallelApply = method(Options => {Strategy => null}) -parallelApply(BasicList, Function) := o -> (L, f) -> ( - if o.Strategy === "raw" then return parallelApplyRaw(L, f); - n := #L; - numThreads := min(n + 1, maxAllowableThreads); - oldAllowableThreads := allowableThreads; - if allowableThreads < numThreads then allowableThreads = numThreads; - numChunks := 3 * numThreads; - res := if n <= numChunks then toList parallelApplyRaw(L, f) else - flatten parallelApplyRaw(pack(L, ceiling(n / numChunks)), chunk -> apply(chunk, f)); - allowableThreads = oldAllowableThreads; - res); - - -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/loadsequence b/M2/Macaulay2/m2/loadsequence index 4adc58394de..1e5f747c4c8 100644 --- a/M2/Macaulay2/m2/loadsequence +++ b/M2/Macaulay2/m2/loadsequence @@ -6,6 +6,7 @@ shared.m2 autoload.m2 system.m2 regex.m2 +threads.m2 profile.m2 debugging.m2 diff --git a/M2/Macaulay2/m2/mathml.m2 b/M2/Macaulay2/m2/mathml.m2 index 6834c4d71a2..21977838dd5 100644 --- a/M2/Macaulay2/m2/mathml.m2 +++ b/M2/Macaulay2/m2/mathml.m2 @@ -86,6 +86,12 @@ mathML Minus := v -> concatenate( "-", mathML v#0, "") mathML Divide := x -> concatenate("", mathML x#0, mathML x#1, "") mathML OneExpression := x -> "1" mathML ZeroExpression := x -> "0" +mathML BinaryOperation := m -> ( + x := mathML m#1; + y := mathML m#2; + if rightPrecedence m#1 < lprec m#0 then x = mathMLparen x; + if precedence m#2 <= rprec m#0 then y = mathMLparen y; + mrow(x | mo m#0 | y)) mathML Sum := v -> ( n := # v; if n === 0 then "0" @@ -163,8 +169,6 @@ rightbrace := mo "}" mathML MapExpression := x -> mrow {mathML x#0, leftarrow, mathML x#1} mathML Option := s -> concatenate("",mathML s#0, doublerightarrow, mathML s#1, "") -mathML Type := -mathML ImmutableType := R -> if R.?mathML then R.mathML else mathML expression R mathML VirtualTally := mathML HashTable := s -> if s.?mathML then s.mathML else concatenate( "",mathML class s,leftbrace, mtable sort apply(pairs s, (k,v) -> {mathML k, doublerightarrow, mathML v}), rightbrace,"", newline ) mathML MutableHashTable := x -> if x.?mathML then x.mathML else ( diff --git a/M2/Macaulay2/m2/matrix.m2 b/M2/Macaulay2/m2/matrix.m2 index 1eb4b71a385..120cd77bf2c 100644 --- a/M2/Macaulay2/m2/matrix.m2 +++ b/M2/Macaulay2/m2/matrix.m2 @@ -3,6 +3,24 @@ needs "modules.m2" +notSameRing := (X,Y) -> ( + if X === Y then error("expected ",pluralsynonym X, " for the same ring") + else error("expected ",synonym X," and ",synonym Y," for the same ring")) +sameRing = (M,N) -> if ring M === ring N then (M,N) else notSameRing(class M,class N) +notToSameRing := (X,Y) -> ( + if X === Y then error("expected ",pluralsynonym X, " for compatible rings") + else error("expected ",synonym X," and ",synonym Y," for compatible rings")) +toSameRing = (M,N) -> ( + R := ring M; S := ring N; + if R =!= S then ( + R' := if instance(R,InexactField) then ring R else R; + S' := if instance(S,InexactField) then ring S else S; + if isPromotable(R',S) then (promote(M,S),N) + else if isPromotable(S',R) then (M,promote(N,R)) + else notToSameRing(class M,class N) + ) + else (M,N)) + oops := R -> error ( if degreeLength R === 1 then "expected degree to be an integer or list of integers of length 1" @@ -79,23 +97,15 @@ InfiniteNumber * Matrix := (r,m) -> (map(target m, source m, matrix(r*(entries m Matrix * InfiniteNumber := (m,r) -> r*m Number * Matrix := RingElement * Matrix := (r,m) -> ( - if ring r =!= ring m then try r = promote(r,ring m) else m = promote(m,ring r); + (r,m) = toSameRing(r,m); map(target m, source m, reduce(target m, raw r * raw m))) Matrix * Number := Matrix * RingElement := (m,r) -> ( - if ring r =!= ring m then try r = promote(r,ring m) else m = promote(m,ring r); + (r,m) = toSameRing(r,m); map(target m, source m, reduce(target m, raw m * raw r))) Matrix / Number := Matrix / RingElement := (m,r) -> m * (1/r) -toSameRing = (m,n) -> ( - if ring m =!= ring n then ( - try (promote(m,ring n) , n) else - try (m , promote(n,ring m)) - else error "expected compatible rings" - ) - else (m,n)) - Matrix _ Sequence := RingElement => (m,ind) -> ( n := (raw m)_ind; if instance(n, RawRingElement) then promote(n, ring m) @@ -176,13 +186,8 @@ Matrix * Matrix := Matrix => (m,n) -> ( q, Degree => degree m + if m.?RingMap then m.RingMap.cache.DegreeMap degree n else degree n)) else ( - R := ring m; + (m,n) = toSameRing(m,n); S := ring target n; - if R =!= S then ( -- use toSameRing? - try m = promote(m,S) else - try n = promote(n,R) else - error "maps over incompatible rings"; - ); M = target m; P := source m; N = source n; @@ -190,13 +195,8 @@ Matrix * Matrix := Matrix => (m,n) -> ( if not isFreeModule P or not isFreeModule Q or rank P =!= rank Q then error "maps not composable"; dif := degrees P - degrees Q; - deg := ( - if #dif === 0 - then degree m + degree n - else if same dif - then degree m + degree n + dif#0 - else toList (degreeLength ring m:0) - ); + deg := degree m + degree n; + if #dif > 0 then if same dif then N = N ** S^{-dif#0} else deg = toList (degreeLength S:0); f := m.RawMatrix * n.RawMatrix; f = rawMatrixRemake2(rawTarget f, rawSource f, deg, f, 0); f = reduce(M,f); @@ -205,8 +205,11 @@ Matrix * Matrix := Matrix => (m,n) -> ( else map(M,N,f))) Matrix#1 = f -> ( - if source f =!= target f then error "expected source and target to agree" - else id_(target f)) + M := target f; + N := source f; + if (M =!= N) and (not isFreeModule M or not isFreeModule N or rank M =!= rank N) + then error "expected source and target to agree" + else id_M) Matrix ^ ZZ := Matrix => BinaryPowerMethod transpose Matrix := Matrix => (cacheValue symbol transpose) ( @@ -290,7 +293,7 @@ ggConcatBlocks = (tar,src,mats) -> ( then f = map(target f, source f, f, Degree => degree mats#0#0); f) -sameringMatrices = mats -> ( +sameRingMatrices = mats -> ( if same apply(mats, m -> (if m.?RingMap then m.RingMap,ring target m, ring source m)) then mats else ( @@ -299,7 +302,7 @@ sameringMatrices = mats -> ( directSum Matrix := f -> Matrix.directSum (1 : f) Matrix.directSum = args -> ( - args = sameringMatrices args; + args = sameRingMatrices args; R := ring args#0; if not all(args, f -> ring f === R) then error "expected matrices all over the same ring"; new Matrix from { @@ -399,7 +402,7 @@ RingElement || RingElement := Matrix => (r,s) -> matrix {{r}} || matrix {{s}} concatCols = mats -> ( mats = nonnull toList mats; if # mats === 1 then return mats#0; - mats = sameringMatrices mats; + mats = sameRingMatrices mats; sources := apply(mats,source); -- if not all(sources, F -> isFreeModule F) then error "expected sources to be free modules"; targets := apply(mats,target); @@ -409,7 +412,7 @@ concatCols = mats -> ( concatRows = mats -> ( mats = nonnull toList mats; if # mats === 1 then return mats#0; - mats = sameringMatrices mats; + mats = sameRingMatrices mats; sources := apply(mats,source); -- if not same sources then error "expected matrices in the same column to have equal sources"; targets := apply(mats,target); @@ -518,7 +521,7 @@ bothFree := (f,g) -> ( or not isFreeModule source g or not isFreeModule target g then error "expected a homomorphism between free modules" else (f,g)) -diff(Matrix, Matrix) := Matrix => ( (f,g) -> map(ring f, rawMatrixDiff(f.RawMatrix, g.RawMatrix)) ) @@ bothFree @@ toSameRing +diff(Matrix, Matrix) := Matrix => ( (f,g) -> map(ring f, rawMatrixDiff(f.RawMatrix, g.RawMatrix)) ) @@ bothFree @@ toSameRing diff(RingElement, RingElement) := RingElement => (f,g) -> (diff(matrix{{f}},matrix{{g}}))_(0,0) diff(Matrix, RingElement) := (m,f) -> diff(m,matrix{{f}}) diff(RingElement, Matrix) := (f,m) -> diff(matrix{{f}},m) @@ -565,25 +568,21 @@ leadTerm(Matrix) := Matrix => m -> ( borel Matrix := Matrix => m -> generators borel monomialIdeal m clean(RR,Matrix) := (epsilon,M) -> map(target M, source M, clean(epsilon,raw M)) -norm(RR,Matrix) := (p,M) -> new RR from norm(p,raw M) -norm(InfiniteNumber,Matrix) := (p,M) -> ( - prec := precision M; - if prec === infinity then ( - error "expected a matrix over RR or CC"; - ) - else ( - norm(numeric(prec,p), M) - ) - ) -norm(Matrix) := (M) -> ( - prec := precision M; - if prec === infinity then ( - error "expected a matrix over RR or CC"; - ) - else ( - norm(numeric(prec,infinity), M) - ) - ) + +norm Matrix := norm_infinity +norm(Number, Matrix) := (p, M) -> ( + R := ring M; + if p == infinity then ( + -- l^infty norm for RR and CC is implemented in the engine + if instance(R, RealField) or instance(R, ComplexField) + then ( + if precision p > precision R + then p = numeric(precision R, infinity); + new RR from norm(p, raw M)) + else max apply(flatten entries M, norm)) + else if isReal p and p >= 1 + then (sum(flatten entries M, x -> (norm(p, x))^p))^(1/p) + else error "expected p >= 1") numRows Matrix := M -> numgens cover target M numColumns Matrix := M -> numgens cover source M @@ -656,7 +655,7 @@ isSubquotient(Module,Module) := (M,N) -> ( and ambient M === ambient N and - (generators M | relations M) % (generators N | relations N) == 0 + fullgens M % fullgens N == 0 and relations N % relations M == 0 ) @@ -735,11 +734,12 @@ inducesWellDefinedMap(Nothing,Nothing,Matrix) := (M,N,f) -> true ----------------------------------------------------------------------------- -vars Ring := Matrix => R -> ( - g := generators R; - if R.?vars then R.vars else R.vars = - map(R^1,,{g})) +vars Ring := Matrix => R -> R.vars ??= map(module R, , {generators R}) +relations Module := Matrix => M -> ( + if M.?relations then M.relations + else map(ambient M, (ring M)^0, 0)) +-- TODO: why are there two places for generators? generators Module := Matrix => opts -> M -> ( if M.?generators then M.generators else if M.cache.?generators then M.cache.generators @@ -747,14 +747,6 @@ generators Module := Matrix => opts -> M -> ( Module_* := M -> apply(numgens M, i -> M_i) -relations Module := Matrix => M -> ( - if M.?relations then M.relations - else ( - R := ring M; - map(ambient M,R^0,0) - ) - ) - degrees Matrix := f -> {degrees target f, degrees source f} coverMap(Module) := Matrix => (M) -> map(M, cover M, id_(cover M)) @@ -781,6 +773,27 @@ leadComponent = method() leadComponent Matrix := List => m -> nonnull for c to numColumns m - 1 list position(numRows m, r -> m_(r,c) != 0, Reverse => true) leadComponent Vector := ZZ => v -> try first leadComponent matrix v else null +midpoint Module := M -> M.cache.midpoint ??= ( + R := midpoint ring M; + if R === ring M then M + else ( + if not isFreeModule M then error "expected a free module"; + R^(-degrees M))) + +intervalMatrixHelper := (func, f) -> ( + M := midpoint target f; + N := midpoint source f; + if M === target f and N === source f then f + else map(M, N, apply(entries f, row -> func \ row))) + +midpoint Matrix := f -> intervalMatrixHelper(midpoint, f) +left Matrix := f -> intervalMatrixHelper(left, f) +right Matrix := f -> intervalMatrixHelper(right, f) +lowerLeft Matrix := f -> intervalMatrixHelper(lowerLeft, f) +lowerRight Matrix := f -> intervalMatrixHelper(lowerRight, f) +upperLeft Matrix := f -> intervalMatrixHelper(upperLeft, f) +upperRight Matrix := f -> intervalMatrixHelper(upperRight, f) + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/matrix1.m2 b/M2/Macaulay2/m2/matrix1.m2 index f74aef3057f..a23b1964a64 100644 --- a/M2/Macaulay2/m2/matrix1.m2 +++ b/M2/Macaulay2/m2/matrix1.m2 @@ -6,17 +6,7 @@ needs "quotient.m2" ----------------------------------------------------------------------------- -notsamering := (X,Y) -> ( - if X === Y then error("expected ",pluralsynonym X, " for the same ring") - else error("expected ",X.synonym," and ",Y.synonym," for the same ring")) -nottosamering := (X,Y) -> ( - if X === Y then error("expected ",pluralsynonym X, " for compatible rings") - else error("expected ",X.synonym," and ",Y.synonym," for compatible rings")) -samering = (M,N) -> if ring M === ring N then (M,N) else notsamering(class M,class N) -tosamering := (M,N) -> if ring M === ring N then (M,N) else ( - z := try 0_(ring M) + 0_(ring N) else nottosamering(class M,class N); - (promote(M,ring z),promote(N,ring z))) - +module RingFamily := module Ring := Module => (cacheValue symbol module)(R -> R^1) matrix(RingFamily,List) := Matrix => opts -> (R,m) -> matrix(default R, m, opts) @@ -57,7 +47,7 @@ makeRawTable := (R,p) -> ( -- this is messy map(Module,Nothing,Matrix) := Matrix => o -> (M,nothing,p) -> ( -- TODO: why give an error? Compare with map(Module,Nothing,RawMatrix) if o.Degree =!= null then error "Degree option given with indeterminate source module"; - samering(M,p); + sameRing(M,p); R := ring M; if M.?generators then ( M' := source M.generators; @@ -167,7 +157,7 @@ concatBlocks = mats -> ( else if #(mats#0) === 1 then concatRows (mats/first) else ( - sameringMatrices flatten mats; + sameRingMatrices flatten mats; sources := applyTable(mats,source); targets := transpose applyTable(mats,target); -- if not same sources then error "expected matrices in the same column to have equal sources"; @@ -269,6 +259,10 @@ matrix(Matrix) := Matrix => opts -> (m) -> ( ) matrix RingElement := matrix Number := opts -> r -> matrix({{r}}, opts) +matrix(Ring, RingElement) := +matrix(RingFamily, RingElement) := +matrix(Ring, Number) := +matrix(RingFamily, Number) := opts -> (R, f) -> matrix(R, {{f}}, opts) matrix List := Matrix => opts -> L -> ( if #L === 0 then return matrix(ZZ, {}); @@ -374,8 +368,8 @@ subquotient(Module,Nothing,Nothing) := (F,g,r) -> F Matrix ** Matrix := Matrix => (A, B) -> tensor(A, B) tensor(Matrix, Matrix) := Matrix => {} >> opts -> ((f, g) -> ( - samering(target f,target g); - samering(source f,source g); + sameRing(target f,target g); + sameRing(source f,source g); R := ring target f; if f === id_(R^1) then return g; if g === id_(R^1) then return f; @@ -490,11 +484,11 @@ Module / Ideal := Module => (M,J) -> M / (J * M) Ideal#AfterPrint = Ideal#AfterNoPrint = (I) -> (Ideal," of ",ring I) Ideal ^ ZZ := Ideal => (I,n) -> ideal symmetricPower(n,generators I) -Ideal * Ideal := Ideal => ((I,J) -> ideal flatten (generators I ** generators J)) @@ samering -Ideal * Module := Module => ((I,M) -> subquotient (generators I ** generators M, relations M)) @@ samering -Ideal + Ideal := Ideal => ((I,J) -> ideal (generators I | generators J)) @@ tosamering -Ideal + RingElement := Ideal + Number := ((I,r) -> I + ideal r) @@ tosamering -RingElement + Ideal := Number + Ideal := ((r,I) -> ideal r + I) @@ tosamering +Ideal * Ideal := Ideal => ((I,J) -> ideal flatten (generators I ** generators J)) @@ sameRing +Ideal * Module := Module => ((I,M) -> subquotient (generators I ** generators M, relations M)) @@ sameRing +Ideal + Ideal := Ideal => ((I,J) -> ideal (generators I | generators J)) @@ toSameRing +Ideal + RingElement := Ideal + Number := ((I,r) -> I + ideal r) @@ toSameRing +RingElement + Ideal := Number + Ideal := ((r,I) -> ideal r + I) @@ toSameRing Ideal _ ZZ := RingElement => (I,n) -> (generators I)_(0,n) Matrix % Ideal := Matrix => ((f,I) -> if numRows f === 1 @@ -504,7 +498,7 @@ Matrix % Ideal := Matrix => ((f,I) -> R := ring I; S := R/I; lift(promote(f,S),R)) - ) @@ samering + ) @@ sameRing Vector % Ideal := (v,I) -> new class v from {v#0%I} numgens Ideal := (I) -> numgens source generators I leadTerm Ideal := Ideal => (I) -> ideal leadTerm gb I @@ -522,7 +516,7 @@ Ideal == Ring := (I,R) -> ( Ring == Ideal := (R,I) -> I == R Ideal == Ideal := (I,J) -> ( - samering(I,J); + sameRing(I,J); ( generators I == generators J or -- if isHomogeneous I and isHomogeneous J -- can be removed later -- then gb I == gb J diff --git a/M2/Macaulay2/m2/matrix2.m2 b/M2/Macaulay2/m2/matrix2.m2 index 595700ed651..4b8172cef6c 100644 --- a/M2/Macaulay2/m2/matrix2.m2 +++ b/M2/Macaulay2/m2/matrix2.m2 @@ -202,10 +202,10 @@ mingens Module := Matrix => opts -> M -> M.cache.mingens ??= if isFreeModule M t if M.?generators then ( if M.?relations then ( if opts.Strategy === Complement and isHomogeneous M and isAffineRing ring M then ( - c := mingens mingb (M.generators|M.relations); + c := mingens mingb fullgens M; c * complement(M.relations // c)) else ( - tot := mingb(M.generators|M.relations); + tot := mingb fullgens M; rel := mingb(M.relations); mingens mingb (mingens tot % rel))) else mingens mingb M.generators) @@ -243,7 +243,7 @@ trim Module := Module => opts -> M -> M.cache#(symbol trim => opts) ??= if isFre if gns === M.generators and rlns === M.relations then M else subquotient(F, gns, zr rlns)) else if opts.Strategy === Inhomogeneous then ( - tot := mingb(M.generators|M.relations); + tot := mingb fullgens M; rel := mingb(M.relations); if tot === M.generators and rel === M.relations then M diff --git a/M2/Macaulay2/m2/methods.m2 b/M2/Macaulay2/m2/methods.m2 index 75037deefdb..d6a68ca9d37 100644 --- a/M2/Macaulay2/m2/methods.m2 +++ b/M2/Macaulay2/m2/methods.m2 @@ -226,7 +226,7 @@ method = methodDefaults >> opts -> args -> ( -- get the options used when a method was declared -- TODO: doesn't work for MethodFunctionSingle, MethodFunctionBinary methodOptions = method(TypicalValue => OptionTable) -methodOptions Function := methodOptions Symbol := f -> null +methodOptions Function := methodOptions Symbol := methodOptions List := f -> null methodOptions MethodFunctionWithOptions := MultipleArgsWithOptionsGetMethodOptions methodOptions MethodFunction := MultipleArgsNoOptionsGetMethodOptions methodOptions Command := f -> methodOptions f#0 @@ -276,6 +276,7 @@ setupMethods((), { coefficients, monomials, size, sum, product, nullhomotopy, module, raw, content, leadTerm, leadCoefficient, leadMonomial, components, assign, realPart, imaginaryPart, conjugate, + left, right, lowerLeft, lowerRight, upperLeft, upperRight, relations, inverse, numeric, numericInterval, floor, ceiling, round, degree, multidegree, presentation, dismiss, precision, norm, clean, fraction, part, @@ -301,6 +302,7 @@ default = method() -- m ()) random = method(Options => { + CoefficientRing => null, MaximalRank => false, Density => 1., UpperTriangular => false, diff --git a/M2/Macaulay2/m2/modules.m2 b/M2/Macaulay2/m2/modules.m2 index af281368b41..38d667f440b 100644 --- a/M2/Macaulay2/m2/modules.m2 +++ b/M2/Macaulay2/m2/modules.m2 @@ -47,10 +47,12 @@ scan( {ZZ,QQ}, K -> ( )) scan(( - (ZZ, { QQ, RR', CC', RRi' }), - (QQ, { RR', CC', RRi' }), - (RR',{ RR', CC', RRi' }), - (CC', { CC' }) + (ZZ, { QQ, RR', CC', RRi', CCi' }), + (QQ, { RR', CC', RRi', CCi' }), + (RR',{ RR', CC', RRi', CCi' }), + (CC', { CC', CCi' }), + (RRi', { RRi', CCi' }), + (CCi', { CCi' }) ), (K,Ls) -> scan(Ls, L -> ( p := makepromoter 0; @@ -79,6 +81,7 @@ Vector.synonym = "vector" Vector _ ZZ := (v,i) -> (ambient v#0)_(i,0) entries Vector := v -> entries ambient v#0 / first norm Vector := v -> norm v#0 +norm(Number, Vector) := (p, v) -> norm(p, v#0) expression Vector := v -> VectorExpression apply(flatten entries super v#0,expression) net Vector := v -> net expression v describe Vector := v -> Describe expression FunctionApplication( @@ -281,7 +284,7 @@ Ring ^ ZZ := Module => (R, n) -> ( Ring ^ List := Module => (R, degs) -> ( if not R.?RawRing then error "non-engine free modules with degrees not implemented yet"; -- check the args - degs = - splice degs; + degs = - splice\splice degs; degrk := degreeLength R; if #degs === 0 then () else if isListOfIntegers degs then ( if degrk != 1 @@ -356,7 +359,7 @@ isSubset(Module, Module) := (M, N) -> ( ambient M === ambient N and if not M.?relations and not N.?relations then issub(generators M, generators N) else if M.?relations and N.?relations then ( - isequal(M.relations, N.relations) and issub(generators M, generators N | N.relations)) + isequal(M.relations, N.relations) and issub(generators M, fullgens N)) -- see the code for subquotient: if present, M.relations is nonzero; same for N -- so one of the modules has nonzero relations and the other doesn't else false) diff --git a/M2/Macaulay2/m2/modules2.m2 b/M2/Macaulay2/m2/modules2.m2 index 9496095db4d..20f2e6c5fac 100644 --- a/M2/Macaulay2/m2/modules2.m2 +++ b/M2/Macaulay2/m2/modules2.m2 @@ -124,6 +124,10 @@ presentation Module := Matrix => M -> M.cache.presentation ??= ( ----------------------------------------------------------------------------- +-- whether a minimalPresentation is already cached +-- TODO: simplify this caching system +hasMinPres = M -> any(select(keys M.cache, Option), o -> o#0 === symbol minimalPresentation) + minimalPresentation(Module) := prune(Module) := Module => opts -> (cacheValue (symbol minimalPresentation => opts)) (M -> ( if isFreeModule M then ( M.cache.pruningMap = id_M; @@ -174,9 +178,11 @@ addHook((minimalPresentation, Module), (opts, M) -> ( if R === ZZ then ( f := presentation M; (g,ch) := smithNormalForm(f, ChangeMatrix => {true, false}); - piv := select(pivots g,ij -> abs g_ij === 1); + pivs := pivots g; + piv := select(pivs, ij -> abs g_ij === 1); rows := first \ piv; - cols := last \ piv; + pivs = last \ pivs; + cols := last \ piv | select(toList(0..numColumns f - 1), i -> not isMember(i, pivs)); (g,ch) = (submatrix'(g,rows,cols),submatrix'(ch,rows,)); N := cokernel g; N.cache.pruningMap = map(M,N,id_(target ch) // ch); -- yuk, taking an inverse here, gb should give inverse change matrices, or the pruning map should go the other way diff --git a/M2/Macaulay2/m2/monideal.m2 b/M2/Macaulay2/m2/monideal.m2 index 35fb9fd7804..82d0c40dcd9 100644 --- a/M2/Macaulay2/m2/monideal.m2 +++ b/M2/Macaulay2/m2/monideal.m2 @@ -37,9 +37,9 @@ MonomialIdeal#1 = I -> monomialIdeal 1_(ring I) MonomialIdeal ^ ZZ := MonomialIdeal => BinaryPowerMethod MonomialIdeal ^ Array := MonomialIdeal => (I, e) -> monomialIdeal (ideal I)^e -MonomialIdeal + MonomialIdeal := MonomialIdeal => ((I, J) -> newMonomialIdeal(ring I, raw I + raw J)) @@ samering -MonomialIdeal * MonomialIdeal := MonomialIdeal => ((I, J) -> newMonomialIdeal(ring I, raw I * raw J)) @@ samering -MonomialIdeal - MonomialIdeal := MonomialIdeal => ((I, J) -> newMonomialIdeal(ring I, raw I - raw J)) @@ samering +MonomialIdeal + MonomialIdeal := MonomialIdeal => ((I, J) -> newMonomialIdeal(ring I, raw I + raw J)) @@ sameRing +MonomialIdeal * MonomialIdeal := MonomialIdeal => ((I, J) -> newMonomialIdeal(ring I, raw I * raw J)) @@ sameRing +MonomialIdeal - MonomialIdeal := MonomialIdeal => ((I, J) -> newMonomialIdeal(ring I, raw I - raw J)) @@ sameRing MonomialIdeal * Ring := MonomialIdeal => (I, S) -> if ring I === S then I else monomialIdeal(generators I ** S) Ring * MonomialIdeal := MonomialIdeal => (S, I) -> I ** S diff --git a/M2/Macaulay2/m2/multilin.m2 b/M2/Macaulay2/m2/multilin.m2 index 0462e7529d0..8877c88542a 100644 --- a/M2/Macaulay2/m2/multilin.m2 +++ b/M2/Macaulay2/m2/multilin.m2 @@ -40,10 +40,10 @@ getMinorsStrategy := (R, m, strat) -> RawMinorsStrategyCodes#strat ?? ( koszul = method() koszul(ZZ, Matrix) := Matrix => (i, m) -> map(ring m, rawKoszul(i, raw m)) -koszul Matrix := -* ChainComplex => *- m -> missingPackage "OldChainComplexes" +koszul Matrix := -* ChainComplex => *- m -> missingPackage "either Complexes or OldChainComplexes" eagonNorthcott = method() -eagonNorthcott Matrix := -* ChainComplex => *- m -> missingPackage "OldChainComplexes" +eagonNorthcott Matrix := -* ChainComplex => *- m -> missingPackage "either Complexes or OldChainComplexes" ----------------------------------------------------------------------------- -- symmetricAlgebra @@ -153,8 +153,8 @@ minors(ZZ, Matrix) := Ideal => opts -> (j, m) -> ( if f =!= null then f#0, if f =!= null then f#1))) -pfaffians = method(TypicalValue => Ideal) -pfaffians(ZZ, Matrix) := (j, m) -> ( +pfaffians = method() +pfaffians(ZZ, Matrix) := Ideal => (j, m) -> ( ideal(map(ring m, rawPfaffians(j,raw m)))) pfaffian = method() diff --git a/M2/Macaulay2/m2/mutablemat.m2 b/M2/Macaulay2/m2/mutablemat.m2 index 01197e1fc4e..88ebf891ca1 100644 --- a/M2/Macaulay2/m2/mutablemat.m2 +++ b/M2/Macaulay2/m2/mutablemat.m2 @@ -45,9 +45,9 @@ mutableMatrix(RingFamily,ZZ,ZZ) := o -> (R,nrows,ncols) -> mutableMatrix(default matrix MutableMatrix := o -> m -> map(ring m, rawMatrix raw m) clean(RR,MutableMatrix) := (epsilon,M) -> map(ring M, clean(epsilon,raw M)) -norm(RR,MutableMatrix) := (p,M) -> new RR from norm(p,raw M) -norm(InexactField,MutableMatrix) := (p,M) -> norm(numeric(precision M, p), M) -norm(MutableMatrix) := (M) -> new RR from norm(numeric(precision M,infinity),raw M) + +norm MutableMatrix := norm_infinity +norm(Number, MutableMatrix) := lookup(norm, Number, Matrix) mutableIdentity = method(Options => {Dense => true}, TypicalValue=>MutableMatrix) mutableIdentity(Ring,ZZ) := o -> (R,nrows) -> diff --git a/M2/Macaulay2/m2/packages.m2 b/M2/Macaulay2/m2/packages.m2 index c611e59e872..74cbf523676 100644 --- a/M2/Macaulay2/m2/packages.m2 +++ b/M2/Macaulay2/m2/packages.m2 @@ -428,7 +428,7 @@ export List := v -> ( if class sym#1 =!= String then error("expected a string: ", nam); sym = getGlobalSymbol(pd, sym#1)) else if instance(sym, String) then ( - if match("^[[:alpha:]]$", sym) then error ("cannot export single-letter symbol ", getGlobalSymbol(pd, sym)); + if match("^[[:alpha:]]$", sym) then error ("cannot export single-letter symbol '", sym, "'"); nam = sym; sym = if pd#?nam then pd#nam else getGlobalSymbol(pd, nam)) else error ("'export' expected a string or an option but was given ", sym, ", of class ", class sym); diff --git a/M2/Macaulay2/m2/polyrings.m2 b/M2/Macaulay2/m2/polyrings.m2 index e4cf369ab9c..c8cf9394399 100644 --- a/M2/Macaulay2/m2/polyrings.m2 +++ b/M2/Macaulay2/m2/polyrings.m2 @@ -287,6 +287,11 @@ selectVariables(List,PolynomialRing) := (v,R) -> ( antipode = method(); antipode RingElement := (f) -> new ring f from rawAntipode raw f; +midpoint PolynomialRing := R -> R.cache.midpoint ??= ( + S := midpoint coefficientRing R; + if S === coefficientRing R then R + else S monoid R) + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/pushforward.m2 b/M2/Macaulay2/m2/pushforward.m2 index 45894f132e0..0e586c3371f 100644 --- a/M2/Macaulay2/m2/pushforward.m2 +++ b/M2/Macaulay2/m2/pushforward.m2 @@ -235,6 +235,25 @@ addHook((kernel, Matrix), Strategy => "PushForward", M := coimage map(target m, f ** source m, raw m); image pushNonLinear(options pushForward, f, M))) +----------------------------------------------------------------------------- + +-- pushforward the module to PP^n via S/I <-- S +-- this is used for instance before computing pdim +-- or regularity of a module over a quotient ring. +-- cf. https://github.com/Macaulay2/M2/issues/3321 +-- and https://github.com/Macaulay2/M2/issues/3656 +-- TODO: can we lift generators and relations and avoid presentation? +liftModule = M -> ( + if instance(ring M, PolynomialRing) then M + else cokernel liftMorphism presentation M) +liftMorphism = f -> f.cache#"liftMorphism" ??= ( + if instance(ring f, PolynomialRing) then return f; + g := presentation ring f; + S := ring g; + -- TODO: sometimes lifting to ring g is enough, how can we detect this? + -- TODO: why doesn't lift(f, ring g) do this automatically? + map(target f ** S, source f ** S, lift(cover f, S)) ** cokernel g) + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/quotring.m2 b/M2/Macaulay2/m2/quotring.m2 index 16675829f80..d78480a4e2b 100644 --- a/M2/Macaulay2/m2/quotring.m2 +++ b/M2/Macaulay2/m2/quotring.m2 @@ -232,8 +232,15 @@ Ring / List := Ring / Sequence := QuotientRing => (R,f) -> R / promote(ideal f, ----------------------------------------------------------------------------- -presentation PolynomialRing := Matrix => R -> map(R^1, R^0, 0) -presentation QuotientRing := Matrix => R -> R.presentation ??= ( +ZZ.presentation = +QQ.presentation = R -> presentation module R +presentation Ring := Matrix => R -> ( + if R.?presentation then R.presentation R + else notImplemented "presentation for this ring") +presentation InexactFieldFamily := Matrix => R -> presentation default R +presentation PolynomialRing := +presentation InexactField := Matrix => R -> presentation module R +presentation QuotientRing := Matrix => R -> R.cache.presentation ??= ( S := ambient R; f := generators ideal R; while class S === QuotientRing do ( -- untested code @@ -276,7 +283,7 @@ char QuotientRing := (stashValue symbol char) ((S) -> ( if isPrime p or isMember(QQ,S.baseRings) then return if S == 0 then 1 else p; relns := presentation S; if relns == 0 then return char ring relns; - if coefficientRing S =!= ZZ then notImplemented(); + if ultimate(coefficientRing, S) =!= ZZ then notImplemented(); g := generators gb relns; if g == 0 then return char ring g; m := g_(0,0); @@ -353,6 +360,11 @@ getPrimeWithRootOfUnity(ZZ,ZZ) := opt-> (n,r1) -> ( (p,r2) ); +-- the "midpoint" of a polynomial in a quotient ring isn't well-defined +-- what if it's R/I, but I has generators with intervals as coefficients? +midpoint QuotientRing := R -> ( + if isFinitePrimeField R then R else error "not well-defined") + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/reals.m2 b/M2/Macaulay2/m2/reals.m2 index 3431f655e31..85c6fa2c2ae 100644 --- a/M2/Macaulay2/m2/reals.m2 +++ b/M2/Macaulay2/m2/reals.m2 @@ -13,9 +13,11 @@ globalAssignment ImmutableType RR.synonym = "real number" RRi.synonym = "real interval" CC.synonym = "complex number" +CCi.synonym = "complex interval" RR.texMath = ///{\mathbb R}/// RRi.texMath = ///{\square\mathbb R}/// CC.texMath = ///{\mathbb C}/// +CCi.texMath = ///{\square\mathbb C}/// Number.synonym = "number" InexactFieldFamily.synonym = "inexact field family" InexactNumber.synonym = "inexact number" @@ -35,25 +37,33 @@ raw InexactField := R -> R.RawRing RR.InexactField = RealField = new Type of InexactField ; RealField.synonym = "real field" RRi.InexactField = RealIntervalField = new Type of InexactField ; RealIntervalField.synonym = "real interval field" CC.InexactField = ComplexField = new Type of InexactField; ComplexField.synonym = "complex field" +CCi.InexactField = ComplexIntervalField = new Type of InexactField; ComplexIntervalField.synonym = "complex interval field" -Nothing' = Nothing -- maybe we'll want to rename it later... RingFamily_* := RR -> RR#(symbol _*) RingFamily_* := RRi -> RRi#(symbol _*) +RingFamily_* := CC -> CC#(symbol _*) +RingFamily_* := CCi -> CCi#(symbol _*) RingFamily_* = (RR,e) -> RR#(symbol _*) = e RingFamily_* = (RRi,e) -> RRi#(symbol _*) = e -InexactNumber' = new Type of Nothing' +RingFamily_* = (CC,e) -> CC#(symbol _*) = e +RingFamily_* = (CCi,e) -> CCi#(symbol _*) = e +InexactNumber' = new Type of Number RR_* = RR' = new Type of InexactNumber' RRi_* = RRi' = new Type of InexactNumber' CC_* = CC' = new Type of InexactNumber' +CCi_* = CCi' = new Type of InexactNumber' RR'.texMath = ///{\mathbb R}_*/// RRi'.texMath = ///{\square\mathbb R}_*/// CC'.texMath = ///{\mathbb C}_*/// +CCi'.texMath = ///{\square\mathbb C}_*/// setAttribute(CC',PrintNet,"CC" | "*"^-1) +setAttribute(CCi',PrintNet,"CCi" | "*"^-1) setAttribute(RR',PrintNet,"RR" | "*"^-1) setAttribute(RRi',PrintNet,"RRi" | "*"^-1) setAttribute(CC',PrintNames,"CC_*") +setAttribute(CCi',PrintNames,"CCi_*") setAttribute(RR',PrintNames,"RR_*") setAttribute(RRi',PrintNames,"RRi_*") setAttribute(InexactNumber',PrintNet,"InexactNumber" | "*"^-1) @@ -62,8 +72,9 @@ protect back RR'.back = RR RRi'.back = RRi CC'.back = CC -new RealField of Nothing' from ZZ := memoize ( - (RealField,Nothing',prec) -> newClass(RealField,Nothing', +CCi'.back = CCi +new RealField of Number from ZZ := memoize ( + (RealField,Number,prec) -> newClass(RealField,Number, hashTable { symbol precision => prec, symbol Engine => true, @@ -71,8 +82,8 @@ new RealField of Nothing' from ZZ := memoize ( symbol isBasic => true, symbol RawRing => rawRR prec })) -new ComplexField of Nothing' from ZZ := memoize( - (ComplexField,Nothing',prec) -> newClass(ComplexField,Nothing', +new ComplexField of Number from ZZ := memoize( + (ComplexField,Number,prec) -> newClass(ComplexField,Number, hashTable { symbol precision => prec, symbol Engine => true, @@ -80,15 +91,24 @@ new ComplexField of Nothing' from ZZ := memoize( symbol baseRings => {ZZ,QQ,RR_prec}, symbol RawRing => rawCC prec })) -new RealIntervalField of Nothing' from ZZ := memoize ( - (RealIntervalField,Nothing',prec) -> newClass(RealIntervalField,Nothing', +new RealIntervalField of Number from ZZ := memoize ( + (RealIntervalField,Number,prec) -> newClass(RealIntervalField,Number, hashTable { symbol precision => prec, symbol Engine => true, - symbol baseRings => {ZZ,QQ}, + symbol baseRings => {ZZ,QQ,RR_prec}, symbol isBasic => true, symbol RawRing => rawRRi prec })) +new ComplexIntervalField of Number from ZZ := memoize ( + (ComplexIntervalField,Number,prec) -> newClass(ComplexIntervalField,Number, + hashTable { + symbol precision => prec, + symbol Engine => true, + symbol baseRings => {ZZ,QQ,RR_prec,CC_prec,RRi_prec}, + symbol isBasic => true, + symbol RawRing => rawCCi prec + })) precision InexactField := R -> R.precision InexactFieldFamily _ ZZ := (T,prec) -> new T.InexactField of T#(symbol _*) from prec -- oops... default InexactFieldFamily := R -> R_defaultPrecision @@ -96,14 +116,18 @@ default InexactFieldFamily := R -> R_defaultPrecision diameter' = diameter diameter = method() diameter RRi := diameter' +diameter CCi := diameter' -- lift and promote between real or complex rings promote(RawRingElement,RR') := (x,R) -> new RR from x promote(RawRingElement,RRi') := (x,R) -> new RRi from x promote(RawRingElement,CC') := (x,R) -> new CC from x +promote(RawRingElement,CCi') := (x,R) -> new CCi from x promote(RawRingElement,Number) := (x,R) -> new R from x promote(RawRingElement,RingElement) := (x,R) -> new R from x -promote(Number,InexactNumber) := (x,RR) -> promote(x,default RR) +promote(QQ,RR) := +promote(ZZ,RR) := (x,RR) -> promote(x,default RR) +promote(Number,CC) := (x,CC) -> promote(x,default CC) promote(ZZ,RR') := promote(QQ,RR') := promote(RR,RR') := (i,K) -> toRR(K.precision,i) @@ -115,20 +139,52 @@ promote(ZZ,RRi') := promote(QQ,RRi') := promote(RR,RRi') := (i,K) -> toRRi(K.precision,i,i) promote(RRi,RRi') := (i,K) -> toRRi(K.precision,left(i),right(i)) -lift(Number,InexactNumber) := opts -> (x,RR) -> lift(x,default RR,opts) +promote(ZZ,CCi') := +promote(QQ,CCi') := (i,K) -> toCCi(toRRi(i),interval 0) +promote(RR,CCi') := (i,K) -> toCCi(toRRi(precision i,i,i),toRRi(precision i, 0,0)) +promote(RRi,CCi') := (i,K) -> toCCi(i, interval 0) +promote(CC,CCi') := (i,K) -> toCCi(toRRi(precision i,realPart i,realPart i),toRRi(precision i, imaginaryPart i, imaginaryPart i)) +promote(CCi,CCi') := (i,K) -> toCCi(realPart i, imaginaryPart i) -- this should be fixed +lift(RingElement, InexactNumber) := +lift(Number, InexactNumber) := opts -> (x, K) -> lift(x, default K, opts) liftable(Number,InexactNumber) := (x,RR) -> liftable(x,default RR) -liftable(CC,RR'):= (z,RR) -> imaginaryPart z == 0 -lift(CC,RR'):= opts -> (z,RR) -> ( - if imaginaryPart z == 0 then realPart z - else if opts.Verify then error "lift: complex number is not real" - ) - -liftable(RRi,RR) := (z,RR) -> diameter(z) == 0 -lift(RRi,RR') := opts -> (r,RR) -> ( - if diameter(r) == 0 then lift(midpoint(r),RR) - else if opts.Verify then error "lift: interval has positive diameter" -) +liftable(CC, RR') := +liftable(CC, RRi') := +liftable(CCi, RRi') := (x, K) -> imaginaryPart x == 0 +liftable(RRi, RR') := +liftable(CCi, CC') := (x, K) -> diameter x == 0 +liftable(CCi, RR') := (z, K) -> ( + imaginaryPart z == 0 and diameter realPart z == 0) + +lift(RR, RR') := opts -> (x, K) -> numeric(precision K, x) +lift(RRi, RR') := opts -> (x, K) -> ( + if liftable(x, K) then lift(midpoint x, K) + else if opts.Verify then error "lift: interval has positive diameter") +lift(CC, RR') := opts -> (x, K) -> ( + if liftable(x, K) then lift(realPart x, K) + else if opts.Verify then error "lift: complex number is not real") +lift(CCi,RR') := opts -> (x, K) -> ( + if liftable(x, K) then lift(midpoint realPart x, K) + else if opts.Verify then error "lift: complex interval not a real number") + +lift(CC, CC') := opts -> (x, K) -> ( + toCC(precision K, realPart x, imaginaryPart x)) +lift(CCi, CC') := opts -> (x, K) -> ( + if liftable(x, K) + then toCC(precision K, midpoint realPart x, midpoint imaginaryPart x) + else if opts.Verify then error "lift: interval has positive diameter") + +lift(RRi, RRi') := opts -> (x, K) -> toRRi(precision K, left x, right x) +lift(CC, RRi') := opts -> (x, K) -> ( + if liftable(x, K) then toRRi(precision K, realPart x, realPart x) + else if opts.Verify then error "lift: complex number is not real") +lift(CCi, RRi') := opts -> (x, K) -> ( + if liftable(x, K) then toRRi(precision K, left realPart x, right realPart x) + else if opts.Verify then error "lift: complex interval is not real") + +lift(CCi, CCi') := opts -> (x, K) -> ( + toCCi(precision K, realPart x, imaginaryPart x)) -- lift and promote to and from other rings @@ -138,8 +194,10 @@ numeric Number := x -> numeric(defaultPrecision, x) numeric CC := identity numeric RR := identity numeric RRi := identity +numeric CCi := identity numeric(ZZ,Number) := toRR numeric(ZZ,RRi) := (prec,x) -> toRRi(prec,left(x),right(x)) +numeric(ZZ,CCi) := (prec,x) -> toCCi(prec,realPart(x),imaginaryPart(x)) numeric(ZZ,CC) := toCC infty := prec -> 1/toRR(prec,0) numeric InfiniteNumber := infinity -> infinity#0 * infty defaultPrecision @@ -156,7 +214,17 @@ ZZ _ ComplexField := QQ _ ComplexField := RR _ ComplexField := CC _ ComplexField := (x,R) -> toCC(R.precision,x) - +ZZ _ ComplexIntervalField := +QQ _ ComplexIntervalField := +RR _ ComplexIntervalField := +RRi _ ComplexIntervalField := (x,R) -> toCCi(R.precision,x, interval 0) +CC _ ComplexIntervalField := (x,R) -> toCCi(R.precision,x) +CCi _ ComplexIntervalField := (x,R) -> toCCi(R.precision,x) + +internalRepresentation = z -> if z === 0. then 0/1 else if isFinite z then ( + (prec,sgn,expt,m,numbits) := partsRR z; + sgn * m / 2^(numbits - expt) + ) lift(RR,QQ) := opts -> (r,QQ) -> ( if r == 0 then return 0/1; r' := r; @@ -172,7 +240,7 @@ lift(RR,QQ) := opts -> (r,QQ) -> ( d := m_(1,0); q := n / d; if r === numeric(p,q) then return q; - if r' == 0 or abs(n*d) > p2 then return promote(r,QQ); + if r' == 0 or abs(n*d) > p2 then return internalRepresentation r; r' = 1/r' ; )) lift(RR,ZZ) := opts -> (r,ZZ) -> ( @@ -180,31 +248,41 @@ lift(RR,ZZ) := opts -> (r,ZZ) -> ( if r == i then i else if opts.Verify then error "lift: real number is not integer") lift(CC,QQ) := lift(CC,ZZ) := opts -> (z,R) -> ( - if imaginaryPart z == 0 then lift(realPart z, R) + if imaginaryPart z == 0 then lift(realPart z, R, opts) else if opts.Verify then error "lift: complex number not real" ) -promote(RR,QQ) := (z,QQ) -> if z === 0. then 0/1 else if isFinite z then ( - (prec,sgn,expt,m,numbits) := partsRR z; - sgn * m / 2^(numbits - expt) - ) else error "promote(RR,QQ): non-finite number encountered" liftable(RRi,QQ) := (z,RR) -> diameter(z) == 0 -liftable(RRi,ZZ) := (z,RR) -> diameter(z) == 0 lift(RRi,QQ) := opts -> (r,QQ) -> ( if diameter(r) == 0 then lift(midpoint(r),QQ) else if opts.Verify then error "lift: interval has positive diameter" ) lift(RRi,ZZ) := opts -> (r,ZZ) -> ( - if diameter(r) == 0 then lift(midpoint(r),ZZ) + if diameter(r) == 0 then lift(midpoint(r),ZZ, opts) else if opts.Verify then error "lift: interval has positive diameter" ) +liftable(CCi, QQ) := (x, K) -> imaginaryPart x == 0 and diameter x == 0 +lift(CCi, QQ) := opts -> (x, K) -> ( + if liftable(x, K) then lift(realPart x, K) + else if opts.Verify then error "lift: complex interval not rational number") +lift(CCi, ZZ) := opts -> (x, R) -> ( + if liftable(x, QQ) then lift(midpoint realPart x, ZZ, opts) + else if opts.Verify then error "lift: complex interval not an integer") + +lift(Constant, Number) := opts -> (x, R) -> ( + lift(numeric(precision R, x), R, opts)) +lift(Constant, InexactNumber) := opts -> (x, R) -> ( + lift(numeric(precision default R, x), default R, opts)) + ring RR := x -> new RealField of RR' from precision x ring RRi := x -> new RealIntervalField of RRi' from precision x ring CC := x -> new ComplexField of CC' from precision x +ring CCi := x -> new ComplexIntervalField of CCi' from precision x new RR from RawRingElement := (RRR,x) -> ( assert( RRR === RR ); rawToRR x) new RRi from RawRingElement := (RRRi,x) -> ( assert( RRRi === RRi ); rawToRRi x) new CC from RawRingElement := (CCC,x) -> ( assert( CCC === CC ); rawToCC x) +new CCi from RawRingElement := (CCCi,x) -> ( assert( CCCi === CCi ); rawToCCi x) -- arithmetic operations @@ -236,7 +314,32 @@ imaginaryPart QQ := imaginaryPart InexactNumber := imaginaryPart0 imaginaryPart Number := imaginaryPart0 @@ numeric +left Number := identity +left RRi := left0 +left CCi := x -> error "use lowerLeft or upperLeft" + +right Number := identity +right RRi := right0 +right CCi := x -> error "use lowerRight or upperRight" + +lowerLeft Number := identity +lowerLeft RRi := left +lowerLeft CCi := z -> (left realPart z) + (left imaginaryPart z)*ii + +lowerRight Number := identity +lowerRight RRi := right +lowerRight CCi := z -> (right realPart z) + (left imaginaryPart z)*ii + +upperLeft Number := identity +upperLeft RRi := left +upperLeft CCi := z -> (left realPart z) + (right imaginaryPart z)*ii + +upperRight Number := identity +upperRight RRi := right +upperRight CCi := z -> (right realPart z) + (right imaginaryPart z)*ii + conjugate CC := z -> toCC(precision z, realPart z, - imaginaryPart z) +conjugate CCi := z -> toCCi(precision z, realPart z, - imaginaryPart z) conjugate Constant := conjugate @@ numeric isConstant Number := i -> true @@ -259,6 +362,8 @@ random RR := RR => opts -> x -> x * rawRandomRRUniform precision x random(RR,RR) := opts -> (x,y) -> x + random(y-x) RR'.random = opts -> R -> rawRandomRRUniform R.precision CC'.random = opts -> C -> rawRandomCC C.precision +RRi'.random = opts -> R -> rawRandomRRi R.precision +CCi'.random = opts -> C -> rawRandomCCi C.precision random RingFamily := opts -> R -> random(default R,opts) random QQ := QQ => opts -> x -> rawFareyApproximation( @@ -266,7 +371,7 @@ random QQ := QQ => opts -> x -> rawFareyApproximation( -- algebraic operations and functions -RR.isBasic = CC.isBasic = RRi.isBasic = true +RR.isBasic = CC.isBasic = RRi.isBasic = CCi.isBasic = true Thing ** InexactFieldFamily := (X,T) -> X ** default T @@ -283,7 +388,7 @@ char InexactField := R -> 0 pi = new Constant from { symbol pi, pi0, piRRi0 } EulerConstant = new Constant from { symbol EulerConstant, mpfrConstantEuler, eRRi0} CatalanConstant = new Constant from { symbol CatalanConstant, mpfrConstantCatalan, cRRi0} -ii = new Constant from { symbol ii, ConstantII} +ii = new Constant from { symbol ii, ConstantII, p -> toCCi(p, 0, 1)} ring Constant := ring @@ numeric promote(Constant, InexactNumber') := @@ -298,7 +403,7 @@ lngamma RR := x -> ( if s == -1 then y + ii * numeric_(precision y) pi else y ) lngamma Number := lngamma @@ numeric -lngamma RRi := lngamma CC := lgamma +lngamma RRi := lngamma CC := lngamma CCi := lgamma expression Constant := hold toString Constant := net Constant := c -> toString c#0 @@ -361,8 +466,8 @@ Constant _ Ring := (c,R) -> ( Number _ RingFamily := Constant _ RingFamily := (x, R) -> x_(default R) -- TODO: find examples, or remove -Number ^ RingFamily := -Constant ^ RingFamily := (x, R) -> lift(x, default R) -- TODO: set Verify => false? +Number ^ RingFamily := +RingElement ^ RingFamily := (x, R) -> lift(x, default R) -- TODO: set Verify => false? Constant + Number := (c,x) -> numeric c + x Number + Constant := (x,c) -> x + numeric c @@ -385,13 +490,10 @@ Constant ! := c -> (numeric c)! -- printing -toString RealField := R -> concatenate("RR_",toString R.precision) -toString RealIntervalField := R -> concatenate("RRi_",toString R.precision) -toString ComplexField := R -> concatenate("CC_",toString R.precision) +toString InexactField := R -> concatenate( + toString (parent R).back, "_", toString R.precision) +expression InexactField := R -> Subscript((parent R).back, R.precision) -expression RealField := R -> new Subscript from {symbol RR, R.precision} -expression RealIntervalField := R -> new Subscript from {symbol RRi, R.precision} -expression ComplexField := R -> new Subscript from {symbol CC, R.precision} expression RR := x -> ( if x < 0 then ( @@ -450,6 +552,7 @@ InexactNumber#AfterPrint = x -> (class x," (of precision ",precision x,")") isReal = method() isReal RRi := isReal RR := isReal QQ := isReal ZZ := x -> true isReal CC := z -> imaginaryPart z == 0 +isReal CCi := z -> imaginaryPart z == 0 isReal Constant := isReal @@ numeric isReal InfiniteNumber := x -> false @@ -480,6 +583,10 @@ BesselY = method() BesselY(ZZ, Number) := (n, x) -> BesselY'(n, numeric x) BesselY(Number, Number) := (n, x) -> BesselY'(numeric n, numeric x) +ring ComplexField := R -> CC +ring RealField := R -> RR +ring RealIntervalField := R -> RRi + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/ringmap.m2 b/M2/Macaulay2/m2/ringmap.m2 index 2f573c3836b..c6a93b8b2e5 100644 --- a/M2/Macaulay2/m2/ringmap.m2 +++ b/M2/Macaulay2/m2/ringmap.m2 @@ -53,6 +53,8 @@ Ring#id = R -> map(R, R, vars R) map(RingFamily, Thing, Thing) := RingMap => opts -> (R, S, m) -> map(default R, S, m, opts) map(Thing, RingFamily, Thing) := RingMap => opts -> (R, S, m) -> map(R, default S, m, opts) +map(RingFamily, Thing) := RingMap => opts -> (R, S) -> map(default R, S, opts) +map(Thing, RingFamily) := RingMap => opts -> (R, S) -> map(R, default S, opts) map(Ring, Ring) := RingMap => opts -> (R, S ) -> map(R, S, matrix(R, {{}}), opts) map(Ring, Ring, RingMap) := RingMap => opts -> (R, S, f) -> map(R, S, matrix f, opts) @@ -317,7 +319,7 @@ algorithms#(kernel, RingMap) = new MutableHashTable from { assert (not chh or G#?"rawGBSetHilbertFunction log"); -- ensure the Hilbert function hint was actually used in gb.m2 ideal mapback selectInSubring(1,generators G) ), - + ZZ => (opts, f) -> if source f === ZZ then ideal char target f, Default => (opts, f) -> ( (F, R) := (target f, source f); numsame := 0; @@ -342,7 +344,7 @@ algorithms#(kernel, RingMap) = new MutableHashTable from { } -- Installing hooks for kernel RingMap -scan({Default, "AffineRing", FractionField}, strategy -> +scan({Default, ZZ, "AffineRing", FractionField}, strategy -> addHook(key := (kernel, RingMap), algorithms#key#strategy, Strategy => strategy)) ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/m2/rings.m2 b/M2/Macaulay2/m2/rings.m2 index 61c16bf8068..10494b50119 100644 --- a/M2/Macaulay2/m2/rings.m2 +++ b/M2/Macaulay2/m2/rings.m2 @@ -68,6 +68,7 @@ ZZ.isCommutative = true QQ.isCommutative = true RR.isCommutative = true RRi.isCommutative = true +CCi.isCommutative = true isRing = method(TypicalValue => Boolean) isRing Thing := R -> false @@ -102,12 +103,15 @@ isConstant = method(TypicalValue => Boolean) isConstant RingElement := r -> liftable(r, coefficientRing ring r) lift = method(Dispatch => {Thing, Type, Type}, Options => {Verify => true}) -Number ^ Ring := lift +Number ^ Ring := RingElement ^ Ring := lift promote = method(Dispatch => {Thing, Type, Type}) Number _ Ring := promote -isPromotable = (R,S) -> lookup(promote,R,S) =!= null +isPromotable = method(TypicalValue => Boolean) +isPromotable(RingFamily,RingFamily) := +isPromotable(RingFamily,Ring) := +isPromotable(Ring,Ring) := (R,S) -> lookup(promote,R,S) =!= null -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " diff --git a/M2/Macaulay2/m2/robust.m2 b/M2/Macaulay2/m2/robust.m2 index 8ea24fe3e6b..9c06bc4e4b3 100644 --- a/M2/Macaulay2/m2/robust.m2 +++ b/M2/Macaulay2/m2/robust.m2 @@ -4,8 +4,6 @@ needs "lists.m2" needs "max.m2" needs "nets.m2" -simpleToString := toString - timelimit := (t,f) -> (alarm t; r := f(); alarm 0; r) printingTimeLimit = 20 @@ -37,8 +35,6 @@ String.Format = format silentRobustNet = (wid,ht,sec,y) -> ( truncNet(wid,ht, try timelimit (sec, () -> checkNet if lookup(symbol Format,class y) =!= null then (lookup(symbol Format,class y)) y else net y) - else - try timelimit (sec, () -> checkString toExternalString y) else ( alarm 0; simpleToString y) @@ -58,9 +54,12 @@ silentRobustStringWithClass = (wid,sec,y) -> ( part1 := silentRobustString(wid - width part2,sec, y); concatenate(part1, part2)); + +-- mainly called by processErrorArgs0 in debugging.dd to print errors +Thing.RobustPrintStringMethod = obj -> try silentRobustString(40, 3, obj) + -- mainly called by KeyNotFound in hashtables.dd to print missing key errors -Thing.RobustPrintMethod = (msg, obj) -> try toString stack { msg | ":", - horizontalJoin("\t", silentRobustNetWithClass(60, 5, 3, obj)) } else msg +Thing.RobustPrintNetMethod = obj -> try silentRobustNetWithClass(60, 5, 3, obj) hush := false scan(flexibleBinaryOperators, op -> ( diff --git a/M2/Macaulay2/m2/set.m2 b/M2/Macaulay2/m2/set.m2 index 3e48efe5013..57ef4c56389 100644 --- a/M2/Macaulay2/m2/set.m2 +++ b/M2/Macaulay2/m2/set.m2 @@ -94,7 +94,8 @@ new Set from VisibleList := Set => (Set, X) -> set' X -- set operations elements Set := List => keys -installMethod(union, () -> set {}) +union() := () -> set {} +union Set := identity union(Set, Set) := Set + Set := Set => (x,y) -> merge(x,y,(i,j)->i) -- Set ++ Set := Set => (x,y) -> applyKeys(x,i->(0,i)) + applyKeys(y,j->(1,j)) @@ -105,11 +106,12 @@ Set * Set := Set => (x,y) -> ( then set select(keys x, k -> y#?k) else set select(keys y, k -> x#?k) ) +intersect Set := Set => {} >> o -> identity intersect(Set, Set) := Set => {} >> o -> (x,y) -> x*y Set - Set := Set => (x,y) -> applyPairs(x, (i,v) -> if not y#?i then (i,v)) -List - Set := List => (x,y) -> select(x, i -> not y#?i) -Set - List := Set => (x,y) -> x - set y +VisibleList - Set := List => (x,y) -> select(x, i -> not y#?i) +Set - VisibleList := Set => (x,y) -> x - set y -- sum Set := s -> sum toList s diff --git a/M2/Macaulay2/m2/startup.m2.in b/M2/Macaulay2/m2/startup.m2.in index c2bf8faf54b..b369d759eec 100644 --- a/M2/Macaulay2/m2/startup.m2.in +++ b/M2/Macaulay2/m2/startup.m2.in @@ -4,6 +4,24 @@ -- this file gets incorporated into the executable file bin/M2 as the string 'startupString' -- we want to ignore the --datarootdir setting, whatever that means, so here we we mention it: @datarootdir@ +-------------------------------------------------------------------------------- + +interpreter := () -> ( + loadDepth = errorDepth = 3; + n := commandInterpreter(); + if class n === ZZ and 0 <= n and n < 128 then exit n; + if n === null then exit 0; + debuggingMode = false; + stopIfError = true; + stderr << "error: can't interpret return value as an exit code" << endl; + exit 1) + +-- false if we return after an abort +firstTime := class path === Symbol +if not firstTime then interpreter() + +-------------------------------------------------------------------------------- + interpreterDepth = loadDepth = errorDepth = 0 debuggingMode = true stopIfError = false @@ -16,8 +34,6 @@ if gotarg "--notify" then notify = true if gotarg "--stop" then stopIfError = true if gotarg "--int" then handleInterrupts = false -firstTime := class PackageDictionary === Symbol - if firstTime then ( -- we do this bit *before* "debug Core", so that Core (the symbol, not the package), -- which may not be there yet, ends up in the right dictionary @@ -35,7 +51,6 @@ if firstTime then ( ) -- we can't make this an else-clause, because then "Core" will be in the wrong dictionary disassemble ( () -> debug Core ) -if not firstTime then debug Core -- we need access to the private symbols (we remove the Core private dictionary later.) toString := value' getGlobalSymbol if firstTime then "simpleToString" else "toString" @@ -345,7 +360,6 @@ matchpart := (pat,i,s) -> substring_((regex(pat, s))#i) s notdir := s -> matchpart("[^/]*$",0,s) nocore = false noinitfile = false -interpreter := commandInterpreter M2version := () -> ( initcurrentlayout(); @@ -562,9 +576,9 @@ if firstTime then processCommandLineOptions 1 srcversion = M2version() if firstTime and not nobanner then ( - if topLevelMode === TeXmacs then stderr << TeXmacsBegin << "verbatim:"; - stderr << "Macaulay2, version " << srcversion << newline << flush; - if topLevelMode === TeXmacs then stderr << TeXmacsEnd << flush) + if topLevelMode === TeXmacs then << TeXmacsBegin << "verbatim:"; + << "Macaulay2, version " << srcversion << newline << flush; + if topLevelMode === TeXmacs then << TeXmacsEnd << flush) scan(commandLine, arg -> if arg === "-q" then noinitfile = true) homeDirectory = getenv "HOME" | "/" @@ -618,19 +632,8 @@ loadCore = path -> ( (core "runStartFunctions")() -loadDepth = 0; -( loadDepth = 3; - processCommandLineOptions 4; - n := 0; - errorDepth = loadDepth = 3; - n = interpreter(); - if class n === ZZ and 0 <= n and n < 128 then exit n; - if n === null then exit 0; - debuggingMode = false; - stopIfError = true; - stderr << "error: can't interpret return value as an exit code" << endl; - exit 1; - ) +(loadDepth = 3; processCommandLineOptions 4; interpreter()) + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/d startup.m2 all && make -C $M2BUILDDIR/Macaulay2/bin all " -- End: diff --git a/M2/Macaulay2/m2/testing.m2 b/M2/Macaulay2/m2/testing.m2 index 3dde85e6f51..bf890379bee 100644 --- a/M2/Macaulay2/m2/testing.m2 +++ b/M2/Macaulay2/m2/testing.m2 @@ -83,7 +83,10 @@ loadTestDir := pkg -> ( tests = method() tests Package := pkg -> ( - if pkg#?"documentation not loaded" then pkg = loadPackage(pkg#"pkgname", LoadDocumentation => true, Reload => true); + if pkg#?"documentation not loaded" + then pkg = loadPackage(pkg, + FileName => pkg#"source file", + LoadDocumentation => true); if not pkg#?"test directory loaded" then loadTestDir pkg; previousMethodsFound = new NumberedVerticalList from pkg#"test inputs" ) diff --git a/M2/Macaulay2/m2/texmacs.m2 b/M2/Macaulay2/m2/texmacs.m2 index 446f4c17597..dbf8153ce66 100644 --- a/M2/Macaulay2/m2/texmacs.m2 +++ b/M2/Macaulay2/m2/texmacs.m2 @@ -20,6 +20,7 @@ Thing#{TeXmacs,Print} = send := v -> ( << tmhtml fixn mathMode mtable {{po(), red "=",fmt v}}) Nothing#{TeXmacs,Print} = identity InexactNumber#{TeXmacs,Print} = v -> withFullPrecision ( () -> send v ) +Hypertext#{TeXmacs, Print} = v -> ( << tmhtml fixn html v ) tmAfterPrint = x -> ( << endl diff --git a/M2/Macaulay2/m2/threads.m2 b/M2/Macaulay2/m2/threads.m2 new file mode 100644 index 00000000000..2af6a95ca96 --- /dev/null +++ b/M2/Macaulay2/m2/threads.m2 @@ -0,0 +1,55 @@ +----------------------------------------------------------------------------- +-- AtomicInt +----------------------------------------------------------------------------- + +AtomicInt.synonym = "atomic integer" + +scan({symbol +=, symbol -=, symbol &=, symbol |=, symbol ^^=}, + op -> typicalValues#(op, AtomicInt) = ZZ) + +store = method() +store(AtomicInt, ZZ) := atomicStore + +exchange = method() +exchange(AtomicInt, ZZ) := atomicExchange + +compareExchange = method() +compareExchange(AtomicInt, ZZ, ZZ) := atomicCompareExchange + +----------------------------------------------------------------------------- +-- Mutex +----------------------------------------------------------------------------- + +Mutex.synonym = "mutex" +globalAssignment Mutex +net Mutex := x -> toString ( + if hasAttribute(x, ReverseDictionary) + then getAttribute(x, ReverseDictionary) + else x) + +lock = method() +lock Mutex := lock0 + +tryLock = method() +tryLock Mutex := tryLock0 + +unlock = method() +unlock Mutex := unlock0 + +----------------------------------------------------------------------------- + +parallelApplyRaw = (L, f) -> + -- 'reverse's to minimize thread switching in 'taskResult's: + reverse (taskResult \ reverse apply(L, e -> schedule(f, e))); +parallelApply = method(Options => {Strategy => null}) +parallelApply(BasicList, Function) := o -> (L, f) -> ( + if o.Strategy === "raw" then return parallelApplyRaw(L, f); + n := #L; + numThreads := min(n + 1, maxAllowableThreads); + oldAllowableThreads := allowableThreads; + if allowableThreads < numThreads then allowableThreads = numThreads; + numChunks := 3 * numThreads; + res := if n <= numChunks then toList parallelApplyRaw(L, f) else + flatten parallelApplyRaw(pack(L, ceiling(n / numChunks)), chunk -> apply(chunk, f)); + allowableThreads = oldAllowableThreads; + res); diff --git a/M2/Macaulay2/m2/typicalvalues.m2 b/M2/Macaulay2/m2/typicalvalues.m2 index 76256290af4..01e36736981 100644 --- a/M2/Macaulay2/m2/typicalvalues.m2 +++ b/M2/Macaulay2/m2/typicalvalues.m2 @@ -126,11 +126,11 @@ generateTypicalValues = (srcdir) -> ( -- if missing or not successfully generated, tvalues.m2 is regenerated directly if not fileExists typicalValuesSource or not match("-- DONE", get typicalValuesSource) -then generateTypicalValues(currentFileDirectory | "../d/") +then generateTypicalValues(topSrcdir | "Macaulay2/d/") ----------------------------------------------------------------------------- -- numerical functions that will be wrapped -redefs := hashTable apply({acos, agm, asin, atan, atan2, Beta, cos, cosh, cot, coth, csc, csch, Digamma, eint, erf, erfc, exp, expm1, Gamma, inverseErf, inverseRegularizedBeta, inverseRegularizedGamma, log, log1p, regularizedBeta, regularizedGamma, sec, sech, sin, sinh, sqrt, tan, tanh, zeta}, +redefs := hashTable apply({acos, agm, asin, atan, atan2, Beta, cos, cosh, cot, coth, csc, csch, Digamma, eint, erf, erfc, exp, expm1, Gamma, inverseErf, inverseRegularizedBeta, inverseRegularizedGamma, log, log1p, polylog, regularizedBeta, regularizedGamma, sec, sech, sin, sinh, sqrt, tan, tanh, zeta}, f -> f => method()); variants := new MutableHashTable; @@ -152,35 +152,35 @@ typval = x -> ( f := redefs#f'; args := drop(drop(x,-1),1); installMethod append(prepend(f,args),last x => f'); - if args === sequence RR then variants#(f,Number) = f' @@ numeric + if args === sequence InexactNumber then variants#(f,Number) = f' @@ numeric else if #args === 2 then ( - if args#0 === RR then variants#(f,Number,args#1) = (x,y) -> f'(numeric_(precision y) x,y); - if args#1 === RR then variants#(f,args#0,Number) = (x,y) -> f'(x,numeric_(precision x) y); - if args === (RR,RR) then variants#(f,Number,Number) = (x,y) -> f'(numeric x,numeric y); -- phew + if args#0 === InexactNumber then variants#(f,Number,args#1) = (x,y) -> f'(numeric_(precision y) x,y); + if args#1 === InexactNumber then variants#(f,args#0,Number) = (x,y) -> f'(x,numeric_(precision x) y); + if args === (InexactNumber,InexactNumber) then variants#(f,Number,Number) = (x,y) -> f'(numeric x,numeric y); -- phew ) else if #args === 3 then ( - if args#0 === RR then + if args#0 === InexactNumber then variants#(f, Number, args#1, args#2) = (x,y,z) -> f'(numeric(min(precision y, precision z), x), y, z); - if args#1 === RR then + if args#1 === InexactNumber then variants#(f, args#0, Number, args#2) = (x,y,z) -> f'(x, numeric(min(precision x, precision z), y), z); - if args#2 === RR then + if args#2 === InexactNumber then variants#(f, args#0, args#1, Number) = (x,y,z) -> f'(x, y, numeric(min(precision x, precision y), z)); - if args#0 === RR and args#1 === RR then + if args#0 === InexactNumber and args#1 === InexactNumber then variants#(f, Number, Number, args#2) = (x,y,z) -> f'(numeric_(precision z) x, numeric_(precision z) y, z); - if args#0 === RR and args#2 === RR then + if args#0 === InexactNumber and args#2 === InexactNumber then variants#(f, Number, args#1, Number) = (x,y,z) -> f'(numeric_(precision y) x, y, numeric_(precision y) z); - if args#1 === RR and args#2 === RR then + if args#1 === InexactNumber and args#2 === InexactNumber then variants#(f, args#0, Number, Number) = (x,y,z) -> f'(x, numeric_(precision x) y, numeric_(precision x) z); - if args === (RR, RR, RR) then + if args === (InexactNumber, InexactNumber, InexactNumber) then variants#(f, Number, Number, Number) = (x,y,z) -> f'(numeric x, numeric y, numeric z); ); diff --git a/M2/Macaulay2/man/Makefile.in b/M2/Macaulay2/man/Makefile.in index d2aaff4f666..f1d0f31f0aa 100644 --- a/M2/Macaulay2/man/Makefile.in +++ b/M2/Macaulay2/man/Makefile.in @@ -13,4 +13,3 @@ distclean: clean; rm -f M2.1 # Local Variables: # compile-command: "make -C $M2BUILDDIR/Macaulay2/man " # End: -.NOTPARALLEL: diff --git a/M2/Macaulay2/packages/=distributed-packages b/M2/Macaulay2/packages/=distributed-packages index 2e978e20420..cbca486e8bd 100644 --- a/M2/Macaulay2/packages/=distributed-packages +++ b/M2/Macaulay2/packages/=distributed-packages @@ -38,7 +38,6 @@ Varieties Schubert2 PushForward LocalRings -PruneComplex BoijSoederberg BGG Bruns @@ -146,8 +145,6 @@ RandomPoints SwitchingFields SpectralSequences SectionRing -OldPolyhedra -OldToricVectorBundles K3Carpets ChainComplexOperations NumericalCertification @@ -182,7 +179,6 @@ GroebnerWalk RandomMonomialIdeals Matroids NumericalImplicitization -NonminimalComplexes CoincidentRootLoci RelativeCanonicalResolution RandomCurvesOverVerySmallFiniteFields @@ -290,5 +286,11 @@ AllMarkovBases Tableaux CpMackeyFunctors JSONRPC +SimplicialModules MatrixFactorizations PathSignatures +MacaulayPosets +MRDI +EliminationTemplates +WittVectors +Padic diff --git a/M2/Macaulay2/packages/A1BrouwerDegrees.m2 b/M2/Macaulay2/packages/A1BrouwerDegrees.m2 index eb0869d8afb..c8b3b3178bb 100644 --- a/M2/Macaulay2/packages/A1BrouwerDegrees.m2 +++ b/M2/Macaulay2/packages/A1BrouwerDegrees.m2 @@ -213,7 +213,7 @@ Node a package for working with A1-Brouwer degree computations and quadratic forms Description Text - This package is intended to allow the computation of local and global A1-Brouer degrees in both the stable and unstable settings, and for manipulations of unstable Grothendieck-Witt classes and symmetric bilinear forms including their invariants and decompositions. + This package is intended to allow the computation of local and global A1-Brouwer degrees in both the stable and unstable settings, and for manipulations of unstable Grothendieck-Witt classes and symmetric bilinear forms including their invariants and decompositions. Version history: @@ -256,6 +256,21 @@ Node getSumDecomposition beta gamma = makeGWuClass(matrix(QQ, {{11, 0},{0,22}}), 8) isIsomorphicForm(beta, gamma) + Citation + @article{zbMATH08005952, + author = {Borisov, Nikita and Brazelton, Thomas and Espino, Frenly and Hagedorn, Thomas and Han, Zhaobo and Lopez Garcia, Jordy and Louwsma, Joel and Ong, Wern Juin Gabriel and Tawfeek, Andrew R.}, + title = {{{\(\mathbb{A}^1\)}}-Brouwer degrees in {Macaulay2}}, + fjournal = {The Journal of Software for Algebra and Geometry}, + journal = {J. Softw. Algebra Geom.}, + issn = {1948-7916}, + volume = {14}, + pages = {175--187}, + year = {2024}, + language = {English}, + doi = {10.2140/jsag.2024.14.175}, + keywords = {14F42,55M25,68W30,11E04,14N10}, + zbMATH = {8005952} + } /// load "./A1BrouwerDegrees/Documentation/ArithmeticMethodsDoc.m2" diff --git a/M2/Macaulay2/packages/A1BrouwerDegrees/Code/UnstableLocalGlobalDegrees.m2 b/M2/Macaulay2/packages/A1BrouwerDegrees/Code/UnstableLocalGlobalDegrees.m2 index 1e1794bd74d..1d7eba9d639 100644 --- a/M2/Macaulay2/packages/A1BrouwerDegrees/Code/UnstableLocalGlobalDegrees.m2 +++ b/M2/Macaulay2/packages/A1BrouwerDegrees/Code/UnstableLocalGlobalDegrees.m2 @@ -294,7 +294,7 @@ getLocalUnstableA1Degree (RingElement, RingElement, RingElement) := (UnstableGro -- If the base field is a finite field, allow the root to be integer, rational, or from the same finite field if instance(kk, GaloisField) and not (ring r === QQ or ring r === ZZ or (instance(ring r, GaloisField) and kk.order == (ring r).order)) then error "root not from the base field of the polynomial"; - -- If we are here, then we have already verified that the base field of f,g is a fintie field and that r is an element of the same finite field, so we can use the two-input method + -- If we are here, then we have already verified that the base field of f,g is a finite field and that r is an element of the same finite field, so we can use the two-input method getLocalUnstableA1Degree(f/g, r) ) diff --git a/M2/Macaulay2/packages/A1BrouwerDegrees/Documentation/AnisotropicDimensionDoc.m2 b/M2/Macaulay2/packages/A1BrouwerDegrees/Documentation/AnisotropicDimensionDoc.m2 index 9520c6d7ba6..84545cd3801 100644 --- a/M2/Macaulay2/packages/A1BrouwerDegrees/Documentation/AnisotropicDimensionDoc.m2 +++ b/M2/Macaulay2/packages/A1BrouwerDegrees/Documentation/AnisotropicDimensionDoc.m2 @@ -42,7 +42,7 @@ doc /// Text By the Witt Decomposition Theorem, any non-degenerate form decomposes uniquely as $\beta \cong n \mathbb{H} \oplus \beta_a$ where the form $\beta_a$ is anisotropic. The rank of $\beta_a$ is called the anisotropic dimension of $\beta$. - The anisotropic dimension of a form defined over the rational numbers is the maximum of the @TO2(getAnisotropicDimensionQQp, "getAnisotropicDimensionQQp")@ anistropic dimension at each of the completions of $\mathbb{Q}$ at the @TO2(getRelevantPrimes, "relevant primes")@. + The anisotropic dimension of a form defined over the rational numbers is the maximum of the @TO2(getAnisotropicDimensionQQp, "getAnisotropicDimensionQQp")@ anisotropic dimension at each of the completions of $\mathbb{Q}$ at the @TO2(getRelevantPrimes, "relevant primes")@. References [KC18] P. Koprowski, A. Czogala, "Computing with quadratic forms over number fields," @ITALIC("Journal of Symbolic Computation")@, 2018. SeeAlso diff --git a/M2/Macaulay2/packages/AInfinity.m2 b/M2/Macaulay2/packages/AInfinity.m2 index 9cc12c749f6..ca8cb2de325 100644 --- a/M2/Macaulay2/packages/AInfinity.m2 +++ b/M2/Macaulay2/packages/AInfinity.m2 @@ -696,7 +696,7 @@ picture Matrix := o -> (M1) -> ( picture Module := o -> M -> picture(id_M,o) picture Complex := o -> C -> netList apply(toList(min C+1..max C), i-> picture(C.dd_i,o)) -picture ChainComplex := o -> C -> netList apply(toList(min C+1..max C), i-> picture(C.dd_i,o)) +--picture ChainComplex := o -> C -> netList apply(toList(min C+1..max C), i-> picture(C.dd_i,o)) flattenBlocks = method() flattenBlocks Module := (F) -> ( @@ -1023,7 +1023,7 @@ golodBetti0 = (F,G,b) ->( mods := apply(symbs, s -> directSum apply(#s, i-> G_(s_i_0)**tensor(ring F, apply(s_i_1, j->F_(j))))); -- was tensorL - betti chainComplex apply(b,i->map(mods_i,mods_(i+1),0)) + betti complex apply(b,i->map(mods_i,mods_(i+1),0)) ) @@ -1283,7 +1283,7 @@ SeeAlso doc /// Key picture - (picture, ChainComplex) +-- (picture, ChainComplex) (picture, Complex) (picture, Matrix) (picture, Module) @@ -1297,7 +1297,7 @@ Usage picture M Inputs F:Complex - F:ChainComplex +-- F:ChainComplex m:Matrix of map between labeled direct sum modules M:Module @@ -1837,15 +1837,15 @@ oo.cache.indices A.cache.indices labeledTensorChainComplex = method() -labeledTensorChainComplex List := ChainComplex => L -> ( +labeledTensorChainComplex List := Complex => L -> ( --L = {C_0..C_(p-1)}, list chain complexes. Form the tensor product of the C_i --in such a way that if the tensor products of the modules (C_i)_m are labeled, --then the modules of the tensor product are direct sums of modules from the hashtable, so that --componentsAndIndices applied to pC gives the correct list of indices, and --thus picture pC.dd_m works. - if class L_0 =!= ChainComplex then error"Input should be a list of ChainComplexes."; + if class L_0 =!= Complex then error"Input should be a list of ChainComplexes."; S := ring L_0; - if #L == 1 and class L_0 === ChainComplex then ( + if #L == 1 and class L_0 === Complex then ( B := L_0; F := for i from min B to max B list labeler({i}, B_i); B' := complex for i from min B to max B -1 list map(F_(i-min B),F_(i+1-min B), B.dd_(i+1)); @@ -1884,7 +1884,7 @@ suitable := v-> if min v == 0 then position (v, vv -> vv == 1) else null; (L_p).dd_(indsrc_p)** tensor(S, apply(#L-p-1, q -> L_(p+q+1)_(indtar_(p+q+1))))) )))))); - (chainComplex d)[-sum(L, ell -> min ell)]) + (complex d)[-sum(L, ell -> min ell)]) B G labeledTensorChainComplex{chainComplex B,chainComplex G} diff --git a/M2/Macaulay2/packages/AbstractToricVarieties.m2 b/M2/Macaulay2/packages/AbstractToricVarieties.m2 index 455061a36e8..8ac7dc31814 100644 --- a/M2/Macaulay2/packages/AbstractToricVarieties.m2 +++ b/M2/Macaulay2/packages/AbstractToricVarieties.m2 @@ -24,7 +24,7 @@ export { "CompleteIntersectionInToric", "Ambient", "CI", - "liftPicToCDiv", -- these might now be in NormalNoricVarieties? + "liftPicToCDiv", -- these might now be in NormalToricVarieties? "liftClToWDiv" } diff --git a/M2/Macaulay2/packages/AdjunctionForSurfaces.m2 b/M2/Macaulay2/packages/AdjunctionForSurfaces.m2 index 16680e5ec43..6bd3663b6c4 100644 --- a/M2/Macaulay2/packages/AdjunctionForSurfaces.m2 +++ b/M2/Macaulay2/packages/AdjunctionForSurfaces.m2 @@ -24,7 +24,7 @@ newPackage( HomePage => "http://www.math.uni-sb.de/ag/schreyer/"} }, Headline => "Adjunction for Surfaces", - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, Keywords => {"Projective Algebraic Geometry"} ) @@ -174,7 +174,7 @@ adjunctionProcess(Ideal):= J -> ( a:=symbol a; b:=symbol b; ab:={symbol a,symbol b}; Pn:=ring J;n:= dim Pn-1; deg := degree I; sectGenus:=genus(I+ideal(Pn_0)); - betti(fI:=res(I,FastNonminimal=>true)); + betti(fI:=res(I,Strategy=>Nonminimal)); c:=codim I; betti (G:=((dual fI[-c])**Pn^{-n-1})); betti(omega:=prune HH_0 G); @@ -207,7 +207,7 @@ adjunctionProcess(Ideal):= J -> ( n=dim Pn-1; deg=degree I; sectGenus=genus(I+ideal (Pn_0)); - betti(fI=res(I,FastNonminimal=>true)); + betti(fI=res(I,Strategy=>Nonminimal)); betti (G=((dual fI[-c])**Pn^{-n-1})); betti(omega=prune HH_0 G); betti(D=transpose presentation omega); @@ -231,7 +231,7 @@ adjunctionProcess(Ideal,ZZ):= (J,k) -> ( N:=0; ab:={symbol a,symbol b}; Pn:=ring J;n:= dim Pn-1; deg := degree I; sectGenus:=genus(I+ideal(Pn_0)); - betti(fI:=res(I,FastNonminimal=>true)); + betti(fI:=res(I,Strategy=>Nonminimal)); c:= codim I; betti (G:=((dual fI[-c])**Pn^{-n-1})); betti(omega:=prune HH_0 G); @@ -266,7 +266,7 @@ adjunctionProcess(Ideal,ZZ):= (J,k) -> ( n=dim Pn-1; deg=degree I; sectGenus=genus(I+ideal (Pn_0)); - betti(fI=res(I,FastNonminimal=>true)); + betti(fI=res(I,Strategy=>Nonminimal)); betti (G=((dual fI[-c])**Pn^{-n-1})); betti(omega=prune HH_0 G); betti(D=transpose presentation omega); @@ -753,7 +753,7 @@ doc /// 3) P2(9;3p_1,...,3p_8) - 4) Y=P(E) where E is a indecomposabe rank 2 vector bundle over an elliptic curve + 4) Y=P(E) where E is a indecomposable rank 2 vector bundle over an elliptic curve and H=3B, where B in Y is the section with B^2=1. Example diff --git a/M2/Macaulay2/packages/AlgebraicSplines.m2 b/M2/Macaulay2/packages/AlgebraicSplines.m2 index 01ba4fdd422..148ad05bf77 100644 --- a/M2/Macaulay2/packages/AlgebraicSplines.m2 +++ b/M2/Macaulay2/packages/AlgebraicSplines.m2 @@ -23,7 +23,7 @@ newPackage( Keywords => {"Applied Algebraic Geometry"}, Configuration => {}, DebuggingMode => false, - PackageImports => { "OldChainComplexes", "Elimination" }, + PackageImports => { "Complexes", "Elimination" }, PackageExports => { "FourierMotzkin" } @@ -1133,7 +1133,7 @@ cellularComplex = method( --- is the homology of the simplicial complex relative --- to its boundary. -------------------------------------------------- -cellularComplex(List) := ChainComplex => opts -> (F) -> ( +cellularComplex(List) := Complex => opts -> (F) -> ( if opts.InputType === "Polyhedral" then ( print "Need a List of Vertices"; chain := null; @@ -1145,7 +1145,7 @@ cellularComplex(List) := ChainComplex => opts -> (F) -> ( C := apply(d+1, i-> getCodimDFacesSimplicial(F,i)); boundaryC := join({{}},apply(d, i-> getCodimDFacesSimplicial(boundaryF,i))); intC := apply(#C, i -> select(C_i, f -> not member(f,boundaryC_i))); - chain = chainComplex(reverse apply(#intC-1, c-> sub(simpBoundary(intC_c,intC_(c+1)),S))) + chain = complex(reverse apply(#intC-1, c-> sub(simpBoundary(intC_c,intC_(c+1)),S))) ); chain ) @@ -1167,7 +1167,7 @@ cellularComplex(List) := ChainComplex => opts -> (F) -> ( --With this input, output should be an acyclic complex except for HH_3 -------------------------------------------------- -cellularComplex(List,List) := ChainComplex => opts -> (V,F) -> ( +cellularComplex(List,List) := Complex => opts -> (V,F) -> ( d := (# first V); S := createSplineRing(d,opts); if issimplicial(V,F) then ( @@ -1175,7 +1175,7 @@ cellularComplex(List,List) := ChainComplex => opts -> (V,F) -> ( C := apply(d+1, i-> getCodimDFacesSimplicial(F,i)); boundaryC := join({{}},apply(d, i-> getCodimDFacesSimplicial(boundaryF,i))); intC := apply(#C, i -> select(C_i, f -> not member(f,boundaryC_i))); - chain := chainComplex(reverse apply(#intC-1, c-> sub(simpBoundary(intC_c,intC_(c+1)),S ))) + chain := complex(reverse apply(#intC-1, c-> sub(simpBoundary(intC_c,intC_(c+1)),S ))) ) else ( bComp := boundaryComplex(V,F); --Construct list whose ith element is interior intersections of codim i-- @@ -1203,7 +1203,7 @@ cellularComplex(List,List) := ChainComplex => opts -> (V,F) -> ( --variables on each face) orList := apply(idList, L->apply(L,I->orient I)); --set up the chain complex - chain = chainComplex(reverse apply(#intC-1, c-> ( + chain = complex(reverse apply(#intC-1, c-> ( L1 := {intC_(c+1),idList_(c+1),orList_(c+1)}; L2 := {intC_c,idList_c,orList_c}; sub(polyBoundary(V,L2,L1),S) @@ -1234,7 +1234,7 @@ idealsComplex=method(Options=>{ --Outputs: The Schenck-Stillman complex of ideals ------------------------------------------ -idealsComplex(List,List,ZZ):=ChainComplex => opts -> (V,F,r)->( +idealsComplex(List,List,ZZ):=Complex => opts -> (V,F,r)->( d := #(first V); S := createSplineRing(d,opts); if issimplicial(V,F) then ( @@ -1264,7 +1264,7 @@ idealsComplex(List,List,ZZ):=ChainComplex => opts -> (V,F,r)->( newMod )); --defining the chain complex - CCSS :=chainComplex(reverse apply(#intC-1, c-> ( + CCSS :=complex(reverse apply(#intC-1, c-> ( inducedMap(fullmodulelist_(c+1),fullmodulelist_c,sub(simpBoundary(intC_c,intC_(c+1)),S)) )) ) @@ -1311,7 +1311,7 @@ idealsComplex(List,List,ZZ):=ChainComplex => opts -> (V,F,r)->( newMod )); --set up the chain complex - CCSS = chainComplex(reverse apply(#intC-1, c-> ( + CCSS = complex(reverse apply(#intC-1, c-> ( L1 := {intC_(c+1),idList_(c+1),orList_(c+1)}; L2 := {intC_c,idList_c,orList_c}; M := sub(polyBoundary(V,L2,L1),S); @@ -1355,7 +1355,7 @@ splineComplex=method(Options=>{ -------------------------------------------------- -splineComplex(List,List,ZZ):=ChainComplex => opts -> (V,F,r)->( +splineComplex(List,List,ZZ):=Complex => opts -> (V,F,r)->( d := #(first V); S := createSplineRing(d,opts); if issimplicial(V,F) then ( @@ -1386,7 +1386,7 @@ splineComplex(List,List,ZZ):=ChainComplex => opts -> (V,F,r)->( fullmodulelist = append(fullmodulelist,newMod) )); --defining the chain complex - CCSS :=chainComplex(reverse apply(#intC-1, c-> ( + CCSS :=complex(reverse apply(#intC-1, c-> ( map(fullmodulelist_(c+1),fullmodulelist_c,sub(simpBoundary(intC_c,intC_(c+1)),S)) )) ); @@ -1434,7 +1434,7 @@ splineComplex(List,List,ZZ):=ChainComplex => opts -> (V,F,r)->( fullmodulelist = append(fullmodulelist,newMod) )); --set up the chain complex - CCSS = chainComplex(reverse apply(#intC-1, c-> ( + CCSS = complex(reverse apply(#intC-1, c-> ( L1 := {intC_(c+1),idList_(c+1),orList_(c+1)}; L2 := {intC_c,idList_c,orList_c}; M := sub(polyBoundary(V,L2,L1),S); @@ -2036,7 +2036,7 @@ doc /// InputType=>String Outputs - C:ChainComplex + C:Complex cellular chain complex of $\Delta$ relative to its boundary Description Text @@ -2091,7 +2091,7 @@ doc /// VariableName=>Symbol Outputs - C:ChainComplex + C:Complex Billera-Schenck-Stillman chain complex of ideals Description Text @@ -2454,7 +2454,7 @@ doc /// VariableName=>Symbol Outputs - C:ChainComplex + C:Complex Billera-Schenck-Stillman spline complex Description Text diff --git a/M2/Macaulay2/packages/BeginningMacaulay2.m2 b/M2/Macaulay2/packages/BeginningMacaulay2.m2 index d438232f09b..25429cad19e 100644 --- a/M2/Macaulay2/packages/BeginningMacaulay2.m2 +++ b/M2/Macaulay2/packages/BeginningMacaulay2.m2 @@ -6,7 +6,7 @@ newPackage( "BeginningMacaulay2", Version => "1.0", Date => "November 3, 2009", }, Headline => "Mathematicians' Introduction to Macaulay2", Keywords => {"Documentation"}, - PackageExports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, AuxiliaryFiles => true ) beginDocumentation() diff --git a/M2/Macaulay2/packages/BeginningMacaulay2/tutorial b/M2/Macaulay2/packages/BeginningMacaulay2/tutorial index a123eeb51e0..f36888c113b 100644 --- a/M2/Macaulay2/packages/BeginningMacaulay2/tutorial +++ b/M2/Macaulay2/packages/BeginningMacaulay2/tutorial @@ -314,7 +314,7 @@ Description A great deal of subtle information about a module is visible using free resolutions. For an example, we begin by turning $R/I$ into a module. Here the code @ TT "R^1" @ produces the free module of - rank 1 over $R$, and @ TO2 {"OldChainComplexes :: resolution(Module)", "res"} @ computes a free resolution: + rank 1 over $R$, and @ TO2 {"Complexes :: resolution(Module)", "res"} @ computes a free resolution: Example M=R^1/I Mres = res M @@ -615,10 +615,10 @@ Description Example (ker phi1)/(image phi2) Text - We could also use the data type @TO "OldChainComplexes :: ChainComplex"@ + We could also use the data type @TO "Complexes :: Complex"@ and use a built-in facility to take homology (in our case $H_1$): Example - FF = chainComplex(phi1,phi2) + FF = complex{phi1,phi2} FF.dd homology FF presentation (homology FF)_1 @@ -629,12 +629,12 @@ Description There are other ways to construct Koszul complexes. One way is as the tensor product of chain complexes of length 1: Example - FF = chainComplex matrix {{x^2}} ** chainComplex matrix {{x*y^2}} + FF = complex matrix {{x^2}} ** complex matrix {{x*y^2}} FF.dd Text Another way is by using the function @ TO koszul @, designed for that purpose: Example - FF = koszul matrix {{x^2, x*y^2}} + FF = koszulComplex matrix {{x^2, x*y^2}} FF.dd Text diff --git a/M2/Macaulay2/packages/Benchmark.m2 b/M2/Macaulay2/packages/Benchmark.m2 index af9497e3004..c813f64bb35 100644 --- a/M2/Macaulay2/packages/Benchmark.m2 +++ b/M2/Macaulay2/packages/Benchmark.m2 @@ -8,7 +8,7 @@ newPackage ( }, Keywords => {"Miscellaneous"}, HomePage => "https://macaulay2.com/", - PackageImports => {"OldChainComplexes", "XML"}, + PackageImports => {"Complexes", "XML"}, Version => "1.0" ) diff --git a/M2/Macaulay2/packages/BernsteinSato.m2 b/M2/Macaulay2/packages/BernsteinSato.m2 index 21311cc713f..c35e50b3cd8 100644 --- a/M2/Macaulay2/packages/BernsteinSato.m2 +++ b/M2/Macaulay2/packages/BernsteinSato.m2 @@ -18,7 +18,7 @@ newPackage( PackageExports => { "WeylAlgebras", "HolonomicSystems", - "OldChainComplexes", + "Complexes", }, AuxiliaryFiles => true, DebuggingMode => false diff --git a/M2/Macaulay2/packages/BernsteinSato/CC.m2 b/M2/Macaulay2/packages/BernsteinSato/CC.m2 index 8fe0f99997a..e1211666996 100644 --- a/M2/Macaulay2/packages/BernsteinSato/CC.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/CC.m2 @@ -69,7 +69,7 @@ cotanbun RingElement := f -> ( --------------------------------------------------------------------- -- multiplicity -- --- given a Poincare polynomial for a module outputs its multiplicity +-- given a Poincaré polynomial for a module outputs its multiplicity multiplicityPoincare := method() multiplicityPoincare RingElement := f-> ( R := ring f; diff --git a/M2/Macaulay2/packages/BernsteinSato/DHom.m2 b/M2/Macaulay2/packages/BernsteinSato/DHom.m2 index 38edc51a51e..51a96ce2dae 100644 --- a/M2/Macaulay2/packages/BernsteinSato/DHom.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/DHom.m2 @@ -70,11 +70,11 @@ Ddual Module := M -> ( outputList := {}; C := Dresolution(M, LengthLimit => n+1); - m0 := transpose Dtransposition C.dd#n; - m1 := transpose Dtransposition C.dd#(n+1); + m0 := transpose Dtransposition C.dd_n; + m1 := transpose Dtransposition C.dd_(n+1); - F0 := map(C#n, C#(n-1), m0); - F1 := map(C#(n+1), C#n, m1); + F0 := map(C_n, C_(n-1), m0); + F1 := map(C_(n+1), C_n, m1); pres := presentation homology(F1,F0); cokernel zeroize Dprune pres) @@ -191,9 +191,9 @@ polynomialSolutions(Module, List) := options -> (M, w) -> ( C := Dtransposition dual B); pInfo (2, "\t\t\t time = " | tInfo | " seconds"); - Ker := zeroize gens kernel C.dd#-n; + Ker := zeroize gens kernel C.dd_(-n); Rels := presentation image Ker; - Im := zeroize C.dd#-(n-1); + Im := zeroize C.dd_(-(n-1)); if (Im%Ker) != 0 then error "expected reduction to 0"; Syz := Im//Ker; NewDual := Rels | Syz; @@ -205,39 +205,40 @@ polynomialSolutions(Module, List) := options -> (M, w) -> ( if integrateTable#n == 0 then answer = matrix{{0_K}} else ( - chainMap := new MutableHashTable; - chainMap.source = F; - chainMap.target = C; - chainMap.degree = -n; - chainMap#0 = map(C#-n, F#0, Ker); + -- chainMap := new MutableHashTable; + -- chainMap.source = F; + -- chainMap.target = C; + -- chainMap.degree = -n; + -- chainMap#0 = map(C#-n, F#0, Ker); pInfo(1, "computing chain map 1 ..."); tInfo = toString first timing ( - bottomCompose := (Ker)*F.dd#1; - if (zeroize bottomCompose)%(zeroize C.dd#-(n-1)) != 0 then - error "expected reduction to 0 -- possible lack of gb problem?"; - nextLiftMap := (zeroize bottomCompose)//(zeroize C.dd#-(n-1)); - chainMap#1 = map(C#-(n-1), F#1, nextLiftMap); ); - pInfo (2, "\t\t\t time = " | tInfo | " seconds"); - i = 2; + bottomCompose := (Ker)*F.dd_1; + if (zeroize bottomCompose)%(zeroize C.dd_-(n-1)) != 0 then + error "expected reduction to 0 -- possible lack of gb problem?"; + nextLiftMap := (zeroize bottomCompose)//(zeroize C.dd_-(n-1)); + -- chainMap#1 = map(C#-(n-1), F#1, nextLiftMap); + ); + pInfo (2, "\t\t\t time = " | tInfo | " seconds"); + i = 2; while i <= n do ( pInfo(1, "computing chain map " | i | " ..."); tInfo = toString first timing ( - bottomCompose = nextLiftMap*F.dd#i; - if ((zeroize bottomCompose)%(zeroize C.dd#-(n-i)) != 0) then + bottomCompose = nextLiftMap*F.dd_i; + if ((zeroize bottomCompose)%(zeroize C.dd_-(n-i)) != 0) then error "expected reduction to 0 -- possible lack of gb problem?"; - nextLiftMap = (zeroize bottomCompose)//(zeroize C.dd#-(n-i)); - chainMap#i = map(C#-(n-i), F#i, nextLiftMap); + nextLiftMap = (zeroize bottomCompose)//(zeroize C.dd_-(n-i)); + -- chainMap#i = map(C#-(n-i), F#i, nextLiftMap); i = i+1; ); pInfo (2, "\t\t\t time = " | tInfo | " seconds"); ); answer = nextLiftMap*(integrateTable#n); answer = substitute(Dtransposition(answer), diffSub); - chainMap = new ChainComplexMap from chainMap; + -- chainMap = new ChainComplexMap from chainMap; ); - answer = (entries answer)#0; - ) + answer = (entries answer)#0; + ) else error "expected Alg 'GD' or 'Duality'"; @@ -585,9 +586,9 @@ DHom(Module, Module, List) := options -> (M, N, w) -> ( FMN = FMN[-n]; C := WW.twistMap FMN; - Ker := zeroize gens kernel C.dd#0; + Ker := zeroize gens kernel C.dd_0; Rels := presentation image Ker; - Im := zeroize C.dd#1; + Im := zeroize C.dd_1; if Im%Ker != 0 then error "expected reduction to 0"; Syz := Im//Ker; @@ -603,36 +604,36 @@ DHom(Module, Module, List) := options -> (M, N, w) -> ( if restrictTable#n == 0 then answer = 0_K else ( - chainMap := new MutableHashTable; - chainMap.source = F; - chainMap.target = C; - chainMap.degree = 0; - chainMap#0 = map(C#0, F#0, Ker); + -- chainMap := new MutableHashTable; + -- chainMap.source = F; + -- chainMap.target = C; + -- chainMap.degree = 0; + -- chainMap#0 = map(C#0, F#0, Ker); pInfo(1, "computing chain map 1 ... "); tInfo = toString first timing ( - bottomCompose := (Ker)*F.dd#1; - if (zeroize bottomCompose)%(zeroize C.dd#1) != 0 then + bottomCompose := (Ker)*F.dd_1; + if (zeroize bottomCompose)%(zeroize C.dd_1) != 0 then error "expected reduction to 0 -- possible lack of gb problem?"; - nextLiftMap := (zeroize bottomCompose)//(zeroize C.dd#1); - chainMap#1 = map(C#1, F#1, nextLiftMap); + nextLiftMap := (zeroize bottomCompose)//(zeroize C.dd_1); + -- chainMap#1 = map(C#1, F#1, nextLiftMap); ); pInfo (2, "\t\t\t time = " | tInfo | " seconds"); i := 2; while i <= n do ( pInfo(1, "computing chain map " | i | " ..." ); tInfo = toString first timing ( - bottomCompose = nextLiftMap*F.dd#i; - if (zeroize bottomCompose)%(zeroize C.dd#i) != 0 then + bottomCompose = nextLiftMap*F.dd_i; + if (zeroize bottomCompose)%(zeroize C.dd_i) != 0 then error "expected reduction to 0 -- possible lack of gb problem?"; - nextLiftMap = (zeroize bottomCompose)//(zeroize C.dd#i); - chainMap#i = map(C#i, F#i, nextLiftMap); + nextLiftMap = (zeroize bottomCompose)//(zeroize C.dd_i); + -- chainMap#i = map(C#i, F#i, nextLiftMap); i = i+1; ); pInfo (2, "\t\t\t time = " | tInfo | " seconds"); ); answer = WW.twistInvMap(nextLiftMap*restrictTable#n); temp := answer; - chainMap = new ChainComplexMap from chainMap; + -- chainMap = new ChainComplexMap from chainMap; ); if answer == 0 then basisList := matrix{{0_K}} @@ -793,7 +794,7 @@ ExternalProduct(Module, Module) := options -> (M, N) -> ( incM**incN ) -ExternalProduct(ChainComplex, ChainComplex) := options -> (F, G) -> ( +ExternalProduct(Complex, Complex) := options -> (F, G) -> ( pInfo(1, "ENTERING ExternalProduct ..."); @@ -924,9 +925,9 @@ TEST /// -- DExt (Module, Module, List) -- ExternalProduct (Module, Module) --- ExternalProduct (ChainComplex, ChainComplex) +-- ExternalProduct (Complex, Complex) -- ExternalProduct (Module, Module) .... with TwistMap => true --- ExternalProduct (ChainComplex, ChainComplex) .... with TwistMap => true +-- ExternalProduct (Complex, Complex) .... with TwistMap => true diff --git a/M2/Macaulay2/packages/BernsteinSato/DOC/Drestriction.m2 b/M2/Macaulay2/packages/BernsteinSato/DOC/Drestriction.m2 index e06b9943426..2704b065bb1 100644 --- a/M2/Macaulay2/packages/BernsteinSato/DOC/Drestriction.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/DOC/Drestriction.m2 @@ -34,7 +34,7 @@ document { "w" => List => "a weight vector" }, Outputs => { - ChainComplex => {"a Schreyer resolution of the D-module ", EM "M", + Complex => {"a Schreyer resolution of the D-module ", EM "M", " or a resolution adapted to a weight vector ", EM "w", " of the form ", EM "(-u,u)"} }, diff --git a/M2/Macaulay2/packages/BernsteinSato/DOC/other.m2 b/M2/Macaulay2/packages/BernsteinSato/DOC/other.m2 index f35c47616ab..b4afc44d303 100644 --- a/M2/Macaulay2/packages/BernsteinSato/DOC/other.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/DOC/other.m2 @@ -106,7 +106,7 @@ logCohomology(f) } document { -- local? - Key => {ExternalProduct, (ExternalProduct,ChainComplex,ChainComplex), (ExternalProduct,Module,Module)}, + Key => {ExternalProduct, (ExternalProduct,Complex,Complex), (ExternalProduct,Module,Module)}, Headline => "external product of modules or complexes" } document { -- local? diff --git a/M2/Macaulay2/packages/BernsteinSato/DeRham.m2 b/M2/Macaulay2/packages/BernsteinSato/DeRham.m2 index 142d5ae7b3d..ded2c2be827 100644 --- a/M2/Macaulay2/packages/BernsteinSato/DeRham.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/DeRham.m2 @@ -154,11 +154,11 @@ deRhamAll RingElement := options -> f -> ( pInfo(2, "\t Degree " | i | "..."); tInfo := toString first timing ( while j < n-i do ( - vertMap := zeroize dbl#(n-i-1)^[(j,-j+n-i-1)]* - (dbl.dd#(n-i))*(dbl#(n-i)_[(j,-j+n-i)]); + vertMap := zeroize dbl_(n-i-1)^[{j,-j+n-i-1}]* + (dbl.dd_(n-i))*(dbl_(n-i)_[{j,-j+n-i}]); push := vertMap*pull; - horMap := zeroize dbl#(n-i-1)^[(j,-j+n-i-1)]* - (dbl.dd#(n-i))*(dbl#(n-i)_[(j+1,-j+n-i-1)]); + horMap := zeroize dbl_(n-i-1)^[{j,-j+n-i-1}]* + (dbl.dd_(n-i))*(dbl_(n-i)_[{j+1,-j+n-i-1}]); if ((Dtransposition push) % (Dtransposition horMap) != 0) then error "syzygy should be produced but wasn't!"; pull = (Dtransposition push) // (Dtransposition horMap); @@ -167,7 +167,7 @@ deRhamAll RingElement := options -> f -> ( ); ); pInfo(2, "\t\t\t time = " | tInfo | " seconds" ); - transfers = append(transfers, i => pull); + transfers = append(transfers, i => matrix entries pull); i = i+1; ); outputList = outputList | {TransferCycles => @@ -238,11 +238,11 @@ iAllt Module:= options -> I -> ( pInfo(2, "\t Degree " | i | "..."); tInfo := toString first timing ( while j < n-i do ( - vertMap := zeroize dbl#(n-i-1)^[(j,-j+n-i-1)]* - (dbl.dd#(n-i))*(dbl#(n-i)_[(j,-j+n-i)]); + vertMap := zeroize dbl_(n-i-1)^[{j,-j+n-i-1}]* + (dbl.dd_(n-i))*(dbl_(n-i)_[{j,-j+n-i}]); push := vertMap*pull; - horMap := zeroize dbl#(n-i-1)^[(j,-j+n-i-1)]* - (dbl.dd#(n-i))*(dbl#(n-i)_[(j+1,-j+n-i-1)]); + horMap := zeroize dbl_(n-i-1)^[{j,-j+n-i-1}]* + (dbl.dd_(n-i))*(dbl_(n-i)_[{j+1,-j+n-i-1}]); if ((Dtransposition push) % (Dtransposition horMap) != 0) then error "syzygy should be produced but wasn't!"; pull = (Dtransposition push) // (Dtransposition horMap); @@ -353,7 +353,7 @@ getReducedTransfer (HashTable,ZZ) := (cohom,k) -> ( WtotempW := map (tempW, W, vars tempW); -- map to the lex ring. tktemp := apply(tk,i->apply(i,j->WtotempW(j))); - iii := WtotempW( ((cohom#VResolution).dd)#1 ); + iii := WtotempW( (cohom#VResolution).dd_1 ); ggg := gb iii; tkreduced := apply(tktemp,i->apply(i,j-> (j % ggg))) ) diff --git a/M2/Macaulay2/packages/BernsteinSato/Dresolution.m2 b/M2/Macaulay2/packages/BernsteinSato/Dresolution.m2 index b5f38ac6ac4..cf919e3eda1 100644 --- a/M2/Macaulay2/packages/BernsteinSato/Dresolution.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/Dresolution.m2 @@ -79,24 +79,28 @@ Dresolution Module := options -> M -> ( pInfo (2, "\t\t\t Rank = " | rank source N | "\t time = " | tInfo | " seconds"); - M.cache.resolution = new ChainComplex; - M.cache.resolution.ring = W; - M.cache.resolution#0 = target m; - M.cache.resolution#1 = source m; - M.cache.resolution.dd#0 = map(W^0, target m, 0); - M.cache.resolution.dd#1 = m; + M.cache#"resolutionMaps" = {m}; + + -- M.cache.resolution = new ChainComplex; + -- M.cache.resolution.ring = W; + -- M.cache.resolution#0 = target m; + -- M.cache.resolution#1 = source m; + -- M.cache.resolution.dd#0 = map(W^0, target m, 0); + -- M.cache.resolution.dd#1 = m; i := 2; while source m != 0 and i <= options.LengthLimit do ( pInfo (2, "\t Degree " | i | "..."); tInfo = toString first timing (m = kerGB m); - M.cache.resolution#i = source m; - M.cache.resolution.dd#i = m; + M.cache#"resolutionMaps" = append(M.cache#"resolutionMaps", m); + -- M.cache.resolution#i = source m; + -- M.cache.resolution.dd#i = m; pInfo(2, "\t\t\t Rank = " | rank source m | "\t time = " | tInfo | " seconds"); i = i+1; ); - M.cache.resolution.length = i-1; + M.cache.resolution = complex M.cache#"resolutionMaps"; + -- M.cache.resolution.length = i-1; M.cache.resolution ) @@ -178,13 +182,14 @@ Dresolution (Module, List) := options -> (M, w) -> ( -- get the degree shifts right (need to check this against OT paper) if not M.cache.?resolution then M.cache.resolution = new MutableHashTable; - M.cache.resolution#w = new ChainComplex; - M.cache.resolution#w.ring = W; + --M.cache.resolution#w = new ChainComplex; + --M.cache.resolution#w.ring = W; s := rank source N; t := rank target N; - M.cache.resolution#w#0 = target N; - M.cache.resolution#w.dd#0 = map(W^0, M.cache.resolution#w#0, 0); + --M.cache.resolution#w#0 = target N; + --M.cache.resolution#w.dd#0 = map(W^0, M.cache.resolution#w#0, 0); + -- MAKE THE FIRST STEP OF THE RESOLUTION shiftvec := apply(degrees target N, i -> i#0); tempMap := map(HW^(-shiftvec), HW^(rank source N), WtoHW N); @@ -200,9 +205,10 @@ Dresolution (Module, List) := options -> (M, w) -> ( VWwt, shiftvec); ) else shiftvec = shifts(Jgb, Vwt, shiftvec); - M.cache.resolution#w#1 = W^(-shiftvec); - M.cache.resolution#w.dd#1 = map(M.cache.resolution#w#0, - M.cache.resolution#w#1, HWtoW Jgb); + --M.cache.resolution#w#1 = W^(-shiftvec); + -- M.cache.resolution#w.dd#1 = map(M.cache.resolution#w#0, + -- M.cache.resolution#w#1, HWtoW Jgb); + resolutionMaps := {map(target N, W^(-shiftvec), HWtoW Jgb)}; ); pInfo(2, "\t\t\t Rank = " | #shiftvec | "\t time = " | tInfo | " seconds"); @@ -228,14 +234,16 @@ Dresolution (Module, List) := options -> (M, w) -> ( VWwt, shiftvec); ) else shiftvec = shifts(Jgb, Vwt, shiftvec); - M.cache.resolution#w#i = W^(-shiftvec); - M.cache.resolution#w.dd#i = map(M.cache.resolution#w#(i-1), - M.cache.resolution#w#i, HWtoW Jgb); + --M.cache.resolution#w#i = W^(-shiftvec); + -- M.cache.resolution#w.dd#i = map(M.cache.resolution#w#(i-1), + -- M.cache.resolution#w#i, HWtoW Jgb); + resolutionMaps = append(resolutionMaps, map(source last resolutionMaps, W^(-shiftvec), HWtoW Jgb)); ); pInfo(2, "\t\t\t Rank = " | #shiftvec | "\t time = " | tInfo | " seconds"); i = i+1; ); + M.cache.resolution#w = complex resolutionMaps; M.cache.resolution#w ) @@ -264,12 +272,12 @@ b = {3,2}; I = gkz(A,b); F1 = Dres(I, {-1,-2,-21,1,2,21}); F2 = Dres(I, {-1,-2,-20,1,2,20}); -assert all(toList(0..length F1), i -> F1.dd#i - F2.dd#i == 0); +assert all(toList(0..length F1), i -> F1.dd_i - F2.dd_i == 0); F3 = Dres(I, {-1,-2,-21,1,2,21}, Strategy => Vhomogenize); F4a = Dres(I, {-1,-2,-20,1,2,20}, Strategy => Vhomogenize); -assert all(toList(0..length F3), i -> F3.dd#i - F4a.dd#i == 0); +assert all(toList(0..length F3), i -> F3.dd_i - F4a.dd_i == 0); F5 = Dres(I, {-3,-1,-3,3,1,3}); -assert(F5.dd#1 - gbw(gens I, {-3,-1,-3,3,1,3}) == 0); +assert(F5.dd_1 - gbw(gens I, {-3,-1,-3,3,1,3}) == 0); /// diff --git a/M2/Macaulay2/packages/BernsteinSato/Drestriction.m2 b/M2/Macaulay2/packages/BernsteinSato/Drestriction.m2 index 9b5b3be75e4..f7eca4dcfa2 100644 --- a/M2/Macaulay2/packages/BernsteinSato/Drestriction.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/Drestriction.m2 @@ -170,7 +170,7 @@ DrestrictionIdeal(Ideal,List) := options -> (I,w) -> ( outputRequest := {RestrictComplex, Explicit}; outputTable := computeRestriction ((ring I)^1/I, w, -1, 1, outputRequest, options); - M := cokernel (outputTable#RestrictComplex).dd#1; + M := cokernel (outputTable#RestrictComplex).dd_1; R := ring M; if M == 0 then outputIdeal := ideal 1_R else ( @@ -373,7 +373,7 @@ DintegrationIdeal(Ideal,List) := options -> (I,w) -> ( outputRequest := {RestrictComplex, Explicit}; outputTable := computeRestriction (W^1/IF, w, -1, 1, outputRequest, options); - M := cokernel (outputTable#RestrictComplex).dd#1; + M := cokernel (outputTable#RestrictComplex).dd_1; resW := ring M; if M == 0 then outputIdeal := ideal 1_resW else ( @@ -554,8 +554,10 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( if member(Boundaries, output) then imList := {}; if member(GenCycles, output) then explicitList := {}; if member(RestrictComplex,output) then ( - restrictComplex := new ChainComplex; - restrictComplex.ring = resW; + restrictComplexMaps := new MutableHashTable; + local restrictComplex; + -- restrictComplex := new ChainComplex; + -- restrictComplex.ring = resW; ); outputList := {}; if member(Cycles, output) or member(Boundaries, output) or @@ -594,16 +596,17 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( pInfo(2, "\t No integer roots"); pInfo(3, "\t time = " | tInfo); if member(RestrictComplex, output) then ( - restrictComplex#n0 = resW^0; - restrictComplex#n1 = resW^0; - restrictComplex.dd#n0 = map(resW^0,resW^0,0); - restrictComplex.dd#n1 = map(resW^0,resW^0,0); - i = n0+1; - while i < n1 do ( - restrictComplex#i = resW^0; - restrictComplex.dd#i = map(resW^0,resW^0,0); - i = i+1; - ); + restrictComplex = complex resW^0; + -- restrictComplex#n0 = resW^0; + -- restrictComplex#n1 = resW^0; + -- restrictComplex.dd#n0 = map(resW^0,resW^0,0); + -- restrictComplex.dd#n1 = map(resW^0,resW^0,0); + -- i = n0+1; + -- while i < n1 do ( + -- restrictComplex#i = resW^0; + -- restrictComplex.dd#i = map(resW^0,resW^0,0); + -- i = i+1; + -- ); ); if member(HomologyModules, output) then homologyList = apply (toList(n0+1..n1-1), i -> (i => resW^0)); @@ -649,8 +652,8 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( -- the monomial generators {dx^a} (as left D_m-module) of -- F_k1[u_i]((D_n/tD_n) e_i) / F_k0[u_i]((D_n/tD_n) e_i) tInfo = toString first timing ( - s := numgens target C.dd#(n0+1); - targetDeg := degrees target C.dd#(n0+1); + s := numgens target C.dd_(n0+1); + targetDeg := degrees target C.dd_(n0+1); targetGens := {}; if explicitFlag then targetMat := map(W^0,W^0,0); i = 0; @@ -672,9 +675,9 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( -- "sourceGens" is a list of r lists, where the i-th list contains -- the monomial generators {dx^a} (as left D_m-module) of -- F_k1[u_i]((D_n/tD_n) e_i) / F_k0[u_i]((D_n/tD_n) e_i) - m := C.dd#(n0+1); - r := numgens C#(n0+1); - sourceDeg := degrees C#(n0+1); + m := C.dd_(n0+1); + r := numgens C_(n0+1); + sourceDeg := degrees C_(n0+1); sourceGens := {}; if explicitFlag then sourceMat := map(W^0,W^0,0); i = 0; @@ -724,10 +727,11 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( oldDiff = WtoresW oldDiff; ); if member(RestrictComplex, output) then ( - restrictComplex#n0 = resW^(rank target oldDiff); - restrictComplex#(n0+1) = resW^(rank source oldDiff); - restrictComplex.dd#(n0+1) = map(restrictComplex#n0, - restrictComplex#(n0+1), oldDiff); + -- restrictComplex#n0 = resW^(rank target oldDiff); + -- restrictComplex#(n0+1) = resW^(rank source oldDiff); + -- restrictComplex.dd#(n0+1) = map(restrictComplex#n0, + -- restrictComplex#(n0+1), oldDiff); + restrictComplexMaps#(n0+1) = map(resW^(rank target oldDiff), resW^(rank source oldDiff), oldDiff); ); if member(Cycles, output) or member(Boundaries, output) or member(GenCycles, output) then ( @@ -747,16 +751,16 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( currDeg := n0 + 2; --newKernel = 0; --explicitKernel = 0; - while currDeg <= n1 and C#?(currDeg) do ( + while currDeg <= n1 do ( -- and currDeg <= length C do ( -- MAKE THE NEXT SOURCE MODULE -- "sourceGens" is a list of r lists, where the i-th list contains -- the monomial generators {dx^a} (as left D_m-module) of -- F_k1[u_i]((D_n/tD_n) e_i) / F_k0[u_i]((D_n/tD_n) e_i) pInfo(2, "\t Degree " | currDeg | "..."); tInfo = toString first timing ( - r = numgens C#currDeg; - m = C.dd#currDeg; - sourceDeg = degrees C#(currDeg); + r = numgens C_currDeg; + m = C.dd_currDeg; + sourceDeg = degrees C_(currDeg); sourceGens = {}; if explicitFlag then sourceMat = map(W^0,W^0,0); i = 0; @@ -806,9 +810,10 @@ computeRestriction = (M,wt,n0,n1,output,options) -> ( -- UPDATE THE OUTPUT LIST if member(RestrictComplex, output) then ( - restrictComplex#currDeg = resW^(rank source newDiff); - restrictComplex.dd#currDeg = map(restrictComplex#(currDeg-1), - restrictComplex#currDeg, newDiff); + -- restrictComplex#currDeg = resW^(rank source newDiff); + -- restrictComplex.dd#currDeg = map(restrictComplex#(currDeg-1), + -- restrictComplex#currDeg, newDiff); + restrictComplexMaps#currDeg = map(source restrictComplexMaps#(currDeg-1), resW^(rank source newDiff), newDiff); ); if member(HomologyModules, output) then ( tempHomology := homology(zeroize oldDiff, zeroize newDiff); @@ -875,7 +880,12 @@ if member(BFunction, output) then outputList = append( if member(VResolution, output) then outputList = append( outputList, VResolution => C); if member(RestrictComplex, output) then outputList = append( - outputList, RestrictComplex => restrictComplex); + outputList, RestrictComplex => + if restrictComplex =!= null then + restrictComplex + else + complex restrictComplexMaps + ); if member(ResToOrigRing, output) then outputList = append( outputList, ResToOrigRing => resWtoW); diff --git a/M2/Macaulay2/packages/BernsteinSato/TST/tests.m2 b/M2/Macaulay2/packages/BernsteinSato/TST/tests.m2 index b4c3e2a96fb..03fd31c0d35 100644 --- a/M2/Macaulay2/packages/BernsteinSato/TST/tests.m2 +++ b/M2/Macaulay2/packages/BernsteinSato/TST/tests.m2 @@ -47,14 +47,14 @@ assert(Drestriction(0, I, {1,3,5}) == QQ^0); Dr = Drestriction(I, {1,3,5}); assert(all(toList(0..3), i -> (Dr#i == QQ^0))); Dc = DrestrictionComplex(I, {1,3,5}); -assert(all(toList(0..3), i -> (Dc.dd#i == map QQ^0)) ); +assert(all(toList(0..3), i -> (Dc.dd_i == map QQ^0)) ); assert(DintegrationIdeal(I, {1,3,5}) == ideal 1_QQ); assert(Dintegration(0, I, {1,3,5}) == QQ^0); Dr = Dintegration(I, {1,3,5}); assert(all(toList(0..3), i -> (Dr#i == QQ^0))); Dc = DintegrationComplex(I, {1,3,5}); -assert(all(toList(0..3), i -> (Dc.dd#i == map QQ^0)) ); +assert(all(toList(0..3), i -> (Dc.dd_i == map QQ^0)) ); /// TEST /// @@ -91,10 +91,10 @@ F = DintegrationAll(I, {1,3,5}); FV = F#VResolution; FC = F#GenCycles; assert all(toList(0..3), i -> ( - apps = Dtransposition ((FV.dd#i)*(FC#i)); + apps = Dtransposition ((FV.dd_i)*(FC#i)); if (apps == 0) then true - else (apps % directSum apply(toList(1..rank target apps), - j -> matrix{W.dpairVars#1}) == 0) )); + else (apps % map(target apps, , directSum apply(toList(1..rank target apps), + j -> matrix{W.dpairVars#1})) == 0) )); /// TEST /// diff --git a/M2/Macaulay2/packages/Bertini/TST/makeWitnessSetFiles.tst.m2 b/M2/Macaulay2/packages/Bertini/TST/makeWitnessSetFiles.tst.m2 index 5867217d00e..80773fff474 100644 --- a/M2/Macaulay2/packages/Bertini/TST/makeWitnessSetFiles.tst.m2 +++ b/M2/Macaulay2/packages/Bertini/TST/makeWitnessSetFiles.tst.m2 @@ -13,7 +13,7 @@ runBertini(storeBM2Files) makeWitnessSetFiles(storeBM2Files,2)--creats a witness point file for all dimension 2 components and a linear slice file for dimension 2 components. --When we import the SliceFile, we get a list. ----The first element of the list is a list of pairs of coefficient names and their value. -----The second element of the list is a list of pairs of names of linear functions and the lieanr function. +----The second element of the list is a list of pairs of names of linear functions and the linear function. L=importSliceFile(storeBM2Files) assert(#L==2) assert (#L_0==8) diff --git a/M2/Macaulay2/packages/BettiCharacters.m2 b/M2/Macaulay2/packages/BettiCharacters.m2 index 09f86e2e6e4..2ffe347b380 100644 --- a/M2/Macaulay2/packages/BettiCharacters.m2 +++ b/M2/Macaulay2/packages/BettiCharacters.m2 @@ -1,5 +1,5 @@ -------------------------------------------------------------------------------- --- Copyright 2021-2023 Federico Galetto +-- Copyright 2021-2026 Federico Galetto -- -- This program is free software: you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free Software @@ -18,16 +18,16 @@ newPackage( "BettiCharacters", - Version => "2.1", - Date => "February 26, 2023", + Version => "2.6", + Date => "Dec 22, 2025", AuxiliaryFiles => false, Authors => {{Name => "Federico Galetto", Email => "galetto.federico@gmail.com", HomePage => "http://math.galetto.org"}}, Headline => "finite group characters on free resolutions and graded modules", DebuggingMode => false, - Keywords => {"Commutative Algebra"}, PackageExports => {"Complexes"}, + Keywords => {"Commutative Algebra"}, Certification => { "journal name" => "Journal of Software for Algebra and Geometry", "journal URI" => "https://msp.org/jsag/", @@ -55,16 +55,18 @@ export { "CharacterDecomposition", "CharacterTable", "decomposeCharacter", - "inverseRingActors", + "degreeOrbit", + "degreeRepresentative", "Labels", - "numActors", "ringActors", + "Semidirect", "Sub", "symmetricGroupActors", - "symmetricGroupTable" + "symmetricGroupTable", + "hyperoctahedralGroupTable", + "hyperoctahedralGroupActors" } - ---------------------------------------------------------------------- -- Types ---------------------------------------------------------------------- @@ -76,27 +78,29 @@ Action = new Type of HashTable ActionOnComplex = new Type of Action ActionOnGradedModule = new Type of Action --- equality check for actions implemented below - --- equality for characters as raw hash tables -Character == Character := (A,B) -> A === B - ---------------------------------------------------------------------- -- Characters and character tables ----------------------------------- ---------------------------------------------------------------------- +-- function to take a single degree and make it into a list +-- this is the default degreeOrbit function to be used by +-- all actions GxT where T is the torus that gives the grading +-- actions by semidirect products G⋉T need a different function +bracketize := d -> {d} + -- method for returning characters of various action types -character = method(TypicalValue=>Character) +character = method(TypicalValue=>Character,Options=>{Semidirect=>(bracketize,identity)}) -- construct a finite dimensional character by hand +-- new constructor with v2.5 -- INPUT: --- 1) polynomial ring (dictates coefficients and degrees) --- 2) integer: character length (or number of actors) --- 3) hash table for raw character: (homdeg,deg) => character matrix -character(PolynomialRing,ZZ,HashTable) := Character => (R,cl,H) -> ( - -- check first argument is a polynomial ring over a field - if not isField coefficientRing R then ( - error "character: expected polynomial ring over a field"; +-- 1) polynomial ring (over a field) +-- 2) hash table for raw character: (homdeg,deg) => character matrix +character(PolynomialRing,HashTable) := Character => op -> (R,H) -> ( + -- check polynomial ring is over a field + F := coefficientRing R; + if not isField F then ( + error "character: expected degree ring over a field"; ); -- check keys are in the right format k := keys H; @@ -104,8 +108,12 @@ character(PolynomialRing,ZZ,HashTable) := Character => (R,cl,H) -> ( class i#0 =!= ZZ or class i#1 =!= List) then ( error "character: expected keys of the form (ZZ,List)"; ); + -- check if character ring is already present + -- if not, construct it and cache it + DR := R#cache#degreesRing ??= F degreesMonoid R; + -- get degree length + dl := numgens DR; -- check degree vectors are allowed - dl := degreeLength R; degs := apply(k,last); if any(degs, i -> #i != dl or any(i, j -> class j =!= ZZ)) then ( error ("character: expected integer degree vectors of length " | toString(dl)); @@ -115,124 +123,214 @@ character(PolynomialRing,ZZ,HashTable) := Character => (R,cl,H) -> ( if any(v, i -> class i =!= Matrix) then ( error "character: expected characters to be matrices"; ); + if any(v, i -> numRows i != 1) then ( + error ("character: expected characters to be one-row matrices"); + ); + cl := numColumns first v; if any(v, i -> numColumns i != cl) then ( - error ("character: expected characters to be one-row matrices with " | toString(cl) | " columns"); + error ("character: expected matrices to have the same number of columns"); ); -- move character values into given ring - H2 := try applyValues(H, v -> promote(v,R)) else ( - error "character: could not promote characters to given ring"; + H = try applyValues(H, v -> promote(v,F)) else ( + error "character: could not promote characters to field of coefficients"; ); + -- partition keys by hom degree, then extract internal degree + -- so {(0,{0}),(0,{1})} goes to 0=>{(0,{0}),(0,{1})} + pk := partition(i -> i#0,k); + -- for each hom degree, multiply each char matrix with degree monomial + -- then add them all + H' := applyValues(pk, l -> sum apply(l, d -> (H#d) * DR_(d#1)) ); new Character from { cache => new CacheTable, - (symbol ring) => R, + (symbol degreesRing) => DR, + (symbol degreeOrbit) => first op.Semidirect, + (symbol degreeRepresentative) => last op.Semidirect, (symbol numActors) => cl, - (symbol characters) => H2, + (symbol characters) => H', } ) +-- equality for characters as raw hash tables +Character == Character := (A,B) -> A === B + -- direct sum of characters -- modeled after code in Macaulay2/Core/matrix.m2 +-- plus and + added after v2.1 to match difference +Character + Character := Character => directSum +plus(Character,Character) := Character => directSum Character ++ Character := Character => directSum directSum Character := c -> Character.directSum (1 : c) Character.directSum = args -> ( - -- check ring is the same for all summands - R := (args#0).ring; - if any(args, c -> c.ring =!= R) - then error "directSum: expected characters all over the same ring"; + -- check degreesRing is the same for all summands + DR := (args#0).degreesRing; + if any(args, c -> c.degreesRing =!= DR) + then error "directSum: expected characters over the same ring"; -- check character length is the same for all summands cl := (args#0).numActors; if any(args, c -> c.numActors != cl) then error "directSum: expected characters all of the same length"; + -- check degreeOrbit is the same for all summands + degOrb := (args#0).degreeOrbit; + if any(args, c -> c.degreeOrbit =!= degOrb) + then error "directSum: characters have different degree orbit functions"; + -- check degreeRepresentative is the same for all summands + degRep := (args#0).degreeRepresentative; + if any(args, c -> c.degreeRepresentative =!= degRep) + then error "directSum: characters have different degree representative functions"; + -- raw character of direct sum (could have zero entries) + H := fold( (c1,c2) -> merge(c1,c2,plus), apply(args, c -> c.characters) ); new Character from { cache => new CacheTable, - (symbol ring) => R, + (symbol degreesRing) => DR, + (symbol degreeOrbit) => degOrb, + (symbol degreeRepresentative) => degRep, (symbol numActors) => cl, -- add raw characters - (symbol characters) => fold( (c1,c2) -> merge(c1,c2,plus), - apply(args, c -> c.characters) ), + (symbol characters) => applyPairs(H,(k,v)->if not zero v then (k,v)), } ) --- tensor product of characters (auxiliary functions) --- function to add sequences (homological,internal) degrees -addDegrees = (d1,d2) -> apply(d1,d2,plus) - -- function to multiply character matrices (Hadamard product) multiplyCharacters = (c1,c2) -> ( e1 := flatten entries c1; e2 := flatten entries c2; m := apply(e1,e2,times); - matrix{m} + -- ensure char matrix has right degrees + map(target c1,source c1,matrix{m}) ) -- tensor product of characters -- modeled after directSum, but only works for two characters Character ** Character := Character => tensor tensor(Character,Character) := Character => {} >> opts -> (c1,c2) -> ( - -- check ring is the same for all factors - R := c1.ring; - if (c2.ring =!= R) + -- check degreesRing is the same for all factors + DR := c1.degreesRing; + if (c2.degreesRing =!= DR) then error "tensor: expected characters all over the same ring"; -- check character length is the same for all summands cl := c1.numActors; if (c2.numActors != cl) then error "tensor: expected characters all of the same length"; + -- check degreeOrbit is the same for all summands + degOrb := c1.degreeOrbit; + if (c2.degreeOrbit =!= degOrb) + then error "tensor: characters have different degree orbit functions"; + -- check degreeRepresentative is the same for all summands + degRep := c1.degreeRepresentative; + if (c2.degreeRepresentative =!= degRep) + then error "tensor: characters have different degree representative functions"; + -- raw character of tensor product (may contain zeros) + -- homological degrees should be added, hence the first plus + -- raw characters should be Hadamard multiplied + -- if different homological degrees add up to the same value, + -- the corresponding characters should be added, hence the last plus + H := combine(c1.characters,c2.characters,plus,multiplyCharacters,plus); new Character from { cache => new CacheTable, - (symbol ring) => R, + (symbol degreesRing) => DR, + (symbol degreeOrbit) => degOrb, + (symbol degreeRepresentative) => degRep, (symbol numActors) => cl, -- multiply raw characters - (symbol characters) => combine(c1.characters,c2.characters, - addDegrees,multiplyCharacters,plus) + (symbol characters) => applyPairs(H,(k,v)->if not zero v then (k,v)) } ) +-- tensor power (new in v2.5) +-- M2 uses BinaryPowerMethod in M2/Macaulay2/d/actors.d +-- to construct tensor powers of rings, modules, etc. +-- however, this requires a constructor for the inverse/dual +-- which in the case of characters requires additional user input +-- we define a recursive tensor power only for positive exponents +Character ^** ZZ := Character => (c,n) -> ( + -- return error for negative exponents + if n < 0 then ( + error "Character ^** ZZ: not implemented for negative exponents; use dual"; + ) + -- for n=0, return trivial character in hom degree zero + else if n == 0 then ( + H := hashTable { + (0,matrix{toList(c.numActors:1_(c.degreesRing))}) + }; + new Character from { + cache => new CacheTable, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, + (symbol numActors) => c.numActors, + (symbol characters) => H + } + ) + -- for n=1, return original character + else if n == 1 then ( + c + ) + -- for n>=2, then reduce to lower power + else ( + tensor(c, c ^** (n-1) ) + ) + ) + -- shift homological degree of characters -Character Array := Character => (C,A) -> ( +Character Array := Character => (c,A) -> ( if # A =!= 1 then error "Character Array: expected array of length 1"; n := A#0; if not instance(n,ZZ) then error "Character Array: expected an integer"; new Character from { cache => new CacheTable, - (symbol ring) => C.ring, - (symbol numActors) => C.numActors, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, + (symbol numActors) => c.numActors, -- homological shift raw characters - (symbol characters) => applyKeys(C.characters, - k -> (k#0 - n, k#1)) + (symbol characters) => applyKeys(c.characters, + k -> k - n) } ) +-- character dual +-- borrowing default options from alexander dual method +alexopts = {Strategy=>0}; + -- character of dual/contragredient representation with conjugation -dual(Character,RingMap) := Character => {} >> o -> (c,phi) -> ( +dual(Character,RingMap) := Character => alexopts >> o -> (c,phi) -> ( -- check characteristic - R := c.ring; - if char(R) != 0 then ( + DR := c.degreesRing; + F := coefficientRing DR; + if char(F) != 0 then ( error "dual: use permutation constructor in positive characteristic"; ); -- check conjugation map - F := coefficientRing R; if (source phi =!= F or target phi =!= F or phi^2 =!= id_F) then ( - error "dual: expected an order 2 automorphism of the coefficient field"; + error "dual: expected an order 2 automorphism of the base field"; ); - -- error if characters cannot be lifted to coefficient field - H := try applyValues(c.characters, v -> lift(v,F)) else ( - error "dual: could not lift characters to coefficient field"; + -- ring map that inverts variables in degree ring + -- this sends a degree T^d to its opposite T^(-d) + inv := map(DR,DR,apply(gens DR, T -> T^(-1))); + -- create hash table for raw dual + H := applyPairs(c.characters, + (k,v) -> ( + (M,C) := coefficients v; + M = inv M; + C = promote(phi(lift(C,F)),DR); + -- M2 1.26.06 changes degrees of M*C + -- use map to set those degrees back to 0 + (-k, map(DR^1,DR^(numColumns C),M*C)) + ) ); - -- conjugation map to the polynomial ring - Phi := map(R,F) * phi; new Character from { cache => new CacheTable, - (symbol ring) => R, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, (symbol numActors) => c.numActors, - (symbol characters) => applyPairs(H, - (k,v) -> ( apply(k,minus), Phi v ) - ) + (symbol characters) => H } ) -- character of dual/contragredient representation without conjugation -dual(Character,List) := Character => {} >> o -> (c,perm) -> ( +dual(Character,List) := Character => alexopts >> o -> (c,perm) -> ( n := c.numActors; if #perm != n then ( error "dual: expected permutation size to match character length"; @@ -241,30 +339,151 @@ dual(Character,List) := Character => {} >> o -> (c,perm) -> ( if set perm =!= set(1..n) then ( error ("dual: expected a permutation of {1,..," | toString(n) | "}"); ); + -- ring map that inverts variables in degree ring + -- this sends a degree T^d to its opposite T^(-d) + DR := c.degreesRing; + inv := map(DR,DR,apply(gens DR, T -> T^(-1))); new Character from { cache => new CacheTable, - (symbol ring) => c.ring, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, (symbol numActors) => n, + -- dual lives in opposite homological dimension + -- for internal degrees apply the inversion map + -- permute columns for values of dual character (symbol characters) => applyPairs(c.characters, - (k,v) -> ( apply(k,minus), v_(apply(perm, i -> i-1)) ) + (k,v) -> (-k, (inv v)_(apply(perm, i -> i-1))) ) } ) +-- extract character by homological dimension (added after v2.1) +Character _ ZZ := Character => (c,i) -> ( + H := select(pairs c.characters, p -> first p == i); + new Character from { + cache => new CacheTable, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, + (symbol numActors) => c.numActors, + (symbol characters) => hashTable H + } + ) + +-- extract several characters by hom dim (added after v2.1) +Character _ List := Character => (c,l) -> ( + if any(l, i -> not instance(i,ZZ)) then ( + error "Character_List: expected a list of integers"; + ); + -- unique avoids adding pieces of the character multiple times + directSum(apply(unique l, i -> c_i)) + ) + +-- extract characters by degree (added after v2.1) +Character ^ List := Character => (c,degs) -> ( + -- if single degree, repackage as list (defer checks) + if any(degs,i->not instance(i,List)) then ( + degs = {degs}; + ); + -- check all degrees are compatible + dl := numgens c.degreesRing; + if all(degs,d->all(d,i->instance(i,ZZ)) and #d==dl) then ( + -- find all degrees in the orbit of degs and remove duplicates + exps := unique flatten apply(degs, d -> c.degreeOrbit d); + -- get corresponding monomials in character ring + DR := c.degreesRing; + mons := apply(exps, e -> DR_e); + -- extract those monomials from character + H := applyValues(c.characters, v -> ( + (M,C) := coefficients(v,Monomials=>mons); + M*C + ) + ); + return new Character from { + cache => new CacheTable, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, + (symbol numActors) => c.numActors, + (symbol characters) => H + } + ) else ( + error ("Character^List: expected a (list of) (multi)degree(s) of length " | toString(dl)); + ); + ) + +-- multiplication of character with a scalar (added after v2.1) +ZZ * Character := +QQ * Character := +RingElement * Character := Character => (r,c) -> ( + try a := promote(r,c.degreesRing) else ( + error "RingElement*Character: could not promote scalar to field of character"; + ); + H := applyPairs(c.characters,(k,v)->( + w := a*v; + if not zero w then (k,w) + ) + ); + new Character from { + cache => new CacheTable, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, + (symbol numActors) => c.numActors, + (symbol characters) => H + } + ) + +-- for commutativity +Character * ZZ := +Character * QQ := +Character * RingElement := Character => (c,r) -> r*c + +-- additive inverse of a character (added after v2.1) +- Character := +minus Character := Character => c -> ( + new Character from { + cache => new CacheTable, + (symbol degreesRing) => c.degreesRing, + (symbol degreeOrbit) => c.degreeOrbit, + (symbol degreeRepresentative) => c.degreeRepresentative, + (symbol numActors) => c.numActors, + (symbol characters) => applyValues(c.characters,v->-v) + } + ) + +-- difference of characters (added after v2.1) +Character - Character := +difference(Character,Character) := Character => +(c1,c2) -> directSum(c1,-c2) + + + +---------------------------------------------------------------------- +-- Character tables and decompositions ------------------------------- +---------------------------------------------------------------------- + -- method to construct character tables characterTable = method(TypicalValue=>CharacterTable,Options=>{Labels => {}}); -- character table constructor using conjugation +-- modified after v2.1 to be defined over a field +-- modified after v2.1 to allow TeX labels -- INPUT: -- 1) list of conjugacy class sizes -- 2) matrix of irreducible character values --- 3) ring over which to construct the table +-- 3) field over which to construct the table -- 4) ring map, conjugation of coefficients --- OPTIONAL: list of labels for irreducible characters -characterTable(List,Matrix,PolynomialRing,RingMap) := CharacterTable => -o -> (conjSize,charTable,R,phi) -> ( +-- OPTIONAL: lists of labels for irreducible characters +characterTable(List,Matrix,Ring,RingMap) := CharacterTable => +o -> (conjSize,charTable,F,phi) -> ( + -- check third argument is a field + if not isField F then ( + error "characterTable: expected third argument to be a field"; + ); -- check characteristic - if char(R) != 0 then ( + if char(F) != 0 then ( error "characterTable: use permutation constructor in positive characteristic"; ); n := #conjSize; @@ -272,56 +491,70 @@ o -> (conjSize,charTable,R,phi) -> ( if numRows charTable != n or numColumns charTable != n then ( error "characterTable: expected matrix size to match number of conjugacy classes"; ); - -- promote character matrix to R - X := try promote(charTable,R) else ( - error "characterTable: could not promote character table to given ring"; + -- promote character matrix to F + X := try promote(charTable,F) else ( + error "characterTable: could not promote character table to given field"; ); - -- check conjugation map - F := coefficientRing R; if (source phi =!= F or target phi =!= F or phi^2 =!= id_F) then ( error "characterTable: expected an order 2 automorphism of the coefficient ring"; ); -- check orthogonality relations ordG := sum conjSize; - C := diagonalMatrix(R,conjSize); - Phi := map(R,F) * phi; - m := C*transpose(Phi charTable); + C := diagonalMatrix(F,conjSize); + m := C*transpose(phi charTable); -- if x is a character in a one-row matrix, then x*m is the one-row matrix -- containing the inner products of x with the irreducible characters - if X*m != ordG*map(R^n) then ( + if X*m != ordG*map(F^n) then ( error "characterTable: orthogonality relations not satisfied"; ); - -- check user labels or create default ones + -- get user labels or create default ones if o.Labels == {} then ( - l := for i to n-1 list "X"|toString(i); - ) else ( - if #o.Labels != n then ( - error ("characterTable: expected " | toString(n) | " labels"); - ); - if not all(o.Labels, i -> instance(i, Net)) then ( - error "characterTable: expected labels to be strings (or nets)"; - ); - l = o.Labels; + netLabels := for i to n-1 list net(expression("ê­“")_(expression i)); + texLabels := for i to n-1 list ("\\chi_{" | toString(i) | "}"); + ) + else if (#o.Labels == 2 and all(o.Labels,x -> class x === List)) then ( + netLabels = first o.Labels; + texLabels = last o.Labels; + ) + else ( + netLabels = o.Labels; + texLabels = o.Labels; + ); + -- check labels have the right format + if (#netLabels != n or #texLabels != n) then ( + error ("characterTable: expected " | toString(n) | " labels"); + ); + if not all(netLabels, i -> instance(i, Net)) then ( + error "characterTable: expected labels to be strings (or nets)"; + ); + if not all(texLabels, i -> instance(i, Net)) then ( + error "characterTable: expected labels to be strings (or nets)"; ); new CharacterTable from { (symbol numActors) => #conjSize, (symbol size) => conjSize, (symbol table) => X, - (symbol ring) => R, + (symbol ring) => F, (symbol matrix) => m, - (symbol Labels) => l, + (symbol Labels) => {netLabels,texLabels}, } ) -- character table constructor without conjugation +-- modified after v2.1 to be defined over a field +-- modified after v2.1 to allow TeX labels -- INPUT: -- 1) list of conjugacy class sizes -- 2) matrix of irreducible character values --- 3) ring over which to construct the table +-- 3) field over which to construct the table -- 4) list, permutation of conjugacy class inverses --- OPTIONAL: list of labels for irreducible characters -characterTable(List,Matrix,PolynomialRing,List) := CharacterTable => -o -> (conjSize,charTable,R,perm) -> ( +-- OPTIONAL: lists of labels for irreducible characters +characterTable(List,Matrix,Ring,List) := CharacterTable => +o -> (conjSize,charTable,F,perm) -> ( + -- check third argument is a field + if not isField F then ( + error "characterTable: expected third argument to be a field"; + ); n := #conjSize; -- check all arguments have the right size if numRows charTable != n or numColumns charTable != n then ( @@ -330,9 +563,9 @@ o -> (conjSize,charTable,R,perm) -> ( if #perm != n then ( error "characterTable: expected permutation size to match number of conjugacy classes"; ); - -- promote character matrix to R - X := try promote(charTable,R) else ( - error "characterTable: could not promote character table to given ring"; + -- promote character matrix to F + X := try promote(charTable,F) else ( + error "characterTable: could not promote character table to given field"; ); -- check permutation has the right entries if set perm =!= set(1..n) then ( @@ -340,40 +573,54 @@ o -> (conjSize,charTable,R,perm) -> ( ); -- check characteristic ordG := sum conjSize; - if ordG % char(R) == 0 then ( + if ordG % char(F) == 0 then ( error "characterTable: characteristic divides order of the group"; ); -- check orthogonality relations - C := diagonalMatrix(R,conjSize); - P := map(R^n)_(apply(perm, i -> i-1)); + C := diagonalMatrix(F,conjSize); + P := map(F^n)_(apply(perm, i -> i-1)); m := C*transpose(X*P); -- if x is a character in a one-row matrix, then x*m is the one-row matrix -- containing the inner products of x with the irreducible characters - if X*m != ordG*map(R^n) then ( + if X*m != ordG*map(F^n) then ( error "characterTable: orthogonality relations not satisfied"; ); - -- check user labels or create default ones + -- get user labels or create default ones if o.Labels == {} then ( - l := for i to n-1 list "X"|toString(i); - ) else ( - if #o.Labels != n then ( - error ("characterTable: expected " | toString(n) | " labels"); - ); - if any(o.Labels, i -> class i =!= String and class i =!= Net) then ( - error "characterTable: expected labels to be strings (or nets)"; - ); - l = o.Labels; + netLabels := for i to n-1 list net(expression("ê­“")_(expression i)); + texLabels := for i to n-1 list ("\\chi_{" | toString(i) | "}"); + ) + else if (#o.Labels == 2 and all(o.Labels,x -> class x === List)) then ( + netLabels = first o.Labels; + texLabels = last o.Labels; + ) + else ( + netLabels = o.Labels; + texLabels = o.Labels; + ); + -- check labels have the right format + if (#netLabels != n or #texLabels != n) then ( + error ("characterTable: expected " | toString(n) | " labels"); + ); + if not all(netLabels, i -> instance(i, Net)) then ( + error "characterTable: expected labels to be strings (or nets)"; + ); + if not all(texLabels, i -> instance(i, Net)) then ( + error "characterTable: expected labels to be strings (or nets)"; ); new CharacterTable from { (symbol numActors) => #conjSize, (symbol size) => conjSize, (symbol table) => X, - (symbol ring) => R, + (symbol ring) => F, (symbol matrix) => m, - (symbol Labels) => l, + (symbol Labels) => {netLabels,texLabels}, } ) +-- equality for character tables as raw hash tables +CharacterTable == CharacterTable := (A,B) -> A === B + -- new method for character decomposition decomposeCharacter = method(TypicalValue=>CharacterDecomposition); @@ -381,26 +628,26 @@ decomposeCharacter = method(TypicalValue=>CharacterDecomposition); decomposeCharacter(Character,CharacterTable) := CharacterDecomposition => (C,T) -> ( -- check character and table are over same ring - R := C.ring; - if T.ring =!= R then ( - error "decomposeCharacter: expected character and table over the same ring"; + F := coefficientRing C.degreesRing; + if T.ring =!= F then ( + error "decomposeCharacter: expected character and table over the same field"; ); -- check number of actors is the same if C.numActors != T.numActors then ( error "decomposeCharacter: character length does not match table"; ); + -- order of the group = sum of conjugacy class sizes ord := sum T.size; -- create decomposition hash table D := applyValues(C.characters, char -> 1/ord*char*T.matrix); - -- find non zero columns of table for printing - M := matrix apply(values D, m -> flatten entries m); - p := positions(toList(0..numColumns M - 1), i -> M_i != 0*M_0); new CharacterDecomposition from { + cache => new CacheTable, (symbol numActors) => C.numActors, - (symbol ring) => R, - (symbol Labels) => T.Labels, + (symbol degreesRing) => C.degreesRing, + (symbol degreeOrbit) => C.degreeOrbit, + (symbol degreeRepresentative) => C.degreeRepresentative, (symbol decompose) => D, - (symbol positions) => p + (symbol Labels) => T.Labels } ) @@ -409,10 +656,21 @@ Character / CharacterTable := CharacterDecomposition => decomposeCharacter -- recreate a character from decomposition character(CharacterDecomposition,CharacterTable) := -Character => (D,T) -> ( +Character => op -> (D,T) -> ( + -- check decomposition and table are over same ring + F := coefficientRing D.degreesRing; + if T.ring =!= F then ( + error "character: expected decomposition and table over the same field"; + ); + -- check number of actors is the same + if D.numActors != T.numActors then ( + error "character: decomposition and table have different number of actors"; + ); new Character from { cache => new CacheTable, - (symbol ring) => D.ring, + (symbol degreesRing) => D.degreesRing, + (symbol degreeOrbit) => D.degreeOrbit, + (symbol degreeRepresentative) => D.degreeRepresentative, (symbol numActors) => D.numActors, (symbol characters) => applyValues(D.decompose, i -> i*T.table), } @@ -421,6 +679,9 @@ Character => (D,T) -> ( -- shortcut to recreate character from decomposition CharacterDecomposition * CharacterTable := Character => character +-- equality for character decompositions as raw hash tables +CharacterDecomposition == CharacterDecomposition := (A,B) -> A === B + ---------------------------------------------------------------------- -- Actions on complexes and characters of complexes ------------------ ---------------------------------------------------------------------- @@ -429,7 +690,9 @@ CharacterDecomposition * CharacterTable := Character => character -- optional argument Sub=>true means ring actors are passed -- as one-row matrices of substitutions, Sub=>false means -- ring actors are passed as matrices -action = method(TypicalValue=>Action,Options=>{Sub=>true}) +-- Semidirect option (added after v2.2) +action = method(TypicalValue=>Action, + Options=>{Sub=>true,Semidirect=>(bracketize,identity)}) -- constructor for action on resolutions -- INPUT: @@ -437,13 +700,15 @@ action = method(TypicalValue=>Action,Options=>{Sub=>true}) -- 2) a list of actors on the ring variables -- 3) a list of actors on the i-th module of the resolution -- 4) homological index i -action(Complex,List,List,ZZ):=ActionOnComplex=>op->(C,l,l0,i) -> ( - --check C is a homogeneous min free res over a poly ring over a field +action(Complex,List,List,ZZ) := ActionOnComplex => op -> (C,l,l0,i) -> ( + --check C is a homogeneous complex over a poly ring over a field + --NOTE: minimality is necessary, but assumed R := ring C; if not isPolynomialRing R then ( error "action: expected a complex over a polynomial ring"; ); - if not isField coefficientRing R then ( + F := coefficientRing R; + if not isField F then ( error "action: expected coefficients in a field"; ); if not all(length C,i -> isFreeModule C_(i+min(C))) then ( @@ -453,29 +718,34 @@ action(Complex,List,List,ZZ):=ActionOnComplex=>op->(C,l,l0,i) -> ( error "action: complex is not homogeneous"; ); --check the matrix of the action on the variables has right size - n := dim R; + n := numgens R; if not all(l,g->numColumns(g)==n) then ( error "action: ring actor matrix has wrong number of columns"; ); + --move ring actors to ring for uniformity + l = try apply(l, g -> promote(g,R)) else ( + error "action: could not promote actors to ring of complex"; + ); if op.Sub then ( - if not all(l,g->numRows(g)==1) then ( - error "action: expected ring actor matrix to be a one-row substitution matrix"; + --if ring actors are substitutions, they must be one-row matrices + if not all(l,g->numRows(g)==1) then ( + error "action: expected ring actors to be a one-row matrices"; ); - --convert variable substitutions to matrices - l=apply(l,g->lift(g,R)//(vars R)); ) else ( - --if ring actors are matrices they must be square - if not all(l,g->numRows(g)==n) then ( + --if ring actors are matrices, they must be square + if not all(l,g->numRows(g)==n) then ( error "action: ring actor matrix has wrong number of rows"; ); - --lift action matrices to R for uniformity with - --input as substitutions - l=apply(l,g->promote(g,R)); + --convert them to substitutions + l = apply(l, g -> (vars R) * g); ); --check list of group elements has same length if #l != #l0 then ( error "action: lists of actors must have equal length"; ); + -- check if character ring is already present + -- if not, construct it and cache it + DR := R#cache#degreesRing ??= F degreesMonoid R; --check size of module actors matches rank of starting module r := rank C_i; if not all(l0,g->numColumns(g)==r and numRows(g)==r) then ( @@ -490,7 +760,9 @@ action(Complex,List,List,ZZ):=ActionOnComplex=>op->(C,l,l0,i) -> ( (symbol target) => C, (symbol numActors) => #l, (symbol ringActors) => l, - (symbol inverseRingActors) => apply(l,inverse), + (symbol degreesRing) => DR, + (symbol degreeOrbit) => first op.Semidirect, + (symbol degreeRepresentative) => last op.Semidirect, } ) @@ -499,7 +771,7 @@ action(Complex,List,List,ZZ):=ActionOnComplex=>op->(C,l,l0,i) -> ( action(Complex,List) := ActionOnComplex => op -> (C,l) -> ( R := ring C; l0 := toList(#l:(id_(R^1))); - action(C,l,l0,min C,Sub=>op.Sub) + action(C,l,l0,min C,Sub=>op.Sub,Semidirect=>op.Semidirect) ) -- equality check for actions on complexes @@ -524,91 +796,117 @@ numActors(Action) := ZZ => A -> A.numActors -- Sub=>false returns square matrices ringActors = method(TypicalValue=>List,Options=>{Sub=>true}) ringActors(Action) := List => op -> A -> ( - if op.Sub then apply(A.ringActors,g->(vars ring A)*g) + if not op.Sub then ( + GB := gb(vars A.ring,StopWithMinimalGenerators=>true,ChangeMatrix=>true); + apply(A.ringActors,g->g//GB) + ) else A.ringActors ) --- returns the inverses of the actors on ring variables --- same options as ringActors -inverseRingActors = method(TypicalValue=>List,Options=>{Sub=>true}) -inverseRingActors(Action) := List => op -> A -> ( - if op.Sub then apply(A.inverseRingActors,g->(vars ring A)*g) - else A.inverseRingActors - ) - -- returns various group actors actors = method(TypicalValue=>List) -- returns actors on resolution in a given homological degree --- if homological degree is not the one passed by user, +-- if homological degree is not the one passed by user upon construction, -- the actors are computed and stored actors(ActionOnComplex,ZZ) := List => (A,i) -> ( - -- homological degrees where action is already cached - places := apply(keys A.cache, k -> k#1); - C := target A; - if zero(C_i) then return toList(numActors(A):map(C_i)); - if i > max places then ( - -- function for actors of A in hom degree i - f := A -> apply(inverseRingActors A,actors(A,i-1), - -- given a map of free modules C.dd_i : F <-- F', - -- the inverse group action on the ring (as substitution) - -- and the group action on F, computes the group action on F' - (gInv,g0) -> (g0*C.dd_i)//sub(C.dd_i,gInv) - ); - -- make cache function from f and run it on A - ((cacheValue (symbol actors,i)) f) A - ) else ( - -- function for actors of A in hom degree i - f = A -> apply(inverseRingActors A,actors(A,i+1), - -- given a map of free modules C.dd_i : F <-- F', - -- the inverse group action on the ring (as substitution) - -- and the group action on F', computes the group action on F - (gInv,g0) -> C.dd_(i+1) \\ (sub(C.dd_(i+1), gInv) * g0) + -- if not cached, compute + if not A.cache#?(symbol actors,i) then ( + -- homological degrees where action is already cached + places := apply(select(keys A.cache, k -> instance(k,Sequence) and k#0 == symbol actors), k -> k#1); + -- get the complex + C := A.target; + -- if zero in that hom degree, return zeros + if zero(C_i) then return toList(A.numActors:map(C_i)); + -- if hom degree is to the right of previously computed + if i > max places then ( + -- compute GB of differential but only up to min gens + -- NOTE: does not work if ChangeMatrix=>false (which is default) + GB := gb(C.dd_i,StopWithMinimalGenerators=>true,ChangeMatrix=>true); + A.cache#(symbol actors,i) = + apply(A.ringActors, actors(A,i-1), + -- given a map of free modules C.dd_i : F <-- F', + -- the group action on the ring (as substitution) + -- and the group action on F, computes the group action on F' + (g,g0) -> g0*sub(C.dd_i,g)//GB + ); + ) + -- if hom degree is to the left of previously computed + else ( + -- may need to compute inverse of ring actors + if not A.cache.?inverse then ( + --convert variable substitutions to matrices + --then invert and convert back to substitutions + R := A.ring; + b := gb(vars R,StopWithMinimalGenerators=>true,ChangeMatrix=>true); + A.cache.inverse = apply(A.ringActors, g -> + (vars R) * (inverse lift(g//b,coefficientRing R)) + ); + ); + GB = gb(transpose(C.dd_(i+1)),StopWithMinimalGenerators=>true,ChangeMatrix=>true); + A.cache#(symbol actors,i) = + apply(A.cache.inverse,actors(A,i+1), + -- given a map of free modules C.dd_i : F <-- F', + -- the inverse group action on the ring (as substitution) + -- and the group action on F', computes the group action on F + (gInv,g0) -> ( + transpose(transpose(sub(C.dd_(i+1),gInv)*g0)//GB) + ) + ); ); - -- make cache function from f and run it on A - ((cacheValue (symbol actors,i)) f) A - ) + ); + -- return cached value + A.cache#(symbol actors,i) ) -- return the character of one free module of a resolution -- in a given homological degree -character(ActionOnComplex,ZZ) := Character => (A,i) -> ( - -- if complex is zero in hom degree i, return empty character - if zero (target A)_i then ( - return new Character from { - cache => new CacheTable, - (symbol ring) => ring A, - (symbol numActors) => numActors A, - (symbol characters) => hashTable {}, - }; - ); - -- function for character of A in hom degree i - f := A -> ( - -- separate degrees of i-th free module - degs := hashTable apply(unique degrees (target A)_i, d -> - (d,positions(degrees (target A)_i,i->i==d)) +character(ActionOnComplex,ZZ) := Character => op -> (A,i) -> ( + -- if not cached, compute + if not A.cache#?(symbol character,i) then ( + F := coefficientRing A.ring; + DR := A.degreesRing; + n := A.numActors; + -- if complex is zero in hom degree i, return empty character, don't cache + if zero (A.target)_i then ( + return new Character from { + cache => new CacheTable, + (symbol degreesRing) => DR, + (symbol degreeOrbit) => A.degreeOrbit, + (symbol degreeRepresentative) => A.degreeRepresentative, + (symbol numActors) => n, + (symbol characters) => hashTable {}, + }; ); -- create raw character from actors - H := applyPairs(degs, - (d,indx) -> ((i,d), - matrix{apply(actors(A,i), g -> trace g_indx^indx)} + a := actors(A,i); + r := rank((A.target)_i) - 1; + -- for each basis element extract corresponding diagonal entry + -- put it in a row matrix and multiply by degree, then add + -- this will give the graded raw character as a matrix + raw := sum parallelApply(toList(0..r), j -> ( + d := degree( ((A.target)_i)_j ); + lift(matrix{apply(a, g -> g_(j,j) )},F) * (DR_d) ) ); - new Character from { + -- cache character + A.cache#(symbol character,i) = new Character from { cache => new CacheTable, - (symbol ring) => ring A, - (symbol numActors) => numActors A, - (symbol characters) => H, - } + (symbol degreesRing) => DR, + (symbol degreeOrbit) => A.degreeOrbit, + (symbol degreeRepresentative) => A.degreeRepresentative, + (symbol numActors) => A.numActors, + (symbol characters) => hashTable {i=>raw}, + }; ); - -- make cache function from f and run it on A - ((cacheValue (symbol character,i)) f) A + -- return cached value + A.cache#(symbol character,i) ) -- return characters of all free modules in a resolution -- by repeatedly using previous function -character ActionOnComplex := Character => A -> ( - C := target A; +character ActionOnComplex := Character => op -> A -> ( + C := A.target; directSum for i from min(C) to min(C)+length(C) list character(A,i) ) @@ -624,7 +922,7 @@ character ActionOnComplex := Character => A -> ( action(PolynomialRing,List,List) := action(QuotientRing,List,List) := action(Ideal,List,List) := -action(Module,List,List):=ActionOnGradedModule=>op->(M,l,l0) -> ( +action(Module,List,List) := ActionOnGradedModule => op -> (M,l,l0) -> ( -- check M is graded over a poly ring over a field -- the way to get the ring depends on the class of M if instance(M,Ring) then ( @@ -635,31 +933,34 @@ action(Module,List,List):=ActionOnGradedModule=>op->(M,l,l0) -> ( if not isPolynomialRing R then ( error "action: expected a module/ideal/quotient over a polynomial ring"; ); - if not isField coefficientRing R then ( + F := coefficientRing R; + if not isField F then ( error "action: expected coefficients in a field"; ); if not isHomogeneous M then ( error "action: module/ideal/quotient is not graded"; ); --check matrix of action on variables has right size - n := dim R; + n := numgens R; if not all(l,g->numColumns(g)==n) then ( error "action: ring actor matrix has wrong number of columns"; ); + --move ring actors to ring for uniformity + l = try apply(l, g -> promote(g,R)) else ( + error "action: could not promote actors to ring of module"; + ); if op.Sub then ( + --if ring actors are substitutions, they must be one-row matrices if not all(l,g->numRows(g)==1) then ( - error "action: expected ring actor matrix to be a one-row substitution matrix"; + error "action: expected ring actors to be a one-row matrices"; ); - --convert variable substitutions to matrices - l=apply(l,g->lift(g,R)//(vars R)); ) else ( - --if ring actors are matrices they must be square - if not all(l,g->numRows(g)==n) then ( + --if ring actors are matrices, they must be square + if not all(l,g->numRows(g)==n) then ( error "action: ring actor matrix has wrong number of rows"; ); - --lift action matrices to R for uniformity with - --input as substitutions - l=apply(l,g->promote(g,R)); + --convert them to substitutions + l = apply(l, g -> (vars R) * g); ); --check list of group elements has same length if #l != #l0 then ( @@ -667,9 +968,9 @@ action(Module,List,List):=ActionOnGradedModule=>op->(M,l,l0) -> ( ); --check size of module actors matches rank of ambient module if instance(M,Module) then ( - F := ambient M; - ) else ( F = R^1; ); - r := rank F; + A := ambient M; + ) else ( A = R^1; ); + r := rank A; if not all(l0,g->numColumns(g)==r and numRows(g)==r) then ( error "action: module actor matrix has wrong number of rows or columns"; ); @@ -681,17 +982,22 @@ action(Module,List,List):=ActionOnGradedModule=>op->(M,l,l0) -> ( ) else ( M' = module M; ); + -- check if character ring is already present + -- if not, construct it and cache it + DR := R#cache#degreesRing ??= F degreesMonoid R; --store everything into a hash table new ActionOnGradedModule from { cache => new CacheTable, (symbol ring) => R, (symbol target) => M, + (symbol module) => M', (symbol numActors) => #l, (symbol ringActors) => l, - (symbol inverseRingActors) => apply(l,inverse), - (symbol actors) => apply(l0,g->map(F,F,g)), - (symbol module) => M', - (symbol relations) => image relations M', + (symbol actors) => apply(l0,g->map(A,A,g)), + (symbol relations) => gb image relations M', + (symbol degreesRing) => DR, + (symbol degreeOrbit) => first op.Semidirect, + (symbol degreeRepresentative) => last op.Semidirect, } ) @@ -707,7 +1013,7 @@ action(Module,List) := ActionOnGradedModule => op -> (M,l) -> ( ) else ( l0 = toList(#l:(id_(module ambient M))); ); - action(M,l,l0,Sub=>op.Sub) + action(M,l,l0,Sub=>op.Sub,Semidirect=>op.Semidirect) ) -- equality check for actions on graded modules @@ -718,60 +1024,97 @@ ActionOnGradedModule == ActionOnGradedModule := (A,B) -> A === B -- returns actors on component of given multidegree -- the actors are computed and stored actors(ActionOnGradedModule,List) := List => (A,d) -> ( - M := A.module; - -- get basis in degree d as map of free modules - -- how to get this depends on the class of M - b := ambient basis(d,M); - if zero b then return toList(numActors(A):map(source b)); - -- function for actors of A in degree d - f := A -> apply(ringActors A, A.actors, (g,g0) -> ( - --g0*b acts on the basis of the ambient module - --sub(-,g) acts on the polynomial coefficients - --result must be reduced against module relations - --then factored by original basis to get action matrix - (sub(g0*b,g) % A.relations) // b + -- ensure function is computed with rep of degree orbit + degRep := A.degreeRepresentative d; + -- if not cached, compute + if not A.cache#?(symbol actors,degRep) then ( + M := A.module; + -- get basis in degree d as map of free modules + -- (after semidirect: single degree d replaced by degree orbit) + degList := A.degreeOrbit d; + -- collect bases for degrees in orbit and join horizontally + b := rsort fold( (x,y) -> x|y, apply(degList, d -> ambient basis(d,M))); + -- the basis command returns a matrix with columns in decreasing order + -- joining basis from different degrees may break this order + -- the rsort at the beginning recovers M2's default order + -- this sorting was made necessary after introducing semidirect options + -- actors matrix would be useless without out as it may not match basis + if zero b then ( + A.cache#(symbol actors,degRep) = toList(A.numActors:map(source b)); ) + else ( + GB := gb(b,StopWithMinimalGenerators=>true,ChangeMatrix=>true); + A.cache#(symbol actors,degRep) = + apply(A.ringActors, A.actors, + --g0*b acts on the basis of the ambient module + --sub(-,g) acts on the polynomial coefficients + --result must be reduced against module relations + --then factored by original basis to get action matrix + (g,g0) -> (sub(g0*b,g) % A.relations) // GB + ); ); - -- make cache function from f and run it on A - ((cacheValue (symbol actors,d)) f) A + ); + -- return cached value + A.cache#(symbol actors,degRep) ) -- returns actors on component of given degree actors(ActionOnGradedModule,ZZ) := List => (A,d) -> actors(A,{d}) -- return character of component of given multidegree -character(ActionOnGradedModule,List) := Character => (A,d) -> ( - acts := actors(A,d); - if all(acts,zero) then ( - return new Character from { - cache => new CacheTable, - (symbol ring) => ring A, - (symbol numActors) => numActors A, - (symbol characters) => hashTable {}, - }; - ); - -- function for character of A in degree d - f := A -> ( - new Character from { - cache => new CacheTable, - (symbol ring) => ring A, - (symbol numActors) => numActors A, - (symbol characters) => hashTable {(0,d) => matrix{apply(acts, trace)}}, - } +character(ActionOnGradedModule,List) := Character => op -> (A,d) -> ( + -- ensure function is computed with rep of degree orbit + degRep := A.degreeRepresentative d; + -- if not cached, compute + if not A.cache#?(symbol character,degRep) then ( + F := coefficientRing A.ring; + DR := A.degreesRing; + -- zero action, return empty character and don't cache + acts := actors(A,degRep); + if all(acts,zero) then ( + return new Character from { + cache => new CacheTable, + (symbol degreesRing) => DR, + (symbol degreeOrbit) => A.degreeOrbit, + (symbol degreeRepresentative) => A.degreeRepresentative, + (symbol numActors) => A.numActors, + (symbol characters) => hashTable {}, + }; + ); + -- otherwise make character of A in degree d + -- create raw character from actors + r := (numRows first acts) - 1; + degList := degrees source first acts; + -- for each basis element extract corresponding diagonal entry + -- put it in a row matrix and multiply by degree, then add + -- this will give the graded raw character as a matrix + raw := sum parallelApply(toList(0..r), j -> ( + d := degList_j; + lift(matrix{apply(acts, g -> g_(j,j) )},F) * (DR_d) + ) + ); + A.cache#(symbol character,degRep) = new Character from { + cache => new CacheTable, + (symbol degreesRing) => DR, + (symbol degreeOrbit) => A.degreeOrbit, + (symbol degreeRepresentative) => A.degreeRepresentative, + (symbol numActors) => A.numActors, + (symbol characters) => hashTable {0 => raw}, + }; ); - -- make cache function from f and run it on A - ((cacheValue (symbol character,d)) f) A + -- return cached value + A.cache#(symbol character,degRep) ) -- return character of component of given degree -character(ActionOnGradedModule,ZZ) := Character => (A,d) -> ( +character(ActionOnGradedModule,ZZ) := Character => op -> (A,d) -> ( character(A,{d}) ) -- return character of components in a range of degrees -character(ActionOnGradedModule,ZZ,ZZ) := Character => (A,lo,hi) -> ( - if not all(gens ring A, v->(degree v)=={1}) then ( - error "character: expected a ZZ-graded polynomial ring"; +character(ActionOnGradedModule,ZZ,ZZ) := Character => op -> (A,lo,hi) -> ( + if degreeLength A.ring != 1 then ( + error "character: expected module over a ZZ-graded polynomial ring"; ); directSum for d from lo to hi list character(A,d) ) @@ -831,49 +1174,51 @@ murnaghanNakayama = memoize murnaghanNakayama -- symmetric group character table symmetricGroupTable = method(TypicalValue=>CharacterTable); -symmetricGroupTable PolynomialRing := R -> ( - -- check argument is a polynomial ring over a field - if not isField coefficientRing R then ( - error "symmetricGroupTable: expected polynomial ring over a field"; - ); - -- check number of variables - n := dim R; +symmetricGroupTable(ZZ,Ring) := (n,F) -> ( + -- check n is at least one if n < 1 then ( - error "symmetricGroupTable: expected a positive number of variables"; + error "symmetricGroupTable: expected first argument to be a positive integer"; + ); + -- check second argument is a field + if not isField F then ( + error "symmetricGroupTable: expected second argument to be a field"; ); -- check characteristic - if n! % (char R) == 0 then ( - error ("symmetricGroupTable: expected characteristic not dividing " | toString(n) | "!"); + if n! % (char F) == 0 then ( + error ("symmetricGroupTable: expected field characteristic not dividing " | toString(n) | "!"); ); -- list partitions P := apply(partitions n, toList); -- compute table using Murnaghan-Nakayama -- uses murnaghanNakayama unexported function with -- code in BettiCharacters.m2 immediately before this method - X := matrix(R, table(P,P,murnaghanNakayama)); + X := matrix(F, table(P,P,murnaghanNakayama)); -- compute size of conjugacy classes conjSize := apply(P/tally, t -> n! / product apply(pairs t, (k,v) -> k^v*v! ) ); -- matrix for inner product - m := diagonalMatrix(R,conjSize)*transpose(X); + m := diagonalMatrix(F,conjSize)*transpose(X); + -- prepare labels + pows := apply(P/tally,t -> apply(rsort keys t, k -> Power(k,t#k))); + netLabels := apply(pows,p -> "(" | horizontalJoin between(",",p/net) | ")"); + texLabels := apply(pows,p -> texMath toSequence p); new CharacterTable from { (symbol numActors) => #P, (symbol size) => conjSize, (symbol table) => X, - (symbol ring) => R, + (symbol ring) => F, (symbol matrix) => m, -- compact partition notation used for symmetric group labels - (symbol Labels) => apply(P, p -> ( - t := tally toList p; - pows := apply(rsort keys t, k -> net Power(k,t#k)); - commas := #pows-1:net(","); - net("(")|horizontalJoin mingle(pows,commas)|net(")") - ) - ) + (symbol Labels) => {netLabels,texLabels} } ) +-- symmetric group table for backwards compatibility +symmetricGroupTable PolynomialRing := R -> ( + symmetricGroupTable(numgens R,coefficientRing R) + ) + -- symmetric group variable permutation action symmetricGroupActors = method(); symmetricGroupActors PolynomialRing := R -> ( @@ -882,7 +1227,7 @@ symmetricGroupActors PolynomialRing := R -> ( error "symmetricGroupActors: expected polynomial ring over a field"; ); -- check number of variables - n := dim R; + n := numgens R; if n < 1 then ( error "symmetricGroupActors: expected a positive number of variables"; ); @@ -897,31 +1242,231 @@ symmetricGroupActors PolynomialRing := R -> ( ) ) +--------------------------------------------------------------------- +-- Specialized functions for hyperoctahedral groups (v2.6) ---------- +--------------------------------------------------------------------- + +-- auxiliary unexported function for the size of conjugacy classes of Hn +-- a conjugacy class of Hn is indexed by a bipartition (alpha,beta) +-- where alpha is the cycle type of balanced cycles and +-- beta is the cycle type of unbalanced cycles +hConjSize = (alpha, beta) -> ( + -- convert to lists + p := toList alpha; + q := toList beta; + -- get total size + n := (sum p)+(sum q); + -- size of conjugacy class of alpha in symmetric group + a := (sum p)! / product apply(pairs tally p, (k,v) -> k^v*v! ); + -- size of conjugacy class of beta in symmetric group + b := (sum q)! / product apply(pairs tally q, (k,v) -> k^v*v! ); + -- get weight from first partition + binomial(n,sum p) * a * b * 2^(n-#p-#q) + ) ----------------------------------------------------------------------- --- Overloaded Methods ----------------------------------------------------------------------- +-- auxiliary unexported function for the cycle type of a permutation +-- pass a permutation as a list which represents the 2nd row +-- of the 2-row notation, start from 0 +cycleType = sigma -> ( + used := {}; + rsort while #used < #sigma list ( + u := min toList( set(sigma) - set(used) ); + cycle := {}; + while not isMember(u,cycle) do ( + cycle = append(cycle,u); + u = sigma_u; + ); + used = used | cycle; + #cycle + ) + ) --- get object acted upon -target(Action) := A -> A.target +-- auxiliary unexported function for the value of an irreducible character of Hn +-- the irreducible character of Hn indexed by a bipartition (lambda,mu) +-- is evaluated at an element in the conjugacy class indexed by (alpha,beta) +hCharValue = (lambda,mu,alpha,beta) -> ( + -- get weight of first partition + k := sum toList lambda; + -- get weight of bipartition + n := k + sum toList mu; + -- list 0 to n-1, used a few times + N := toList(0..n-1); + -- form element of cycle type alpha,beta + -- as a 01-vector s and a permutation sigma + s := toList((sum toList alpha):0); + s = s | flatten for u in beta list ( {1} | toList(u-1:0) ); + L := N; + sigma := flatten for u in (toList alpha)|(toList beta) list ( + l := take(L,u); + L = drop(L,u); + rotate(1,l) + ); + -- get cosets of the Young subgroup Sk x S(n-k) + -- that contribute to the induced character + G := select(subsets(n,k), gamma -> isSubset(sigma_gamma,gamma)); + -- for each conjugacy class, compute contribution to character + sum for gamma in G list ( + -- restrict sigma to gamma + p := sigma_gamma; + -- and find its cycle type + -- convert p to a permutation on 0 to #p-1 + H := new HashTable from pack(2,mingle {sort p,toList(0..#p-1)}); + -- get the cycle type of p + cp := cycleType apply(p, i -> H#i); + -- restrict sigma to the complement of gamma + q := sigma_(N-set(gamma)); + -- and find its cycle type + -- convert q to a permutation on 0 to #q-1 + H = new HashTable from pack(2,mingle {sort q,toList(0..#q-1)}); + -- get the cycle type of q + cq := cycleType apply(q, i -> H#i); + -- pad gamma to a permutation of 0..n-1 + gammaN := gamma | (N-set(gamma)); + -- weight vector for the Z_2^n-action + vk := toList(k:0) | toList(n-k:1); + -- dot product of s and vk + e := sum apply(s_gammaN,vk, (i,j) -> i*j); + (-1)^e * murnaghanNakayama(toList lambda,cp) * murnaghanNakayama(toList mu,cq) + ) + ) --- get polynomial ring acted upon -ring Action := PolynomialRing => A -> A.ring +-- symmetric group character table +hyperoctahedralGroupTable = method(TypicalValue=>CharacterTable); +hyperoctahedralGroupTable(ZZ,Ring) := (n,F) -> ( + -- check n is at least one + if n < 1 then ( + error "hyperoctahedralGroupTable: expected first argument to be a positive integer"; + ); + -- check second argument is a field + if not isField F then ( + error "hyperoctahedralGroupTable: expected second argument to be a field"; + ); + -- check characteristic + if n! % (char F) == 0 then ( + error ("hyperoctahedralGroupTable: expected field characteristic not dividing " | toString(n) | "!*2^" | toString(n)); + ); + -- list bipartitions of n + B := flatten for i to n list ( + flatten table(partitions (n-i),partitions i, identity) + ); + -- make matrix of character table + X := matrix(F, table(B,B, (a,b) -> hCharValue(a_0,a_1,b_0,b_1))); + -- compute size of conjugacy classes + conjSize := apply(B, b -> hConjSize(b_0,b_1)); + -- matrix for inner product + m := diagonalMatrix(F,conjSize)*transpose(X); + -- prepare labels + bitallies := apply(B, b -> (tally toList b_0,tally toList b_1)); + -- turn tallies into powers, make empty tally into a single zero + pows := apply(bitallies, (a,b) -> + (if a === tally{} then ({Power(0,1)}) else (apply(rsort keys a, k -> Power(k,a#k))), + if b === tally{} then ({Power(0,1)}) else (apply(rsort keys b, k -> Power(k,b#k)))) + ); + netLabels := apply(pows, (a,b) -> + "(" | horizontalJoin between(",",a/net) | ";" | horizontalJoin between(",",b/net) | ")" + ); + texLabels := apply(pows,p -> ( + a := texMath toSequence p_0; + b := texMath toSequence p_1; + -- remove additional closing and opening parentheses + substring(0,#a-7,a) | ";" | substring(6,b) + ) + ); + new CharacterTable from { + (symbol numActors) => #B, + (symbol size) => conjSize, + (symbol table) => X, + (symbol ring) => F, + (symbol matrix) => m, + -- compact partition notation used for hyperoctahedral group labels + (symbol Labels) => {netLabels,texLabels} + } + ) +-- hyperoctahedral group variable permutation action +hyperoctahedralGroupActors = method(); +hyperoctahedralGroupActors PolynomialRing := R -> ( + -- check argument is a polynomial ring over a field + if not isField coefficientRing R then ( + error "hyperoctahedralGroupActors: expected polynomial ring over a field"; + ); + -- check number of variables + n := numgens R; + if n < 1 then ( + error "hyperoctahedralGroupActors: expected a positive number of variables"; + ); + flatten for i to n list ( + flatten for p in partitions(n-i) list ( + for q in partitions(i) list ( + L := gens R; + alpha := flatten for u in (toList p) list ( + l := take(L,u); + L = drop(L,u); + rotate(1,l) + ); + beta := flatten for u in (toList q) list ( + l := take(L,u); + L = drop(L,u); + take(l,-u+1) | {minus first l} + ); + matrix { alpha | beta } + ) + ) + ) + ) --------------------------------------------------------------------- -- Pretty printing of new types ------------------------------------- --------------------------------------------------------------------- -- printing for characters +-- the next function preps a character for printing by caching +-- a bigraded hash table of its data as before v2.5 +prepCharacter := c -> ( + DR := c.degreesRing; + F := coefficientRing DR; + -- go through homological degrees + -- collect multidegrees in the same orbit of the group action + -- and save a single character with the degree representative + h := new MutableHashTable; + for k in keys c.characters do ( + raw := c.characters#k; + mons := flatten entries monomials raw; + while mons =!= {} do ( + m := first mons; + d := c.degreeRepresentative first exponents m; + orbit := select(mons, f -> c.degreeRepresentative first exponents f == d); + C := lift(last coefficients(raw, Monomials=>orbit),F); + h#(k,d) = matrix{toList (numRows C:1_F)} * C; + mons = mons - set(orbit); + ); + ); + c.cache.print = new HashTable from h; + ) + +-- create net for pretty printing of character net Character := c -> ( - if c.characters =!= hashTable {} then ( - bottom := stack(" ", - stack (horizontalJoin \ apply(sort pairs c.characters, - (k,v) -> (net k, " => ", net v))) - ) - ) else bottom = null; - stack("Character over "|(net c.ring), bottom) + if not c.cache.?print then prepCharacter c; + bottom := apply(sort pairs c.cache.print, + (k,v) -> {net k} | apply(flatten entries v,net)); + F := coefficientRing c.degreesRing; + stack("Character over "|(net F)," ", + netList(bottom,BaseRow=>0,Alignment=>Right,Boxes=>{false,{1}},HorizontalSpace=>2)) + ) + +-- create tex string for characters +texMath Character := c -> ( + if not c.cache.?print then prepCharacter c; + -- make table headers, one column per actor + s := concatenate("\\begin{array}{c|",c.numActors:"r","}\n"); + -- character entries + rows := apply(sort pairs c.cache.print, + (k,v) -> concatenate(texMath k,"&", + between("&",apply(flatten entries v,texMath)) + ) + ); + -- assemble and close array + s | concatenate(between("\\\\ \n",rows),"\n\\end{array}") ) -- printing for character tables @@ -929,27 +1474,92 @@ net CharacterTable := T -> ( -- top row of character table a := {{""} | T.size}; -- body of character table - b := apply(pack(1,T.Labels),entries T.table,(i,j)->i|j); + b := apply(pack(1,first T.Labels),entries T.table,(i,j)->i|j); stack("Character table over "|(net T.ring)," ", netList(a|b,BaseRow=>1,Alignment=>Right,Boxes=>{{1},{1}},HorizontalSpace=>2) ) ) +-- tex string for character tables +texMath CharacterTable := T -> ( + -- make table headers, one column per actor + s := concatenate("\\begin{array}{c|",T.numActors:"r","}\n"); + -- print size of "conjugacy" classes + s = s | concatenate("&",between("&",apply(T.size,texMath)),"\\\\ \\hline\n"); + -- get matrix of table entries and convert to strings + M := for row in entries(T.table) list ( + concatenate(between("&",apply(row,texMath))) + ); + -- put character label in front of its row + M = apply(last T.Labels,M,(l,r)->l|"&"|r); + -- close the array + s | concatenate(between("\\\\ \n",M),"\n\\end{array}") + ) + -- printing character decompositions +-- the next function preps a character for printing by caching +-- a bigraded hash table of its data as before v2.5 +prepDecomposition := D -> ( + DR := D.degreesRing; + F := coefficientRing DR; + -- go through homological degrees + -- collect multidegrees in the same orbit of the group action + -- and save a single character with the degree representative + h := new MutableHashTable; + for k in keys D.decompose do ( + raw := D.decompose#k; + mons := flatten entries monomials raw; + while mons =!= {} do ( + m := first mons; + d := D.degreeRepresentative first exponents m; + orbit := select(mons, f -> D.degreeRepresentative first exponents f == d); + C := lift(last coefficients(raw, Monomials=>orbit),F); + h#(k,d) = matrix{toList (numRows C:1_F)} * C; + mons = mons - set(orbit); + ); + ); + D.cache.print = new HashTable from h; + ) + +-- create net for pretty printing of character decomposition net CharacterDecomposition := D -> ( - p := D.positions; + if not D.cache.?print then prepDecomposition D; + -- find non zero columns of table for printing + M := matrix apply(values D.decompose, m -> flatten entries m); + p := positions(toList(0..numColumns M - 1), i -> M_i != 0*M_0); -- top row of decomposition table - a := {{""} | D.Labels_p }; + a := {{""} | (first D.Labels)_p }; -- body of decomposition table - b := apply(sort pairs D.decompose,(k,v) -> {k} | (flatten entries v)_p ); + b := apply(sort pairs D.cache.print, + (k,v) -> {k} | (flatten entries v)_p ); stack("Decomposition table"," ", - netList(a|b,BaseRow=>1,Alignment=>Right,Boxes=>{{1},{1}},HorizontalSpace=>2) + netList(a|b,BaseRow=>1,Alignment=>Right,Boxes=>{{1},{1}},HorizontalSpace=>2) ) ) +-- tex string for character decompositions +texMath CharacterDecomposition := D -> ( + if not D.cache.?print then prepDecomposition D; + -- find non zero columns of table for printing + M := matrix apply(values D.decompose, m -> flatten entries m); + p := positions(toList(0..numColumns M - 1), i -> M_i != 0*M_0); + -- make table headers, one column per nonzero irrrep + s := concatenate("\\begin{array}{c|",#p:"r","}\n"); + -- top row with labels of characters appearing in decomposition + s = s | concatenate("&",between("&",(last D.Labels)_p),"\\\\ \\hline\n"); + -- decomposition table entries + rows := apply(sort pairs D.cache.print, + (k,v) -> concatenate(texMath k,"&", + between("&",apply((flatten entries v)_p,texMath)) + ) + ); + -- assemble and close array + s | concatenate(between("\\\\ \n",rows),"\n\\end{array}") + ) + -- printing for Action type net Action := A -> ( - (net class target A)|" with "|(net numActors A)|" actors" + (net class A.target)|" with "|(net (A.numActors))|" actors" ) @@ -1010,13 +1620,30 @@ Node (BOLD "2.1: ", "Adds equality checks for actions and characters. Contains several small improvements to the code and documentation, including a new multigraded - example.") + example."), + (BOLD "2.2: ", "Characters and character tables are now + defined over fields (instead of polynomial rings). + This version also introduces new character operations + and $\\TeX$ printing for characters and character tables."), + (BOLD "2.3: ", "New option for the action of a semidirect + product of a finite group acting on a torus. + Improved caching and removed calls to deprecated functions."), + (BOLD "2.4: ", "Introduces significant optizimations to the core + algorithm for computing Betti characters. Removed the ", + TT "inverseRingActors", " method since it is not used anymore."), + (BOLD "2.5: ", "Overhauls the internal representation of characters + to better handle actions of semidirect products; v2.5 characters + are incompatible with those from previous versions. Adds tensor + powers of characters. Removes some less used methods to access + keys of actions and characters. Requires Macaulay2 1.24.05."), + (BOLD "2.6: ", "Adds methods for hyperoctahedral group.") }@ Subnodes :Defining and computing actions action actors :Characters and related operations + "Character class" character "Character operations" :Character tables and decompositions @@ -1026,17 +1653,110 @@ Node "Equality checks" symmetricGroupActors symmetricGroupTable + hyperoctahedralGroupActors + hyperoctahedralGroupTable :Examples "BettiCharacters Example 1" "BettiCharacters Example 2" "BettiCharacters Example 3" "BettiCharacters Example 4" + "BettiCharacters Example 5" +Node + Key + "Character class" + Headline + internal representation of graded characters + Description + Text + Version 2.5 of the @TO "BettiCharacters"@ package introduces + a new internal representation of graded characters. + Although character printouts look the same as in + previous versions, this new + representation is incompatible with the one from earlier + versions of the package. The earlier representation was + not sufficient to compute tensor products of characters of + the semidirect product of a finite group with a torus + (see @TO "Semidirect"@). Not only the new representation + makes this possible, it also generally makes character + operations more natural and sets the groundwork for + functionality to be added in future versions. + + To understand how characters are stored, consider the + following example. + Example + R = QQ[x,y,z] + I = ideal(x+y+z,x*y+x*z+y*z,x*y*z) + Q = R/I + S2 = symmetricGroupActors R + A = action(Q,S2) + a = character(A,0,10) + Text + The quotient ring @TT "Q"@ is a module concentrated in + homological degree zero. It is an Artinian ring with + nonzero components in degrees zero, one, two, and three. + Hence, the printout of the character of @TT "Q"@ + shows four rows indexed by pairs @TT "(h,d)"@, with + @TT "h"@ the homological degree and @TT "d"@ the + internal degree. + + Internally, the character stores a single matrix for + each homological degree. To separate the internal + degrees, the character uses elements from the + @TO "degreesMonoid"@ of the ambient ring of @TT "Q"@. + Factoring out the monomials of the monoid of degrees, + gives the characters of the graded components. + Example + a.characters + coefficients a.characters#0 + Text + With this setup, the character in each homological + degree is a matrix with values in a "character ring". + This character ring is constructed as @TT "F[M]"@, + where @TT "F"@ is the field of coefficients of @TT "R"@ + and @TT "M"@ is the @TO "degreesMonoid"@ of @TT "R"@. + The character ring is stored with each action and + character, and in the cache table of the polynomial + ring under the key @TT "degreesRing"@. + Example + A.degreesRing + a.degreesRing + R.cache#degreesRing + Text + When different characters are combined with methods + such as @TO "BettiCharacters::directSum(Character)"@ or + @TO "BettiCharacters::tensor(Character,Character)"@, the package + checks that all characters have values in the same + character ring. All actions and characters of + objects (rings, ideals, modules, resolutions) over + the same ambient ring automatically share the + same character ring. + + The character ring can be defined just as well + when the ambient ring is multigraded and in the + presence of actions by the semidirect product of + a finite group and the torus responsible for the + multigrading. + Example + S = QQ[u,v,w,Degrees=>{{1,0,0},{0,1,0},{0,0,1}}] + J = ideal(u^2,v^2,w^2,u*v*w) + RJ = res J + S3 = symmetricGroupActors(S) + B = action(RJ,S3,Semidirect=>{uniquePermutations,rsort}) + b = character B + b.characters + b.degreesRing + Caveat + To avoid issues such as incompatible character rings + or character rings being recreated over and over, + the handling of character rings is currently not + exposed to the user. + Node Key "Character operations" Headline - shift, direct sum, dual, and tensor product + including shift, direct sum, dual, and tensor product Description Text The @TO BettiCharacters@ package contains @@ -1044,7 +1764,12 @@ Node See links below for more details. SeeAlso (symbol SPACE,Character,Array) + (symbol _,Character,ZZ) + (symbol ^,Character,List) + (symbol *,RingElement,Character) + (minus,Character) (directSum,Character) + (difference,Character,Character) (dual,Character,RingMap) (tensor,Character,Character) @@ -1060,8 +1785,8 @@ Node Specht ideal associated with the partition (5,2). The action of the symmetric group on the resolution of this ideal is described in - @arXiv("2010.06522", - "K. Shibata, K. Yanagawa - Minimal free resolutions of the Specht ideals of shapes (n−2,2) and (d,d,1)")@. + @HREF("https://doi.org/10.1142/S0219498823501992", + "K. Shibata, K. Yanagawa - Elementary construction of minimal free resolutions of the Specht ideals of shapes (n−2,2) and (d,d,1)")@. The same ideal is also the ideal of the 6-equals subspace arrangement in a 7-dimensional affine space. This point of view is explored in @@ -1080,7 +1805,7 @@ Node I1=ideal apply({4,5,6,7}, i -> (x_1-x_2)*(x_3-x_i)); I2=ideal apply(subsets({3,4,5,6,7},2), s -> (x_1-x_(s#0))*(x_2-x_(s#1))); I=I1+I2 - RI=freeResolution I + RI=res I betti RI Text Next we set up the group action on the resolution. @@ -1104,7 +1829,7 @@ Node using a compact notation (the exponents indicate how many times a part is repeated). Example - T = symmetricGroupTable R + T = symmetricGroupTable(7,QQ) decomposeCharacter(c,T) Text As expected from the general theory, we find a single @@ -1119,9 +1844,9 @@ Node the sign character just constructed: the result is the same as the character of the resolution. Example - sgn = character(R,15,hashTable {(0,{7}) => + signrep = character(R,hashTable {(0,{7}) => matrix{{1,-1,-1,1,-1,1,-1,1,1,-1,1,-1,1,-1,1}}}) - dual(c,id_QQ)[-5] ** sgn === c + dual(c,id_QQ)[-5] ** signrep == c Text The second argument in the @TT "dual"@ command is the restriction of complex conjugation to the field of @@ -1149,7 +1874,7 @@ Node Example R=QQ[x_1..x_6] I=intersect(apply(subsets(gens R,4),x->(ideal x)^3)) - RI=freeResolution I + RI=res I betti RI Text Next, we set up the group action on the resolution. @@ -1173,7 +1898,7 @@ Node using a compact notation (the exponents indicate how many times a part is repeated). Example - T = symmetricGroupTable R + T = symmetricGroupTable(6,QQ) decomposeCharacter(c,T) Text The description provided in @@ -1212,10 +1937,10 @@ Node H = jacobian transpose jacobian f4 f6 = -1/54*det(H) I = minors(2,jacobian matrix{{f4,f6}}) - RI = freeResolution I + RI = res I betti RI I2 = I^2; - RI2 = freeResolution I2 + RI2 = res I2 betti RI2 Text The unique simple group of order 168 acts as described @@ -1262,7 +1987,7 @@ Node and decompose the Betti characters of the resolutions. The arguments are: a list with the cardinality of the conjugacy classes, a matrix with the values of the irreducible - characters, the base polynomial ring, and the complex + characters, the base field, and the complex conjugation map restricted to the field of coefficients. See @TO characterTable@ for more details. Example @@ -1274,11 +1999,11 @@ Node {7,-1,1,-1,0,0}, {8,0,-1,0,1,1}}; conj = map(kk,kk,{a^6}) - T = characterTable(s,m,R,conj) + T = characterTable(s,m,kk,conj) a1/T a2/T Text - Since @TT "X0"@ is the trivial character, + Since $\chi_0$ is the trivial character, this computation shows that the free module in homological degree two in the resolution of the defining ideal of the Klein configuration is a direct sum @@ -1327,7 +2052,7 @@ Node Example R = QQ[x_1,x_2,y_1,y_2,y_3,Degrees=>{2:{1,0},3:{0,1}}] I = intersect(ideal(x_1,x_2),ideal(y_1,y_2,y_3)) - RI = freeResolution I + RI = res I G = { matrix{{x_1,x_2,y_2,y_3,y_1}}, matrix{{x_1,x_2,y_2,y_1,y_3}}, @@ -1356,6 +2081,63 @@ Node character(B,{1,2}) +Node + Key + "BettiCharacters Example 5" + Headline + semidirect product of torus and symmetric group + Description + Text + We present the example in the introduction of + @HREF("https://doi.org/10.1112/jlms.12551", + "S. Murai, C. Raicu - An equivariant Hochster’s formula for $\\mathfrak{S}_n$-invariant monomial ideals")@. + + Consider the ideal $I$ in three variables generated by + monomials whose exponent vectors are permutations of + $(4,1,1)$ or $(5,2,0)$. This ideal is clearly stable + under the permutation action of $\mathfrak{S}_3$. + Moreover, $I$ is compatible with the fine grading + on $R = \Bbbk [x_1,x_2,x_3]$ given by $\deg (x_i) = e_i + \in \mathbb{Z}^3$. We compute a minimal free resolution of + $R/I$ and show its Betti diagram. + Example + R = QQ[x_1..x_3,Degrees=>{{1,0,0},{0,1,0},{0,0,1}}] + I = ideal(x_1^4*x_2*x_3,x_1*x_2^4*x_3,x_1*x_2*x_3^4, + x_1^5*x_2^2,x_1^5*x_3^2,x_1^2*x_2^5,x_1^2*x_3^5,x_2^5*x_3^2,x_2^2*x_3^5) + RI = res I + betti RI + Text + Next, we set up the action of the semidirect product + $(\Bbbk^\times)^3 \rtimes \mathfrak{S}_3$ where + $\mathfrak{S}_3$ acts on $(\Bbbk^\times)^3$ by + permuting entries. This results in $\mathfrak{S}_3$ + acting on the grading group $\mathbb{Z}^3$ (the character + group of $(\Bbbk^\times)^3$) by permuting the + entries of the degree vectors. Thus, the orbit of a + degree $d\in \mathbb{Z}^3$ consists of all permutations + of $d$; we fix the nonincreasing permutation of $d$ as + the distinguished representative of this orbit. + See @TO "Semidirect"@ for details. + Example + S3 = symmetricGroupActors(R) + A = action(RI,S3,Semidirect=>{uniquePermutations,rsort}) + c = character A + Text + To match the description of the paper, which resolves the + ideal $I$ instead of the quotient $R/I$, we remove + the component in homological degree 0, then shift the + complex to the left. Finally, the resulting character is + decomposed against the character table of $\mathfrak{S}_3$. + Example + c = (c - c_0)[1] + T = symmetricGroupTable(3,QQ) + decomposeCharacter(c,T) + Text + The irreducible representations found above match + our expectations as can be verified by + applying Pieri's rule to the description + in Example 1.4 of Murai and Raicu's paper. + Node Key Action @@ -1368,10 +2150,8 @@ Node Subnodes ActionOnComplex ActionOnGradedModule - (net,Action) - (ring,Action) ringActors - (target,Action) + (net,Action) Node Key @@ -1405,10 +2185,16 @@ Node @TO BettiCharacters@. Subnodes (symbol SPACE,Character,Array) + (symbol *,RingElement,Character) + (symbol ^,Character,List) + (symbol _,Character,ZZ) + (minus,Character) (directSum,Character) + (difference,Character,Character) (dual,Character,RingMap) (net,Character) (tensor,Character,Character) + (texMath,Character) Node Key @@ -1421,12 +2207,121 @@ Node Example R = QQ[x,y,z] I = ideal(x*y,x*z,y*z) - RI = freeResolution I + RI = res I S3 = symmetricGroupActors R A = action(RI,S3) a = character A a[-10] +Node + Key + (symbol _,Character,ZZ) + (symbol _,Character,List) + Headline + extract component + Description + Text + Extract the component(s) of a character in + the given homological dimension(s). + Example + R = QQ[x,y,z] + I = ideal vars R + RI = res I + S3 = symmetricGroupActors R + A = action(RI,S3) + c = character A + c_3 + c_{1,3} + +Node + Key + (symbol ^,Character,List) + Headline + extract graded component + Description + Text + Extract the component(s) of a character in + the given (multi)degree(s). + Example + R = QQ[x,y,z] + I = (ideal vars R)^3 + Q = R/I + S3 = symmetricGroupActors R + A = action(Q,S3) + c = character(A,0,10) + c^{1} + c^{{1},{2}} + c^{3} + +Node + Key + (symbol *,RingElement,Character) + (symbol *,ZZ,Character) + (symbol *,QQ,Character) + (symbol *,Character,RingElement) + (symbol *,Character,ZZ) + (symbol *,Character,QQ) + Headline + scalar multiple of a character + Description + Text + Multiply a character with an element in its + field of definition. + Example + R = QQ[x,y,z] + I = (ideal vars R)^3 + Q = R/I + S3 = symmetricGroupActors R + A = action(Q,S3) + c = character(A,0,10) + 2*c + c*(1/3) + Text + As of version 2.5, it is possible to multiply + characters by elements of their degrees ring, + which will result in an internal degree shift. + Example + DR = c.degreesRing + T = DR_0 + c * T^10 + +Node + Key + (minus,Character) + (symbol -,Character) + Headline + additive inverse of a character + Description + Text + Additive inverse of a character. + Example + R = QQ[x,y,z] + I = (ideal vars R)^3 + S3 = symmetricGroupActors R + A = action(I,S3) + c = character(A,0,10) + -c + +Node + Key + (difference,Character,Character) + (symbol -,Character,Character) + Headline + difference of characters + Description + Text + Difference of two characters. + Example + R = QQ[x,y,z] + I = (ideal vars R)^3 + J = ideal(x^3,y^3,z^3) + S3 = symmetricGroupActors R + A1 = action(I,S3) + A2 = action(J,S3) + c1 = character(A1,0,10) + c2 = character(A2,0,10) + c1 - c2 + Node Key CharacterTable @@ -1438,6 +2333,7 @@ Node @TO BettiCharacters@. Subnodes (net,CharacterTable) + (texMath,CharacterTable) Node Key @@ -1450,7 +2346,8 @@ Node @TO BettiCharacters@. Subnodes (net,CharacterDecomposition) - + (texMath,CharacterDecomposition) + Node Key action @@ -1465,6 +2362,7 @@ Node Action (action,Complex,List,List,ZZ) (action,Module,List,List) + Semidirect Sub Node @@ -1518,7 +2416,7 @@ Node Example R = QQ[x_1..x_4] I = ideal apply(subsets(gens R,2),product) - RI = freeResolution I + RI = res I G = {matrix{{x_2,x_3,x_4,x_1}}, matrix{{x_2,x_3,x_1,x_4}}, matrix{{x_2,x_1,x_4,x_3}}, @@ -1527,14 +2425,9 @@ Node A = action(RI,G) Text The group elements acting on the ring can be recovered - using @TO ringActors@, while their inverses can be - recovered using @TO inverseRingActors@. - To recover just the number of group elements, - use @TO numActors@. + using @TO ringActors@. Example ringActors A - inverseRingActors A - numActors A Text The simplified version of this function suffices when dealing with resolutions of quotients of the ring @@ -1563,7 +2456,7 @@ Node these matrices by permuting columns of the identity. Example M = module I - RM = freeResolution M + RM = res M G' = { (id_(R^6))_{2,4,5,0,1,3}, (id_(R^6))_{2,0,1,4,5,3}, (id_(R^6))_{0,4,3,2,1,5}, @@ -1583,12 +2476,12 @@ Node module). This can be achieved as follows. Example E = Ext^3(R^1/I,R^{-4}) - RE = freeResolution E + RE = res E G'' = toList(5:id_(R^1)) action(RE,G,G'',3) Caveat - This function does not check if the complex @TT "C"@ is a - free resolution. If the user passes a complex that is not a + This function does not check if the complex @TT "C"@ is a minimal + free resolution. If the user passes a complex that is not a minimal free resolution, then later computations (i.e., Betti characters) may fail or return meaningless results. @@ -1664,11 +2557,8 @@ Node Text The group elements acting on the ring can be recovered using @TO ringActors@. - To recover just the number of group elements, - use @TO numActors@. Example ringActors A - numActors A Text The simplified version of this function assumes that the group acts trivially on the generator of the @@ -1691,7 +2581,7 @@ Node resolution. Example E = Ext^3(R^1/I,R^{-4}) - RE = freeResolution E + RE = res E G'' = toList(5:id_(R^1)) B = action(RE,G,G'',3) G' = actors(B,0) @@ -1704,11 +2594,14 @@ Node (symbol ==,ActionOnComplex,ActionOnComplex) (symbol ==,ActionOnGradedModule,ActionOnGradedModule) (symbol ==,Character,Character) + (symbol ==,CharacterDecomposition,CharacterDecomposition) + (symbol ==,CharacterTable,CharacterTable) Headline compare actions and characters Description Text - Use @TT "=="@ to check if two actions or characters are equal. + Use @TT "=="@ to check if two actions, characters, + decompositions or tables are equal. For actions, the underlying ring and object (complex or module) must be the same. @@ -1719,14 +2612,16 @@ Node In the case of actions on modules, the @TT "=="@ operator compares the group action on the module generators. - For characters, the underlying ring must be the same, - as well as the number of entries in each character. + For characters, the underlying ring must be the same + (see @TO "Character class"@), + as well as the degree orbit and representative functions + (see @TO "Semidirect"@). Characters are compared across all homological and internal degrees. Example R = QQ[x_1..x_4] I = ideal apply(subsets(gens R, 2), product) - RI = freeResolution I + RI = res I S4 = symmetricGroupActors(R) A = action(RI,S4) G = {map(RI_3, RI_3, {{0, -1, 1}, {1, 1, 0}, {0, 1, 0}}), @@ -1755,8 +2650,6 @@ Node Subnodes (actors,ActionOnComplex,ZZ) (actors,ActionOnGradedModule,List) - inverseRingActors - numActors Node Key @@ -1795,7 +2688,7 @@ Node Example R = QQ[x_1..x_4] I = ideal apply(subsets(gens R,2),product) - RI = freeResolution I + RI = res I G = {matrix{{x_2,x_3,x_4,x_1}}, matrix{{x_2,x_3,x_1,x_4}}, matrix{{x_2,x_1,x_4,x_3}}, @@ -1887,13 +2780,13 @@ Node be concentrated in homological degree zero. Characters may also be constructed by hand using - @TO (character,PolynomialRing,ZZ,HashTable)@. + @TO (character,PolynomialRing,HashTable)@. Subnodes Character (character,ActionOnComplex) (character,ActionOnComplex,ZZ) (character,ActionOnGradedModule,List) - (character,PolynomialRing,ZZ,HashTable) + (character,PolynomialRing,HashTable) (character,CharacterDecomposition,CharacterTable) Node @@ -1930,7 +2823,7 @@ Node Example R = QQ[x_1..x_4] J = intersect(apply(subsets(gens R,3),x->(ideal x)^2)) - RJ = freeResolution J + RJ = res J G = { matrix{{x_2,x_3,x_4,x_1}}, matrix{{x_2,x_3,x_1,x_4}}, matrix{{x_2,x_1,x_4,x_3}}, @@ -1988,7 +2881,7 @@ Node Example R = QQ[x_1..x_4] J = intersect(apply(subsets(gens R,3),x->(ideal x)^2)) - RJ = freeResolution J + RJ = res J G = { matrix{{x_2,x_3,x_4,x_1}}, matrix{{x_2,x_3,x_1,x_4}}, matrix{{x_2,x_1,x_4,x_3}}, @@ -2100,16 +2993,14 @@ Node Node Key - (character,PolynomialRing,ZZ,HashTable) + (character,PolynomialRing,HashTable) Headline construct a character Usage - character(R,l,H) + character(R,H) Inputs R:PolynomialRing - over a field - l:ZZ - character length + a polynomial ring over a field H:HashTable raw character data Outputs @@ -2121,23 +3012,19 @@ Node The user who wishes to define characters by hand may do so with this particular application of the method. - The first argument is the polynomial ring the character - values will live in; this makes it possible to compare or - combine the hand-constructed character with other - characters over the same ring. The second argument is - the length of the character, i.e., the number of conjugacy - classes of the group whose representations the character - is coming from. The third argument is a hash table + The first argument is a polynomial ring over a field. The + character inherits the grading of this polynomial ring + and takes values in the field of coefficients. + The second argument is a hash table containing the "raw" character data. The hash table entries are in the format @TT "(i,d) => c"@, where @TT "i"@ is an integer representing homological degree, @TT "d"@ is a list representing the internal (multi)degree, and - @TT "c"@ is a list containing the values of the character - in the given degrees. Note that the values of the character - are elements in the ring given as the first argument. + @TT "c"@ is a one-row matrix containing the values of the + character in the given degrees. Example R = QQ[x_1..x_3] - regRep = character(R,3, hashTable { + regularRepresentation = character(R, hashTable { (0,{0}) => matrix{{1,1,1}}, (0,{1}) => matrix{{-1,0,2}}, (0,{2}) => matrix{{-1,0,2}}, @@ -2149,7 +3036,7 @@ Node matrix{{x_1,x_2,x_3}} } Q = R/I A = action(Q,S3) - character(A,0,3) === regRep + character(A,0,3) == regularRepresentation Caveat This constructor implements basic consistency checks, but it is still possible to construct objects that are not @@ -2185,14 +3072,14 @@ Node Example s = {2,3,1} M = matrix{{1,1,1},{-1,0,2},{1,-1,1}} - R = QQ[x_1..x_3] P = {1,2,3} - T = characterTable(s,M,R,P) + T = characterTable(s,M,QQ,P) + R = QQ[x_1..x_3] acts = {matrix{{x_2,x_3,x_1}},matrix{{x_2,x_1,x_3}},matrix{{x_1,x_2,x_3}}} A = action(R,acts) c = character(A,0,10) d = c/T - c === d*T + c == d*T SeeAlso characterTable decomposeCharacter @@ -2200,20 +3087,20 @@ Node Node Key characterTable - (characterTable,List,Matrix,PolynomialRing,RingMap) - (characterTable,List,Matrix,PolynomialRing,List) + (characterTable,List,Matrix,Ring,RingMap) + (characterTable,List,Matrix,Ring,List) Headline construct a character table Usage - T = characterTable(s,M,R,conj) - T = characterTable(s,M,R,perm) + T = characterTable(s,M,F,conj) + T = characterTable(s,M,F,perm) Inputs s:List of conjugacy class sizes M:Matrix with character table entries - R:PolynomialRing - over a field + F:Ring + a field conj:RingMap conjugation in coefficient field perm:List @@ -2233,15 +3120,14 @@ Node irreducible character of the group at an element of the $j$-th conjugacy class. - The third argument is a polynomial ring over a field, - the same ring over which the modules and resolutions - are defined whose characters are to be decomposed - against the character table. Note that the matrix in - the second argument must be liftable to this ring. + The third argument is a field, the field of definition + of the characters to be decomposed against the character + table. Note that the matrix in the second argument must + be liftable to this field. (In version 2.1 and earlier, + this argument used to be a polynomial ring.) - Assuming the polynomial ring in the third argument - has a coefficient field @TT "F"@ which is a subfield of the - complex numbers, then the fourth argument is the + Assuming the field in the third argument is a subfield + of the complex numbers, then the fourth argument is the restriction of complex conjugation to @TT "F"@. For example, we construct the character table of the @@ -2258,12 +3144,11 @@ Node F = toField(QQ[w]/ideal(1+w+w^2)) s = {1,3,4,4} M = matrix{{1,1,1,1},{1,1,w,w^2},{1,1,w^2,w},{3,-1,0,0}} - R = F[x_1..x_4] conj = map(F,F,{w^2}) - T = characterTable(s,M,R,conj) + T = characterTable(s,M,F,conj) Text By default, irreducible characters in a character table - are labeled as @TT "X0, X1, ..."@, etc. + are labeled as $\chi_0, \chi_1, \dots$, etc. The user may pass custom labels in a list using the option @TO Labels@. @@ -2296,8 +3181,8 @@ Node as the fourth argument. Example perm = {1,2,4,3} - T' = characterTable(s,M,R,perm) - T' === T + T' = characterTable(s,M,F,perm) + T' == T Caveat This constructor checks orthonormality of the table matrix under the standard scalar product of characters. @@ -2342,9 +3227,9 @@ Node Example s = {2,3,1} M = matrix{{1,1,1},{-1,0,2},{1,-1,1}} - R = QQ[x_1..x_3] P = {1,2,3} - T = characterTable(s,M,R,P) + T = characterTable(s,M,QQ,P) + R = QQ[x_1..x_3] acts = {matrix{{x_2,x_3,x_1}},matrix{{x_2,x_1,x_3}},matrix{{x_1,x_2,x_3}}} A = action(R,acts) c = character(A,0,10) @@ -2354,7 +3239,7 @@ Node by pairs of homological and internal degrees, and whose columns are labeled by the irreducible characters. By default, irreducible characters in a character table - are labeled as @TT "X0, X1, ..."@, etc, and the same + are labeled as $\chi_0, \chi_1, \dots$, etc, and the same labeling is inherited by the character decomposition. The user may pass custom labels in a list using the option @TO Labels@ when constructing the character @@ -2368,6 +3253,8 @@ Node Key (directSum,Character) (symbol ++,Character,Character) + (symbol +,Character,Character) + (plus,Character,Character) Headline direct sum of characters Usage @@ -2381,7 +3268,8 @@ Node Description Text Returns the direct sum of the input characters. - The operator @TT "++"@ may be used for the same purpose. + The operators @TT "+"@ and @TT "++"@ and the function + @TO (plus,Character,Character)@ may be used for the same purpose. Example R = QQ[x_1..x_3] I = ideal(x_1+x_2+x_3) @@ -2401,34 +3289,30 @@ Node Node Key + dual (dual,Character,RingMap) (dual,Character,List) Headline dual character - Description - Text - Returns the dual of a character, i.e., the character - of the dual or contragredient representation. - - The first argument is the original character. - The second argument has two possibilities, listed below. - The page @TO characterTable@ contains some motivation - for using conjugation or permutations of conjugacy - classes when dealing with characters. - Synopsis - Heading - dual character with respect to a conjugation in coefficient field - Usage + Usage dual(c,conj) - Inputs + dual(c,perm) + Inputs c:Character of a finite group action conj:RingMap conjugation in coefficient field - Outputs + perm:List + permutation of conjugacy classes + Outputs :Character - Description - Text + Description + Text + Returns the dual of a character, i.e., the character + of the dual or contragredient representation. + + The first argument is the original character. + Assuming the polynomial ring over which the character is defined has a coefficient field @TT "F"@ which is a subfield of the complex numbers, then the second argument is the @@ -2448,22 +3332,9 @@ Node F = toField(QQ[w]/ideal(1+w+w^2)) R = F[x_1..x_4] conj = map(F,F,{w^2}) - X = character(R,4,hashTable {(1,{2}) => matrix{{1,1,w,w^2}}}) + X = character(R,hashTable {(1,{2}) => matrix{{1,1,w,w^2}}}) X' = dual(X,conj) - Synopsis - Heading - dual character with respect to a permutation of conjugacy classes - Usage - dual(c,perm) - Inputs - c:Character - of a finite group action - perm:List - permutation of conjugacy classes - Outputs - :Character - Description - Text + Text If working over coefficient fields of positive characteristic or if one wishes to avoid defining conjugation, one may replace the second argument by a list containing a permutation @@ -2483,38 +3354,77 @@ Node Example perm = {1,2,4,3} dual(X,perm) === X' + Text + The page @TO characterTable@ contains some motivation + for using conjugation or permutations of conjugacy + classes when dealing with characters. SeeAlso characterTable - + Node Key - inverseRingActors - (inverseRingActors,Action) + hyperoctahedralGroupActors + (hyperoctahedralGroupActors,PolynomialRing) Headline - get inverse of action on ring generators + standard action of the hyperoctahedral group Usage - inverseRingActors(A) + hyperoctahedralGroupActors(R) Inputs - A:Action + R:PolynomialRing Outputs - G:List - of group elements + :List Description Text - Returns a @TO List@ of group elements - acting on the vector space spanned by the variables - of the polynomial ring associated with the object - acted upon. - These are the inverses of the elements originally - defined by the user when constructing the action. - By default, these elements are - expressed as one-row substitution matrices as those - accepted by @TO substitute@. One may obtain these elements - as square matrices by setting the optional input @TO Sub@ - to @TT "false"@. - SeeAlso - action + The hyperoctahedral group is the Weyl group of type B. It can be + realized as the automorphism group of the hypercube, as the group of + signed permutation matrices, or as the semidirect product + $\mathbb{Z}_2^n \rtimes S_n$ of the symmetric group $S_n$ acting on + $\mathbb{Z}_2^n$ by permutations. The standard action on a polynomial + ring in $n$ variables is the multiplication action of the signed + $n\times n$ permutation matrices on the vector of the variables. + + This function returns a list of of matrices, each representing an + element of the hyperoctahedral group acting on the variables + of the polynomial ring in the input as a signed permutation. This + simplifies the setup for hyperoctahedral group actions with the + @TO action@ command. + + The output list + contains one element for each conjugacy class of + the hyperoctahedral group. The conjugacy classes are + in bijection with the bipartitions of $n$, i.e., pairs of partitions + of two integers adding up to $n$, where $n$ is the + number of variables. The first partition gives the cycle type of a + permutation of an initial subset of variables without signs changes. + The second partition gives the cycle type of a permutation of the + remaining variables with each cycle containing a single sign change. + Example + R=QQ[x_1..x_3] + hyperoctahedralGroupActors(R) +Node + Key + hyperoctahedralGroupTable + (hyperoctahedralGroupTable,ZZ,Ring) + Headline + character table of the hyperoctahedral group + Usage + hyperoctahedralGroupTable(n,F) + Inputs + n:ZZ + positive + F:Ring + a field + Outputs + :CharacterTable + Description + Text + Returns the character table of the hyperoctahedral group + $H_n$ over the field @TT "F"@. The irreducible + characters are indexed by bipartitions of $n$, i.e., + pairs of partitions of two integers adding up to $n$. + Example + hyperoctahedralGroupTable(3,QQ) Node Key @@ -2529,9 +3439,16 @@ Node @TO BettiCharacters@. By default, irreducible characters in a character table - are labeled as @TT "X0, X1, ..."@, etc. + are labeled as $\chi_0, \chi_1, \dots$, etc. The user may pass custom labels in a list using - this option. + this option. Labels can be passed as a list containing two lists: + the first list should contain strings or nets to label + characters in a M2 interactive session, while the second list + should contain TeX strings to label characters when outputting + to TeX format (remember to escape backslashes as needed). + Up to version 2.1, a single list of net labels + was accepted; this option is maintained for compatibility (the same + labels are also used for the TeX output). The next example sets up the character table of the dihedral group $D_4$, generated by an order 4 rotation $r$ @@ -2557,18 +3474,23 @@ Node {1,1,-1,1,-1}, {1,1,-1,-1,1}, {2,-2,0,0,0}}; - T = characterTable({1,1,2,2,2},M,R,{1,2,3,4,5}, - Labels=>{"triv","rho1","rho2","rho3","dim2"}) + T = characterTable({1,1,2,2,2},M,QQ,{1,2,3,4,5}, + Labels=>{{"triv","rho1","rho2","rho3","dim2"}, + {"triv","\\rho_1","\\rho_2","\\rho_3","\\chi^2"}}) + tex T Text The same labels are automatically used when decomposing characters against a labeled character table. Example A = action(R,D8) - c = character(A,0,8) - decomposeCharacter(c,T) + c = character(A,0,5) + d = decomposeCharacter(c,T) + tex d Text The labels are stored in the character table under the - key @TT "Labels"@. + key @TT "Labels"@. In particular, two character tables + of the same group that are equal in all aspects except + for their labels will fail an equality check. SeeAlso characterTable decomposeCharacter @@ -2614,50 +3536,38 @@ Node Format objects of type @TO CharacterDecomposition@ for printing. See @TO net@ for more information. - Node Key - numActors - (numActors,Action) + (texMath,Character) Headline - number of acting elements - Usage - numActors(A) - Inputs - A:Action - Outputs - :ZZ + convert to TeX math format Description Text - Returns the number of group elements passed by the user - when defining the given action. - This number is not necessarily the order of the acting - group because in order to compute characters it is - enough to work with a representative of each conjugacy - class of the group. - SeeAlso - action - + Format objects of type @TO Character@ + for printing in TeX format. + See @TO texMath@ for more information. Node Key - (ring,Action) + (texMath,CharacterTable) Headline - get ring of object acted upon - Usage - ring(A) - Inputs - A:Action - Outputs - :PolynomialRing - associated with the object acted upon + convert to TeX math format Description Text - Returns the polynomial ring associated with the object - being acted upon. - SeeAlso - action + Format objects of type @TO CharacterTable@ + for printing in TeX format. + See @TO texMath@ for more information. +Node + Key + (texMath,CharacterDecomposition) + Headline + convert to TeX math format + Description + Text + Format objects of type @TO CharacterDecomposition@ + for printing in TeX format. + See @TO texMath@ for more information. Node Key @@ -2689,12 +3599,96 @@ Node action +Node + Key + Semidirect + [action, Semidirect] + [character, Semidirect] + [(symbol *, CharacterDecomposition, CharacterTable), Semidirect] + degreeOrbit + degreeRepresentative + Headline + action of semidirect product with torus + Description + Text + Consider a polynomial ring $R$ and an $R$-module $M$ + with a $\mathbb{Z}^r$-grading corresponding to the action of a + torus $T$. Let $G$ be a finite group acting on $R$ + and $M$ in a way that is compatible with multiplication. + Then the semidirect product $T\rtimes G$ acts on $R$ and $M$. + For a given degree $d \in \mathbb{Z}^r$, the graded + components $R_d$ and $M_d$ need not be representations of $G$. + However, if $\mathcal{O}$ is the orbit of $d$ under the action + of $G$ on the character group $\mathbb{Z}^r$ of $T$, then + $\bigoplus_{d\in\mathcal{O}} R_d$ and + $\bigoplus_{d\in\mathcal{O}} M_d$ are representations of + $T\rtimes G$. Starting with version 2.3, the + @TO "BettiCharacters"@ package allows one to compute the + characters of $G$ on these representations using the + @TO "Semidirect"@ option of the @TO "action"@ method, + and specifying a single degree $d$ in the orbit $\mathcal{O}$. + + The value of the @TO "Semidirect"@ option is a list of two + functions. The first function takes as input a degree $d$ + and returns its orbit $\mathcal{O}$ as output. This function is + stored in the action under the key @TO "degreeOrbit"@. The second + function takes as input a degree $d$ and returns a user-chosen + representative $d'$ from the orbit $\mathcal{O}$ of $d$. This + function is stored in the action under the key @TO "degreeRepresentative"@. + When computing the actors or the characters of $G$ on + $\bigoplus_{d\in\mathcal{O}} R_d$ and $\bigoplus_{d\in\mathcal{O}} M_d$, + the values are stored only for the chosen representative $d'$, + and computing the actors or characters of $G$ for another degree + in the same orbit produces the same result as for $d'$. + By default, both functions are set to the identity, which + corresponds to the action of the direct product $T\times G$. + + A typical use case is that of the symmetric group $\mathfrak{S}_n$ + acting on a fine graded polynomial ring $\Bbbk [x_1,\dots,x_n]$ by + permuting the variables. The symmetric group also acts by + permuting the entries of the degrees $d \in \mathbb{Z}^n$. + In this case, the orbit of $d$ consists of all its permutations, + which can be obtained with the function @TO "uniquePermutations"@. + As a representative of this orbit we choose the unique degree $d$ + whose entries are sorted in nonincreasing order from left to right; + this can be obtained with the function @TO "rsort"@. + + We illustrate this use case. First, consider the action + on the polynomial ring. + Example + R = QQ[x_1..x_4,Degrees=>{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}] + S4 = symmetricGroupActors R + A = action(R,S4,Semidirect=>{uniquePermutations,rsort}) + actors(A,{1,1,1,0}) + character(A,{1,1,1,0}) + Text + As expected, the character is the same if we compute it + for a different degree in the same orbit. + Example + oo == character(A,{1,0,1,1}) + Text + Next, consider the quotient by an ideal stable under the group action. + Example + I = ideal apply(subsets(gens R,3),product) + M = R/I + B = action(M,S4,Semidirect=>{uniquePermutations,rsort}) + character(B,{2,1,0,0}) + Text + Similarly, the @TO "Semidirect"@ option can be used + for actions on complexes and for computing Betti characters + of a module. + Example + RI = res I + C = action(RI,S4,Semidirect=>{uniquePermutations,rsort}) + character C + SeeAlso + action + Node Key Sub [action, Sub] [ringActors, Sub] - [inverseRingActors, Sub] Headline format ring actors as one-row substitution matrices Description @@ -2718,7 +3712,7 @@ Node Example R = QQ[x_1..x_4] I = ideal apply(subsets(gens R,2),product) - RI = freeResolution I + RI = res I G = { (id_(R^4))_{1,2,3,0}, (id_(R^4))_{1,2,0,3}, (id_(R^4))_{1,0,3,2}, @@ -2727,16 +3721,14 @@ Node A = action(RI,G,Sub=>false) Text Similarly, setting @TT "Sub=>false"@ - causes @TO ringActors@ and @TO inverseRingActors@ + causes @TO ringActors@ to return the group elements acting on the ring as square matrices. With the default setting @TT "Sub=>true"@, the same elements are returned as one-row substitution matrices. Example ringActors(A,Sub=>false) - inverseRingActors(A,Sub=>false) ringActors(A) - inverseRingActors(A) Node @@ -2778,53 +3770,46 @@ Node Node Key symmetricGroupTable + (symmetricGroupTable,ZZ,Ring) (symmetricGroupTable,PolynomialRing) Headline character table of the symmetric group Usage - symmetricGroupTable(R) + symmetricGroupTable(n,F) Inputs - R:PolynomialRing + n:ZZ + positive + F:Ring + a field Outputs :CharacterTable Description Text Returns the character table of the symmetric group - $S_n$, where $n$ is the number of variables of the - polynomial ring in the input. The irreducible + $S_n$ over the field @TT "F"@. The irreducible characters are indexed by the partitions of $n$ written using a compact notation where an exponent indicates how many times a part is repeated. The computation uses the recursive Murnaghan-Nakayama formula. Example - R=QQ[x_1..x_4] - symmetricGroupTable(R) + symmetricGroupTable(4,QQ) + Text + If @TT "R"@ is a polynomial ring, then + @TT "symmetricGroupTable R"@ calls + @TT "symmetricGroupTable(numgens R,coefficientRing R)"@. + This is kept for compatibility with versions 2.1 and earlier + of the package to create the character table of the symmetric + group acting on the variables of @TT "R"@ over the + coefficient field of @TT "R"@. SeeAlso "BettiCharacters Example 1" "BettiCharacters Example 2" -Node - Key - (target,Action) - Headline - get object acted upon - Usage - target(A) - Inputs - A:Action - Description - Text - Returns the object being acted upon. - Depending on the action, this object may be a - @TO Complex@, a @TO PolynomialRing@, a - @TO QuotientRing@, an @TO Ideal@, or a @TO Module@. - SeeAlso - action - Node Key (tensor,Character,Character) (symbol **,Character,Character) + (symbol ^**,Character,ZZ) Headline tensor product of characters Usage @@ -2852,8 +3837,23 @@ Node observed by tensoring with the character of the sign representation concentrated in degree 3. Example - sgn = character(R,3, hashTable { (0,{3}) => matrix{{1,-1,1}} }) - dual(a,{1,2,3}) ** sgn === a + signrep = character(R, hashTable { (0,{3}) => matrix{{1,-1,1}} }) + dual(a,{1,2,3}) ** signrep === a + Synopsis + Usage + c ^** m + Inputs + A:Character + m:ZZ + Outputs + :Character + the m-th tensor power of c + Description + Text + Starting with version 2.5, this package allows nonnegative + tensor powers of characters using @TO (symbol ^**,Character,ZZ)@. + Example + a ^** 3 /// @@ -2866,55 +3866,53 @@ TEST /// clearAll R = QQ[x,y,z] I = ideal(x*y,x*z,y*z) -RI = freeResolution I +RI = res I S3 = {matrix{{y,z,x}},matrix{{y,x,z}},matrix{{x,y,z}}} assert(S3 == symmetricGroupActors(R)) A = action(RI,S3) -a = character(R,3,hashTable { +a = character(R,hashTable { ((0,{0}), matrix{{1,1,1}}), ((1,{2}), matrix{{0,1,3}}), ((2,{3}), matrix{{-1,0,2}}) }) -assert((character A) === a) +assert((character A) == a) B = action(R,S3) -b = character(R,3,hashTable { +b = character(R,hashTable { ((0,{0}), matrix{{1,1,1}}), ((0,{1}), matrix{{0,1,3}}), ((0,{2}), matrix{{0,2,6}}), ((0,{3}), matrix{{1,2,10}}) }) -assert(character(B,0,3) === b) +assert(character(B,0,3) == b) C = action(I,S3) -c = character(R,3,hashTable { +c = character(R,hashTable { ((0,{2}), matrix{{0,1,3}}), ((0,{3}), matrix{{1,1,7}}) }) -assert(character(C,0,3) === c) +assert(character(C,0,3) == c) D = action(R/I,S3) -d = character(R,3,hashTable { +d = character(R,hashTable { ((0,{0}), matrix{{1,1,1}}), ((0,{1}), matrix{{0,1,3}}), ((0,{2}), matrix{{0,1,3}}), ((0,{3}), matrix{{0,1,3}}) }) -assert(character(D,0,3) === d) -assert(b === c++d) -cS3 = symmetricGroupTable(R) +assert(character(D,0,3) == d) +assert(b == c++d) +cS3 = symmetricGroupTable(3,QQ) assert( cS3.table == - matrix{{1_R,1,1},{-1,0,2},{1,-1,1}}) + matrix{{1_QQ,1,1},{-1,0,2},{1,-1,1}}) adec = a/cS3 -assert( set keys adec.decompose === - set {(0,{0}),(1,{2}),(2,{3})}) -assert( adec.decompose#(0,{0}) == matrix{{1_R,0,0}}) -assert( adec.decompose#(1,{2}) == matrix{{1_R,1,0}}) -assert( adec.decompose#(2,{3}) == matrix{{0,1_R,0}}) +assert( set keys adec.decompose == set {0,1,2}) +CR = a.degreesRing +T = CR_0 +assert( adec.decompose#0 == matrix{{1_QQ,0,0}} * T^0) +assert( adec.decompose#1 == matrix{{1_QQ,1,0}} * T^2) +assert( adec.decompose#2 == matrix{{0,1_QQ,0}} * T^3) ddec = d/cS3 -assert( set keys ddec.decompose === - set {(0,{0}),(0,{1}),(0,{2}),(0,{3})}) -assert( ddec.decompose#(0,{0}) == matrix{{1_R,0,0}}) -assert( ddec.decompose#(0,{1}) == matrix{{1_R,1,0}}) -assert( ddec.decompose#(0,{2}) == matrix{{1_R,1,0}}) -assert( ddec.decompose#(0,{3}) == matrix{{1_R,1,0}}) +assert( set keys ddec.decompose == set {0}) +assert( ddec.decompose#0 == matrix{{1_QQ,0,0}} * T^0 + + matrix{{1_QQ,1,0}} * (T^1+T^2+T^3)) /// -- Test 1 (non-monomial ideal, symmetric group) @@ -2928,7 +3926,7 @@ I = ideal( (x_1-x_2)*(x_3-x_5), (x_1-x_2)*(x_3-x_4) ) -RI = freeResolution I +RI = res I S5 = for p in partitions(5) list ( L := gens R; g := for u in p list ( @@ -2940,39 +3938,39 @@ S5 = for p in partitions(5) list ( ) assert(S5 == symmetricGroupActors(R)) A = action(RI,S5) -a = character(R,7,hashTable { +a = character(R,hashTable { ((0,{0}), matrix{{1,1,1,1,1,1,1}}), ((1,{2}), matrix{{0,-1,1,-1,1,1,5}}), ((2,{3}), matrix{{0,1,-1,-1,1,-1,5}}), ((3,{5}), matrix{{1,-1,-1,1,1,-1,1}}) }) -assert((character A) === a) +assert((character A) == a) B = action(R,S5) -b = character(R,7,hashTable { +b = character(R,hashTable { ((0,{0}), matrix{{1,1,1,1,1,1,1}}), ((0,{1}), matrix{{0,1,0,2,1,3,5}}), ((0,{2}), matrix{{0,1,1,3,3,7,15}}), ((0,{3}), matrix{{0,1,1,5,3,13,35}}) }) -assert(character(B,0,3) === b) +assert(character(B,0,3) == b) C = action(I,S5) -c = character(R,7,hashTable { +c = character(R,hashTable { ((0,{2}), matrix{{0,-1,1,-1,1,1,5}}), ((0,{3}), matrix{{0,-2,1,-1,0,4,20}}) }) -assert(character(C,0,3) === c) +assert(character(C,0,3) == c) D = action(R/I,S5) -d = character(R,7,hashTable { +d = character(R,hashTable { ((0,{0}), matrix{{1,1,1,1,1,1,1}}), ((0,{1}), matrix{{0,1,0,2,1,3,5}}), ((0,{2}), matrix{{0,2,0,4,2,6,10}}), ((0,{3}), matrix{{0,3,0,6,3,9,15}}) }) -assert(character(D,0,3) === d) -assert(b === c++d) -cS5 = symmetricGroupTable(R) +assert(character(D,0,3) == d) +assert(b == c++d) +cS5 = symmetricGroupTable(5,QQ) assert( cS5.table == - matrix{{1_R,1,1,1,1,1,1}, + matrix{{1_QQ,1,1,1,1,1,1}, {-1,0,-1,1,0,2,4}, {0,-1,1,-1,1,1,5}, {1,0,0,0,-2,0,6}, @@ -2981,19 +3979,19 @@ assert( cS5.table == {1,-1,-1,1,1,-1,1}} ) adec = a/cS5 -assert( set keys adec.decompose === - set {(0,{0}),(1,{2}),(2,{3}),(3,{5})}) -assert( adec.decompose#(0,{0}) == matrix{{1_R,0,0,0,0,0,0}}) -assert( adec.decompose#(1,{2}) == matrix{{0,0,1_R,0,0,0,0}}) -assert( adec.decompose#(2,{3}) == matrix{{0,0,0,0,1_R,0,0}}) -assert( adec.decompose#(3,{5}) == matrix{{0,0,0,0,0,0,1_R}}) +assert( set keys adec.decompose == set {0,1,2,3}) +CR = a.degreesRing +T = CR_0 +assert( adec.decompose#0 == matrix{{1_QQ,0,0,0,0,0,0}} * T^0) +assert( adec.decompose#1 == matrix{{0,0,1_QQ,0,0,0,0}} * T^2) +assert( adec.decompose#2 == matrix{{0,0,0,0,1_QQ,0,0}} * T^3) +assert( adec.decompose#3 == matrix{{0,0,0,0,0,0,1_QQ}} * T^5) ddec = d/cS5 -assert( set keys ddec.decompose === - set {(0,{0}),(0,{1}),(0,{2}),(0,{3})}) -assert( ddec.decompose#(0,{0}) == matrix{{1_R,0,0,0,0,0,0}}) -assert( ddec.decompose#(0,{1}) == matrix{{1_R,1,0,0,0,0,0}}) -assert( ddec.decompose#(0,{2}) == matrix{{2_R,2,0,0,0,0,0}}) -assert( ddec.decompose#(0,{3}) == matrix{{3_R,3,0,0,0,0,0}}) +assert( set keys ddec.decompose == set {0}) +assert( ddec.decompose#0 == matrix{{1_QQ,0,0,0,0,0,0}} * T^0 + + matrix{{1_QQ,1,0,0,0,0,0}} * T^1 + + matrix{{2_QQ,2,0,0,0,0,0}} * T^2 + + matrix{{3_QQ,3,0,0,0,0,0}} * T^3) /// -- Test 2 (non symmetric group, tests actors) @@ -3014,25 +4012,25 @@ a = { map(R^{4:-3},R^{4:-3},{{w,0,0,0},{0,w^2,0,0},{0,0,w^3,0},{0,0,0,w^4}}), map(R^{4:-3},R^{4:-3},{{0,0,0,1},{0,0,1,0},{0,1,0,0},{1,0,0,0}}) } -assert(actors(A,3) === a) -ca = character(R,4, hashTable {((0,{3}), matrix{apply(a,trace)})}) -assert(character(A,3) === ca) +assert(actors(A,3) == a) +ca = character(R, hashTable {((0,{3}), lift(matrix{apply(a,trace)},kk))}) +assert(character(A,3) == ca) d1=map(R^1,R^{4:-3},{{x^3,x^2*y,x*y^2,y^3}}) d2=map(R^{4:-3},R^{3:-4},{{-y,0,0},{x,-y,0},{0,x,-y},{0,0,x}}) -Rm=complex{d1,d2} +Rm=complex({d1,d2}) B = action(Rm,D5) -assert(actors(B,1) === a) -cb1 = character(R,4, hashTable {((1,{3}), matrix{apply(a,trace)})}) -assert(character(B,1) === cb1) +assert(actors(B,1) == a) +cb1 = character(R, hashTable {((1,{3}), lift(matrix{apply(a,trace)},kk))}) +assert(character(B,1) == cb1) b = { map(R^{3:-4},R^{3:-4},{{1,0,0},{0,1,0},{0,0,1}}), map(R^{3:-4},R^{3:-4},{{w^2,0,0},{0,1,0},{0,0,w^3}}), map(R^{3:-4},R^{3:-4},{{w^4,0,0},{0,1,0},{0,0,w}}), map(R^{3:-4},R^{3:-4},{{0,0,-1},{0,-1,0},{-1,0,0}}) } -assert(actors(B,2) === b) -cb2 = character(R,4, hashTable {((2,{4}), matrix{apply(b,trace)})}) -assert(character(B,2) === cb2) +assert(actors(B,2) == b) +cb2 = character(R, hashTable {((2,{4}), lift(matrix{apply(b,trace)},kk))}) +assert(character(B,2) == cb2) /// -- Test 3 (multigraded ideal, product of symmetric groups) @@ -3040,7 +4038,7 @@ TEST /// clearAll R = QQ[x_1,x_2,y_1,y_2,Degrees=>{2:{1,0},2:{0,1}}] I = intersect(ideal(x_1,x_2),ideal(y_1,y_2)) -RI = freeResolution I +RI = res I G = { matrix{{x_2,x_1,y_2,y_1}}, matrix{{x_2,x_1,y_1,y_2}}, @@ -3048,7 +4046,7 @@ G = { matrix{{x_1,x_2,y_1,y_2}} } A = action(RI,G) -a = character(R,4,hashTable { +a = character(R,hashTable { ((0,{0,0}), matrix{{1,1,1,1}}), ((1,{1,1}), matrix{{0,0,0,4}}), ((2,{1,2}), matrix{{0,0,-2,2}}), @@ -3057,7 +4055,7 @@ a = character(R,4,hashTable { }) assert((character A) == a) B = action(R,G) -b = character(R,4,hashTable { +b = character(R,hashTable { ((0,{0,2}), matrix{{1,3,1,3}}), ((0,{2,0}), matrix{{1,1,3,3}}) }) @@ -3070,14 +4068,60 @@ assert(character(C,{0,2}) ++ character(C,{2,0}) == b) TEST /// clearAll R = QQ[x_1..x_4] -K = freeResolution ideal vars R +K = res ideal vars R S4 = symmetricGroupActors(R) A = action(K,S4) c = character A -sgn = character(R,5, hashTable { (-4,{-4}) => matrix{{-1,1,1,-1,1}} }) +signrep = character(R, hashTable { (-4,{-4}) => matrix{{-1,1,1,-1,1}} }) -- check duality of representations in Koszul complex -- which is true up to a twist by a sign representation -assert(dual(c,id_QQ) == c ** sgn) +assert(dual(c,id_QQ) == c ** signrep) +/// + +-- Test 5 (additive inverse, scalar multiplication, difference, degree selection) +TEST /// +clearAll +R = QQ[x,y,z] +I = (ideal vars R)^3 +J = ideal(x^3,y^3,z^3) +S3 = symmetricGroupActors R +A1 = action(I,S3) +A2 = action(J,S3) +c1 = character(A1,0,10) +c2 = character(A2,0,10) +assert(-c1 == (-1)*c1) +assert(c1 ++ c1 == 2*c1) +c = character(R, hashTable { + (0,{5}) => matrix{{0,1,3}}, + (0,{6}) => matrix{{1,1,1}} + }) +assert( (c1 - c2)^{{5},{6}} == c) +/// + +-- Test 6 (fine grading, semidirect product with symmetric group) +TEST /// +clearAll +R = QQ[x,y,z,Degrees=>{{1,0,0},{0,1,0},{0,0,1}}] +I = ideal(x*y,x*z,y*z) +RI = res I +S3 = symmetricGroupActors R +A1 = action(RI,S3,Semidirect=>{uniquePermutations,rsort}) +c1 = character(A1) +d1 = character(R, hashTable { + (0,{0,0,0}) => matrix{{1,1,1}}, + (1,{1,1,0}) => matrix{{0,1,1}}, + (1,{1,0,1}) => matrix{{0,0,1}}, + (1,{0,1,1}) => matrix{{0,0,1}}, + (2,{1,1,1}) => matrix{{-1,0,2}} + }, + Semidirect=>{uniquePermutations,rsort}) +assert( c1 === d1) +A2 = action(R,S3,Semidirect=>{uniquePermutations,rsort}) +c2 = character(A2,{0,3,0}) ++ character(A2,{1,0,2}) ++ character(A2,{1,1,1}) +T = c2.degreesRing +d2 = map(T^{0},T^{0,0,0},matrix {{T_0*T_1*T_2, T_0*T_1*T_2+T_2^3, + T_0^3+T_0^2*T_1+T_0^2*T_2+T_0*T_1^2+T_0*T_1*T_2+T_0*T_2^2+T_1^3+T_1^2*T_2+T_1*T_2^2+T_2^3}}) +assert( c2.characters#0 == d2) /// end diff --git a/M2/Macaulay2/packages/Binomials.m2 b/M2/Macaulay2/packages/Binomials.m2 index 6e821d2d405..5e6b551fe03 100644 --- a/M2/Macaulay2/packages/Binomials.m2 +++ b/M2/Macaulay2/packages/Binomials.m2 @@ -30,7 +30,7 @@ newPackage( HomePage => "http://www.thomas-kahle.de"}}, Headline => "specialized routines for binomial ideals", Keywords => {"Commutative Algebra"}, - PackageImports => {"FourTiTwo", "Cyclotomic", "LLLBases", "MinimalPrimes", "Elimination"}, + PackageImports => {"FourTiTwo", "Cyclotomic", "LLLBases", "MinimalPrimes", "Elimination", "Classic"}, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry: Macaulay2", "journal URI" => "https://msp.org/jsag/", @@ -318,7 +318,7 @@ randomBinomialIdeal = (R,numge,maxdeg, maxwidth, homog) -> ( -ra ); -- filling with zeros - m = random (m |z); + m = shuffle (m |z); ge = ge | {makeBinomial (R,m,1)}; ); ) @@ -331,7 +331,7 @@ randomBinomialIdeal = (R,numge,maxdeg, maxwidth, homog) -> ( ra ); -- filling with zeros - m = random (m |z); + m = shuffle (m |z); ge = ge | {makeBinomial (R,m,1)}; ); ); diff --git a/M2/Macaulay2/packages/BoijSoederberg.m2 b/M2/Macaulay2/packages/BoijSoederberg.m2 index 74884fe9f2e..31a27e208d8 100644 --- a/M2/Macaulay2/packages/BoijSoederberg.m2 +++ b/M2/Macaulay2/packages/BoijSoederberg.m2 @@ -12,7 +12,7 @@ newPackage( }, Headline => "Betti diagram operations useful for investigating the Boij-Soederberg conjectures", Keywords => {"Commutative Algebra"}, - PackageExports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, DebuggingMode => false ) @@ -20,7 +20,7 @@ export { "mat2betti", -- documented "lowestDegrees", -- documented "highestDegrees", -- documented - "isPure", -- documented + --"isPure", -- documented "makePureBetti", --documented "makePureBettiDiagram", --documented "pureBetti", -- documented @@ -245,7 +245,7 @@ highestDegrees BettiTally := (B) -> ( max apply(B1, k -> k#2) )) -isPure = method() +--isPure = method() isPure BettiTally := (B) -> lowestDegrees B == highestDegrees B @@ -1168,7 +1168,7 @@ assert(dotProduct(A2, mat2betti B2) == 2) -* supportFunctional is NOT functional yet *- supportFunctional=method() -supportFunctional(ChainComplex, ChainComplex):=(E,F)->( +supportFunctional(Complex, Complex):=(E,F)->( --E should be a chain complex starting in degree 0 and going to negative degrees. --F should be a chain complex starting in a positive degree and going to degree 0 -- the code is meant to execute @@ -1179,27 +1179,27 @@ supportFunctional(ChainComplex, ChainComplex):=(E,F)->( minF := min degreesF; maxF := max degreesF; HHE:=HH E; - L:=for i from 0 to length E list matrix{{hf(minF..maxF, (HH E)#(-i))}}; + L:=for i from 0 to length E list matrix{{hf(minF..maxF, HHE_(-i))}}; A:=transpose L_0; for i from 1 to length L -1 do A = A|(transpose L_i); AA:=map(ZZ^(maxF-minF-lengthF+1), ZZ^(lengthF+1), (p,q)-> sum(0..min(q,length E), - j->if HHE#?(-j) then (-1)^(q-j)*hilbertFunction(-p-q, (HHE)#(-j)) else 0)); + j->if HHE_(-j) != 0 then (-1)^(q-j)*hilbertFunction(-p-q, (HHE)_(-j)) else 0)); dotProduct(AA, minF, betti F) ) -supportFunctional(ChainComplex, BettiTally):=(E,B)->( +supportFunctional(Complex, BettiTally):=(E,B)->( lengthF := max apply(keys B, K->first K); degreesF := apply(keys B, K->last K); minF := min degreesF; maxF := max degreesF; HHE := HH E; - L := for i from 0 to length E list matrix{{hf(minF..maxF, (HH E)#(-i))}}; + L := for i from 0 to length E list matrix{{hf(minF..maxF, HHE_(-i))}}; A := transpose L_0; for i from 1 to length L-1 do A = A|(transpose L_i); AA := map(ZZ^(maxF-minF-lengthF+1), ZZ^(lengthF+1), (p,q)-> sum(0..min(q,length E), - j->if HHE#?(-j) then (-1)^(q-j)*hilbertFunction(-p-q, (HHE)#(-j)) else 0)); + j->if HHE_(-j) != 0 then (-1)^(q-j)*hilbertFunction(-p-q, HHE_(-j)) else 0)); dotProduct(AA, minF, B) ) @@ -1420,7 +1420,7 @@ bott(List, ZZ):=(L,u)->( ) bott(List,ZZ,ZZ,Symbol):=(L,low,high,old)->( - --produces the betti diagram of the tate resolution of the sheaf S_L(Q), + --produces the Betti diagram of the Tate resolution of the sheaf S_L(Q), --between the column whose index is "low" and the column whose index is "high" n:=#L; r:=high-low-n; @@ -1433,7 +1433,7 @@ bott(List,ZZ,ZZ,Symbol):=(L,low,high,old)->( ) bott(List,ZZ,ZZ):=(L,low,high)->( - --produces the betti diagram of the tate resolution of the sheaf S_L(Q), + --produces the Betti diagram of the Tate resolution of the sheaf S_L(Q), --between the column whose index is "low" and the column whose index is "high" n:=#L; C := for u from low-n to high list ( @@ -1503,7 +1503,7 @@ document { Key => BoijSoederberg, TO makePureBetti, TO pureBettiDiagram, TO makePureBettiDiagram, - TO isPure + TO (isPure, BettiTally) }, SUBSECTION "Cohomology tables", UL { @@ -1575,7 +1575,7 @@ document { B1 = lowestDegrees B pureBettiDiagram B1 ///, - SeeAlso => {highestDegrees,isPure} + SeeAlso => {highestDegrees,(isPure, BettiTally)} } document { @@ -1594,11 +1594,11 @@ document { highestDegrees B lowestDegrees B ///, - SeeAlso => {lowestDegrees,isPure} + SeeAlso => {lowestDegrees,(isPure, BettiTally)} } document { - Key => {(isPure,BettiTally),isPure}, + Key => {(isPure,BettiTally)}, Headline => "is a Betti diagram pure?", Usage => "isBure B", Inputs => { @@ -1922,7 +1922,7 @@ document { Caveat => {"Currently, the error messages are not that illuminating. The [lowDegree, highDegree], if given, must be as large as the actual degree range"}, - SeeAlso => {mat2betti, lowestDegrees, highestDegrees, isPure, pureBettiDiagram} + SeeAlso => {mat2betti, lowestDegrees, highestDegrees, (isPure, BettiTally), pureBettiDiagram} } document { diff --git a/M2/Macaulay2/packages/Brackets.m2 b/M2/Macaulay2/packages/Brackets.m2 index 437a3ede47f..59c4a5d8344 100644 --- a/M2/Macaulay2/packages/Brackets.m2 +++ b/M2/Macaulay2/packages/Brackets.m2 @@ -53,7 +53,7 @@ bracketRing (VisibleList, ZZ) := o -> (vectorSymbols, d) -> ( x := symbol x; R := o.CoefficientRing[x_(1,1)..x_(n,d)]; X := matrix for i from 1 to n list for j from 1 to d list x_(i,j); - nBrackets := rsort(sort \ subsets(vectorSymbols, d)); -- important for "Tableux order" + nBrackets := rsort(sort \ subsets(vectorSymbols, d)); -- important for "Tableaux order" bracketIndices := rsort(sort \ subsets(#vectorSymbols, d)); minorsX := apply(bracketIndices, R -> det X^R); y := symbol y; diff --git a/M2/Macaulay2/packages/Bruns.m2 b/M2/Macaulay2/packages/Bruns.m2 index 6bcaf96f52e..d1eaff43751 100644 --- a/M2/Macaulay2/packages/Bruns.m2 +++ b/M2/Macaulay2/packages/Bruns.m2 @@ -20,7 +20,7 @@ newPackage( HomePage=>"http://www.msri.org/~de"}}, Headline => "make a 3-generator ideal with an \"any\" resolution", Keywords => {"Commutative Algebra"}, - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, DebuggingMode => false ) @@ -159,7 +159,7 @@ evansGriffith(Matrix, ZZ) := (f,n)->( -------------------------------------------------------------- --- isSyzgy +-- isSyzygy -- checks if a module is a d-th syzygy -------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/CellularResolutions.m2 b/M2/Macaulay2/packages/CellularResolutions.m2 index 5a93200a5a4..9b1190ab4c8 100644 --- a/M2/Macaulay2/packages/CellularResolutions.m2 +++ b/M2/Macaulay2/packages/CellularResolutions.m2 @@ -2,11 +2,11 @@ -- This file is in the public domain newPackage( "CellularResolutions", - Version => "1.0", - Date => "May 17, 2023", + Version => "1.1", + Date => "December 23, 2025", Authors => { - {Name => "Jay Yang", Email => "jayy@wustl.edu"}, - {Name => "Aleksandra Sobieska", Email => "asobieska@math.wisc.edu"} + {Name => "Jay Yang", Email => "jay.k.yang@vanderbilt.edu"}, + {Name => "Aleksandra Sobieska", Email => "sobieskasnyd@marshall.edu"} }, Headline => "cellular resolutions of monomial ideals", AuxiliaryFiles => true, -- set to true if package comes with auxiliary files @@ -28,7 +28,6 @@ export {--types "cellLabel", "hullComplex", "isCycle", --- "isFree", "isMinimal", "isSimplex", "newCell", @@ -43,7 +42,6 @@ export {--types "InferLabels", "LabelRing", "Reduced" - --"Prune" } protect labelRing protect label @@ -66,7 +64,7 @@ CellComplex.GlobalReleaseHook = globalReleaseFunction Cell = new Type of MutableHashTable Cell.synonym = "cell" -maxAndAllCells := (lst) -> ( +maxAndAllCells = lst -> ( if #lst == 0 then return (new HashTable,new HashTable); bdfn := c -> set boundaryCells c; maxcells := set lst; @@ -82,7 +80,7 @@ maxAndAllCells := (lst) -> ( ) --returns a hashtable of lists of cells indexed by dimension -cellsFromMaxCells := lst -> ( +cellsFromMaxCells = lst -> ( pendingCells := set lst; finishedCells := {}; while #pendingCells !=0 do ( @@ -94,7 +92,7 @@ cellsFromMaxCells := lst -> ( ) --Private constructor, creates the cache -mkCellComplex := (labelRingVal, cellsVal, maxCellsVal) -> ( +mkCellComplex = (labelRingVal, cellsVal, maxCellsVal) -> ( new CellComplex from { symbol labelRing => labelRingVal, symbol cells => cellsVal, @@ -106,12 +104,12 @@ mkCellComplex := (labelRingVal, cellsVal, maxCellsVal) -> ( ) cellComplex = method(Options=>true, TypicalValue=>CellComplex) -cellComplex(Ring,List) := {} >> o -> (R,maxCells) -> ( +cellComplex(Ring,List) := CellComplex => {} >> o -> (R,maxCells) -> ( (realMaxCells,allCells) := maxAndAllCells maxCells; mkCellComplex(R, allCells, realMaxCells) ) -cellComplex(Ring,SimplicialComplex) := {Labels=>null} >> o -> (S,C) -> ( +cellComplex(Ring,SimplicialComplex) := CellComplex => {Labels=>null} >> o -> (S,C) -> ( R := ring C; Cfaces := new HashTable from faces C; --cells indexes Cells by monomials corresponding to faces of the simplicial complex @@ -120,10 +118,8 @@ cellComplex(Ring,SimplicialComplex) := {Labels=>null} >> o -> (S,C) -> ( for simplex in Cfaces#i do ( label := (if class(o.Labels) === HashTable - then (if o.Labels#?simplex - then o.Labels#simplex - else null - ) + then + o.Labels#simplex ?? null else null); bd := if i==0 then {} @@ -138,7 +134,7 @@ cellComplex(Ring,SimplicialComplex) := {Labels=>null} >> o -> (S,C) -> ( ) maxCells = method(TypicalValue=>HashTable) -maxCells(CellComplex) := (cacheValue (symbol maxCells)) (cellComplex -> +maxCells(CellComplex) := HashTable => (cacheValue (symbol maxCells)) (cellComplex -> ( lst := flatten values cells cellComplex; if #lst == 0 then return new HashTable; @@ -154,20 +150,20 @@ maxCells(CellComplex) := (cacheValue (symbol maxCells)) (cellComplex -> )) --Define dimension for cell -dim(Cell) := (cell) -> cell.cellDimension +dim Cell := ZZ => cell -> cell.cellDimension --Define dimension for cell complex -dim(CellComplex) := (cellComplex) -> max keys cellComplex.cells +dim CellComplex := ZZ => cellComplex -> max keys cellComplex.cells --Define ring for cell complex -ring(CellComplex) := (cellComplex) -> cellComplex.labelRing +ring CellComplex := Ring => cellComplex -> cellComplex.labelRing cellLabel = method() -cellLabel(Cell) := (cell) -> cell.label +cellLabel Cell := Thing => cell -> cell.label --Make a cell, internal function -makeCell := (lst, l, d) -> ( +makeCell = (lst, l, d) -> ( bdim := -1; for cell in lst do ( if bdim < 0 @@ -182,21 +178,21 @@ makeCell := (lst, l, d) -> ( } ); -chainToVirtualTally := (lst) -> ( +chainToVirtualTally = (lst) -> ( if lst == {} then new VirtualTally from {} else sum(lst, (cell,deg) -> new VirtualTally from {cell => deg}) ) boundary = method() -boundary(Cell) := List => (cell) -> cell.boundary +boundary Cell := List => cell -> cell.boundary boundaryCells = method(TypicalValue=>List) -boundaryCells(Cell) := (cell) -> apply(boundary(cell), c -> first c) +boundaryCells Cell := List => cell -> apply(boundary(cell), c -> first c) --Boundary function, returns the boundary as a VirtualTally -boundaryTally := (cell) -> chainToVirtualTally cell.boundary +boundaryTally = cell -> chainToVirtualTally cell.boundary -internalCycleCheck := (lst) -> ((sum(lst,l -> ( +internalCycleCheck = lst -> ((sum(lst,l -> ( c := l#0; deg := l#1; if deg>0 @@ -205,19 +201,19 @@ internalCycleCheck := (lst) -> ((sum(lst,l -> ( --Check if a chain, represented by a list is a boundary isCycle = method(TypicalValue=>Boolean) -isCycle(List) := {Reduced=>true} >> o -> (lst) -> +isCycle List := Boolean => {Reduced=>true} >> o -> (lst) -> (if o.Reduced then ( p := partition(x -> dim (x#0) == 0, lst); - zeroDimCells := if p#?true then p#true else {}; - otherCells := if p#?false then p#false else {}; + zeroDimCells := p#true ?? {}; + otherCells := p#false ?? {}; internalCycleCheck otherCells and sum(zeroDimCells,last) == 0 ) else internalCycleCheck lst) --Figure out an orientation automatically -inferOrientation := (lst) -> ( +inferOrientation = lst -> ( if #lst == 2 and (dim first lst) == 0 then ( ret := {(lst#0,1),(lst#1,-1)}; if not isCycle ret then error "The given list of cells do not form a cycle"; @@ -256,7 +252,7 @@ inferOrientation := (lst) -> ( ) --Convert it to a submodule of R^1 if possible -toModule := (R,x) -> ( +toModule = (R,x) -> ( if instance(x,Module) then return x; if instance(x,Ideal) then return module x; if instance(x,RingElement) then return image matrix {{x}}; @@ -264,7 +260,7 @@ toModule := (R,x) -> ( error "Expected a Module, Ideal, RingElement, or Number" ) -inferLabel := boundary -> ( +inferLabel = boundary -> ( if boundary == {} then return 1; if instance(boundary#0,Sequence) then return boundary/first//inferLabel; if all(boundary/cellLabel,b -> instance(b,RingElement) or instance(b,Number)) @@ -282,7 +278,7 @@ newCell = method(Options => {CellDimension=>null}, TypicalValue=>Cell) newCell(List,Number) := newCell(List,RingElement) := newCell(List,Module) := -newCell(List,Ideal) := opt -> (boundary,label) -> ( +newCell(List,Ideal) := Cell => opt -> (boundary,label) -> ( if #boundary!=0 and instance(boundary#0,Cell) then return newCell(inferOrientation boundary,label,CellDimension=>opt.CellDimension); if not isCycle boundary then error "Expected the boundary to be a cycle"; @@ -291,11 +287,11 @@ newCell(List,Ideal) := opt -> (boundary,label) -> ( if opt.CellDimension=!=null and dim c > cd then error "Incorrect CellDimension optional parameter"; c ) -newCell(List) := opt -> cells -> newCell(cells,inferLabel cells,CellDimension=>opt.CellDimension); +newCell List := Cell => opt -> cells -> newCell(cells,inferLabel cells,CellDimension=>opt.CellDimension); -isSimplexBoundary := (lst) -> ( +isSimplexBoundary = (lst) -> ( if #lst==0 then return true; bdim := dim first lst#0; all(lst,isSimplex @@ first) and @@ -306,11 +302,11 @@ isSimplexBoundary := (lst) -> ( ) isSimplex = method(TypicalValue=>Boolean); -isSimplex(Cell) := cell -> +isSimplex Cell := Boolean => cell -> isSimplexBoundary boundary cell newSimplexCell = method(TypicalValue=>Cell); -newSimplexCell(List) := (boundary) -> ( +newSimplexCell List := Cell => boundary -> ( if #boundary!=0 and instance(boundary#0,Cell) then return newSimplexCell inferOrientation boundary; if not isSimplexBoundary boundary then error "The given boundary is not a valid boundary for a simplex"; @@ -319,7 +315,7 @@ newSimplexCell(List) := (boundary) -> ( newSimplexCell(List,Number) := newSimplexCell(List,RingElement) := newSimplexCell(List,Module) := -newSimplexCell(List,Ideal) := (boundary,label) -> ( +newSimplexCell(List,Ideal) := Cell => (boundary,label) -> ( if #boundary!=0 and instance(boundary#0,Cell) then return newSimplexCell(inferOrientation boundary,label); if not isSimplexBoundary boundary then error "The given boundary is not a valid boundary for a simplex"; @@ -328,7 +324,7 @@ newSimplexCell(List,Ideal) := (boundary,label) -> ( --Relabel function relabelCellComplex = method(Options=>{InferLabels=>true},TypicalValue=>CellComplex); -relabelCellComplex(CellComplex,HashTable) := o -> (C,T) -> ( +relabelCellComplex(CellComplex,HashTable) := CellComplex => o -> (C,T) -> ( dimC := dim C; R := ring C; tablecellsbydim := for i to dimC list select(keys T, c -> dim c == i); @@ -349,7 +345,7 @@ relabelCellComplex(CellComplex,HashTable) := o -> (C,T) -> ( cellComplex(R, flatten values relabeledcells) ) -RingMap ** CellComplex := (f,c) -> ( +RingMap ** CellComplex := CellComplex => (f,c) -> ( if source f =!= ring c then error "source ring should match label ring"; R := source f; S := target f; @@ -362,12 +358,8 @@ RingMap ** CellComplex := (f,c) -> ( --Get list of cells cells = method(); -cells(CellComplex) := HashTable => (cellComplex) -> cellComplex.cells -cells(ZZ,CellComplex) := List => (r,cellComplex) -> ( - if cellComplex.cells#?r - then cellComplex.cells#r - else {} - ) +cells CellComplex := HashTable => cellComplex -> cellComplex.cells +cells(ZZ,CellComplex) := List => (r,cellComplex) -> cellComplex.cells#r ?? {} skeleton(ZZ,CellComplex) := CellComplex => (n,cellComplex) -> ( c := new HashTable from select(pairs cellComplex.cells, (k,v) -> k<=n); @@ -375,20 +367,18 @@ skeleton(ZZ,CellComplex) := CellComplex => (n,cellComplex) -> ( ) --take a hash table of RingElements/Matrices, and make a matrix, or 0 -sparseBlockMap := (codomain,domain,ht) -> ( +sparseBlockMap = (codomain,domain,ht) -> ( ks := keys ht; if ks == {} then return map(codomain,domain,0); rows := max (ks/first) + 1; columns := max (ks/(p->p#1)) + 1; - maybeHt := p -> ( - if ht#?p then ht#p else 0 - ); + maybeHt := p -> ht#p ?? 0; assert(rows <= #components codomain); assert(columns <= #components domain); map(codomain,domain,matrix apply(#components codomain,i -> apply(#components domain, j -> maybeHt(i,j))))) --Create one boundary map in the chain complex -boundaryMap(ZZ,CellComplex) := opts -> (r,cellComplex) -> ( +boundaryMap(ZZ,CellComplex) := Matrix => opts -> (r,cellComplex) -> ( R := cellComplex.labelRing; t := r-1; rCells := cells(r,cellComplex); @@ -418,7 +408,7 @@ boundaryMap(ZZ,CellComplex) := opts -> (r,cellComplex) -> ( sparseBlockMap(codomain,domain,new HashTable from L) ); -complex(CellComplex) := {Reduced=>true, Prune=>true} >> o -> (cellComplex) -> ( +complex CellComplex := Complex => {Reduced=>true, Prune=>true} >> o -> cellComplex -> ( if not cellComplex.cache.?complex then ( cellComplex.cache.complex = (complex apply(max((dim cellComplex) + 1,1), r -> boundaryMap(r,cellComplex)))[1] @@ -436,23 +426,23 @@ complex(CellComplex) := {Reduced=>true, Prune=>true} >> o -> (cellComplex) -> ( ); --Get homology directly from cell complex -homology(ZZ,CellComplex) := opts -> (i,cellComplex) -> ( +homology(ZZ,CellComplex) := Module => opts -> (i,cellComplex) -> ( homology_i complex(cellComplex) ); -homology(CellComplex) := opts -> (cellComplex) -> ( +homology CellComplex := Complex => opts -> cellComplex -> ( homology complex(cellComplex) ); --Get cohomology directly from cell complex -cohomology(ZZ,CellComplex) := opts -> (i,cellComplex) -> ( +cohomology(ZZ,CellComplex) := Module => opts -> (i,cellComplex) -> ( cohomology_i Hom(complex(cellComplex),cellComplex.labelRing^1) ); ---------- ---Here there be polyhedra ---------- -cellComplex(Ring,Polyhedron) := {Labels => null} >> o -> (R,P) -> ( +cellComplex(Ring,Polyhedron) := CellComplex => {Labels => null} >> o -> (R,P) -> ( if not isCompact P then error "The given polyhedron is not compact."; Pdim := dim P; Pfaces := applyPairs(faces P, (i,lst) -> (Pdim-i,apply(lst,first))); @@ -476,7 +466,7 @@ cellComplex(Ring,Polyhedron) := {Labels => null} >> o -> (R,P) -> ( cellComplex(R,flatten values cells) ); -cellComplex(Ring,PolyhedralComplex) := {Labels => null} >> o -> (R,P) -> ( +cellComplex(Ring,PolyhedralComplex) := CellComplex => {Labels => null} >> o -> (R,P) -> ( Pdim := dim P; Pfaces := applyPairs(faces P, (i,lst) -> (Pdim-i-1,apply(lst,first))); verts := vertices P; @@ -503,7 +493,7 @@ cellComplex(Ring,PolyhedralComplex) := {Labels => null} >> o -> (R,P) -> ( -- Posets ------------- -facePoset(CellComplex) := (cellComplex) -> ( +facePoset CellComplex := Poset => cellComplex -> ( G := flatten values cells cellComplex; contain := (a,b) -> member(a,boundaryCells b) or a === b;-- a contained or equal b P := poset(G,contain); @@ -518,25 +508,25 @@ facePoset(CellComplex) := (cellComplex) -> ( --isFree = method(TypicalValue => Boolean); --check if all the labels are free modules -isFree(CellComplex) := (cellComplex) -> ( +isFree CellComplex := Boolean => cellComplex -> ( R := cellComplex.labelRing; all(flatten values cells cellComplex,c -> isFreeModule prune toModule(R,cellLabel c)) ) -isCellMinimal := (R,cell) -> ( +isCellMinimal = (R,cell) -> ( label := toModule(R,cellLabel cell); all(boundary cell, c -> toModule(R,cellLabel first c) != label) ) isMinimal = method(TypicalValue => Boolean) --Check if a labeled cell complex is minimal, Note: we assume the cell complex is free (see isFree) -isMinimal(CellComplex) := (cellComplex) -> ( +isMinimal CellComplex := Boolean => cellComplex -> ( R := cellComplex.labelRing; all(flatten values cells cellComplex,c -> isCellMinimal(R,c)) ) subcomplex = method(TypicalValue => CellComplex, Options=>{LabelRing=>null}) -subcomplex(CellComplex,RingElement) := o -> (C,m) -> ( +subcomplex(CellComplex,RingElement) := CellComplex => o -> (C,m) -> ( allCells := flatten values cells C; R := ring C; S := if o.LabelRing =!= null then o.LabelRing else coefficientRing R; @@ -553,7 +543,7 @@ subcomplex(CellComplex,RingElement) := o -> (C,m) -> ( cellComplex(S,values finalCells) ) -subcomplex(CellComplex,List) := o -> (C,d) -> ( +subcomplex(CellComplex,List) := CellComplex => o -> (C,d) -> ( allCells := flatten values cells C; R := ring C; S := if o.LabelRing =!= null then o.LabelRing else coefficientRing R; @@ -570,38 +560,59 @@ subcomplex(CellComplex,List) := o -> (C,d) -> ( cellComplex(R,values finalCells) ) -subcomplex(CellComplex,ZZ) := o -> (C,d) -> subcomplex(C,{d},LabelRing=>o.LabelRing); +subcomplex(CellComplex,ZZ) := CellComplex => o -> (C,d) -> subcomplex(C,{d},LabelRing=>o.LabelRing); -CellComplex _ RingElement := (C,m) -> (subcomplex(C,m)) -CellComplex _ List := (C,d) -> (subcomplex(C,d)) -CellComplex _ ZZ := (C,d) -> (subcomplex(C,{d})) +CellComplex _ RingElement := CellComplex => (C,m) -> (subcomplex(C,m)) +CellComplex _ List := CellComplex => (C,d) -> (subcomplex(C,d)) +CellComplex _ ZZ := CellComplex => (C,d) -> (subcomplex(C,{d})) --------- -- Output --------- -net(Cell) := (cell) -> ( +net Cell := Net => cell -> ( "Cell of dimension " | (dim cell) | " with label " | (net cellLabel cell) ) -net(CellComplex) := (cellComplex) -> ( - if hasAttribute (cellComplex, ReverseDictionary) then return getAttribute (cellComplex, ReverseDictionary); +texMath Cell := String => cell -> ( + "\\textrm{Cell of dimension }" | (texMath dim cell) | "\\textrm{ with label }" | (texMath cellLabel cell) + ) + +describe CellComplex := Expression => cellComplex -> ( d := dim cellComplex; nTotalCells := #(flatten values cells cellComplex); if nTotalCells == 0 - then "empty CellComplex" + then Describe "empty cell complex" else ( - ("CellComplex over " | (net cellComplex.labelRing) | " of dimension " | d | " with " | nTotalCells | " total cells") || - stack(apply(d+1,i -> net cells_i cellComplex))) - ); + Describe (("Cell complex over ") (describe cellComplex.labelRing)) ("of dimension " | d | " with " | nTotalCells | " total cells") + )) + +net CellComplex := Net => cellComplex -> ( + if hasAttribute (cellComplex, ReverseDictionary) then return net getAttribute (cellComplex, ReverseDictionary); + d := dim cellComplex; + nTotalCells := #(flatten values cells cellComplex); + if nTotalCells == 0 + then "empty cell complex" + else ( + "Cell complex over " | net cellComplex.labelRing |" of dimension " | d | " with " | nTotalCells | " total cells" + )) +texMath CellComplex := String => cellComplex -> ( + if hasAttribute (cellComplex, ReverseDictionary) then return texMath getAttribute (cellComplex, ReverseDictionary); + d := dim cellComplex; + nTotalCells := #(flatten values cells cellComplex); + if nTotalCells == 0 + then "\\textrm{empty cell complex}" + else ( + ("\\textrm{Cell complex over }" | (texMath cellComplex.labelRing) | "\\textrm{ of dimension }" | d | "\\textrm{ with }" | nTotalCells | "\\textrm{ total cells }\n") + )) ------------------------ -- Common cell complexes ------------------------ cellComplexSphere = method(TypicalValue=>CellComplex); -cellComplexSphere(Ring,ZZ) := (R,n) -> ( +cellComplexSphere(Ring,ZZ) := CellComplex => (R,n) -> ( if n<0 then error "cellComplexSphere expects a non-negative integer"; v := newSimplexCell {}; if n==0 then ( @@ -615,7 +626,7 @@ cellComplexSphere(Ring,ZZ) := (R,n) -> ( ) cellComplexRPn = method(TypicalValue=>CellComplex); -cellComplexRPn(Ring,ZZ) := (R,n) -> ( +cellComplexRPn(Ring,ZZ) := CellComplex => (R,n) -> ( if n<0 then error "cellComplexRPn expects a non-negative integer"; t := newSimplexCell {}; if n==0 then return cellComplex(R,{t}); @@ -627,7 +638,7 @@ cellComplexRPn(Ring,ZZ) := (R,n) -> ( ) cellComplexTorus = method(TypicalValue=>CellComplex); -cellComplexTorus(Ring,ZZ) := (R,n) -> ( +cellComplexTorus(Ring,ZZ) := CellComplex => (R,n) -> ( if n<0 then error "cellComplexTorus expects a non-negative integer"; v := newSimplexCell {}; if n==0 then return cellComplex(R,{v}); @@ -645,7 +656,7 @@ cellComplexTorus(Ring,ZZ) := (R,n) -> ( ---------------------------- taylorComplex = method(TypicalValue=>CellComplex); -taylorComplex(MonomialIdeal) := (I) -> ( +taylorComplex MonomialIdeal := CellComplex => I -> ( gensI := I_*; r := #gensI; if r == 0 then error "taylorComplex expects a non-zero monomialIdeal"; @@ -661,7 +672,7 @@ taylorComplex(MonomialIdeal) := (I) -> ( ) scarfComplex = method(TypicalValue=>CellComplex) -scarfComplex(MonomialIdeal) := (I) -> ( +scarfComplex MonomialIdeal := CellComplex => I -> ( gensI := I_*; r := #gensI; if r == 0 then error "scarfComplex expects a non-zero monomialIdeal"; @@ -701,12 +712,12 @@ scarfComplex(MonomialIdeal) := (I) -> ( ) hullComplex = method(TypicalValue=>CellComplex); -hullComplex(MonomialIdeal) := (I) -> ( +hullComplex MonomialIdeal := CellComplex => I -> ( n := numgens ring I; hullComplex((n+1)!+1,I) ) -hullComplex(ZZ,MonomialIdeal) := (t,I) -> hullComplex(t/1,I) -hullComplex(QQ,MonomialIdeal) := (t,I) -> ( +hullComplex(ZZ,MonomialIdeal) := CellComplex => (t,I) -> hullComplex(t/1,I) +hullComplex(QQ,MonomialIdeal) := CellComplex => (t,I) -> ( gensI := I_*; R := ring I; n := numgens R; @@ -729,26 +740,39 @@ hullComplex(QQ,MonomialIdeal) := (t,I) -> ( cellComplex(R,flatten values cells) ) -isWellDefined(Cell) := (C) -> ( +isWellDefined Cell := Boolean => C -> ( R := ring C.label; M := toModule(R,C.label); - if not isSubmodule M then return false; + if not isSubmodule M then ( + if debugLevel > 0 then stderr << "isWellDefined: Expected a label describing a submodule" << endl; + return false; + ); --check that the boundary labels are compatible --boundary is a list of pairs where the first element is the cell - if not all(C.boundary, x -> isSubset(M,toModule(R,(x#0).label))) then return false; + if not all(C.boundary, x -> isSubset(M,toModule(R,(x#0).label))) then ( + if debugLevel > 0 then stderr << "isWellDefined: Boundary cell has incompatible label" << endl; + return false; + ); --check that the boundary is a cycle in homology isCycle(C.boundary) ) -isWellDefined(CellComplex) := (C) -> ( +isWellDefined CellComplex := Boolean => C -> ( allCells := flatten values C.cells; containingModule := null; for cell in allCells do ( - if ring cell.label =!= C.labelRing then return false; + if ring cell.label =!= C.labelRing then ( + if debugLevel > 0 then stderr << "isWellDefined: Cell labels should be over the label ring" << endl; + return false; + ); + --Don't print a further debug here, reply on the output of isWellDefined cell if not isWellDefined cell then return false; M := toModule(C.labelRing,cell.label); if containingModule === null then containingModule = ambient M; - if containingModule != ambient M then return false; + if containingModule != ambient M then ( + if debugLevel > 0 then stderr << "isWellDefined: Cells have labels from different ambient modules" << endl; + return false; + ); ); true ) @@ -762,3 +786,13 @@ load "./CellularResolutions/doc.m2" load "./CellularResolutions/tests.m2" end + +restart +installPackage("CellularResolutions", RerunExamples => true) +loadPackage "CellularResolutions" +R = QQ[x] +texMath newCell({},x^2) +peek CellularResolutions +check CellularResolutions + +viewHelp "CellularResolutions" diff --git a/M2/Macaulay2/packages/CellularResolutions/doc.m2 b/M2/Macaulay2/packages/CellularResolutions/doc.m2 index 2185991e55b..b0d803c0be2 100644 --- a/M2/Macaulay2/packages/CellularResolutions/doc.m2 +++ b/M2/Macaulay2/packages/CellularResolutions/doc.m2 @@ -16,10 +16,68 @@ doc /// functions to work with cell complexes. For some direct ways to construct common cellular resolutions for monomial ideals, see @TO taylorComplex@ and @TO hullComplex@. + Introductions to cellular resolutions can be found in: + Text + @UL { + { + HREF("https://services.math.duke.edu/~ezra/", "Ezra Miller"), + " and ", + HREF("https://math.berkeley.edu/~bernd/", "Bernd Sturmfels"), + ", ", + HREF("https://www.springer.com/gp/book/9780387223568", + "Combinatorial commutative algebra"), + ", Graduate Texts in Mathematics 227, ", + "Springer-Verlag, New York, 2005. ", + "ISBN: 0-387-22356-8" }, + { + HREF("https://en.wikipedia.org/wiki/Dave_Bayer", "Dave Bayer"), + ", ", + HREF("https://pi.math.cornell.edu/~irena/", "Ireena Peeva"), + " and ", + HREF("https://math.berkeley.edu/~bernd/", "Bernd Sturmfels"), + ", ", + HREF("https://dx.doi.org/10.4310/MRL.1998.v5.n1.a3", + "Monomial Resolutions"), + ", Mathematical Research Letters 5, pp. 31-46 (1998)" }, + { + HREF("https://en.wikipedia.org/wiki/Dave_Bayer", "Dave Bayer"), + " and ", + HREF("https://math.berkeley.edu/~bernd/", "Bernd Sturmfels"), + ", ", + HREF("https://doi.org/10.1515/crll.1998.083", + "Cellular resolutions of monomial modules"), + ", Journal für die reine und angewandte Mathematik, vol. 1998, no. 502, 1998, pp. 123-140."} + }@ Text More generally, cell complexes can be constructed by creating cells using @TO newCell@ or @TO newSimplexCell@, and then the maximal - cells can be provided to @TO cellComplex@ to construct a cell complex + cells can be provided to @TO cellComplex@ to construct a cell complex. + Text + The example below follows Example 1.6 in @HREF("https://doi.org/10.1515/crll.1998.083", + "Cellular resolutions of monomial modules")@, + where two simplicial complexes X and Y are constructed + on the same monomial ideal. However, only X supports a resolution. + Example + S = QQ[a,b,c, Degrees=>{{1,0,0},{0,1,0},{0,0,1}}]; + v101 = newCell({},a*c) + v012 = newCell({},b*c^2); v020 = newCell({},b^2); v210 = newCell({},a^2*b); + + e112 = newCell({v101,v012}) + e022 = newCell({v012,v020}); e220 = newCell({v020,v210}); e211 = newCell({v210,v101}); + + e121 = newCell({v101,v020}); + f122 = newCell({e112,e022,e121}) + f221 = newCell({e220,e211,e121}); + X = cellComplex(S,{f122,f221}) + + e212 = newCell({v210,v012}); f212 = newCell({e112,e211,e212}); f222 = newCell({e022,e220,e212}); + Y = cellComplex(S,{f212,f222}) + + prune HH X + prune HH Y + + Y121 = subcomplex(Y,a*b^2*c) + prune HH Y121 /// doc /// @@ -509,6 +567,7 @@ doc /// doc /// Key (boundaryMap,ZZ,CellComplex) + [(boundaryMap,ZZ,CellComplex), Labels] Headline compute the boundary map of a cell complex from r-faces to (r-1)-faces Usage @@ -518,6 +577,8 @@ doc /// the source cell-dimension C : CellComplex the CellComplex + Labels => List + which is ignored by this particular function Outputs : Matrix the boundary map from r-faces to (r-1)-faces of C @@ -717,6 +778,7 @@ doc /// doc /// Key (cohomology,ZZ,CellComplex) + [(cohomology,ZZ,CellComplex),Degree] Headline cohomology of a cell complex Usage @@ -725,6 +787,8 @@ doc /// r : ZZ a non-negative integer C : CellComplex + Degree => ZZ + which is ignored by this particular function Outputs : Module the r-th cohomology module of C @@ -1501,3 +1565,102 @@ doc /// D = taylorComplex monomialIdeal {a,b,c} subcomplex(D,{1,1,0}) /// + +doc /// + Key + (describe,CellComplex) + Headline + Describes a cell complex or cell + Usage + describe C + Inputs + C : {CellComplex, Cell} + Outputs + : Expression + Description + Text + Unlike @TO (net,CellComplex)@ this version ignores the name from @TO GlobalAssignHook@. + Consequently, this method returns a short description of the cell complex regardless of + if it has been assigned to a global variable. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},y); + e1 = newSimplexCell({v1,v2},x); + e2 = newSimplexCell({v1,v2},y); + f = newCell({(e1,1),(e2,-1)},x*y); + describe cellComplex(R,{f}) + X = cellComplex(R,{f}); + describe X + SeeAlso + describe + (net, CellComplex) +/// + +doc /// + Key + (net,CellComplex) + (net,Cell) + Headline + Describes a cell complex or cell + Usage + net C + Inputs + C : {CellComplex, Cell} + Outputs + : Net + Description + Text + For cell complexes, this method uses @TO GlobalAssignHook@ to use the name of the cell complex + if the cell complex is assigned to a global variable. + Otherwise the method returns a short description of the cell complex. + Text + For individual cells, the description contains only the dimension and the label, + the data of the boundary of the cell is not given in the description. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},y); + e1 = newSimplexCell({v1,v2},x); + e2 = newSimplexCell({v1,v2},y); + f = newCell({(e1,1),(e2,-1)},x*y); + net cellComplex(R,{f}) + net f + X = cellComplex(R,{f}); + net X + SeeAlso + (texMath, CellComplex) + (texMath, Cell) +/// + +doc /// + Key + (texMath,CellComplex) + (texMath,Cell) + Headline + Describes a cell complex or cell using TeX + Usage + texMath C + Inputs + C : {CellComplex, Cell} + Outputs + : String + Description + Text + This method functions mostly as @TO (net, CellComplex)@ and @TO (net, Cell)@ does. + The primary difference in the output is that the output is formatted for TeX. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},y); + e1 = newSimplexCell({v1,v2},x); + e2 = newSimplexCell({v1,v2},y); + f = newCell({(e1,1),(e2,-1)},x*y); + texMath cellComplex(R,{f}) + texMath f + X = cellComplex(R,{f}); + texMath X + SeeAlso + (net, CellComplex) + (net, Cell) +/// diff --git a/M2/Macaulay2/packages/ChainComplexOperations.m2 b/M2/Macaulay2/packages/ChainComplexOperations.m2 index 3168db3d9ae..97bdf4c7046 100644 --- a/M2/Macaulay2/packages/ChainComplexOperations.m2 +++ b/M2/Macaulay2/packages/ChainComplexOperations.m2 @@ -6,7 +6,7 @@ Email => "de@msri.org"}}, Headline => "sym2, wedge2, chi2 of a ChainComplex", Keywords => {"Homological Algebra"}, - PackageExports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, DebuggingMode => false ) @@ -45,36 +45,36 @@ reverseFactors(Module, Module, ZZ,ZZ) := (P,Q,s,t) ->( map(Q**P,P**Q,ta) ) -reverseFactors(ChainComplex, ChainComplex) := (F,G) ->( +reverseFactors(Complex, Complex) := (F,G) ->( --define the iso (F**G --> G**F) tar := G**F; sour := F**G; Ln := symbol Ln; phi := for n from min sour to max sour list ( Ln = for i from max(min G,n-max F) to min(max G,n-min F) list ( - (tar_n)_[(i,n-i)]*reverseFactors(F_(n-i),G_i,n-i,i)*(sour_n)^[(n-i,i)]); + (tar_n)_[{i,n-i}]*reverseFactors(F_(n-i),G_i,n-i,i)*(sour_n)^[{n-i,i}]); sum Ln); -map(tar,sour,n->phi_(n-min sour)) + map(tar,sour,n->phi_(n-min sour)) ) sym2 = method() -sym2 ChainComplex := F ->( +sym2 Complex := F ->( tau := reverseFactors(F,F); G := F**F; Gs := image(id_(F**F)+tau); - GGs := chainComplex(for i from min Gs+1 to max Gs list prune Gs.dd_i); + GGs := complex(for i from min Gs+1 to max Gs list prune Gs.dd_i); GGs[-min G]) wedge2 = method() -wedge2 ChainComplex := F ->( +wedge2 Complex := F ->( tau := reverseFactors(F,F); G := F**F; Gs := image(id_(F**F)-tau); - GGs := chainComplex(for i from min Gs+1 to max Gs list prune Gs.dd_i); + GGs := complex(for i from min Gs+1 to max Gs list prune Gs.dd_i); GGs[-min G]) evenHomologyLength = method() -evenHomologyLength ChainComplex := F -> ( +evenHomologyLength Complex := F -> ( len := 0; L := for i from min F to max F list( if even i then len = length(HH_i F) else len = 0; @@ -83,7 +83,7 @@ evenHomologyLength ChainComplex := F -> ( sum L) oddHomologyLength = method() -oddHomologyLength ChainComplex := F -> ( +oddHomologyLength Complex := F -> ( len := 0; L := for i from min F to max F list( if odd i then len = length(HH_i F) else len = 0; @@ -92,7 +92,7 @@ oddHomologyLength ChainComplex := F -> ( sum L) eulerCharacteristic = method() -eulerCharacteristic ChainComplex := F -> ( +eulerCharacteristic Complex := F -> ( len := 0; L := for i from min F to max F list( len = length(HH_i F); @@ -101,10 +101,10 @@ eulerCharacteristic ChainComplex := F -> ( sum L) chi2 = method() -chi2 ChainComplex := F -> eulerCharacteristic sym2 F - eulerCharacteristic wedge2 F +chi2 Complex := F -> eulerCharacteristic sym2 F - eulerCharacteristic wedge2 F excess = method() -excess ChainComplex := F ->( +excess Complex := F ->( excess1a := 2*oddHomologyLength sym2 F; excess1b := 2*evenHomologyLength wedge2 F; G := F**F; @@ -138,7 +138,7 @@ beginDocumentation() of characteristic $\neq 2$. The main new (to Eisenbud) tool in Walker's proof was the function chi2. Explicitly, - if F is a ChainComplex of free S-modules with finite length homology, then + if F is a Complex of free S-modules with finite length homology, then chi2 F is the Euler characteristic of sym2 F minus that of wedge2 F. The function chi2 should be regarded as the Euler characteristic of the 2nd Adams operation, applied to F. It has two properties relevant for the proof: @@ -176,7 +176,7 @@ beginDocumentation() doc /// Key reverseFactors - (reverseFactors, ChainComplex, ChainComplex) + (reverseFactors, Complex, Complex) (reverseFactors, Module, Module, ZZ,ZZ) Headline The isomorphism from F**G to G**F when F,G are complexes @@ -184,14 +184,14 @@ doc /// phi = reverseFactors(F,G) phi = reverseFactors(M,N,p,q) Inputs - F:ChainComplex - G:ChainComplex + F:Complex + G:Complex M:Module N:Module p:ZZ q:ZZ Outputs - phi:ChainComplexMap + phi:ComplexMap to G**F from F**G Description Text @@ -199,8 +199,8 @@ doc /// In reverseFactors(M,N,p,q) the integers p and q specify the homological degrees of M and N respectively. Example S = ZZ/101[a,b] - F = chainComplex{map(S^1,S^{-1},a)} - G = chainComplex{map(S^1,S^{-1},b)}[3] + F = complex{map(S^1,S^{-1},a)} + G = complex{map(S^1,S^{-1},b)}[3] phi = reverseFactors(F,G) G**F F**G @@ -215,13 +215,13 @@ doc /// doc /// Key oddHomologyLength - (oddHomologyLength, ChainComplex) + (oddHomologyLength, Complex) Headline sum of the lengths of the odd degree homology groups Usage m = oddHomologyLength F Inputs - F:ChainComplex + F:Complex Outputs m:ZZ Caveat @@ -230,13 +230,13 @@ doc /// doc /// Key evenHomologyLength - (evenHomologyLength, ChainComplex) + (evenHomologyLength, Complex) Headline sum of the lengths of the even degree homology groups Usage m = evenHomologyLength F Inputs - F:ChainComplex + F:Complex Outputs m:ZZ Caveat @@ -245,13 +245,13 @@ doc /// doc /// Key eulerCharacteristic - (eulerCharacteristic, ChainComplex) + (eulerCharacteristic, Complex) Headline sum of the lengths of the even degree homology minus the odd degree homology groups Usage m = eulerCharacteristic F Inputs - F:ChainComplex + F:Complex Outputs m:ZZ Caveat @@ -260,7 +260,7 @@ doc /// doc /// Key excess - (excess, ChainComplex) + (excess, Complex) (excess, Module) Headline Difference between the sum of the lengths of Tor_i(M,M) and the Walker bound 2^d*length(M) @@ -268,7 +268,7 @@ doc /// exs = excess F exs = excess M Inputs - F:ChainComplex + F:Complex with finite length homology M:Module of finite length @@ -308,15 +308,15 @@ doc /// doc /// Key sym2 - (sym2, ChainComplex) + (sym2, Complex) Headline symmetric square of a chain complex Usage G = sym2 F Inputs - F:ChainComplex + F:Complex Outputs - G:ChainComplex + G:Complex Description Text If tau: F**F \to F**F is the chain map reversing the factors, with appropriate signs, then @@ -326,15 +326,15 @@ doc /// doc /// Key wedge2 - (wedge2, ChainComplex) + (wedge2, Complex) Headline exterior square of a chain complex Usage G = wedge2 F Inputs - F:ChainComplex + F:Complex Outputs - G:ChainComplex + G:Complex Description Text If tau: F**F \to F**F is the chain map reversing the factors, with appropriate signs, then @@ -343,13 +343,13 @@ doc /// doc /// Key chi2 - (chi2, ChainComplex) + (chi2, Complex) Headline Euler characteristic of the 2nd Adams operation applied to a complex Usage m = chi2 F Inputs - F:ChainComplex + F:Complex Outputs m:ZZ Description @@ -408,7 +408,7 @@ assert (reverseFactors(P,Q,s,t)*reverseFactors(Q,P,s,t) == id_(Q**P)) TEST/// S = ZZ/101[a,b,c] -F = chainComplex{map(S^1,S^1,0)} +F = complex{map(S^1,S^1,0)} assert (try eulerCharacteristic F then "finite" else "undefined" == "undefined") /// diff --git a/M2/Macaulay2/packages/CodingTheory.m2 b/M2/Macaulay2/packages/CodingTheory.m2 index a7712df79ef..5e6e8573e47 100644 --- a/M2/Macaulay2/packages/CodingTheory.m2 +++ b/M2/Macaulay2/packages/CodingTheory.m2 @@ -1790,7 +1790,7 @@ C := linearCode G; for i from 1 to 1 do( message := transpose matrix {(for n from 1 to numgens target G list(random(R)))}; codeword := (transpose G)*message; - errors := sum take(random entries basis target codeword, 3); + errors := sum randomSubset(entries basis target codeword, 3); errors = transpose matrix({errors}); received := codeword+errors; decoded := syndromeDecode(C, received, 8); @@ -3110,7 +3110,7 @@ doc /// C = hammingCode(2,3); msg = matrix {{1,0,1,0}}; v = msg*(C.GeneratorMatrix); - err = matrix take(random entries basis source v, 1); + err = matrix shuffle(entries basis source v, 1); received = (transpose (v+err)); syndromeDecode(C, received, 3); C.cache#"syndromeLUT" diff --git a/M2/Macaulay2/packages/CoincidentRootLoci.m2 b/M2/Macaulay2/packages/CoincidentRootLoci.m2 index 1592fd4e14e..564c9c8c0fe 100644 --- a/M2/Macaulay2/packages/CoincidentRootLoci.m2 +++ b/M2/Macaulay2/packages/CoincidentRootLoci.m2 @@ -958,7 +958,7 @@ JoinOfCoincidentRootLoci * JoinOfCoincidentRootLoci := (X,Y) -> joinOfHooks (toS --=== Binary forms and utilities ================================= --================================================================ -RingElement ~ := (F) -> RingElement := (G) -> ( +RingElement ~ RingElement := (F, G) -> ( if not (isPolynomialRing ring F and isPolynomialRing ring G and numgens ring F == 2 and numgens ring G == 2) then error "expected two binary forms"; K := coefficientRing ring F; if K =!= coefficientRing ring G then error "got different coefficient rings"; diff --git a/M2/Macaulay2/packages/CompleteIntersectionResolutions.m2 b/M2/Macaulay2/packages/CompleteIntersectionResolutions.m2 index 8b7144ec785..3e840c50362 100644 --- a/M2/Macaulay2/packages/CompleteIntersectionResolutions.m2 +++ b/M2/Macaulay2/packages/CompleteIntersectionResolutions.m2 @@ -1899,7 +1899,7 @@ extVsCohomology(Matrix, Module) := (ff,N) ->( --N is an R=S/(ff)-module --M is a high syzy of N --compares the coho tables of the even and odd parts of Ext(M,k) - --with the tate resolution of Ext_S(M,k) as a module + --with the Tate resolution of Ext_S(M,k) as a module --over the exterior alg. S:= ring ff; p := map(ring N,S); diff --git a/M2/Macaulay2/packages/Complexes.m2 b/M2/Macaulay2/packages/Complexes.m2 index 1854e97fe8a..6d244b99f81 100644 --- a/M2/Macaulay2/packages/Complexes.m2 +++ b/M2/Macaulay2/packages/Complexes.m2 @@ -1,7 +1,7 @@ newPackage( "Complexes", - Version => "0.999995", - Date => "1 May 2023", + Version => "1.0", + Date => "May 9, 2026", Authors => { { Name => "Gregory G. Smith", Email => "ggsmith@mast.queensu.ca", @@ -36,6 +36,7 @@ export { "connectingTorMap", "constantStrand", "cylinder", + "eagonNorthcottComplex", "epicResolutionMap", "freeResolution", "homotopyMap", @@ -71,16 +72,38 @@ export { "OverZZ", "Homogenization", "Nonminimal", + "NonminimalWithGB", "Concentration", "Cycle", "Boundary", "InternalDegree", - "UseTarget" + "UseTarget", + + -- From pruneComplex code + "toMutableComplex", + "toChainComplex", + "pruneComplex", + "pruneUnit", + "pruneDiff", + "isScalar", + "Direction", "PruningMap", "UnitTest" } importFrom_Core { "isPackageLoaded", -} + "liftModule", "liftMorphism", + + -- Used in pruneComplex code + "LocalRing", + "raw", + "rawDeleteColumns", + "rawDeleteRows", + "rawMutableComplex", + "rawPruneBetti", + "rawPruneComplex", + "rawPruningMorphism", + } + -- keys into the type `Complex` protect modules @@ -122,10 +145,11 @@ homTensorAdjoint(Module, Module, Module) := (L, M, N) -> ( -- package code ---------------------------------------------------- -------------------------------------------------------------------- load "./Complexes/ChainComplex.m2" -load "./Complexes/FreeResolutions.m2" +load "./Complexes/FreeResolution.m2" load "./Complexes/ChainComplexMap.m2" load "./Complexes/Tor.m2" load "./Complexes/Ext.m2" +load "./Complexes/PruneComplex.m2" -------------------------------------------------------------------- -- interface code to legacy types ---------------------------------- @@ -133,6 +157,35 @@ load "./Complexes/Ext.m2" if isPackageLoaded "OldChainComplexes" then load "./OldChainComplexes/conversion.m2" +koszul Matrix := Complex => m -> koszulComplex m +eagonNorthcott Matrix := Complex => m -> eagonNorthcottComplex m + +-- TODO: talk to Greg about what to do with this code... + ----------------------------------------------------------------------------- + -- constructing a chain complex with prescribed Betti table + ----------------------------------------------------------------------------- + + Ring ^ BettiTally := Complex => (R,B) -> ( + -- donated by Hans-Christian von Bothmer + -- given a betti Table B and a Ring R make a chainComplex + -- with zero maps over R that has betti diagram B. + -- negative entries are ignored + -- rational entries produce an error + -- multigraded R's work only if the betti Tally contains degrees of the correct degree length + p := sort pairs B; -- list of (homological degree, multidegree, weight) + toplev := p/((k,n) -> first k)//max; -- largest homological degree + F := new MutableHashTable; + H := partition(x -> x#0#0, p); -- by homological degree. + directSum for i in keys H list ( + degs := flatten for x in H#i list ( + (i, deg, wt) := x#0; + n := x#1; + toList(n : -deg) + ); + complex(R^degs, Base => i) + ) + ) + -------------------------------------------------------------------- -- package documentation ------------------------------------------- -------------------------------------------------------------------- @@ -150,6 +203,7 @@ undocumented{ load "./Complexes/ChainComplexDoc.m2" load "./Complexes/ChainComplexMapDoc.m2" +load "./Complexes/PruneComplexDoc.m2" -------------------------------------------------------------------- -- documentation for legacy type conversion ------------------------ @@ -162,7 +216,7 @@ if isPackageLoaded "OldChainComplexes" then -------------------------------------------------------------------- load "./Complexes/ChainComplexTests.m2" load "./Complexes/FreeResolutionTests.m2" - +load "./Complexes/PruneComplexTests.m2" end------------------------------------------------------------ restart diff --git a/M2/Macaulay2/packages/Complexes/ChainComplex.m2 b/M2/Macaulay2/packages/Complexes/ChainComplex.m2 index 82a5b4595ef..68adb5e8aa8 100644 --- a/M2/Macaulay2/packages/Complexes/ChainComplex.m2 +++ b/M2/Macaulay2/packages/Complexes/ChainComplex.m2 @@ -60,6 +60,28 @@ concentration ComplexMap := Sequence => f -> ( max Complex := ZZ => C -> max concentration C min Complex := ZZ => C -> min concentration C +shiftDegrees = method() +shiftDegrees List := L -> ( + if not all(L, x -> instance(x, Matrix)) or not all(L, isHomogeneous) then L + else ( + prevSource := null; + for i from 0 to #L-1 list ( + if i == 0 then ( + prevSource = source L#0; + L#0) + else ( + deg := degrees prevSource - degrees target L#i; + if not same deg then error "complex: expected composable differential maps"; + f := L#i; + if #deg > 0 and not all(deg#0, a -> a === 0) then ( + f = map(prevSource, (source f) ** (ring f)^(-deg#0),f); + ); + prevSource = source f; + f) + ) + ) + ) + complexOptions = {Base => 0} complex = method(Options => true) complex HashTable := Complex => complexOptions >> opts -> maps -> ( @@ -94,6 +116,7 @@ complex List := Complex => complexOptions >> opts -> L -> ( if not instance(opts.Base, ZZ) then error "expected Base to be an integer"; if uniform L and isMorphism L#0 then ( + L = shiftDegrees L; -- doesn't change L unless all are matrices, with unequal source/target pairs, yet after a shift they are equal. mapHash := hashTable for i from 0 to #L-1 list opts.Base+i+1 => L#i; return complex(mapHash, opts) ); @@ -521,6 +544,8 @@ freeResolution = method(Options => { SortStrategy => 0, -- strategy choice for sorting S-pairs Strategy => null, -- ParallelizeByDegree => false -- currently: only used by Strategy => Nonminimal, gives warning if true and another Strategy selected + -- legacy option, which is deprecated in favor of Strategy => Nonminimsl + -- FastNonminimal => null } ) @@ -594,8 +619,13 @@ heftfun = (wt1,wt2) -> ( else d -> 0 ) +importFrom_Core { "rawBetti", "RawComputation" }; betti Matrix := opts -> f -> betti(complex f, opts) betti Complex := opts -> C -> ( + if opts.Minimize then ( + if not C.cache.?Module then error "expected a nonminimal resolution of a module"; + return minimalBetti C.cache.Module; + ); heftfn := heftfun(opts.Weights, heft ring C); (lo,hi) := C.concentration; new BettiTally from flatten for i from lo to hi list ( @@ -603,15 +633,18 @@ betti Complex := opts -> C -> ( ) ) -pdim Module := M -> length freeResolution minimalPresentation M +-- TODO: should this report infinity in some cases? +-- c.f. https://github.com/Macaulay2/M2/issues/3656 +pdim Module := M -> length freeResolution liftModule minimalPresentation M regularity Ideal := opts -> I -> ( if I == 0 then -infinity else if I == 1 then 0 - else 1 + regularity betti(freeResolution comodule I, opts)) + else 1 + regularity betti(freeResolution liftModule comodule I, opts)) +-- cf. https://github.com/Macaulay2/M2/issues/3321 regularity Module := opts -> M -> ( if not isHomogeneous M then error "regularity: expected homogeneous module"; - regularity betti(freeResolution minimalPresentation M, opts)) + regularity betti(freeResolution liftModule minimalPresentation M, opts)) regularity Complex := opts -> C -> ( if numgens degreesRing ring C =!= 1 then @@ -653,13 +686,16 @@ poincareN Complex := C -> ( f ) -rank Complex := ZZ => C -> ( - (lo, hi) := concentration C; - sum for i from lo to hi list (-1)^i * rank C_i - ) +rank Complex := C -> + sum(pairs C.module, (i, M) -> (-1)^i * rank M) + +hilbertPolynomial Complex := o -> C -> + sum(pairs C.module, (i, M) -> (-1)^i * hilbertPolynomial(M, o)) + +euler Complex := C -> euler hilbertPolynomial C minimalPresentation Complex := -prune Complex := Complex => opts -> (cacheValue symbol minimalPresentation)(C -> ( +prune Complex := Complex => opts -> C -> C.cache.minimalPresentation ??= ( -- opts is ignored here -- to be cached: in the input C: cache the result D -- in the result: cache pruningMap: D --> C @@ -685,7 +721,7 @@ prune Complex := Complex => opts -> (cacheValue symbol minimalPresentation)(C -> D.cache.pruningMap = map(C,D,pruning); D.cache.pruningMap.cache.isCommutative = true; D - )) + ) -------------------------------------------------------------------- -- truncations ----------------------------------------------------- @@ -751,7 +787,7 @@ canonicalTruncation(Complex,Nothing,ZZ) := Complex => (C,lo,hi) -> canonicalTrun importFrom_Truncations { "inducedTruncationMap" } truncateModuleOpts := options(truncate, List, Module) -truncate(ZZ, Complex) := +truncate(ZZ, Complex) := truncate(InfiniteNumber, Complex) := truncate(List, Complex) := Complex => truncateModuleOpts >> opts -> (degs, C) -> ( (lo, hi) := C.concentration; if lo == hi @@ -1113,13 +1149,10 @@ epicResolutionMap Complex := ComplexMap => opts -> C -> resolutionMapPrivate(C, freeResolution Complex := opts -> C -> source resolutionMap(C, opts) augmentationMap = method() -augmentationMap Complex := ComplexMap => - (cacheValue symbol augmentationMap)(C -> ( - if not C.cache.?Module then error "expected a free resolution"; - M := C.cache.Module; - map(complex M, C, i -> if i === 0 then map(M, C_0, 1)) - ) - ) +augmentationMap Complex := ComplexMap => C -> C.cache.augmentationMap ??= ( + if not C.cache.?Module then error "expected a free resolution"; + M := C.cache.Module; + map(complex M, C, i -> if i === 0 then map(M, C_0, 1))) -- TODO: get this to work over fields, poly rings, quotients, and also the local case. -- improve the performance of this function @@ -1200,6 +1233,7 @@ Ext(ZZ, Module, Module) := Module => opts -> (i,M,N) -> ( ) yonedaExtension = method() +yonedaExtension Vector := Complex => f -> yonedaExtension matrix f yonedaExtension Matrix := Complex => f -> ( -- f: R^1 --> Ext^d(M,N) = E -- construct the chain complex: @@ -1341,3 +1375,36 @@ koszulComplex List := Complex => {Concentration => null} >> opts -> L -> ( if #L === 0 then error "expected a non-empty list"; koszulComplex(matrix{L}, opts) ) + +eagonNorthcottComplex = method(Options => true) +eagonNorthcottComplex Matrix := Complex => {} >> opts -> f -> ( + -- code is by GREG SMITH, but is experimental, and + -- should be replaced by engine code + -- Modified by ELIANA DUARTE to fix the grading for matrices + -- with entries of arbitrary degrees. + if not isHomogeneous f then error "Matrix not homogeneous."; + R := ring f; + m := rank source f; + n := rank target f; + B := hashTable apply(toList(1..m-n+2), + i -> {i, flatten table(subsets(m,n+i-1), compositions(n,i-1), + (p,q) -> {p,q})}); + d1 := map(R^1,, {apply(B#1, r -> determinant f_(r#0))}); + nextDegrees := toSequence(-flatten degrees source d1); + d := {d1}; + j:=2; + while j if not isSubset(p#0,q#0) then 0_R + else ( + vec := q#1 - p#1; + if any(vec, e -> e < 0 or e > 1) then 0_R + else ( + s := first select(toList(0..#q#0-1), + l -> not isMember(q#0#l, p#0)); + t := first select(toList(0..n-1), l -> vec#l == 1); + (-1)^(s+1)*f_(t,q#0#s)))))}; + j += 1 + ); + complex d + ) diff --git a/M2/Macaulay2/packages/Complexes/ChainComplexDoc.m2 b/M2/Macaulay2/packages/Complexes/ChainComplexDoc.m2 index 957b7a63bf5..c1a1759deac 100644 --- a/M2/Macaulay2/packages/Complexes/ChainComplexDoc.m2 +++ b/M2/Macaulay2/packages/Complexes/ChainComplexDoc.m2 @@ -72,6 +72,7 @@ doc /// TO (symbol SPACE, RingMap, Complex), TO (symbol **, RingMap, Complex), TO (koszulComplex, Matrix), + TO (eagonNorthcottComplex, Matrix), TO (naiveTruncation, Complex, ZZ, ZZ), TO (canonicalTruncation, Complex, ZZ, ZZ), TO (minimalPresentation, Complex), @@ -3479,7 +3480,6 @@ doc /// /// -- comment about minimize and pruneComplex: -- this code can be run for the example ini (minimize,Complex). - needsPackage "PruneComplex" C' = chainComplex C D' = pruneComplex(C', UnitTest => isScalar) g' = D'.cache.pruningMap diff --git a/M2/Macaulay2/packages/Complexes/ChainComplexMap.m2 b/M2/Macaulay2/packages/Complexes/ChainComplexMap.m2 index 08625c4a0dc..2a2c2c9a1e3 100644 --- a/M2/Macaulay2/packages/Complexes/ChainComplexMap.m2 +++ b/M2/Macaulay2/packages/Complexes/ChainComplexMap.m2 @@ -178,6 +178,13 @@ isWellDefined ComplexMap := f -> ( ); for i from lo to hi do ( g := f_i; + if not isWellDefined g then ( + if debugLevel > 0 then ( + << "-- expected all differentials to be well-defined morphisms" << endl; + << "-- differential at index " << i << " fails this condition" << endl; + ); + return false; + ); if source g =!= f.source_i or target g =!= f.target_(i+f.degree) then ( if debugLevel > 0 then ( @@ -520,7 +527,7 @@ canonicalTruncation(ComplexMap,ZZ,Nothing) := canonicalTruncation(ComplexMap,Nothing,ZZ) := ComplexMap => (f,lo,hi) -> canonicalTruncation(f, (lo,hi)) truncateMatrixOpts := options(truncate, List, Matrix) -truncate(ZZ, ComplexMap) := +truncate(ZZ, ComplexMap) := truncate(InfiniteNumber, ComplexMap) := truncate(List, ComplexMap) := ComplexMap => truncateMatrixOpts >> opts -> (degs, f) -> ( d := degree f; C := truncate(degs, source f, opts); @@ -857,7 +864,7 @@ extend(Complex,Complex,Matrix) := ComplexMap => opts -> (D, C, f) -> extend(D, C -- sign convention: Using Conrad (Grothendieck Duality) sign choice for cone, pg 8 of intro. -- NOTE: one could extend this to complex maps which commute, but have nonzero degree, -- IF this would be useful at all. -cone ComplexMap := Complex => f -> ( +cone ComplexMap := Complex => f -> f.cache.cone ??= ( if not isComplexMorphism f then error "expected a complex morphism"; B := source f; diff --git a/M2/Macaulay2/packages/Complexes/ChainComplexMapDoc.m2 b/M2/Macaulay2/packages/Complexes/ChainComplexMapDoc.m2 index 25d90fa9b7c..b12cb6747d7 100644 --- a/M2/Macaulay2/packages/Complexes/ChainComplexMapDoc.m2 +++ b/M2/Macaulay2/packages/Complexes/ChainComplexMapDoc.m2 @@ -1576,6 +1576,7 @@ doc /// canonicalTruncation(f, (lo, hi)) Inputs f:ComplexMap + which commutes with the differentials lo:ZZ or {\tt -infinity} or {\tt null} (the latter two give no lower bound) hi:ZZ @@ -1584,7 +1585,8 @@ doc /// :ComplexMap Description Text - Returns a new complex map which drops (sets to zero) all modules + Returns a new complex map (commuting with the differentials) + which drops (sets to zero) all modules outside the given range in the source, and modifies the ends to preserve homology in the given range. The degree of the map {\tt f} is used to determine the truncation of the target. @@ -1594,9 +1596,9 @@ doc /// R = ZZ/101[a..d]; C = (freeResolution coker matrix{{a,b,c}})[1] D = freeResolution coker matrix{{a*b,a*c,b*c}} - E = freeResolution coker matrix{{a^2,b^2,c*d}} - f = randomComplexMap(D, C) - g = randomComplexMap(E, D) + E = (freeResolution coker matrix{{a^2*b,a*b*c,b*c*d}})[-1] + f = randomComplexMap(D, C, Cycle => true) + g = randomComplexMap(E, D, Cycle => true) h = g * f Text We use these maps to illustrate canonical truncation. diff --git a/M2/Macaulay2/packages/Complexes/ChainComplexTests.m2 b/M2/Macaulay2/packages/Complexes/ChainComplexTests.m2 index 484acfc3389..4b886daae80 100644 --- a/M2/Macaulay2/packages/Complexes/ChainComplexTests.m2 +++ b/M2/Macaulay2/packages/Complexes/ChainComplexTests.m2 @@ -947,12 +947,13 @@ TEST /// assert(g == 0) /// -TEST /// - -- canonical truncation + -* restart needsPackage "Complexes" *- +TEST /// + -- canonical truncation R = ZZ/101[a,b,c,d,e] I = intersect(ideal(a,b),ideal(c,d,e)) C = freeResolution I @@ -986,13 +987,12 @@ TEST /// /// - -TEST /// - -- canonical truncation, more interesting example(s) -* restart needsPackage "Complexes" *- +TEST /// + -- canonical truncation, more interesting example(s) R = ZZ/101[a,b,c,d,e] I = intersect(ideal(a,b),ideal(c,d,e)) J = ideal(a^2, b^2, a*d, b*c^2) @@ -1015,43 +1015,58 @@ TEST /// ZHCE0 = ker dd^HCE_0; f0 = map(ZHCD0, R^1, random(R^(numgens ZHCD0), R^1)) f = homomorphism(0, f0, HCD) - isWellDefined f - isNullHomotopic f + assert isWellDefined f + assert not isNullHomotopic f g0 = map(ZHDE0, R^1, random(R^(numgens ZHDE0), R^1)) g = homomorphism(0, g0, HDE) - isWellDefined g - isNullHomotopic g + assert isWellDefined g + assert not isNullHomotopic g h = g * f - isWellDefined h - isNullHomotopic h + assert isWellDefined h + assert isNullHomotopic h f' = canonicalTruncation(f, (-3,-1)); g' = canonicalTruncation(g, (-3,-1)); h' = canonicalTruncation(h, (-3,-1)); + assert isWellDefined f' + assert isWellDefined g' + assert isWellDefined h' assert(g' * f' == h') f' = prune canonicalTruncation(f, (-3,-3)); g' = prune canonicalTruncation(g, (-3,-3)); h' = prune canonicalTruncation(h, (-3,-3)); + assert isWellDefined f' + assert isWellDefined g' + assert isWellDefined h' assert(g' * f' == h') + f' = canonicalTruncation(f, (-3,-3)); g' = canonicalTruncation(g, (-3,-3)); h' = canonicalTruncation(h, (-3,-3)); + assert isWellDefined f' + assert isWellDefined g' + assert isWellDefined h' assert(g' * f' == h') f' = canonicalTruncation(f, (-4,-4)); g' = canonicalTruncation(g, (-4,-4)); h' = canonicalTruncation(h, (-4,-4)); + assert isWellDefined f' + assert isWellDefined g' + assert isWellDefined h' assert(g' * f' == h') f' = canonicalTruncation(f, (-4,-2)); g' = canonicalTruncation(g, (-4,-2)); h' = canonicalTruncation(h, (-4,-2)); + assert isWellDefined f' + assert isWellDefined g' + assert isWellDefined h' assert(g' * f' == h') g = canonicalTruncation(f, (-3,-2)) - isWellDefined g - isComplexMorphism g - + assert isWellDefined g + assert isComplexMorphism g /// TEST /// @@ -1158,7 +1173,6 @@ TEST /// C = freeResolution(I, Strategy => Nonminimal) ---- elapsedTime minimize C -- very slow, TODO: fix this -* - needsPackage "PruneComplex" needsPackage "ChainComplexExtras" C' = chainComplex freeResolution(I, Strategy => Nonminimal) elapsedTime pruneComplex(C', UnitTest=>isScalar) -- 17.7 sec on my MBP diff --git a/M2/Macaulay2/packages/Complexes/FreeResolutions.m2 b/M2/Macaulay2/packages/Complexes/FreeResolution.m2 similarity index 91% rename from M2/Macaulay2/packages/Complexes/FreeResolutions.m2 rename to M2/Macaulay2/packages/Complexes/FreeResolution.m2 index a5834a84b98..5ff28bde11a 100644 --- a/M2/Macaulay2/packages/Complexes/FreeResolutions.m2 +++ b/M2/Macaulay2/packages/Complexes/FreeResolution.m2 @@ -43,7 +43,7 @@ importFrom_Core { "Computation" } -importFrom_Core "Resolution" +--importFrom_Core "Resolution" ResolutionObject = new Type of MutableHashTable ResolutionObject.synonym = "resolution object" @@ -56,7 +56,7 @@ freeResolution Module := Complex => opts -> M -> ( -- This handles caching, hooks for different methods of computing -- resolutions or over different rings which require different algorithms. -- - -- Nonminimal: true if the computation is constructed using the Nonminimal strategy. + -- Nonminimal: true if the computation is constructed using the Nonminimal or NonminimalWithGB strategies -- LengthLimit prescribes the length of the computed complex. -- DegreeLimit is a lower limit on what will be computed degree-wise, but more might be computed. R := ring M; @@ -72,7 +72,7 @@ freeResolution Module := Complex => opts -> M -> ( C = M.cache.Resolution; if not C.cache.?LengthLimit or not C.cache.?DegreeLimit then error "internal error: Resolution should have both a LengthLimit and DegreeLimit"; - if C.cache.Nonminimal === (opts.Strategy === Nonminimal) and + if C.cache.Nonminimal === (opts.Strategy === Nonminimal or opts.Strategy === NonminimalWithGB) and C.cache.LengthLimit >= opts.LengthLimit and C.cache.DegreeLimit >= opts.DegreeLimit then ( C' := naiveTruncation(C, -infinity, opts.LengthLimit); @@ -85,14 +85,14 @@ freeResolution Module := Complex => opts -> M -> ( ); if M.cache.?ResolutionObject then ( RO := M.cache.ResolutionObject; - if (opts.Strategy === null and RO.Strategy =!= 4) or --4 is the magic number for Nonminimal. In this case, we need to recompute the resolution. + if (opts.Strategy === null and (RO.Strategy =!= 4 and RO.Strategy =!= 5)) or --4 is the magic number for Nonminimal. 5 for NonminimalGB. In these cases, we need to recompute the resolution. opts.Strategy === RO.Strategy then ( if RO.isComputable(opts.LengthLimit, opts.DegreeLimit) -- this is informational: does not change RO. then ( RO.compute(opts.LengthLimit, opts.DegreeLimit); -- it is possible to interrupt this and then the following lines do not happen. C = RO.complex(opts.LengthLimit); - C.cache.Nonminimal = (RO.Strategy === 4); -- magic number: this means Nonminimal to the engine... + C.cache.Nonminimal = (RO.Strategy === 4 or RO.Strategy === 5); -- magic number: this means Nonminimal, or NonminimalWithGB to the engine... C.cache.LengthLimit = if max C < opts.LengthLimit then infinity else opts.LengthLimit; C.cache.DegreeLimit = opts.DegreeLimit; C.cache.Module = M; @@ -117,7 +117,7 @@ freeResolution Module := Complex => opts -> M -> ( if C =!= null then ( assert(instance(C, Complex)); - C.cache.Nonminimal = (RO.Strategy === 4); -- magic number: this means Nonminimal to the engine... + C.cache.Nonminimal = (RO.Strategy === 4 or RO.Strategy === 5); -- magic number: this means Nonminimal, NonminimalWithGB to the engine... C.cache.LengthLimit = if max C < opts.LengthLimit then infinity else opts.LengthLimit; C.cache.DegreeLimit = opts.DegreeLimit; C.cache.Module = M; @@ -350,6 +350,35 @@ resolutionInEngine4 = (opts, M) -> ( resolutionObjectInEngine(opts, M, gbM) ) +resolutionInEngine5 = (opts, M) -> ( + -- opts are the options from resolution. Includes Strategy, LengthLimit, DegreeLimit. + -- M is a Module. + + -- first determine if this method applies. + -- Return null if not, as quickly as possible + R := ring M; + if not ( + R.?Engine and + heft R =!= null and + (isSkewCommutative R or isCommutative R) and ( + A := ultimate(coefficientRing, R); + A =!= R and isField A + )) + then return null; + + if gbTrace > 0 then + << "[Doing freeResolution Strategy => NonminimalGB]" << endl; + RO := M.cache.ResolutionObject; -- this exists already + if RO.Strategy === null then RO.Strategy = 5 + else if RO.Strategy === NonminimalWithGB then RO.Strategy = 5 + else error "our internal logic is flawed"; + + gbM := presentation M; + -- TODO: check that gbM is monic and otherwise give an error + resolutionObjectInEngine(opts, M, gbM) + ) + + resolutionInEngine = (opts, M) -> ( R := ring M; if isQuotientRing R or isSkewCommutative R @@ -476,6 +505,7 @@ protect HomogenizedModule protect DehomogenizationMap protect HomogenizedModuleResolution protect Nonminimal +protect NonminimalWithGB resolutionByHomogenization = (opts, M) -> ( R := ring M; @@ -528,6 +558,7 @@ resolutionByHomogenization = (opts, M) -> ( RO.complex(opts.LengthLimit) ) +addHook((freeResolution, Module), resolutionInEngine5, Strategy => NonminimalWithGB) addHook((freeResolution, Module), resolutionInEngine4, Strategy => Nonminimal) addHook((freeResolution, Module), resolutionBySyzygies, Strategy => Syzygies) addHook((freeResolution, Module), resolutionByHomogenization, Strategy => Homogenization) @@ -649,7 +680,7 @@ minimalBetti Module := BettiTally => opts -> M -> ( if not useFastNonminimal then return betti resolution(M, DegreeLimit => degreelimit, LengthLimit => lengthlimit); -- At this point, we think we are good to use the faster algorithm. - -- First, we need to comppute the non-minimal resolution to one further step. + -- First, we need to compute the non-minimal resolution to one further step. if instance(opts.LengthLimit, ZZ) then lengthlimit = lengthlimit + 1; C = resolution(M, StopBeforeComputation => true, FastNonminimal => true, ParallelizeByDegree => opts.ParallelizeByDegree, @@ -686,29 +717,40 @@ minimalBetti Module := BettiTally => opts -> M -> ( then ( return truncate(betti(C, Weights => opts.Weights), degreelimit, lengthlimit); ); + if C.cache.Nonminimal then ( + -- as of May 2026, nonminimal resolutions computed earlier do not allow for correct minimal betti diagram. + remove(M.cache, symbol ResolutionObject); + remove(M.cache, symbol Resolution); + ); ); A := ultimate(coefficientRing, R); - if not ( - R.?Engine and - heft R =!= null and - (isSkewCommutative R or isCommutative R) and ( - A =!= R and isField A - )) - then betti freeResolution(M, DegreeLimit => degreelimit, LengthLimit => lengthlimit); + if ( + not R.?Engine or + not (isCommutative R or isSkewCommutative R) or + heft R === null or + not isField A or + A =!= ZZ/(char A) or + A === R + ) + then return betti freeResolution(M, DegreeLimit => degreelimit, LengthLimit => lengthlimit); if lengthlimit === infinity then ( -- reset lengthlimit nvars := # generators(R, CoefficientRing => A); - lengthlimit = nvars + if A === ZZ then 1 else 0; + lengthlimit = nvars + (if A === ZZ then 1 else 0); ); + C = freeResolution(M, DegreeLimit => degreelimit, LengthLimit => lengthlimit + 1, Strategy => Nonminimal, StopBeforeComputation => true); rC := M.cache.ResolutionObject.RawComputation; B := unpackEngineBetti rawMinimalBetti(rC, if opts.DegreeLimit === infinity then {} else if opts.DegreeLimit =!= null then {opts.DegreeLimit} else {}, - if opts.LengthLimit =!= infinity then {opts.LengthLimit} else {}); - betti(B, Weights => heftvec(opts.Weights, heft R)) + if opts.LengthLimit =!= infinity then {lengthlimit} else {}); + remove(M.cache, symbol ResolutionObject); + remove(M.cache, symbol Resolution); + ans := betti(B, Weights => heftvec(opts.Weights, heft R)); + ans ) minimalBetti Ideal := BettiTally => opts -> I -> minimalBetti(comodule I, opts) diff --git a/M2/Macaulay2/packages/Complexes/FreeResolutionTests.m2 b/M2/Macaulay2/packages/Complexes/FreeResolutionTests.m2 index 760e4d2c63b..8a0f8db5c51 100644 --- a/M2/Macaulay2/packages/Complexes/FreeResolutionTests.m2 +++ b/M2/Macaulay2/packages/Complexes/FreeResolutionTests.m2 @@ -58,6 +58,14 @@ TEST /// assert isWellDefined Fn assert(prune HH Fn == complex M) -- only guaranteed to be isomorphic? assert(B1 =!= betti Fn) -- Fn is non minimal in this example + + -- this is meant to be the GB of the above I. + I = ideal matrix {{a*b-c*d, b*c*d-c*d^2, a^3-c^3, a*c*d^2-c^2*d^2, b*c^3-a^2*c*d}} + Fn = freeResolution(I, Strategy => NonminimalWithGB) + assert isWellDefined Fn + assert(prune HH Fn == complex M) -- only guaranteed to be isomorphic? + assert(B1 =!= betti Fn) -- Fn is non minimal in this example + /// TEST /// @@ -142,6 +150,13 @@ TEST /// C = freeResolution(I, LengthLimit => 3, Strategy => Nonminimal) assert isWellDefined C assert(length C == 3) + + I = ideal gens gb ideal I_* + C = freeResolution(I, LengthLimit => 4, Strategy => NonminimalWithGB) + assert isWellDefined C + assert(length C == 4) + + C = freeResolution(I, LengthLimit => 4, Strategy => Nonminimal) /// TEST /// @@ -791,6 +806,7 @@ TEST /// TEST /// -- of length limits and free resolutions + debug needsPackage "Complexes" R = ZZ/32003[a..d]/(a^2-b*c) M = coker vars R; C1 = freeResolution(M, LengthLimit => 4); @@ -832,6 +848,7 @@ TEST /// /// TEST /// + debug needsPackage "Complexes" R = ZZ/101[a..d] M = R^0 C = freeResolution M @@ -849,13 +866,11 @@ TEST /// assert(C2 == 0) /// - -TEST /// --- XXX -* restart needsPackage "Complexes" *- +TEST /// needsPackage "InverseSystems" R = ZZ/101[a..e] F = a^4 + b^4 + c^4 +d^4 + e^4 + (a+b+c+d+e)^4 @@ -865,14 +880,15 @@ needsPackage "Complexes" assert(bt1 == bt2) /// +-* + restart + needsPackage("Complexes") +*- /// -- code to test control-c during a computation. -- We don't know how to make this into a proper test. -- TODO (possibly): allow snapshot of a partially computed resolution. --* - restart debug needsPackage("Complexes") -*- gbTrace=1 S = ZZ/101[vars(0..20)] I = ideal for i from 1 to numgens S list S_(i-1)^i diff --git a/M2/Macaulay2/packages/PruneComplex.m2 b/M2/Macaulay2/packages/Complexes/PruneComplex.m2 similarity index 89% rename from M2/Macaulay2/packages/PruneComplex.m2 rename to M2/Macaulay2/packages/Complexes/PruneComplex.m2 index b18ff0ae3e7..ae56b521b49 100644 --- a/M2/Macaulay2/packages/PruneComplex.m2 +++ b/M2/Macaulay2/packages/Complexes/PruneComplex.m2 @@ -10,62 +10,10 @@ -- pruneComplex may not treat degrees of free modules correctly when working -- on a free chain complex rather than a free resolution. --------------------------------------------------------------------------- -newPackage( - "PruneComplex", - Version => "1.0", - Date => "January 14th, 2017", - Authors => { - {Name => "Mahrud Sayrafi", Email => "mahrud@berkeley.edu", HomePage => "http://ocf.berkeley.edu/~mahrud/"}, - {Name => "Mike Stillman", Email => "mike@math.cornell.edu", HomePage => "http://www.math.cornell.edu/~mike/"} - }, - Headline => "pruning chain complexes over polynomial and local rings", - Keywords => {"Commutative Algebra", "Homological Algebra"}, - -- the latter is needed to get the documentation of FastNonminimal to work - PackageExports => {"Complexes", "OldChainComplexes"}, - AuxiliaryFiles => true - ) - -importFrom_Core { - "LocalRing", - "raw", - "rawDeleteColumns", - "rawDeleteRows", - "rawMutableComplex", - "rawPruneBetti", - "rawPruneComplex", - "rawPruningMorphism", - } - -export { - "toMutableComplex", - "toChainComplex", - "pruneComplex", - "pruneUnit", - "pruneDiff", - "isScalar", --- "findUnit", --- "findAllUnits", --- "findSparseUnit", --- "isQuasiIsomorphism", -- see ChainComplexExtras.m2 --- "isCommutative", --- "isAcyclic", --- "isMinimal", --- "freeRes", --- "testM2", --- "testEngine", --- Options: - "Direction", "PruningMap", "UnitTest" - } - --- << "--------------------------------------------------------------------------------------" << endl; --- << "-- The PruneComplex package is experimental. --" << endl; --- << "-- See the documentation and comments in the package to learn more. --" << endl; --- << "--------------------------------------------------------------------------------------" << endl; --=================================== Chain Complex Operations ===================================-- --- TODO see if the new Chain Complexes package can be incorporated --- converts a ChainComplex into a list of mutable matrices +-- converts a Complex into a list of mutable matrices -- TODO: take an option to make sparse mutable matrices here toMutableComplex = method() toMutableComplex Complex := C -> for i from min C to max C list mutableMatrix C.dd_(i+1) @@ -363,8 +311,8 @@ pruneComplex(List, ZZ) := opts -> (mComplex, nsteps) -> ( (mComplex, pruningMorph) ) -- for backwards compatibility; to be removed -pruneComplex ChainComplex := opts -> C -> pruneComplex(C, -1, opts) -pruneComplex(ChainComplex, ZZ) := opts -> (C, nsteps) -> pruneComplex(complex C, -1, opts) +--pruneComplex ChainComplex := opts -> C -> pruneComplex(C, -1, opts) +--pruneComplex(ChainComplex, ZZ) := opts -> (C, nsteps) -> pruneComplex(complex C, -1, opts) enginePruneComplex = method(Options => options pruneComplex) -- ++ {...} enginePruneComplex List := opts -> C -> enginePruneComplex(C, -1, opts) @@ -410,8 +358,10 @@ enginePruneComplex(List, ZZ) := opts -> (C, nsteps) -> ( -- if all((min C,min(max C, opts.LengthLimit)), i -> (prune HH_i(C) == 0)) then true else false -- ) -- Checks that C is a resolution of N -isQuasiIsomorphism(Complex, Ideal) := Boolean => opts -> (C, I) -> isQuasiIsomorphism(C, coker gens I) -isQuasiIsomorphism(Complex, Module) := Boolean => opts -> (C, N) -> ( + +isQuasiIsomorphismOf = method(Options => options isQuasiIsomorphism) +isQuasiIsomorphismOf(Complex, Ideal) := Boolean => opts -> (C, I) -> isQuasiIsomorphismOf(C, coker gens I) +isQuasiIsomorphismOf(Complex, Module) := Boolean => opts -> (C, N) -> ( R := ring N; D := complex N; if C == 0 then return D == 0; @@ -419,8 +369,11 @@ isQuasiIsomorphism(Complex, Module) := Boolean => opts -> (C, N) -> ( isQuasiIsomorphism(map(D, C, i -> M#i), opts) ) +-* -- Checks that C is an acyclic chain complex isAcyclic = C -> isQuasiIsomorphism(C, 0) +*- + -- Checks whether there are any more units are left in the complex -- Note: this only implies that the resolution is minimal in the local and graded case @@ -431,6 +384,7 @@ isMinimal Complex := Boolean => opts -> C -> ( if any(length C + 1, i -> not isMinimal(matrix (C.dd_i), opts)) then false else true ) +-* -- Checks commutativity of chain complexes C and D with chain complex map M isCommutative ComplexMap := Boolean => f -> ( D := source f; @@ -440,9 +394,47 @@ isCommutative ComplexMap := Boolean => f -> ( return false; true ) +*- + +--============================ Tests Code ===================================-- +-- These two routines are not exported. So tests must `debug needsPackage "Complexes"`. + +-- Returns a non-minimal free resolution, for testing purposes. +-- Homogenizes the ideal w.r.t. a new variable, calculates a free resolution +-- using res with option Strategy => Nonminimal, then dehomogenizes +-- Warning: only works for finite fields +freeRes = I -> ( + -- Warning: only works for finite fields + R := ring I; + homogvar := local homogvar; + S := (coefficientRing R)(monoid [gens R, homogvar]); + Ih := ideal homogenize(sub(gens gb I, S), S_(numgens R)); + C1 := freeResolution(Ih, Strategy => Nonminimal); + phi := map(R,S,gens R | {1}); + phi C1 + ) + +-- Runs various sanity checks +testSuit = (I, P, strategy) -> ( + R := ring I; + if not instance(R, LocalRing) then ( + C := freeRes I; + assert(C.dd^2 == 0); + time D := pruneComplex(C, Strategy => strategy, Direction => "left", PruningMap => true); + assert(D.dd^2 == 0); + if isHomogeneous I then assert(betti D == betti freeResolution I); + assert(isCommutative D.cache.pruningMap); + assert(isQuasiIsomorphismOf(D, I)); + E := D ** R_P; + ) else E = freeResolution I; + RP := ring E; + assert(E.dd^2 == 0); + time F := pruneComplex(E, Strategy => strategy, Direction => "left", PruningMap => true); + return ( + F.dd^2 == 0 and isMinimal F and + isCommutative F.cache.pruningMap and + isQuasiIsomorphismOf(F, ideal(gens I ** RP))) + ) -load ("./PruneComplex/tests.m2") -beginDocumentation() -load ("./PruneComplex/doc.m2") end-- diff --git a/M2/Macaulay2/packages/PruneComplex/doc.m2 b/M2/Macaulay2/packages/Complexes/PruneComplexDoc.m2 similarity index 99% rename from M2/Macaulay2/packages/PruneComplex/doc.m2 rename to M2/Macaulay2/packages/Complexes/PruneComplexDoc.m2 index a8d3eac6364..804948e38e0 100644 --- a/M2/Macaulay2/packages/PruneComplex/doc.m2 +++ b/M2/Macaulay2/packages/Complexes/PruneComplexDoc.m2 @@ -1,4 +1,5 @@ -doc /// +-- TODO: remove this doc node +/// Key PruneComplex Headline @@ -401,7 +402,7 @@ Description For optional inputs, the Engine algorithms only support PruningMap, "left" or "right" as Direction, and isUnit or isScalar as UnitTest. - Advanced users can implement their own strategies in packages/PruneComplex.m2 and run pruneComplex + Advanced users can implement their own strategies in packages/Complexes/PruneComplex.m2 and run pruneComplex using the Strategy => null option. SeeAlso pruneComplex diff --git a/M2/Macaulay2/packages/PruneComplex/tests.m2 b/M2/Macaulay2/packages/Complexes/PruneComplexTests.m2 similarity index 78% rename from M2/Macaulay2/packages/PruneComplex/tests.m2 rename to M2/Macaulay2/packages/Complexes/PruneComplexTests.m2 index 3b806230499..753746aa741 100644 --- a/M2/Macaulay2/packages/PruneComplex/tests.m2 +++ b/M2/Macaulay2/packages/Complexes/PruneComplexTests.m2 @@ -1,42 +1,5 @@ --============================ Tests Sections ===================================-- --- Returns a non-minimal free resolution, for testing purposes. --- Homogenizes the ideal w.r.t. a new variable, calculates a free resolution --- using res with option Strategy => Nonminimal, then dehomogenizes --- Warning: only works for finite fields -freeRes = I -> ( - -- Warning: only works for finite fields - R := ring I; - homogvar := local homogvar; - S := (coefficientRing R)(monoid [gens R, homogvar]); - Ih := ideal homogenize(sub(gens gb I, S), S_(numgens R)); - C1 := freeResolution(Ih, Strategy => Nonminimal); - phi := map(R,S,gens R | {1}); - phi C1 - ) - --- Runs various sanity checks -testSuit = (I, P, strategy) -> ( - R := ring I; - if not instance(R, LocalRing) then ( - C := freeRes I; - assert(C.dd^2 == 0); - time D := pruneComplex(C, Strategy => strategy, Direction => "left", PruningMap => true); - assert(D.dd^2 == 0); - if isHomogeneous I then assert(betti D == betti freeResolution I); - assert(isCommutative D.cache.pruningMap); - assert(isQuasiIsomorphism(D, I)); - E := D ** R_P; - ) else E = freeResolution I; - RP := ring E; - assert(E.dd^2 == 0); - time F := pruneComplex(E, Strategy => strategy, Direction => "left", PruningMap => true); - return ( - F.dd^2 == 0 and isMinimal F and - isCommutative F.cache.pruningMap and - isQuasiIsomorphism(F, ideal(gens I ** RP))) - ) - -- Given a homogeneous module, with a non-minimal resolution, the pruned complex should be homogeneous. TEST /// R = ZZ/32003[x,y,z] @@ -50,7 +13,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" R = ZZ/32003[vars(0..17)] m1 = genericMatrix(R,a,3,3) m2 = genericMatrix(R,j,3,3) @@ -71,7 +34,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" R = ZZ/32003[vars(0..8)] M = genericMatrix(R,3,3) I = minors(2, M) @@ -96,7 +59,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" R = ZZ/32003[a..f] -- @@ -105,11 +68,11 @@ TEST /// D = pruneComplex(C, UnitTest => isScalar, Strategy => null, Direction => "whole") -- 1 4 4 1 assert(D.dd^2 == 0) - assert(isQuasiIsomorphism(D,I)) + assert(isQuasiIsomorphismOf(D,I)) D' = pruneComplex(C, UnitTest => isScalar, Strategy => null, Direction => "both") -- 1 4 4 1 assert(D'.dd^2 == 0) - assert(isQuasiIsomorphism(D',I)) + assert(isQuasiIsomorphismOf(D',I)) -- I = ideal"abc-def,ab2-cd2,acd-b3" @@ -117,11 +80,11 @@ TEST /// D = pruneComplex(C, UnitTest => isScalar) -- 1 3 4 2 assert(D.dd^2 == 0) - assert(isQuasiIsomorphism(D,I)) + assert(isQuasiIsomorphismOf(D,I)) /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" needsPackage "LocalRings" R = ZZ/32003[a..f] P = ideal gens R @@ -137,7 +100,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" R = ZZ/32003[x,y,z] I = ideal"xyz+z5,2x2+y3+z7,3z5+y5" @@ -147,7 +110,7 @@ TEST /// assert(D.dd^2 == 0); assert(isMinimal(D, UnitTest => isScalar)); assert(isCommutative D.cache.pruningMap); - assert(isQuasiIsomorphism(D, I));) + assert(isQuasiIsomorphismOf(D, I));) D = pruneComplex(C, PruningMap => true, UnitTest => isScalar, Strategy => Engine) -- 1 3 4 2 checker(D, I) @@ -170,7 +133,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" needsPackage "LocalRings" R = ZZ/32003[a..f] @@ -186,7 +149,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" needsPackage "LocalRings" R = ZZ/32003[a..d] @@ -201,7 +164,7 @@ TEST /// -- use R -- setMaxIdeal ideal gens R -- L = localResolution I --- assert(isQuasiIsomorphism(L**RM, E')) +-- assert(isQuasiIsomorphismOf(L**RM, E')) /// TEST /// -- An example for comparison with Singular @@ -233,7 +196,7 @@ TEST /// -- An example for comparison with Singular --============================ Engine Tests Sections ===================================-- TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" debug needsPackage "LocalRings" R = ZZ/32003[a..f]; P = ideal gens R; @@ -252,7 +215,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" needsPackage "LocalRings" R = ZZ/32003[a..d] P = ideal"a,b,c" @@ -263,7 +226,7 @@ TEST /// /// TEST /// - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" needsPackage "LocalRings" R = ZZ/32003[x,y,z] I = ideal"xyz+z5,2x2+y3+z7,3z5+y5" @@ -305,7 +268,7 @@ end-- ------------------------------------------------------------------------------------------------------- -- Stress Test for Pruning Complexes in the Engine restart -debug needsPackage "PruneComplex" +debug needsPackage "Complexes" needsPackage "RandomIdeals" R = ZZ/32003[a..f]; @@ -322,15 +285,3 @@ randomCombo = (n, d) -> ( then (i = i + 1) else ( print "Lookit!"; break; ); ); -------------------------------------------------------------------------------------------------------- --- Development stuff --- path = prepend("~/src/m2/M2-local-rings/M2/Macaulay2/packages/", path) -- Mike -restart -needsPackage "PruneComplex" -elapsedTime check PruneComplex -- 18 sec - -restart -uninstallPackage "PruneComplex" -restart -installPackage "PruneComplex" -viewHelp "PruneComplex" diff --git a/M2/Macaulay2/packages/CorrespondenceScrolls.m2 b/M2/Macaulay2/packages/CorrespondenceScrolls.m2 index e4b4b920ae2..bbdee58dc2f 100644 --- a/M2/Macaulay2/packages/CorrespondenceScrolls.m2 +++ b/M2/Macaulay2/packages/CorrespondenceScrolls.m2 @@ -23,7 +23,7 @@ newPackage( HomePage => ""}}, Headline => "correspondence scrolls", Keywords => {"Commutative Algebra"}, - PackageImports => { "Elimination", "OldChainComplexes" } + PackageImports => { "Elimination", "Complexes" } ) export { diff --git a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/AbelianCategoryDoc.m2 b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/AbelianCategoryDoc.m2 index 1dd0bef9c8f..d0133be6d9f 100644 --- a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/AbelianCategoryDoc.m2 +++ b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/AbelianCategoryDoc.m2 @@ -26,7 +26,7 @@ doc /// Moreover, any Mackey functor homomorphism admits a @TO2((kernel,MackeyFunctorHomomorphism),"kernel")@ and a @TO2((cokernel,MackeyFunctorHomomorphism),"cokernel")@. All this data we've collected so far is often called a {\em pre-abelian} structure. Finally, one can prove that images and coimages agree, hence $\text{Mack}_G$ is an {\em abelian category}. - {\bf Enough projectives:} The category $\text{Mack}_{G}$ has {\em exactly two} projective objects, which we denote by $\underline{A}$ and $\underline{B}$. These are the @TO2(makeBurnsideMackeyFunctor, "Burnside Mackey functor")@ and the @TO2(makeUnderlyingFreeMackeyFunctor,"underlying free Mackey functor")@, respectively. The category $\text{Mack}_G$ has {\em enough projectives}, meaning in particular that we can take projective resolutions in order to compute ext and tor groups. + {\bf Enough projectives:} The category $\text{Mack}_{G}$ has a set of two projective generators, which we denote by $\underline{A}$ and $\underline{B}$. These are the @TO2(makeBurnsideMackeyFunctor, "Burnside Mackey functor")@ and the @TO2(makeUnderlyingFreeMackeyFunctor,"underlying free Mackey functor")@, respectively. In particular, the category $\text{Mack}_G$ has {\em enough projectives}, meaning in particular that we can take projective resolutions in order to compute ext and tor groups. {\bf Free resolutions:} Given any Mackey functor we can take its {\em free resolution}. The projective dimension of an object of $\text{Mack}_G$ is generally infinite, so we cannot provide all of the data of a resolution -- nevertheless we can provide a computation that holds in a range. This is accomplished using the @TO(resolution)@ (aka @TO(res)@) method, which inputs a Mackey functor $M$ and an integer $n$, and outputs a free resolution of the form \[M \leftarrow F_0 \leftarrow F_1 \leftarrow \cdots \leftarrow F_n\] diff --git a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/ConstructorsDoc.m2 b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/ConstructorsDoc.m2 index 832bcd54a82..95807c8d5c4 100644 --- a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/ConstructorsDoc.m2 +++ b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/ConstructorsDoc.m2 @@ -219,7 +219,7 @@ doc /// the complex representation Mackey functor for the group $C_p$. Description Text - The {\em complex representation} Mackey functor of a group $G$ is defined by sending $G/H$ to the Grothendieck group of complex $G$-representations. The transfer and restriction come from induction and restriction of $G$-representations. When $G$ is a cyclic group of prime order, this admits a nice form. The underlying module is given by $\ZZ$ with trivial conjugation action, while the fixed module is $\mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{p-1}\}$. The element $\lambda_i$ represents the one dimensional complex representation given by multiplication by $e^{2\pi i/p}$. Restriction is defined as", + The {\em complex representation} Mackey functor of a group $G$ is defined by sending $G/H$ to the Grothendieck group of complex $H$-representations. The transfer and restriction come from induction and restriction of representations. When $G$ is a cyclic group of prime order, this admits a nice form. The underlying module is given by $\ZZ$ with trivial conjugation action, while the fixed module is $\mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{p-1}\}$. The element $\lambda_i$ represents the one dimensional complex representation given by multiplication by $e^{2\pi i/p}$. Restriction is defined as", \[\text{res}_e^{C_p} \colon \mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{p-1}\} \to \mathbb{Z}\] by sending $\lambda_i\mapsto 1$. The transfer \[\text{tr}_e^{C_p} \colon \mathbb{Z} \to \mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{p-1}\}\] @@ -248,7 +248,7 @@ doc /// Description Text - The {\em real representation} Mackey functor of a group $G$ is defined by sending $G/H$ to the Grothendieck group of real orthogonal $G$-representations. The transfer and restriction come from induction and restriction of $G$-representations. When $G$ is a cyclic group of prime order p, with p odd, this admits a nice form. The underlying module is given by $\ZZ$ with trivial conjugation action, while the fixed module is $\mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{(p-1)/2}\}$. The element $\lambda_i$ for $i>0$ represents the two dimensional real representation given by rotation by $(2\pi i)/p$ radians. The element $\lambda_0$ represents the trivial one dimensional representation. Restriction is defined as + The {\em real representation} Mackey functor of a group $G$ is defined by sending $G/H$ to the Grothendieck group of real orthogonal $H$-representations. The transfer and restriction come from induction and restriction of representations. When $G$ is a cyclic group of prime order p, with p odd, this admits a nice form. The underlying module is given by $\ZZ$ with trivial conjugation action, while the fixed module is $\mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{(p-1)/2}\}$. The element $\lambda_i$ for $i>0$ represents the two dimensional real representation given by rotation by $(2\pi i)/p$ radians. The element $\lambda_0$ represents the trivial one dimensional representation. Restriction is defined as \[\text{res}_e^{C_p} \colon \mathbb{Z}\{\lambda_{0},\lambda_1,\dots,\lambda_{(p-1)/2}\} \to \mathbb{Z}\] by sending \[\lambda_i\mapsto \begin{cases} 2 & i>0 \\ 1 & i=0. \end{cases} \] diff --git a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomAlgDoc.m2 b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomAlgDoc.m2 index 6f569c907d7..1d0d68f2fc9 100644 --- a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomAlgDoc.m2 +++ b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomAlgDoc.m2 @@ -4,7 +4,7 @@ Node Tor (Tor,ZZ,CpMackeyFunctor,CpMackeyFunctor) Headline - computes Tor of two Cp-Mackey fuctors + computes Tor of two Cp-Mackey functors Usage Tor_i(M,N) Inputs @@ -25,7 +25,7 @@ Node TorCoh (TorCoh,ZZ,CpMackeyFunctor,CpMackeyFunctor) Headline - computes Tor of two cohomological Cp-Mackey fuctors + computes Tor of two cohomological Cp-Mackey functors Usage TorCoh(i,M,N) Inputs @@ -51,7 +51,7 @@ Node Ext (Ext,ZZ,CpMackeyFunctor,CpMackeyFunctor) Headline - computes Ext of two Cp-Mackey fuctors + computes Ext of two Cp-Mackey functors Usage Ext^i(M,N) Inputs @@ -72,9 +72,9 @@ Node ExtCoh (ExtCoh,ZZ,CpMackeyFunctor,CpMackeyFunctor) Headline - computes Ext of two cohomological Cp-Mackey fuctors + computes Ext of two cohomological Cp-Mackey functors Usage - Ext(i,M,N) + ExtCoh(i,M,N) Inputs i : ZZ M : CpMackeyFunctor diff --git a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomomorphismsDoc.m2 b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomomorphismsDoc.m2 index f479f518836..6e804beed08 100644 --- a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomomorphismsDoc.m2 +++ b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/HomomorphismsDoc.m2 @@ -35,7 +35,7 @@ doc /// Example id_(M) Text - The only $0$-ary operation implemented for Mackey functor homomorphisms is: + The only unary (@TO("Boolean")@-valued) function implemented for Mackey functor homomorphisms is: @UL{ (TO2((isIsomorphism,MackeyFunctorHomomorphism),"isIsomorphism"), " checking if a morphism is an isomorphism") diff --git a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/MackeyFunctorDoc.m2 b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/MackeyFunctorDoc.m2 index a873f6c8eac..516c967b083 100644 --- a/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/MackeyFunctorDoc.m2 +++ b/M2/Macaulay2/packages/CpMackeyFunctors/Documentation/MackeyFunctorDoc.m2 @@ -30,7 +30,7 @@ doc /// {\bf Constructing new Mackey functors:} See @TO("constructing examples of Mackey functors")@. - {\bf Operations with Mackey functor types:} One of the first operations to know about is @TO2((prune,CpMackeyFunctor),"pruning")@ a $C_p$-Mackey functor. This basically takes the data of a Mackey functor and attempts to simplify it and make it more reader-friendly. Pruning is the only unary operation on $C_p$-Mackey functors that we consider. The $0$-ary (@TO("Boolean")@-valued) operations are + {\bf Operations with Mackey functor types:} One of the first operations to know about is @TO2((prune,CpMackeyFunctor),"pruning")@ a $C_p$-Mackey functor. This basically takes the data of a Mackey functor and attempts to simplify it and make it more reader-friendly. Pruning is the only unary operation on $C_p$-Mackey functors that we consider. The unary (@TO("Boolean")@-valued) operations are @UL{ (TO("isTrivialMackeyFunctor")," checks if a Mackey functor is trivial"), @@ -65,11 +65,11 @@ doc /// p : ZZ a prime number R : Matrix - a restriction homomorphim + a restriction homomorphism T : Matrix - a transfer homomorphim + a transfer homomorphism C : Matrix - a conjugation homomorphim + a conjugation homomorphism Outputs : CpMackeyFunctor Description diff --git a/M2/Macaulay2/packages/CpMackeyFunctors/Tests/BoxProductTests.m2 b/M2/Macaulay2/packages/CpMackeyFunctors/Tests/BoxProductTests.m2 index 5aaf987fe3e..2c1cdb410dc 100644 --- a/M2/Macaulay2/packages/CpMackeyFunctors/Tests/BoxProductTests.m2 +++ b/M2/Macaulay2/packages/CpMackeyFunctors/Tests/BoxProductTests.m2 @@ -27,8 +27,8 @@ assert (class (inverse f) ===MackeyFunctorHomomorphism) -- Test induced map on box products for i to 5 do ( - p = (random {2,3,5,7})#0; - n = (random toList (0..20))#0; + p = randomElement {2,3,5,7}; + n = random 21; M = makeRandomCpMackeyFunctor(p); A = makeBurnsideMackeyFunctor(p); timesn = map(A,A,n); @@ -36,4 +36,4 @@ for i to 5 do ( assert (timesn**M == map(A**M,A**M,n)); ); -/// \ No newline at end of file +/// diff --git a/M2/Macaulay2/packages/Cremona.m2 b/M2/Macaulay2/packages/Cremona.m2 index 56137a45d03..b7f47168a27 100644 --- a/M2/Macaulay2/packages/Cremona.m2 +++ b/M2/Macaulay2/packages/Cremona.m2 @@ -1628,7 +1628,7 @@ genericRestriction = (phi) -> ( K:=coefficientRing Pn; x:=local x; H:=K[x_0..x_(n-1)]; - j:=map(H,Pn,random(toList(x_0..x_(n-1))|{random1 H})); + j:=map(H,Pn,shuffle(toList(x_0..x_(n-1))|{random1 H})); j=map(H/j(ideal target phi),target phi,toMatrix j); phi':=j*phi; phi' diff --git a/M2/Macaulay2/packages/DGAlgebras.m2 b/M2/Macaulay2/packages/DGAlgebras.m2 index 9386405057f..5f553d5b30f 100644 --- a/M2/Macaulay2/packages/DGAlgebras.m2 +++ b/M2/Macaulay2/packages/DGAlgebras.m2 @@ -1,16 +1,20 @@ -*- coding: utf-8 -*- newPackage("DGAlgebras", Headline => "Data type for DG algebras", - Version => "1.1.1--with fix of killCycles and new code and docs for displayBlockDiff and blockDiff", - Date => "August 20, 2020", + Version => "2.0", + Date => "April 27, 2026", Authors => { {Name => "Frank Moore", HomePage => "http://www.math.wfu.edu/Faculty/Moore.html", - Email => "moorewf@wfu.edu"} + Email => "moorewf@wfu.edu"}, + {Name => "Keller VandeBogert", + HomePage => "https://sites.google.com/view/kellervandebogert/home", + Email => "keller.v@uky.edu"} }, Keywords => {"Commutative Algebra"}, DebuggingMode => false, - PackageExports => {"IntegralClosure", "OldChainComplexes", "Complexes"} + PackageExports => {"IntegralClosure", "OldChainComplexes", "Complexes"}, + AuxiliaryFiles => true ) export {"DGAlgebra", "DGAlgebraMap", @@ -20,8 +24,9 @@ export {"DGAlgebra", "natural", "cycles", "getBasis", - "koszulComplexDGA", - "acyclicClosure", + "koszulComplexDGA", + "acyclicClosure", + "minimalModel", "toComplex", "toComplexMap", "liftToDGMap", @@ -58,7 +63,74 @@ export {"DGAlgebra", "homologyModule", "dgAlgebraMultMap", "displayBlockDiff", - "blockDiff" + "blockDiff", + -- v1.2 overhaul: accessor / invariant layer + "underlyingRing", + "underlyingAlgebra", + "differential", + "generatorDegrees", + "isValidDGAlgebra", + "isWellDefinedDifferential", + "ensureDGAlgebraCaches", + "invalidateDGAlgebraCache", + -- v1.2 overhaul: ring-manipulation operations + "baseChange", + "subDGAlgebra", + "truncateGenerators", + "restrictDifferential", + "killHomologyAtDegree", + -- v1.2 overhaul: now-public differential helpers + "polyDifferential", + "polyDiffMonomial", + -- v1.2 overhaul: lazy complex accessor + "dgComplex", + -- v2: DGModule type and constructors + "DGModule", + "koszulComplexDGM", + "moduleDifferential", + "isValidDGModule", + "freeDGModule", + "adjoinGenerators", + "semifreeResolution", + "minimalSemifreeResolution", + "isMinimalSemifreeResolution", + "generatorTable", + "dgModuleSummary", + "moduleBlockDiff", + "displayModuleBlockDiff", + -- v2: DGModuleMap type and operations + "DGModuleMap", + "dgModuleMap", + "identityDGModuleMap", + "zeroDGModuleMap", + "liftToDGModuleMap", + -- v2: DGAlgebraMap additions + "identityDGAlgebraMap", + -- v2: tensor-product infrastructure + "tensorFactors", + "tensorInclusions", + -- v2: sub/quotient types + image/kernel/cokernel of DGModuleMap + "DGIdeal", + "dgIdeal", + "DGSubmodule", + "dgSubmodule", + "DGQuotientModule", + "dgQuotientModule", + "isDGIdeal", + "isDGSubmodule", + -- Accessor methods for the new types (also match the symbol keys used + -- internally, so `S.inclusion`, `Q.subDGModule`, etc. resolve to the + -- same symbol in user scope as in package scope). + "inclusion", + "subDGModule", + "projection", + "dgAlgebra", + -- v2: basic module-like operations on DG modules + "isFreeDGModule", + "isZero" + -- isQuasiIsomorphism is re-exported from Complexes; we add a method for DGModuleMap. + -- image / kernel / cokernel on DGModuleMap extend the core generics. + -- A / I (DGAlgebra / DGIdeal), M / S (DGModule / DGSubmodule) use `symbol /`. } -- Questions: @@ -103,6 +175,8 @@ export {"DGAlgebra", -- protected symbols (I don't want to export these symbols, but I use them internally in the code at various places) protect diffs protect basisAlgebra +-- dgAlgebra is now exported as an accessor (auto-protected); do not re-protect here +protect prunedComplex -- Defining the new type DGAlgebra DGAlgebra = new Type of MutableHashTable @@ -110,10 +184,66 @@ globalAssignment DGAlgebra --DGAlgebraMap still in development DGAlgebraMap = new Type of MutableHashTable globalAssignment DGAlgebraMap +-- [functionality v2] DGModule: a DG module over a DGAlgebra. +-- Storage mirrors DGAlgebra: +-- M.dgAlgebra -- the DGAlgebra A +-- M.ring -- A.ring (convenience copy) +-- M.natural -- the underlying graded A.natural-module +-- M.Degrees -- list of generator homological-degree vectors +-- M.diff -- list of images of the module generators under d_M, +-- stored as elements of M.natural (one per generator); +-- parallels A.diff's "images on gens" convention. +-- M.cache -- MutableHashTable with M.cache.diffs#n caching the +-- R-linear differential at hom-degree n. +-- Constructors (koszulComplexDGM) may attach additional fields such as +-- M.module (the underlying R-module) for reference. +DGModule = new Type of MutableHashTable +globalAssignment DGModule + +-- DGModuleMap: a morphism of DG modules over a common DG algebra A. +-- Storage: +-- f.source -- DGModule M (the domain) +-- f.target -- DGModule N (the codomain) +-- f.dgAlgebra -- the shared DGAlgebra A +-- f.natural -- Matrix N.natural <- M.natural; the i-th column +-- holds the image of the i-th natural generator of M, +-- expressed as an element of N.natural. +-- f.cache -- CacheTable (holds e.g. per-degree toComplexMap pieces). +-- Constructors (see dgModuleMap): accept either a full Matrix N.natural <- +-- M.natural or a list of image Vectors (one per M-generator). +DGModuleMap = new Type of MutableHashTable +globalAssignment DGModuleMap -- this command is in the core, but we need it here. -- spots = C -> select(keys C, i -> class i === ZZ) +-- Return the underlying Symbol of a variable-like object (Symbol, +-- IndexedVariable, or a RingElement that is a single generator). +-- Note: baseName on an IndexedVariable is the identity, so we must peel +-- into its first slot to reach the Symbol. +baseSymbolOf = v -> ( + s := baseName v; + if instance(s, IndexedVariable) then s = s#0; + s +) + +-- Build a list of doubly-indexed generator symbols base_(i,j), where i is +-- the homological degree (first entry of the corresponding degree vector) +-- and j is a running count of generators with that homological degree. +-- startingCounts is an (optionally empty) HashTable mapping a homological +-- degree to the number of generators already present at that degree; +-- numbering continues past those counts. +makeDoubleIndexSymbols = (baseSym, degList, startingCounts) -> ( + counters := new MutableHashTable; + scan(keys startingCounts, k -> counters#k = startingCounts#k); + apply(degList, d -> ( + i := first d; + if not counters#?i then counters#i = 0; + counters#i = counters#i + 1; + baseSym_(i, counters#i) + )) +) + -- Modify the standard output for a DGAlgebra net DGAlgebra := A -> ( local diffList; @@ -131,11 +261,43 @@ freeDGAlgebra (Ring,List) := opts -> (R,degList) -> ( -- Input: A ring, a list of degrees of the variables, and a list that defines the differential -- Output: A hash table of type DGAlgebra A := new MutableHashTable; - T := getSymbol(opts.Variable); A#(symbol ring) = R; - varsList := toList (T_1..T_(#degList)); + -- Build the generator names. Two modes, determined by the Variable option: + -- Variable => (default "T") + -- Auto-generate doubly-indexed names base_(i,j), where i is the + -- homological degree (first entry of a degree vector) and j is the + -- running count of generators at that homological degree. + -- Variable => + -- Use the supplied symbols verbatim (Strings are promoted via + -- getSymbol). Length must match #degList. This is the mode used by + -- adjoinVariables / acyclicClosure to preserve existing gen identity. + varsList := if instance(opts.Variable, VisibleList) then ( + if #opts.Variable != #degList then + error("freeDGAlgebra: Variable list has " | toString(#opts.Variable) + | " entries but degree list has " | toString(#degList) | "."); + apply(toList opts.Variable, v -> if instance(v, String) then getSymbol v else v) + ) else ( + baseSym := if instance(opts.Variable, Symbol) then opts.Variable + else getSymbol(toString opts.Variable); + makeDoubleIndexSymbols(baseSym, degList, hashTable{}) + ); + -- Symbol-safety: detect collisions between the DG generator names and + -- existing variables in R. Previously silent; surfaced as bizarre + -- downstream errors (wrong differentials, stale substitutions). + if #varsList > 0 then ( + existingSymbols := set apply(gens R, g -> toString g); + collisions := select(varsList, v -> member(toString v, existingSymbols)); + if #collisions > 0 then + error ("freeDGAlgebra: DG generator name(s) " | toString collisions + | " collide with variable(s) already in the base ring. " + | "Pass Variable => \"\" (or an explicit list of symbols) to freeDGAlgebra / koszulComplexDGA to avoid the clash."); + ); A#(symbol diff) = {}; - if isHomogeneous R then ( + -- Handle empty degree list: build a trivial polynomial ring over R with no T-generators. + if #degList == 0 then ( + A#(symbol natural) = R[varsList, Join => false]; + ) + else if isHomogeneous R then ( -- make sure the degree list has the right form. if #(first degList) != #(first degrees A.ring) + 1 then degList = apply(degList, i -> i | {0}); A#(symbol natural) = (A.ring)[varsList, Degrees => degList, Join => false, SkewCommutative => select(toList(0..(#degList-1)), i -> odd first degList#i)]; @@ -145,7 +307,10 @@ freeDGAlgebra (Ring,List) := opts -> (R,degList) -> ( ); A#(symbol isHomogeneous) = false; A.natural.cache = new CacheTable; - A.natural.cache#(symbol basisAlgebra) = (A.ring)[varsList, Join => false, MonomialOrder => GRevLex, Degrees => apply(degList, i -> {first i}), SkewCommutative => select(toList(0..(#degList-1)), i -> odd first degList#i)]; + if #degList == 0 then + A.natural.cache#(symbol basisAlgebra) = (A.ring)[varsList, Join => false, DegreeMap => (d -> {0}), MonomialOrder => GRevLex] + else + A.natural.cache#(symbol basisAlgebra) = (A.ring)[varsList, Join => false, DegreeMap => (d -> {0}), MonomialOrder => GRevLex, Degrees => apply(degList, i -> {first i}), SkewCommutative => select(toList(0..(#degList-1)), i -> odd first degList#i)]; use A.natural; A#(symbol Degrees) = degList; A#(symbol cache) = new CacheTable; @@ -166,18 +331,28 @@ totalOddDegree := A -> sum select(degrees A.natural / first, i -> odd i) setDiff = method(TypicalValue => DGAlgebra, Options => {InitializeDegreeZeroHomology => true, InitializeComplex => true}) setDiff (DGAlgebra,List) := opts -> (A,diffList) -> ( - A.diff = map(A.natural,A.natural, substitute(matrix {diffList}, A.natural)); + ensureDGAlgebraCaches A; + invalidateDGAlgebraCache A; + if #diffList == 0 then + A.diff = map(A.natural, A.natural, {}) + else + A.diff = map(A.natural,A.natural, substitute(matrix {diffList}, A.natural)); A.isHomogeneous = isHomogeneous A.ring and checkIsHomogeneous(A); if opts.InitializeDegreeZeroHomology then ( - definingIdeal := ideal mingens (ideal A.ring + sub(ideal polyDifferential(1,A), ambient A.ring)); - if definingIdeal == ideal vars ambient A.ring then A#(symbol zerothHomology) = coefficientRing A.ring else A#(symbol zerothHomology) = (ambient A.ring)/definingIdeal; + if #diffList == 0 then A#(symbol zerothHomology) = A.ring + else ( + definingIdeal := ideal mingens (ideal A.ring + sub(ideal polyDifferential(1,A), ambient A.ring)); + if definingIdeal == ideal vars ambient A.ring then A#(symbol zerothHomology) = coefficientRing A.ring else A#(symbol zerothHomology) = (ambient A.ring)/definingIdeal; + ); ); - if opts.InitializeComplex then A.dd = (toComplex(A,totalOddDegree(A)+1)).dd; + if opts.InitializeComplex and #diffList > 0 then A.dd = (toComplex(A,totalOddDegree(A)+1)).dd; A ) setKoszulDiff = method(TypicalValue => DGAlgebra, Options => {InitializeDegreeZeroHomology => true, InitializeComplex => true}) setKoszulDiff (DGAlgebra,List) := opts -> (A,diffList) -> ( + ensureDGAlgebraCaches A; + invalidateDGAlgebraCache A; A.diff = map(A.natural,A.natural, substitute(matrix {diffList}, A.natural)); A.isHomogeneous = isHomogeneous A.ring and checkIsHomogeneous(A); if opts.InitializeDegreeZeroHomology then ( @@ -191,9 +366,10 @@ setKoszulDiff (DGAlgebra,List) := opts -> (A,diffList) -> ( checkIsHomogeneous = method() checkIsHomogeneous DGAlgebra := A -> ( gensList := gens A.natural; + if #gensList == 0 then return true; -- trivially homogeneous diffList := apply(gensList, f -> A.diff(f)); homDegreeShift := {1} | (toList ((#(degree first gensList)-1):0)); - all(#diffList, i -> degree gensList#i - homDegreeShift == degree diffList#i) + all(#diffList, i -> diffList#i == 0 or degree gensList#i - homDegreeShift == degree diffList#i) ) -- cache the basis of a DGAlgebra? @@ -273,7 +449,7 @@ toComplex DGAlgebra := A -> ( toComplex (DGAlgebra,ZZ) := (A,n) -> complex(apply(n, i -> polyDifferential(i+1,A))) -killCycles = method(TypicalValue=>DGAlgebra,Options => {StartDegree => 1, EndDegree => -1}) +killCycles = method(TypicalValue=>DGAlgebra,Options => {StartDegree => 1, EndDegree => -1, Variable => null}) killCycles DGAlgebra := opts -> A -> ( -- for now, this will only work for DG algebras with H_0(A) = k retVal := 0; @@ -295,46 +471,84 @@ killCycles DGAlgebra := opts -> A -> ( homologyGenerators := entries transpose gens image (nthHomology.cache.pruningMap); basisList := flatten entries getBasis(n,A); cycleList := apply(homologyGenerators, gen -> sum apply(#gen, i -> gen#i*basisList#i)); - retVal = adjoinVariables(A,cycleList); + retVal = adjoinVariables(A, cycleList, Variable => opts.Variable); ); retVal ) -adjoinVariables = method(TypicalValue=>DGAlgebra) -adjoinVariables (DGAlgebra, List) := (A,cycleList) -> ( - -- this function will add a new variable to make the elements of cycles boundaries in a new DG algebra (semifree over the input) +adjoinVariables = method(TypicalValue=>DGAlgebra, Options => {Variable => null}) +adjoinVariables (DGAlgebra, List) := opts -> (A, cycleList) -> ( + -- Add new generators whose differentials are the given cycles, producing + -- a new DG algebra semifree over A. Existing generator names/identities + -- are preserved; new generators continue the doubly-indexed naming + -- base_(i,j) using per-homological-degree counters carried over from A. local newDegreesList; + local newCycleDegrees; tempDegree := {1} | toList ((#(degree first cycleList)-1):0); if A.isHomogeneous then - newDegreesList = A.Degrees | apply(cycleList, z -> degree z + tempDegree) + newCycleDegrees = apply(cycleList, z -> degree z + tempDegree) else - newDegreesList = A.Degrees | apply(cycleList, z -> {first degree z + 1}); - B := freeDGAlgebra(A.ring,newDegreesList); - phi := map(B.natural,A.natural,matrix {take(gens B.natural, numgens A.natural)}); + newCycleDegrees = apply(cycleList, z -> {first degree z + 1}); + newDegreesList = A.Degrees | newCycleDegrees; + existingGenNames := apply(gens A.natural, baseName); + baseSymForNew := if opts.Variable === null then ( + if #(gens A.natural) > 0 then baseSymbolOf first gens A.natural + else getSymbol "T" + ) else if instance(opts.Variable, Symbol) then opts.Variable + else getSymbol toString opts.Variable; + startingCounts := new MutableHashTable; + scan(A.Degrees, d -> ( + i := first d; + if not startingCounts#?i then startingCounts#i = 0; + startingCounts#i = startingCounts#i + 1; + )); + newGenNames := makeDoubleIndexSymbols(baseSymForNew, newCycleDegrees, + hashTable pairs startingCounts); + allNames := existingGenNames | newGenNames; + B := freeDGAlgebra(A.ring, newDegreesList, Variable => allNames); + phi := map(B.natural, A.natural, matrix {take(gens B.natural, numgens A.natural)}); newDiffList := (take(flatten entries matrix A.diff, numgens A.natural) | cycleList) / phi; - setDiff(B,newDiffList,InitializeComplex=>false); + setDiff(B, newDiffList, InitializeComplex=>false); B ) -acyclicClosure = method(TypicalValue=>DGAlgebra,Options => {StartDegree => 1, EndDegree => 3}) +acyclicClosure = method(TypicalValue=>DGAlgebra, Options => {StartDegree => 1, EndDegree => 3, Variable => null}) acyclicClosure DGAlgebra := opts -> A -> ( n := opts.StartDegree; endDegree := 3; if opts.EndDegree != 3 then endDegree = opts.EndDegree; while n <= endDegree do ( - A = killCycles(A,StartDegree => n); + A = killCycles(A, StartDegree => n, Variable => opts.Variable); n = n + 1; ); A ) acyclicClosure Ring := opts -> R -> ( - K := koszulComplexDGA(R); - acyclicClosure(K, opts) + varName := if opts.Variable === null then "T" else opts.Variable; + K := koszulComplexDGA(R, Variable => varName); + acyclicClosure(K, StartDegree => opts.StartDegree, EndDegree => opts.EndDegree, Variable => opts.Variable) +) + +-- minimalModel: build the minimal DG algebra resolution of a quotient ring +-- R = Q/I over the polynomial ring Q. For an Ideal I, this is +-- acyclicClosure(koszulComplexDGA I) -- a DG algebra over ring I whose +-- degree-0 homology is (ring I)/I and whose higher homology is killed +-- step by step via Tate's construction. For a QuotientRing R, we use +-- the defining ideal of R inside its ambient polynomial ring. +minimalModel = method(TypicalValue => DGAlgebra, + Options => {StartDegree => 1, EndDegree => 3, Variable => null}) +minimalModel Ideal := opts -> I -> ( + varName := if opts.Variable === null then "T" else opts.Variable; + K := koszulComplexDGA(I, Variable => varName); + acyclicClosure(K, StartDegree => opts.StartDegree, EndDegree => opts.EndDegree, Variable => opts.Variable) ) +minimalModel QuotientRing := opts -> R -> minimalModel(ideal R, opts) -polyDiffMonomial := (A,m) -> ( +polyDiffMonomial = method() +polyDiffMonomial (DGAlgebra, RingElement) := (A,m) -> ( -- uses the Leibniz rule to compute the differential of a traditional monomial + if m == 0 then return 0_(A.natural); dgSign := 1; justMon := first flatten entries first coefficients m; justCoeff := substitute(first flatten entries last coefficients m, ring m); @@ -366,7 +580,13 @@ polyDifferential (ZZ,DGAlgebra) := (n,A) -> ( targetList = flatten entries targetList; mDegree := maxDegree A; if (n == mDegree + 1) then newDiffl = map((A.ring)^(targetDegreeList), (A.ring)^0, 0) - else if n > mDegree + 1 then newDiffl = map((A.ring)^0,(A.ring)^0,0) else ( + else if n > mDegree + 1 then newDiffl = map((A.ring)^0,(A.ring)^0,0) + -- empty-basis guards: without these, coefficients() below errors on an + -- empty monomials list. Formerly users had to override polyDifferential + -- (see KoszulHomHelpers.m2:767-791). Fixed upstream in v1.2. + else if #sourceList == 0 then newDiffl = map((A.ring)^(targetDegreeList), (A.ring)^0, 0) + else if #targetList == 0 then newDiffl = map((A.ring)^0, (A.ring)^(sourceDegreeList), 0) + else ( diffList := matrix {apply(sourceList, m -> polyDiffMonomial(A,m))}; coeffMatrix := substitute((coefficients(diffList, Monomials => targetList))#1, A.ring); newDiffl = map((A.ring)^(targetDegreeList), (A.ring)^(sourceDegreeList), coeffMatrix); @@ -376,7 +596,11 @@ polyDifferential (ZZ,DGAlgebra) := (n,A) -> ( ) ) -polyDifferential (DGAlgebra,RingElement) := (A,f) -> sum apply(terms f, m -> polyDiffMonomial(A,m)) +polyDifferential (DGAlgebra,RingElement) := (A,f) -> ( + ts := terms f; + if #ts == 0 then 0_(A.natural) + else sum apply(ts, m -> polyDiffMonomial(A,m)) +) diff (DGAlgebra,RingElement) := (A,f) -> polyDifferential(A,f); @@ -423,9 +647,19 @@ getDegNModule (ZZ,Ring,Ring) := (n,R,A) -> ( colDegList := degrees source tempMatrix; rowDegList := degrees target tempMatrix; myCols := select(toList (0..rank source tempMatrix - 1), i -> first colDegList#i == n); - myColDegree := apply(colDegList_myCols, h -> -drop(h,1)); + -- Pad or truncate internal-degree vectors to match degreeLength R so that + -- passing an ungraded R (e.g. the coefficient field from zerothHomology of + -- a Koszul DGA on a quotient ring) still works. + dlR := degreeLength R; + adjustDeg := h -> ( + d := -drop(h,1); + if #d == dlR then d + else if #d > dlR then take(d, dlR) + else d | toList((dlR - #d):0) + ); + myColDegree := apply(colDegList_myCols, adjustDeg); tempMatrix = tempMatrix_myCols; - myRowDegree = apply(rowDegList, h -> -drop(h,1)); + myRowDegree = apply(rowDegList, adjustDeg); tempMatrix = map(R^myRowDegree, R^myColDegree,substitute(tempMatrix,R)); prune coker tempMatrix ) @@ -510,17 +744,29 @@ deviationsToPoincare HashTable := opts -> devHash -> ( torAlgebra = method(TypicalValue=>Ring, Options=>{GenDegreeLimit=>infinity,RelDegreeLimit=>infinity}) torAlgebra Ring := opts -> R -> ( + -- NOTE: This single-argument version returns the *free* graded-commutative + -- algebra on the deviations of R. This is the true Tor algebra Tor^R(k,k) + -- only when R is a complete intersection (Tate/Gulliksen). For a general + -- local ring it only recovers the graded k-vector-space structure (Hilbert + -- function); multiplicative relations are NOT computed here. For the actual + -- multiplicative structure, use torAlgebra(R, R/ideal vars R). n := 3; if opts.GenDegreeLimit != infinity then n = opts.GenDegreeLimit; baseRing := coefficientRing R; + -- precompute a partial free resolution so that deviations/... have something + -- finite to chew on; without this, infinite resolutions hang. kRes := freeResolution(coker vars R, LengthLimit => n); X := getSymbol("X"); - -- now build the degreeList and skewList out of the output from deviations RDevs := deviations(R,DegreeLimit=>opts.GenDegreeLimit); degreesList := sort flatten apply(pairs RDevs, p -> toList ((p#1):(flatten{p#0#0,p#0#1}))); skewList := select(#degreesList, i -> odd first degreesList#i); torVars := toList(X_1..X_(#degreesList)); - baseRing[torVars,Degrees=>degreesList, SkewCommutative=>skewList] + polyRing := baseRing[torVars, Degrees => degreesList, SkewCommutative => skewList]; + -- set up the basisAlgebra cache so getBasis(n, T) works downstream. + polyRing' := baseRing[torVars, Degrees => (degreesList / first), SkewCommutative => skewList]; + polyRing.cache = new CacheTable; + polyRing.cache#(symbol basisAlgebra) = polyRing'; + polyRing ) torAlgebra (Ring,Ring) := opts -> (R,S) -> homologyAlgebra(acyclicClosure(R,EndDegree=>opts.GenDegreeLimit) ** S, opts) @@ -756,18 +1002,27 @@ homologyAlgebra DGAlgebra := opts -> A -> ( if A.cache.homologyAlgebra#?GenDegreeLimit and A.cache.homologyAlgebra#GenDegreeLimit >= opts.GenDegreeLimit and A.cache.homologyAlgebra#?RelDegreeLimit and A.cache.homologyAlgebra#RelDegreeLimit >= opts.RelDegreeLimit then HA = A.cache.homologyAlgebra#homologyAlgebra else ( maxDeg := maxDegree A; - + if maxDeg == infinity and (opts.GenDegreeLimit == infinity or opts.RelDegreeLimit == infinity) then return "Must supply upper degree bound on generators and relations if there is a DG algebra generator of even degree."; if opts.GenDegreeLimit != infinity then maxDeg = opts.GenDegreeLimit; - - n := maxDeg; - while n <= maxDeg and prune homology(n,A) == 0 do n = n - 1; - maxHomologyDegree := n + 1; - if opts.RelDegreeLimit != infinity then maxHomologyDegree = opts.RelDegreeLimit; cycleList := getGenerators(A,DegreeLimit=>maxDeg,Verbosity=>opts.Verbosity); + -- Default RelDegreeLimit: relations must be searched at least up to + -- 2 * maxGenDegree so that products of two top-degree generators are + -- reducible. The lastNonZeroH+1 heuristic alone is unsound because + -- generators in degree d can produce products in degree 2d that are + -- not in H_*(A) and must be killed by relations. + maxHomologyDegree := if opts.RelDegreeLimit != infinity then opts.RelDegreeLimit + else ( + n := maxDeg; + while n >= 0 and prune homology(n,A) == 0 do n = n - 1; + lastNonZero := n; + maxGenDeg := if cycleList == {} then 0 else max apply(cycleList, c -> first degree c); + max(lastNonZero + 1, 2 * maxGenDeg) + ); + if cycleList == {} then ( -- put the cycles that the variables represent in the cache. -- return H_0(A) as a ring. @@ -801,8 +1056,9 @@ homologyAlgebra DGAlgebra := opts -> A -> ( dgAlgebraMultMap = method() dgAlgebraMultMap (DGAlgebra,RingElement) := (A,z) -> ( R := A.ring; - d := first degree z; cxA := toComplex A; + if z == 0 then return map(cxA, cxA, i -> map(cxA_i, cxA_i, 0), Degree => 0); + d := first degree z; cxA2 := cxA ** R^(-(drop(degree z,1))); zChainMap := map(cxA,cxA2, i -> map(cxA_(i+d), cxA2_i, sub(last coefficients(z*getBasis(i,A),Monomials=>getBasis(i+d,A)), R)), @@ -820,50 +1076,62 @@ moduleRelationsFromCycleAction (DGAlgebra,RingElement,Module) := (A,z,M) -> ( if A.ring =!= R then error "Expected a DGAlgebra and module over same ring."; HA := HH(A); h := homologyClass(A,z); - -- may add ability to provide maxdeg as an optional argument for toComplex later - cxA := toComplex A; - - -- since z is a cycle, left multiplication by z is a chain map on A + d := first degree z; + + -- left multiplication by z, tensored with M zChainMap := dgAlgebraMultMap(A,z); - -- tensoring with M gives the action of z on A ** M zCMtensM := zChainMap ** M; - -- determine the action of the homology class of z at the level of homology - HM := HH(target zCMtensM); - -- prune the homology to ensure we have minimal generators - pruneHM := prune HM; - pruneZActionHM := prune HH(zCMtensM); - -- ll for Loewy length - -- ll := #(spots pruneHM); - ll := length pruneHM + 1; - - -- at this point we have all the multiplication tables, but we need to put them - -- together to get a module structure. Since HM is finite dimensional, this is not - -- too bad. The next part of the code combines all the actions into a large matrix - -- representing the full multiplication table by z and uses this to construct a list of relations - -- that represent the action of z, of the form ze_j = (z acting on e_j written in terms of basis). - - -- this next block of code constructs the full multiplication table - -- for z on HM. It constructs it as a block matrix, where the (i,j)th block - -- is the left multiplication map M_i --> M_j. Of course most of these are zero - -- since z should be a cycle in a single homological degree. - - -- these commands ensure that the resulting matrix will have the right degrees - -- over HA so that the result will be a graded module. - (lo,hi) := concentration pruneHM; - degsHMTarget := flatten apply(toList(lo..hi), p -> apply(degrees pruneHM_p, d -> {p} | d)); - degsHMSource := flatten apply(toList(lo..hi), p -> apply(degrees pruneHM_p, d -> ({p} | d) + degree z)); - - -- this function builds the blocks as mentioned above - buildBlocks := (i,j) -> ( - if j + first degree z != i then map(pruneHM_i,pruneHM_j,0) - --else if not pruneZActionHM#?j then map(pruneHM_i,pruneHM_j,0) - else pruneZActionHM_j - ); - -- use map to build the matrix (using the matrix constructor for block matrices) - matActOfZ := map(HA^(-degsHMTarget),HA^(-degsHMSource),tensor(map(HA,R),matrix table(ll,ll,buildBlocks))); - -- now subtract from this z times an appropriately graded identity to indicate - -- that this matrix is the result of z acting on HM. - relsFromZAction := map(HA^(-degsHMTarget),HA^(-degsHMSource),h) - matActOfZ; + KM := target zCMtensM; + + -- Pre-compute pruned homology and pruning-map matrices at each hom degree. + -- The pruning map (prune H_i).cache.pruningMap : (prune H_i) --> H_i + -- lets us translate raw chain-map homology into maps between pruned pieces. + (lo,hi) := concentration KM; + pruneHM := new MutableHashTable; + pruneMapMat := new MutableHashTable; + for i from lo to hi do ( + pruneHM#i = prune HH_i(KM); + pruneMapMat#i = matrix (pruneHM#i).cache.pruningMap; + ); + + -- Correctly-lifted action matrix on pruned homology, indexed by source hom degree. + -- actionOnPruned#i : (prune H_i) --> (prune H_{i+d}) + -- obtained by: + -- (prune H_i) --pruningMap--> H_i --HH_i(z)--> H_{i+d} --lift through pTgt--> (prune H_{i+d}) + -- This replaces the old (broken) use of (prune HH(zCMtensM))_i, which can + -- produce pruning maps inconsistent with those of the individual H_i's, + -- yielding action matrices that look like zero after tensoring with HA. + actionOnPruned := new MutableHashTable; + for i from lo to hi - d do ( + hhMat := matrix HH_i(zCMtensM); + composed := hhMat * pruneMapMat#i; + actionOnPruned#i = composed // pruneMapMat#(i+d); + ); + + -- Build the block matrix representing the action on the total graded module. + ll := hi - lo + 1; + -- Degrees (with internal part) so the HA-module presentation is graded. + degsHMTarget := flatten apply(toList(lo..hi), + p -> apply(degrees pruneHM#p, dd -> {p} | dd)); + degsHMSource := flatten apply(toList(lo..hi), + p -> apply(degrees pruneHM#p, dd -> ({p} | dd) + degree z)); + + -- block (i,j): action from pruneHM#j into pruneHM#i, nonzero only when i = j + d. + buildBlock := (iIdx, jIdx) -> ( + i := lo + iIdx; + j := lo + jIdx; + if j + d != i then map(pruneHM#i, pruneHM#j, 0) + else if not actionOnPruned#?j then map(pruneHM#i, pruneHM#j, 0) + else ( + -- actionOnPruned#j is a matrix; wrap it as a map between the pruned modules + aMat := actionOnPruned#j; + map(pruneHM#i, pruneHM#j, aMat) + ) + ); + matActOfZ := map(HA^(-degsHMTarget), HA^(-degsHMSource), + tensor(map(HA,R), matrix table(ll, ll, buildBlock))); + -- relations: h * e_j - (z acting on e_j) = 0 + relsFromZAction := map(HA^(-degsHMTarget), HA^(-degsHMSource), h) - matActOfZ; relsFromZAction ) @@ -891,20 +1159,116 @@ isGolodHomomorphism = method(TypicalValue=>Boolean,Options=>{GenDegreeLimit=>inf isGolodHomomorphism QuotientRing := opts -> R -> first findTrivialMasseyOperation(acyclicClosure(ambient R, EndDegree=>opts.GenDegreeLimit) ** R, opts) DGAlgebra ** Ring := (A,S) -> ( + -- Cache A ** S on A so repeated calls return the same DGAlgebra. + -- Essential for functoriality: if f : M -> N is a DGModuleMap and we + -- base-change, source(f ** S) and target(f ** S) must live in the + -- SAME (A ** S)-world, otherwise their naturals sit over distinct + -- rings and matrix assembly fails. + if A.cache#?"tensorWithRing" and (A.cache#"tensorWithRing")#?S then + return (A.cache#"tensorWithRing")#S; B := freeDGAlgebra(S, A.Degrees); newDiff := apply(flatten entries matrix (A.diff), f -> substitute(f,B.natural)); setDiff(B,newDiff); + if not A.cache#?"tensorWithRing" then + A.cache#"tensorWithRing" = new MutableHashTable; + (A.cache#"tensorWithRing")#S = B; B ) -DGAlgebra ** DGAlgebra := (A,B) -> ( - if A.ring =!= B.ring then error "DGAlgebras must be defined over the same ring."; - -- should I use a block ordering here since it is a tensor product? - C := freeDGAlgebra(A.ring, A.Degrees | B.Degrees); - newDiff := apply(take(flatten entries matrix (A.diff),numgens A.natural), f -> substitute(f,C.natural)); - newDiff = newDiff | apply(flatten entries matrix (B.diff), f -> substitute(f,C.natural)); - setDiff(C,newDiff); - C +-- DGAlgebra ** DGAlgebra: tensor product over the common ground ring A.ring. +-- The result C is a free DGA on the disjoint union of A.Degrees and B.Degrees, +-- graded-commutative automatically via SkewCommutative on odd-degree generators. +-- The differential on generators is d(a ⊗ 1) = d_A(a) ⊗ 1 and +-- d(1 ⊗ b) = 1 ⊗ d_B(b); no sign issues appear on generators (|1|=0), +-- and the Leibniz rule on products is handled by M2's graded-commutative +-- multiplication when we set differentials only on generators. +-- +-- Implementation note: we deliberately avoid `substitute(_, C.natural)` for +-- this pushforward because substitute matches variables by NAME. When A +-- and B share variable names (e.g. A === B), substitute silently maps +-- B's generators onto A's copy inside C and the tensor product comes +-- out wrong. Using explicit inclusion ring maps `iotaA, iotaB` fixes +-- this. +-- +-- On return, C.cache is annotated with: +-- tensorFactors : the ordered pair (A, B) +-- tensorInclusions : the natural inclusion DGAlgebraMaps (A->C, B->C) +-- so downstream tensor-product machinery can recover them. +DGAlgebra ** DGAlgebra := (A, B) -> ( + if A.ring =!= B.ring then + error "DGAlgebras must be defined over the same ring."; + -- Cache A ** B on A so that repeated calls return the same object + -- (important for UX: (f ** g) has source = A ** B and target = + -- A' ** B'; if A == A' and B == B' we want the source and target + -- to coincide, not be two fresh copies of the same algebra). + if A.cache#?"tensorWith" and (A.cache#"tensorWith")#?B then + return (A.cache#"tensorWith")#B; + nA := numgens A.natural; + nB := numgens B.natural; + C := freeDGAlgebra(A.ring, A.Degrees | B.Degrees); + Cgens := gens C.natural; + iotaA := map(C.natural, A.natural, take(Cgens, nA)); + iotaB := map(C.natural, B.natural, drop(Cgens, nA)); + -- Pushforward the differentials on the T-generators. We intentionally + -- iterate over `gens X.natural` rather than `matrix (X.diff)` because + -- the latter, for a tower ring, also includes images of the base-ring + -- generators (d acts as the identity on R, not as zero), which would + -- be wrong to thread into C as differentials of new generators. + dA := apply(gens A.natural, t -> iotaA (A.diff t)); + dB := apply(gens B.natural, t -> iotaB (B.diff t)); + setDiff(C, dA | dB); + -- Record factor data and natural inclusions (DGAlgebraMaps) on C.cache. + C.cache#(symbol tensorFactors) = (A, B); + incAmat := matrix {take(Cgens, nA)}; + incBmat := matrix {drop(Cgens, nA)}; + C.cache#(symbol tensorInclusions) = ( + dgAlgebraMap(C, A, incAmat), + dgAlgebraMap(C, B, incBmat) + ); + if not A.cache#?"tensorWith" then + A.cache#"tensorWith" = new MutableHashTable; + (A.cache#"tensorWith")#B = C; + C +) + +-- tensorFactors C: the ordered pair of DGA factors from which C was +-- built as A ** B. Errors if C was not built as a tensor product. +tensorFactors = method() +tensorFactors DGAlgebra := C -> ( + if not C.cache#?(symbol tensorFactors) then + error "tensorFactors DGAlgebra: this DGAlgebra was not built as a tensor product via `DGAlgebra ** DGAlgebra`."; + C.cache#(symbol tensorFactors) +) + +-- tensorInclusions C: the pair of natural inclusion DGAlgebraMaps +-- (A -> C, B -> C). Errors if C was not built as a tensor product. +tensorInclusions = method() +tensorInclusions DGAlgebra := C -> ( + if not C.cache#?(symbol tensorInclusions) then + error "tensorInclusions DGAlgebra: this DGAlgebra was not built as a tensor product via `DGAlgebra ** DGAlgebra`."; + C.cache#(symbol tensorInclusions) +) + +-- DGAlgebraMap ** DGAlgebraMap: given f : A -> A' and g : B -> B' of +-- DGAlgebras over the common ring R (assumed), build the tensor-product +-- DGAlgebraMap A**B -> A'**B' defined on generators by sending each +-- A-generator to f(·) (embedded in A'**B' via iotaA') and each +-- B-generator to g(·) (embedded via iotaB'). +DGAlgebraMap ** DGAlgebraMap := (f, g) -> ( + A := source f; Ap := target f; + B := source g; Bp := target g; + if A.ring =!= B.ring or Ap.ring =!= Bp.ring then + error "DGAlgebraMap **: factors must share a common ground ring on each side."; + C := A ** B; + Cp := Ap ** Bp; + (iotaAp, iotaBp) := tensorInclusions Cp; + -- Iterate over actual T-generators of A and B (avoiding the + -- base-ring prefix that `matrix f.natural` would include for + -- tower rings with Join => false), and lift via the natural + -- inclusions into Cp.natural. + liftedF := apply(gens A.natural, t -> (iotaAp.natural)(f.natural t)); + liftedG := apply(gens B.natural, t -> (iotaBp.natural)(g.natural t)); + dgAlgebraMap(Cp, C, matrix {liftedF | liftedG}) ) getBoundaryPreimage = method() @@ -946,7 +1310,16 @@ getBoundaryPreimage (DGAlgebra,List) := (A,boundaryList) -> ( getBoundaryPreimage (DGAlgebra,RingElement) := (A,b) -> ( (lifted,myLift) := getBoundaryPreimage(A,{b}); - (lifted,first myLift) + if lifted then return (lifted, first myLift); + -- Non-boundary: the list form returns a coefficient matrix (reduction + -- of the input mod the image of the differential). Convert the single + -- column back to an element of A.natural so both branches return a + -- RingElement in the second slot. + if b == 0 then return (false, 0_(A.natural)); + d := first degree b; + Anbasis := getBasis(d, A); + remElement := first flatten entries (Anbasis * substitute(myLift, A.ring)); + (false, remElement) ) findTrivialMasseyOperation = method(TypicalValue=>Sequence, Options=>{GenDegreeLimit=>infinity,TMOLimit=>infinity}) @@ -1149,11 +1522,57 @@ dgAlgebraMap (DGAlgebra,DGAlgebra,Matrix) := (B,A,fnMatrix) -> ( target DGAlgebraMap := f -> f.target source DGAlgebraMap := f -> f.source --- overload isWellDefined for DGAlgebraMap +-- isWellDefined DGAlgebraMap. Modeled on `isWellDefined ComplexMap` +-- (Complexes/ChainComplexMap.m2 line 134): key-shape check, type checks +-- on each slot, ring compatibility, and the semantic chain-map condition +-- on every DG generator. Diagnostic messages are emitted when +-- `debugLevel > 0`. isWellDefined DGAlgebraMap := f -> ( - A := source f; - B := target f; - all(apply(gens A.natural, x -> f.natural(A.diff(x)) == B.diff(f.natural(x))), identity) + k := keys f; + expectedKeys := set {symbol source, symbol target, symbol natural, symbol ringMap, symbol cache}; + -- `cache` is not always present on legacy DGAlgebraMap objects (net + -- and arithmetic constructors predate the cache slot); tolerate its + -- absence. + kExtra := k - expectedKeys; + kMissing := (expectedKeys - set {symbol cache}) - k; + if #kExtra > 0 or #kMissing > 0 then ( + if debugLevel > 0 then ( + if #kExtra > 0 then << "-- DGAlgebraMap: unexpected key(s): " << toString(toList kExtra) << endl; + if #kMissing > 0 then << "-- DGAlgebraMap: missing key(s): " << toString(toList kMissing) << endl; + ); + return false; + ); + if not instance(f.source, DGAlgebra) or not instance(f.target, DGAlgebra) then ( + if debugLevel > 0 then << "-- DGAlgebraMap: expected source and target to be DGAlgebras" << endl; + return false; + ); + if not isWellDefined f.source then ( + if debugLevel > 0 then << "-- DGAlgebraMap: source is not a well-defined DGAlgebra" << endl; + return false; + ); + if not isWellDefined f.target then ( + if debugLevel > 0 then << "-- DGAlgebraMap: target is not a well-defined DGAlgebra" << endl; + return false; + ); + if not instance(f.natural, RingMap) then ( + if debugLevel > 0 then << "-- DGAlgebraMap: expected f.natural to be a RingMap" << endl; + return false; + ); + if source f.natural =!= (f.source).natural or target f.natural =!= (f.target).natural then ( + if debugLevel > 0 then << "-- DGAlgebraMap: f.natural does not have the expected source/target rings" << endl; + return false; + ); + if not instance(f.ringMap, RingMap) then ( + if debugLevel > 0 then << "-- DGAlgebraMap: expected f.ringMap to be a RingMap" << endl; + return false; + ); + -- Semantic: chain-map condition on every DG generator. + A := f.source; + B := f.target; + ok := all(gens A.natural, x -> f.natural(A.diff(x)) == B.diff(f.natural(x))); + if not ok and debugLevel > 0 then + << "-- DGAlgebraMap: chain-map condition f(d_A x) = d_B(f x) fails on some generator" << endl; + ok ) toComplexMap = method(TypicalValue=>ComplexMap,Options=>{EndDegree=>-1,AssertWellDefined=>true}) @@ -1186,15 +1605,24 @@ toComplexMap (DGAlgebraMap,ZZ) := opts -> (f,n) -> ( -- The code below is if the base rings agree. aDiff := polyDifferential(n,A); bDiff := polyDifferential(n,B); + -- At hom-degrees beyond max degree of A or B, the basis is empty; in that + -- case short-circuit to the zero map between the (possibly-zero) target + -- and source modules so we don't call `coefficients` on an empty matrix. if R === S then ( sourceList = flatten entries getBasis(n,A); targetList = flatten entries getBasis(n,B); + if sourceList == {} or targetList == {} then + return map(source bDiff, source aDiff, 0); coeffMatrix = substitute((coefficients(matrix {sourceList / f.natural}, Monomials => targetList))#1, B.ring); map(source bDiff, source aDiff, coeffMatrix) ) else ( sourceList = flatten entries getBasis(n,A); targetList = flatten entries getBasis(n,B); + if sourceList == {} or targetList == {} then ( + sPushForward0 := pushForward(f.ringMap,source bDiff); + return map(sPushForward0, source aDiff, 0); + ); sCoeffMatrix := substitute((coefficients(matrix {sourceList / f.natural}, Monomials => targetList))#1, B.ring); -- the rest of this code converts the matrix over S to a matrix over R. It's slightly -- different than pushForward(RingMap,Matrix), since the map is between free modules over @@ -1257,6 +1685,8 @@ pushForward(RingMap,Complex) := opts -> (f,C) -> complex toList apply(0..((lengt homology DGAlgebraMap := opts -> f -> ( A := source f; B := target f; + if not isWellDefined f then + error "homology DGAlgebraMap: input is not a DG algebra map (does not commute with differentials); cannot induce a map on homology."; -- the following commands will fail if A and B have generators in even degree, since one -- needs to specify the degrees to look for gens and relations. HA := HH A; @@ -1344,6 +1774,31 @@ liftToDGMap (DGAlgebra,DGAlgebra,RingMap) := opts -> (B,A,f) -> ( dgAlgebraMap(B,A,matrix {imageList}) ) +----- DGAlgebraMap arithmetic / composition / equality / identity -------- +-- Ring-map arithmetic is not meaningful for ring maps as a whole (the +-- sum of two ring maps is not a ring map), so we intentionally skip +/- +-- and scalar multiplication. The operations below are the analogues of +-- the DGModuleMap ops that *do* make sense for ring-valued maps. + +-- Composition: (g * f) : A -> C for f : A -> B and g : B -> C +DGAlgebraMap * DGAlgebraMap := (g, f) -> ( + if (target f).natural =!= (source g).natural then + error "DGAlgebraMap composition: target of first does not match source of second."; + dgAlgebraMap(target g, source f, matrix (g.natural * f.natural)) +) + +-- Equality of DGAlgebraMaps: same source/target DGAlgebras and same +-- image matrix of A.natural generators in B.natural. +DGAlgebraMap == DGAlgebraMap := (f, g) -> ( + (source f) === (source g) + and (target f) === (target g) + and matrix f.natural == matrix g.natural +) + +-- Identity: sends each A.natural generator to itself. +identityDGAlgebraMap = method(TypicalValue => DGAlgebraMap) +identityDGAlgebraMap DGAlgebra := A -> dgAlgebraMap(A, A, vars A.natural) + torMap = method(TypicalValue => RingMap, Options => {GenDegreeLimit=>3}) torMap RingMap := opts -> f -> ( R := source f; @@ -1465,2331 +1920,3558 @@ displayBlockDiff(A, {1,0,3}) displayBlockDiff(A, [{1,0,3}]) /// --------------------- --- Documentation -- --------------------- +------------------------------------------------------------ +-- v1.2 overhaul: accessor / invariant layer -- +------------------------------------------------------------ +-- Rationale: the original DGAlgebra type is a bare +-- MutableHashTable with no encapsulation, forcing downstream +-- code (e.g. KoszulHomHelpers.m2) to reach into A#(symbol …) +-- and to defensively reinitialize caches. The accessors and +-- invariant helpers below let callers treat DGAlgebra as an +-- opaque value with guaranteed cache structure. + +-- ensureDGAlgebraCaches(A): guarantee all standard cache +-- sub-tables exist with the expected types. Idempotent; safe +-- to call on any DGAlgebra, even one in a partially-built +-- state (as happens when user code constructs a DGAlgebra by +-- hand from internal keys). +ensureDGAlgebraCaches = method(TypicalValue => DGAlgebra) +ensureDGAlgebraCaches DGAlgebra := A -> ( + if not A#?(symbol cache) + or A#(symbol cache) === null + or not instance(A#(symbol cache), CacheTable) + then A#(symbol cache) = new CacheTable; + if not A.cache#?(symbol homology) + or not instance(A.cache#(symbol homology), MutableHashTable) + then A.cache#(symbol homology) = new MutableHashTable; + if not A.cache#?(symbol homologyAlgebra) + or not instance(A.cache#(symbol homologyAlgebra), MutableHashTable) + then A.cache#(symbol homologyAlgebra) = new MutableHashTable; + if not A.cache#?(symbol diffs) + or not instance(A.cache#(symbol diffs), MutableHashTable) + then A.cache#(symbol diffs) = new MutableHashTable; + A +) -beginDocumentation() +-- invalidateDGAlgebraCache(A): clear all cached computed +-- values. Called at the top of setDiff / setKoszulDiff so +-- re-setting the differential never leaves stale homology or +-- differential matrices behind. +invalidateDGAlgebraCache = method(TypicalValue => DGAlgebra) +invalidateDGAlgebraCache DGAlgebra := A -> ( + ensureDGAlgebraCaches A; + A.cache#(symbol homology) = new MutableHashTable; + A.cache#(symbol homologyAlgebra) = new MutableHashTable; + A.cache#(symbol diffs) = new MutableHashTable; + if A.cache#?(symbol dgComplex) then remove(A.cache, symbol dgComplex); + A +) -doc /// - Key - DGAlgebras - Headline - Data types and basic functions on differential graded (DG) Algebras. - Description - Text - This package is used to define and manipulate DG algebras. - Contributors - Some documentation was added by Daniel Rostamloo and David Eisenbud. - Subnodes - "Basic operations on DG Algebras" - "The Koszul complex as a DG Algebra" - "Basic operations on DG Algebra Maps" -/// +-- Public accessors. +underlyingRing = method(TypicalValue => Ring) +underlyingRing DGAlgebra := A -> A.ring + +underlyingAlgebra = method(TypicalValue => Ring) +underlyingAlgebra DGAlgebra := A -> A.natural + +differential = method() +differential DGAlgebra := A -> A.diff + +generatorDegrees = method(TypicalValue => List) +generatorDegrees DGAlgebra := A -> A.Degrees + +ring DGAlgebra := A -> A.ring +generators DGAlgebra := opts -> A -> gens A.natural +numgens DGAlgebra := A -> numgens A.natural + +-- isValidDGAlgebra(A): structural invariant check. Returns +-- true if the object has the required keys with the expected +-- types. Does *not* verify d^2 = 0; for that use +-- isWellDefinedDifferential below. +isValidDGAlgebra = method(TypicalValue => Boolean) +isValidDGAlgebra DGAlgebra := A -> ( + if not A#?(symbol ring) or not instance(A#(symbol ring), Ring) then return false; + if not A#?(symbol natural) or not instance(A#(symbol natural), Ring) then return false; + if not A#?(symbol diff) then return false; + if A.diff =!= {} and not instance(A.diff, RingMap) then return false; + if not A#?(symbol Degrees) or not instance(A#(symbol Degrees), List) then return false; + if #A.Degrees != numgens A.natural then return false; + true +) -doc /// - Key - "Basic operations on DG Algebras" - Headline - Outlines some basic operations on DG Algebras - Description - Text - There are several ways to define a DGAlgebra. One can start by defining one 'from scratch'. One does - this by specifying the ring over which the DGAlgebra is defined and the degrees of the generators. The - name of the generators of the DGAlgebra by default is $T_i$, but one may change this by specifying the - optional (string) argument 'Variable'. - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} - A = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{1,1}}) - Text - The command freeDGAlgebra only defines the underlying algebra of A, and not the differential. To set the differential of A, - one uses the command setDiff. - Example - setDiff(A, gens R) - Text - Note that the above is the (graded) Koszul complex on a set of generators of R. A much easier way to define this is to use the - function koszulComplexDGA. - Example - B = koszulComplexDGA(R, Variable=>"S") - Text - One can compute the homology algebra of a DGAlgebra using the homology (or HH) command. - Example - HB = HH B - describe HB - degrees HB - Text - Note that since R is a complete intersection, its Koszul homology algebra is an exterior algebra, which is a - free graded commutative algebra. Note that the internal degree is preserved in the computation of the homology algebra - of B. - Text - One can also adjoin variables to kill cycles in homology. The command killCycles looks for the first positive degree - nonzero homology (say i), and adjoins variables in homological degree i+1 that differentiate to a minimal generating set of this homology, so that the - resulting DGAlgebra now only has homology in degree greater than i (note of course this could introduce new homology in higher degrees). - The command adjoinVariables allows finer control over this procedure. See @ TO adjoinVariables @ for an example. - Example - HB.cache.cycles - C = adjoinVariables(B,{first HB.cache.cycles}) - homologyAlgebra(C,GenDegreeLimit=>4,RelDegreeLimit=>4) - C = killCycles(B) - homologyAlgebra(C,GenDegreeLimit=>4,RelDegreeLimit=>4) - Text - Again, note that since R is a complete intersection, once we adjoin the variables in homological degree two to kill the cycles in degree one, - we obtain a minimal DG Algebra resolution of the residue field of R. Also, note that since C has generators in even degree, one must specify the - optional arguments GenDegreeLimit and RelDegreeLimit to specify the max degree of the computation. To do this, one uses the homologyAlgebra command - rather than the HH command. - Text - This computation could have also been done with the command acyclicClosure. The command acyclicClosure performs the command killCycles sequentially to ensure that the - result has homology in higher and higher degrees, thereby computing (part of) a minimal DG Algebra resolution of the residue field. acyclicClosure has an optional - argument EndDegree that allows the user to specify the maximum homological degree with which to perform this adjunction of variables. The default value of this is 3, since if there - are any variables of degree 3 that need to be added, then each subsequent homological degree will require some variables to be adjoined (Halperin's rigidity theorem). - Example - D = acyclicClosure R - R' = ZZ/101[x,y,z]/ideal{x^2,y^2,z^2,x*y*z} - E = acyclicClosure(R',EndDegree=>5) - tally degrees E.natural - Text - As you can see, since R' is not a complete intersection, the acyclic closure of E requires infinitely many variables; we display the degrees of the first 6 here. - The tally that is displayed gives the deviations of the ring R. One can compute the deviations directly from any minimal free resolution of the residue field - of R', so that using the one provided by res coker vars R is faster. To do this, use the command @ TO deviations @. - Example - deviations(R,DegreeLimit=>6) - deviations(R',DegreeLimit=>6) - Text - As a brief warning, the command @ TO poincareN @ which is used in @ TO deviations @ uses the symbols S and T internally, and may cause problems accessing such rings with the user interface. -/// +-- isWellDefined DGAlgebra. Modeled on `isWellDefined Complex` +-- (Complexes/ChainComplex.m2 line 161): key-shape + type checks +-- via isValidDGAlgebra, then the semantic d^2 = 0 check on every +-- DG generator. Diagnostics via debugLevel > 0. +isWellDefined DGAlgebra := A -> ( + -- Structural check (keys + types). + if not isValidDGAlgebra A then ( + if debugLevel > 0 then ( + missing := select( + {symbol ring, symbol natural, symbol diff, symbol Degrees}, + s -> not A#?s); + if #missing > 0 then + << "-- DGAlgebra: missing required key(s): " << toString missing << endl; + if A#?(symbol Degrees) and A#?(symbol natural) + and #A.Degrees != numgens A.natural then + << "-- DGAlgebra: #A.Degrees (" << #A.Degrees << ") != numgens A.natural (" + << numgens A.natural << ")" << endl; + if A#?(symbol diff) and A.diff =!= {} and not instance(A.diff, RingMap) then + << "-- DGAlgebra: A.diff is present but not a RingMap or {}" << endl; + ); + return false; + ); + -- Ring consistency: A.natural should be a polynomial-like ring over A.ring. + if coefficientRing A.natural =!= A.ring and not (ambient A.natural === A.ring) then ( + -- Allow the case where A.ring is a quotient or base ring of A.natural's coefficients. + -- Soft check: just warn in debug mode. + if debugLevel > 1 then + << "-- DGAlgebra: coefficientRing A.natural does not equal A.ring (this is usually OK)" << endl; + ); + -- Differential degree: d must shift homological degree by -1 on every generator. + if A.diff =!= {} then ( + for g in gens A.natural do ( + dg := diff(A, g); + if dg != 0 then ( + degG := first degree g; + degDG := first degree dg; + if degDG != degG - 1 then ( + if debugLevel > 0 then + << "-- DGAlgebra: d(" << g << ") has homological degree " + << degDG << ", expected " << (degG - 1) << endl; + return false; + ); + ); + ); + ); + -- Semantic: d^2 = 0 on every generator (Leibniz extends to all of A.natural). + if not isWellDefinedDifferential A then ( + if debugLevel > 0 then << "-- DGAlgebra: d^2 fails to vanish on some generator" << endl; + return false; + ); + true +) -doc /// - Key - "The Koszul complex as a DG Algebra" - Headline - an example - Description - Text - The Koszul complex on a sequence of elements $f_1,\dots,f_r$ is a complex of R-modules whose underlying graded R-module - is the exterior algebra on R^r generated in homological degree one. This algebra structure also respects the boundary map - of the complex in the sense that it satisfies the Liebniz rule. That is, $d(ab) = d(a)b + (-1)^{deg a}ad(b)$. When one - speaks of 'the' Koszul complex of a ring, one means the Koszul complex on a minimal set of generators of the maximal ideal of R. - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} - KR = koszulComplexDGA R - Text - One can specify the name of the variable to easily handle multiple Koszul complexes at once. - Example - S = ZZ/101[x,y,z]/ideal{x^3,y^3,z^3,x^2*y^2,y^2*z^2} - KS = koszulComplexDGA(S,Variable=>"U") - Text - To obtain the chain complex associated to the Koszul complex, one may use toComplex. One can also obtain this complex - directly without using the DGAlgebras package by using the command @ TO koszul @. - Example - cxKR = toComplex KR - prune HH cxKR - Text - Since the Koszul complex is a DG algebra, its homology is itself an algebra. One can obtain this algebra using the command - homology, homologyAlgebra, or HH (all commands work). This algebra structure can detect whether or not the ring is a complete - intersection or Gorenstein. - Example - HKR = HH KR - ideal HKR - R' = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3,a*c,a*d,b*c,b*d,a^2*b^2-c^2*d^2} - HKR' = HH koszulComplexDGA R' - numgens HKR' - ann ideal gens HKR' - Text - Note that since the socle of HKR' is one dimensional, HKR' has Poincare duality, and hence R' is Gorenstein. - Text - One can also consider the Koszul complex of an ideal, or a sequence of elements. - Example - Q = ambient R - I = ideal {a^3,b^3,c^3,d^3} - KI = koszulComplexDGA I - HKI = HH KI - describe HKI - use Q - I' = I + ideal{a^2*b^2*c^2*d^2} - KI' = koszulComplexDGA I' - HKI' = HH KI' - describe HKI' - HKI'.cache.cycles - Text - Note that since I is a Q-regular sequence, the Koszul complex is acyclic, and that both homology algebras are algebras over the zeroth homology - of the Koszul complex. -/// +-- isWellDefinedDifferential(A): semantic check that d^2 = 0 +-- on every DG generator. Sufficient because d is an R-linear +-- derivation, so d^2 = 0 on generators implies d^2 = 0 +-- globally. Returns true for the trivial (empty-generator) +-- DG algebra. +isWellDefinedDifferential = method(TypicalValue => Boolean) +isWellDefinedDifferential DGAlgebra := A -> ( + if not isValidDGAlgebra A then return false; + if A.diff === {} then return true; + gensList := gens A.natural; + all(gensList, g -> diff(A, diff(A, g)) == 0) +) -doc /// - Key - "Basic operations on DG Algebra Maps" - Headline - Outlines some basic operations on DGAlgebraMaps - Description - Text - An algebra map between the underlying graded algebras that satisfies the Leibniz rule is a morphism of DG algebras. Such objects - are created using the DGAlgebraMap class. As with DGAlgebras, one can define a DGAlgebraMap 'from scratch' using @ TO dgAlgebraMap @. - Example - R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} - K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") - K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") - f = dgAlgebraMap(K2,K1,matrix{{0,T_1,T_2}}) - Text - Once we define the DGAlgebraMap, it is a good idea to check to see if it indeed satisfies the Leibniz rule. This can be checked by using - isWellDefined. - Example - isWellDefined f - Text - Oops! Let's try that again. - Example - g = dgAlgebraMap(K1,K2,matrix{{Y_2,Y_3}}) - isWellDefined g - Text - One can lift a ring homomorphism in degree zero to a map of DGAlgebras (up to a specified degree) using liftToDGMap. This is helpful - in some of the internal functions of the DGAlgebras package, such as computing the map induced on Tor algebras by a RingMap. - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} - S = R/ideal{a^2*b^2*c^2} - f = map(S,R) - A = acyclicClosure(R,EndDegree=>3) - B = acyclicClosure(S,EndDegree=>3) - phi = liftToDGMap(B,A,f) - Text - Once one has a DGAlgebraMap, one can also obtain the underlying map of complexes via toComplexMap. - Example - cmPhi = toComplexMap(phi,EndDegree=>3) - Text - There are also some auxiliary commands associated with DGAlgebraMaps - Example - source phi - target phi - Text - One can also obtain the map on homology induced by a DGAlgebra map. - Example - HHg = HH g - matrix HHg -/// +------------------------------------------------------------ +-- v1.2 overhaul: ring-manipulation operations -- +------------------------------------------------------------ +-- These expose, as user-level operations, constructions that previously +-- required downstream code to rebuild the DGAlgebra from +-- internal hash keys. + +-- Private helper: transport an element f of A.natural to +-- B.natural, given phi : A.ring -> B.ring and a matrix +-- giving images of the DG generators T_i of A.natural as +-- elements of B.natural. Coefficients (which live in A.ring) +-- are mapped via phi; monomials in T_i are mapped via the +-- supplied image matrix. +transportNaturalElement := (phi, f, Bnat, genImages) -> ( + if f == 0 then return 0_Bnat; + (mons, coeffs) := coefficients f; + monList := flatten entries mons; + coeffList := flatten entries coeffs; + sum apply(#monList, i -> ( + c := sub(coeffList#i, source phi); + -- sub each monomial in T_i's into Bnat via genImages + mImage := sub(monList#i, genImages); + (phi c) * mImage + )) +) -doc /// - Key - DGAlgebra - Headline - The class of all DGAlgebras - Description - Text - Some common ways to create DGAlgebras include @ TO koszulComplexDGA @, @ TO freeDGAlgebra @, @ TO setDiff @, and @ TO acyclicClosure @. - SeeAlso - "Basic operations on DG Algebras" -/// +-- baseChange(A, phi): given a ring map phi : R -> S where +-- R = A.ring, produce a DGAlgebra B over S with "the same" +-- DG generators and differential (after applying phi to the +-- scalar coefficients appearing in the differential). +-- Accepts either a RingMap or a target Ring S (in which +-- case map(S, R) is used). +baseChange = method(TypicalValue => DGAlgebra, + Options => {InitializeDegreeZeroHomology => true, InitializeComplex => false}) +baseChange (DGAlgebra, Ring) := opts -> (A, S) -> baseChange(A, map(S, A.ring), opts) +baseChange (DGAlgebra, RingMap) := opts -> (A, phi) -> ( + if source phi =!= A.ring then + error "baseChange: source of ring map must equal underlyingRing of DGAlgebra"; + S := target phi; + B := freeDGAlgebra(S, A.Degrees); + n := numgens A.natural; + -- Images of the T_i of A.natural inside B.natural + genImages := matrix {take(gens B.natural, n)}; + origDiff := take(flatten entries matrix A.diff, n); + newDiffList := apply(origDiff, f -> transportNaturalElement(phi, f, B.natural, genImages)); + setDiff(B, newDiffList, + InitializeDegreeZeroHomology => opts.InitializeDegreeZeroHomology, + InitializeComplex => opts.InitializeComplex); + B +) -doc /// - Key - freeDGAlgebra - (freeDGAlgebra,Ring,List) - [freeDGAlgebra,Variable] - Headline - Constructs a DGAlgebra - Usage - A = freeDGAlgebra(R,degreeList) - Inputs - R:Ring - The ring over which the DGAlgebra is defined - degreeList:List - A list of degrees of the algebra generators of R. - Outputs - A:DGAlgebra - Description - Text - This function returns a @ TO DGAlgebra @ A whose underlying algebra is a graded commutative - polynomial ring in a number of variables equal to the number of the degrees input. The current version of this package - does not handle algebras A whose underlying algebra is not a polynomial ring. - Example - R = ZZ/101[x,y,z] - A = freeDGAlgebra(R,{{1},{1},{1},{3}}) - A.natural - setDiff(A,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) - Text - The resulting @ TO DGAlgebra @ will not be graded since the differential given does not respect the grading due to the degrees assigned in the definition. - Example - isHomogeneous(A) - Add = toComplex A - B = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{3,3}}) - B.natural - setDiff(B,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) - Text - The result of the above declaration will be graded. - Example - isHomogeneous(B) - Bdd = toComplex B - Text - Note that the differential is not passed into the constructor. The reason for this (at the moment) - is that Macaulay2 does not know what ring the differentials are defined over until after the underlying - algebra is constructed, so the differential is set later with setDiff. Many DG algebras that one - encounters in commutative algebra have been implemented, however, and do not need to be defined 'by hand'. - For example, if one wants to work with the Koszul complex as a DG algebra, then one should see the command @ TO koszulComplexDGA @. - Also, if one wishes to specify the name of the variables used, specify the Variable option; see the example in @ TO dgAlgebraMap @. - Caveat - There is currently a bug handling DG algebras that have no monomials in some degree, but some monomials in a later degree; - for example if one replaces the 3 in the above example with a 5. -/// +-- subDGAlgebra(A, keepIdx): build a new DGAlgebra over the +-- same underlying ring whose DG generators are A's generators +-- at the indices in keepIdx (a List of integers in the range +-- 0..numgens A.natural-1), with the restricted differential. +-- Raises an error if the differential of any kept generator +-- involves a dropped generator (i.e. the subset of indices +-- does not actually span a sub-DG algebra). +subDGAlgebra = method(TypicalValue => DGAlgebra, + Options => {InitializeDegreeZeroHomology => true, InitializeComplex => false}) +subDGAlgebra (DGAlgebra, List) := opts -> (A, keepIdx) -> ( + n := numgens A.natural; + if not all(keepIdx, i -> instance(i, ZZ) and 0 <= i and i < n) then + error "subDGAlgebra: keepIdx must be a List of integers in 0 .. numgens-1"; + if keepIdx != unique keepIdx then + error "subDGAlgebra: keepIdx contains duplicates"; + keepIdx = sort keepIdx; + droppedIdx := select(toList(0..n-1), i -> not member(i, keepIdx)); + newDegList := A.Degrees_keepIdx; + B := freeDGAlgebra(A.ring, newDegList); + -- Build a substitution matrix: each kept T_i -> corresponding gen of B.natural; + -- each dropped T_i -> 0 in B.natural. + subList := for i from 0 to n-1 list ( + j := position(keepIdx, k -> k == i); + if j === null then 0_(B.natural) + else (gens B.natural)#j + ); + imageMatrix := matrix {subList}; + origDiff := take(flatten entries matrix A.diff, n); + -- Safety check: a kept generator's differential must not involve a dropped generator. + droppedGens := (gens A.natural)_droppedIdx; + for i in keepIdx do ( + f := origDiff#i; + suppf := set support f; + if any(droppedGens, g -> member(g, suppf)) then + error ("subDGAlgebra: differential of generator " | toString (gens A.natural)#i + | " involves dropped generator(s) " | toString select(droppedGens, g -> member(g, suppf)) + | "; the subset does not form a sub-DG algebra"); + ); + newDiffList := apply(keepIdx, i -> sub(origDiff#i, imageMatrix)); + setDiff(B, newDiffList, + InitializeDegreeZeroHomology => opts.InitializeDegreeZeroHomology, + InitializeComplex => opts.InitializeComplex); + B +) -doc /// - Key - koszulComplexDGA - (koszulComplexDGA,Ring) - [koszulComplexDGA,Variable] - Headline - Returns the Koszul complex as a DGAlgebra - Usage - A = koszulComplexDGA(R) - Inputs - R:Ring - Returns the Koszul complex on ideal vars R. - Outputs - A:DGAlgebra - Description - Text - To construct the Koszul complex of a minimal set of generators as a @ TO DGAlgebra @ one uses - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} - A = koszulComplexDGA(R) - complexA = toComplex A - complexA.dd - ranks = apply(4, i -> numgens prune HH_i(complexA)) - ranks == apply(4, i -> numgens prune HH_i(koszul vars R)) - Text - One can also compute the homology of A directly with @ TO (homology,ZZ,DGAlgebra) @. One may also specify - the name of the variable using the Variable option. -/// +-- restrictDifferential: alias that takes a list of indices +-- to KEEP (same semantics as subDGAlgebra). Named to match +-- the vocabulary used in KoszulHomHelpers.m2. +restrictDifferential = method(TypicalValue => DGAlgebra, + Options => {InitializeDegreeZeroHomology => true, InitializeComplex => false}) +restrictDifferential (DGAlgebra, List) := opts -> (A, keepIdx) -> subDGAlgebra(A, keepIdx, opts) + +-- truncateGenerators(A, n): return the sub-DG algebra whose +-- DG generators are those of A with first homological +-- degree > n. Replaces the hand-rolled truncateTerm pattern +-- in KoszulHomHelpers.m2:707. +truncateGenerators = method(TypicalValue => DGAlgebra, + Options => {InitializeDegreeZeroHomology => true, InitializeComplex => false}) +truncateGenerators (DGAlgebra, ZZ) := opts -> (A, n) -> ( + keepIdx := select(toList(0..#A.Degrees-1), i -> first A.Degrees#i > n); + subDGAlgebra(A, keepIdx, opts) +) -doc /// - Key - (koszulComplexDGA,Ideal) - Headline - Returns the Koszul complex as a DGAlgebra - Usage - A = koszulComplexDGA(I) - Inputs - I:Ideal - An ideal of a ring R - Outputs - A:DGAlgebra - Description - Text - To construct the Koszul complex on the set of generators of I as a @ TO DGAlgebra @ one uses - Example - R = ZZ/101[a,b,c] - I = ideal{a^3,b^3,c^3,a^2*b^2*c^2} - A = koszulComplexDGA(I) - complexA = toComplex A - complexA.dd - ranks = apply(4, i -> numgens prune HH_i(complexA)) - ranks == apply(4, i -> numgens prune HH_i(koszul gens I)) - Text - One can also compute the homology of A directly with @ TO (homology,ZZ,DGAlgebra) @. -/// +-- killHomologyAtDegree(A, n): find a representing set of +-- cycles for H_n(A) and adjoin variables to make them +-- boundaries, returning a new DGAlgebra. Unlike killCycles +-- (which searches for the first nontrivial degree starting +-- from StartDegree), this targets a specific degree. +killHomologyAtDegree = method(TypicalValue => DGAlgebra) +killHomologyAtDegree (DGAlgebra, ZZ) := (A, n) -> ( + Hn := prune homology(n, A); + if Hn == 0 then return A; + homologyGens := entries transpose gens image (Hn.cache.pruningMap); + basisList := flatten entries getBasis(n, A); + cycleList := apply(homologyGens, gen -> sum apply(#gen, i -> gen#i * basisList#i)); + adjoinVariables(A, cycleList) +) -doc /// - Key - (koszulComplexDGA,List) - Headline - Define the Koszul complex on a list of elements as a DGAlgebra - Usage - A = koszulComplexDGA(diffList) - Inputs - diffList:List - A List of RingElements. The resulting DGAlgebra will be defined over the ring of these elements. - Outputs - A:DGAlgebra -/// +-- dgComplex(A): lazy chain-complex accessor. Computes and caches +-- a Complex representing A's underlying chain complex of free +-- A.ring-modules. Unlike setDiff's InitializeComplex option +-- (which eagerly builds A.dd), this pays only when the caller +-- actually wants the complex, and reuses it on repeat calls. +-- Invalidated by invalidateDGAlgebraCache. +dgComplex = method(TypicalValue => Complex) +dgComplex DGAlgebra := A -> ( + ensureDGAlgebraCaches A; + if A.cache#?(symbol dgComplex) then return A.cache#(symbol dgComplex); + n := maxDegree A; + if n === infinity then ( + -- even generators present; default to totalOddDegree+1 (matches setDiff's choice) + n = sum select(degrees A.natural / first, i -> odd i) + 1; + ); + C := toComplex(A, max(n, 1)); + A.cache#(symbol dgComplex) = C; + C +) -doc /// - Key - (homology,ZZ,DGAlgebra) - Headline - Computes the homology of a DG algebra as a module - Usage - H = homology(n,A) - Inputs - n:ZZ - A:DGAlgebra - Outputs - H:Module - The nth homology of A. - Description - Example - R = ZZ/32003[x,y,z] - A = koszulComplexDGA(R) - apply(numgens R+1, i -> numgens prune homology(i,A)) -/// +------------------------------------------------------------------------- +-- DGModule: DG modules over a DGAlgebra. +-- +-- A DGModule M over a DGAlgebra A is a graded A.natural-module M.natural +-- together with an R-linear differential of homological degree -1 +-- satisfying the Leibniz rule +-- d_M(a . x) = d_A(a) . x + (-1)^{|a|} a . d_M(x). +-- Storage follows the DGAlgebra pattern (see the type declaration). +-- +-- The first concrete constructor is koszulComplexDGM, producing +-- K^R \otimes_R M as a DG module over koszulComplexDGA R. +------------------------------------------------------------------------- + +-- Pretty-print a DGModule. Shows base ring, DG algebra, the natural +-- graded module, and the generator hom-degrees. (Mirrors `net DGAlgebra`.) +net DGModule := M -> ( + myOutput := {net "Base ring => " | net M.ring}; + myOutput = myOutput | {net "DG algebra => " | net M.dgAlgebra.natural}; + myOutput = myOutput | {net "Natural module => " | net M.natural}; + myOutput = myOutput | {net "Generator degrees => " | net M.Degrees}; + myOutput = myOutput | {net "Differentials on gens => " | net M.diff}; + horizontalJoin flatten ("{", stack myOutput, "}") +) -doc /// - Key - setDiff - (setDiff,DGAlgebra,List) - InitializeComplex - [setDiff,InitializeComplex] - InitializeDegreeZeroHomology - [setDiff,InitializeDegreeZeroHomology] - Headline - Sets the differential of a DGAlgebra manually. - Usage - d = setDiff(A,diffList) - Inputs - A:DGAlgebra - A:List - Outputs - A:DGAlgebra - The DGAlgebra with the differential now set. - Description - Example - R = ZZ/101[x,y,z] - A = freeDGAlgebra(R,{{1},{1},{1},{3}}) - setDiff(A,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) - Add = toComplex A - Add.dd - Text - There are two options that are available for this function, and both are designed to bypass certain initializations - that take place by default. - Text - The option InitializeComplex specifies whether or not to compute all differentials of - the complex(up to the sum of the degrees of the odd degree generators) before returning from setDiff. This is useful if - your DGAlgebra has a large number of generators in odd degrees, and you are only interested in computing the homology - in low degrees. The default value of this option is true. - Text - The option InitializeDegreeZeroHomology specifies whether or not to define the quotient ring H_0(A). This is used when - computing HH(A) as a DGAlgebra. This involves computing a Grobner basis of the image of the first differential of A, - and as such, may want to be avoided if there are a large number of DGAlgebra generators in degree 1. The default value of - this options is true. -/// +-- Accessor methods: extend the v1.2 accessor layer to DGModules. +underlyingRing DGModule := M -> M.ring +underlyingAlgebra DGModule := M -> M.natural +generatorDegrees DGModule := M -> M.Degrees +differential DGModule := M -> M.diff + +-- koszulComplexDGM(M): build K^R \otimes_R M as a DG module over +-- koszulComplexDGA R. The module generators sit in hom-degree 0 +-- (so d_M vanishes on them) and the full differential is determined by +-- Leibniz + d on A.natural. +-- +-- Two forms: +-- koszulComplexDGM M -- builds (or reuses) koszulComplexDGA(ring M) +-- koszulComplexDGM(A, M) -- use a caller-supplied DG algebra whose +-- ring must match ring M +koszulComplexDGM = method(TypicalValue => DGModule) +koszulComplexDGM Module := M -> ( + R := ring M; + A := koszulComplexDGA R; + koszulComplexDGM(A, M) +) +koszulComplexDGM (DGAlgebra, Module) := (A, M) -> ( + R := ring M; + if A.ring =!= R then + error "koszulComplexDGM: DG algebra and module must share the same base ring."; + -- Lift the R-presentation of M to an A.natural-presentation, then take + -- the cokernel. Relations sit in hom-degree 0, so d(rel) = 0 and the + -- differential descends to the quotient. + presM := presentation M; + numGens := numRows presM; + presOverA := sub(presM, A.natural); + -- Degree padding: A.natural has one extra degree slot (hom-degree) + -- relative to R, so prepend a 0 to each target-row degree of presM. + rDegs := if numGens == 0 then {} else (degrees target presM); + weightPad := if #rDegs == 0 then ( + if isHomogeneous A.natural and #degrees A.natural > 0 + then #(first degrees A.natural) else 1 + ) else (1 + #(first rDegs)); + genDegs := if numGens == 0 then {} + else apply(rDegs, d -> {0} | d); + -- Free graded A.natural-module with generators in hom-degree 0. + F := (A.natural)^(-genDegs); + naturalModule := coker map(F, , presOverA); + result := new MutableHashTable; + result#(symbol dgAlgebra) = A; + result#(symbol ring) = R; + result#(symbol module) = M; + result#(symbol natural) = naturalModule; + result#(symbol Degrees) = genDegs; + -- d vanishes on hom-degree-0 generators (would land in hom-degree -1 = 0). + result#(symbol diff) = apply(numGens, i -> 0_(naturalModule)); + result#(symbol cache) = new CacheTable; + result.cache#(symbol diffs) = new MutableHashTable; + new DGModule from result +) -doc /// - Key - (isHomogeneous, DGAlgebra) - Headline - Determine if the DGAlgebra respects the gradings of the ring it is defined over. - Usage - isHom = isHomogeneous(A) - Inputs - A:DGAlgebra - Outputs - isHom:Boolean - Whether or not the DGA respects the grading - Description - Example - R = ZZ/101[x,y,z] - A = freeDGAlgebra(R,{{1},{1},{1},{3}}) - setDiff(A,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) - isHomogeneous A - B = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{3,3}}) - setDiff(B,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) - isHomogeneous B -/// +-- moduleBasisPairs(M, n): list of pairs (i, m) describing an A.ring-basis +-- of the hom-degree-n piece of a free DG module M. Here i indexes a free +-- generator e_i of M.natural (hom-degree d_i) and m is an A.natural +-- monomial of hom-degree n - d_i; the corresponding basis element is +-- m * e_i in M.natural. Used by moduleDifferential's general path. +moduleBasisPairs = (M, n) -> ( + A := M.dgAlgebra; + numGens := rank M.natural; + flatten apply(numGens, i -> ( + di := first M.Degrees#i; + needed := n - di; + if needed < 0 then {} + else ( + mons := flatten entries getBasis(needed, A); + apply(mons, m -> (i, m)) + ) + )) +) -doc /// - Key - natural - Headline - The underlying algebra of a DGAlgebra. - Usage - Anat = A.natural - Description - Example - R = ZZ/101[a,b,c,d] - A = koszulComplexDGA(R) - A.natural -/// +-- moduleLeibniz(M, i, m): the Leibniz differential of the basis element +-- m * e_i in a free DG module. Returns a Vector in M.natural. +-- d(m . e_i) = d_A(m) . e_i + (-1)^|m| m . d_M(e_i) +-- where |m| is the hom-degree of m in A. +moduleLeibniz = (M, i, m) -> ( + A := M.dgAlgebra; + ei := (M.natural)_i; + term1 := polyDifferential(A, m) * ei; + signExp := first degree m; + sign := if odd signExp then -1 else 1; + term2 := sign * m * M.diff#i; + term1 + term2 +) -doc /// - Key - cycles - Headline - Cycles chosen when computing the homology algebra of a DGAlgebra - Usage - A.cycles - Description - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^4,c^5,d^6} - A = koszulComplexDGA(R) - apply(maxDegree A + 1, i -> numgens prune homology(i,A)) - HA = homologyAlgebra(A) - numgens HA - HA.cache.cycles - Text -/// +-- moduleDifferential(n, M): the degree-n piece of the DG module's +-- differential, returned as an R-linear map M_n -> M_{n-1}. +-- +-- Two code paths: +-- * Fast path: when M was built by koszulComplexDGM (M.module is set) +-- AND d_M vanishes on every generator, the differential is the +-- tensor d_A \otimes id_M, computed as polyDifferential(n,A) ** M.module. +-- * General Leibniz path: for free DG modules (freeDGModule output) with +-- arbitrary differentials on generators. Works monomial-by-monomial +-- using moduleLeibniz and extracts the R-linear matrix per target +-- generator via `coefficients`. +-- +-- Caching: matches polyDifferential — one slot per n in M.cache.diffs. +moduleDifferential = method(TypicalValue => Matrix) +moduleDifferential (ZZ, DGModule) := (n, M) -> ( + if M.cache.diffs#?n then return M.cache.diffs#n; + A := M.dgAlgebra; + R := A.ring; + -- Fast path for koszulComplexDGM-style DG modules: d_A ⊗ id_M. + if M.?module and all(M.diff, z -> z == 0) then ( + dA := polyDifferential(n, A); + fastResult := dA ** M.module; + M.cache.diffs#n = fastResult; + return fastResult; + ); + -- General Leibniz path. Requires M.natural to be a free graded + -- A.natural-module (freeDGModule output). Bail for non-free M.natural + -- since `entries` on a Vector in a cokernel is not the right semantics. + if not isFreeModule M.natural then + error "moduleDifferential: general path requires M.natural to be a free module (use freeDGModule)."; + if n < 0 then return map(R^0, R^0, 0); + sourcePairs := moduleBasisPairs(M, n); + targetPairs := moduleBasisPairs(M, n-1); + numSrc := #sourcePairs; + numTgt := #targetPairs; + -- R-module degrees (drop hom-degree slot) for source/target + pairDegree := p -> ( + (i, m) := p; + totalDeg := degree(m * (M.natural)_i); + -drop(totalDeg, 1) + ); + srcDegs := apply(sourcePairs, pairDegree); + tgtDegs := apply(targetPairs, pairDegree); + if numSrc == 0 then ( + r0 := map(R^tgtDegs, R^0, 0); + M.cache.diffs#n = r0; + return r0; + ); + if numTgt == 0 then ( + r1 := map(R^0, R^srcDegs, 0); + M.cache.diffs#n = r1; + return r1; + ); + numNatGens := rank M.natural; + -- Group target pairs by generator index j; keep original column index. + tgtByGen := new MutableHashTable; + scan(numNatGens, j -> tgtByGen#j = {}); + scan(numTgt, k -> ( + (j, mp) := targetPairs#k; + tgtByGen#j = append(tgtByGen#j, (k, mp)); + )); + mat := mutableMatrix(R, numTgt, numSrc); + scan(numSrc, c -> ( + (i, m) := sourcePairs#c; + db := moduleLeibniz(M, i, m); + dbList := entries db; + scan(numNatGens, j -> ( + fj := dbList#j; + if fj == 0 then return; + rowsForJ := tgtByGen#j; + if #rowsForJ == 0 then return; + tgtMonsJ := apply(rowsForJ, pr -> pr#1); + coefMat := (coefficients(matrix{{fj}}, Monomials => matrix{tgtMonsJ}))#1; + coefMatR := sub(coefMat, R); + scan(#rowsForJ, idx -> ( + rowIdx := (rowsForJ#idx)#0; + cij := coefMatR_(idx, 0); + if cij != 0 then mat_(rowIdx, c) = cij; + )); + )); + )); + result := map(R^tgtDegs, R^srcDegs, matrix mat); + M.cache.diffs#n = result; + result +) -doc /// - Key - getBasis - (getBasis,ZZ,DGAlgebra) - Headline - Get a basis for a particular homological degree of a DG algebra. - Usage - M = getBasis(n,A) - Inputs - n:ZZ - A:DGAlgebra - Outputs - M:Matrix - The basis of the desired homological degree of the DG Algebra. - Description - Text - This function is to allow for the retrieval of a basis of a particular homological degree of a @ TO DGAlgebra @ - when the underlying algebra A.natural is multigraded. In the code, the homological grading is always the first - integer in the degree tuple, and so this function returns a matrix consisting of all monomials in homological - degree n. - Example - R = ZZ/101[a..d, Degrees=>{1,1,1,2}] - A = koszulComplexDGA(R) - getBasis(3,A) -/// +-- homology(n, M): H_n(M) as an R-module. +homology (ZZ, DGModule) := opts -> (n, M) -> ( + dn := moduleDifferential(n, M); + dnplus1 := moduleDifferential(n+1, M); + homology(dn, dnplus1) +) -doc /// - Key - (getBasis,ZZ,Ring) - Headline - Get a basis for a degree of a ring. - Usage - M = getBasis(n,R) - Inputs - n:ZZ - R:Ring - Outputs - M:Matrix - The basis of the desired degree - Description - Text - This function was not meant for general use, but it fixes the first degree in the degree tuple - of the ring R, and finds a basis of that 'slice' of the ring. It does this by using a cached - version of the ring that forgets all other degrees. A Ring object in Macaulay2 will not have this - cached ring by default, but the rings used internally in the DGAlgebras package will. -/// +------------------------------------------------------------------------- +-- freeDGModule: semifree DG modules over a DGAlgebra. +-- +-- A free DG module over A is a graded A.natural-module F = A.natural^(-degList) +-- with an A.ring-linear differential of hom-degree -1 satisfying Leibniz: +-- d(a . x) = d_A(a) . x + (-1)^|a| a . d_M(x) +-- Storage mirrors DGAlgebra's freeDGAlgebra (M.natural is a free module +-- rather than a cokernel, so Leibniz on monomials is well-defined). The +-- differential is initialized to 0 on every generator; set it via +-- setDiff(M, diffList). +------------------------------------------------------------------------- +freeDGModule = method(TypicalValue => DGModule) +freeDGModule (DGAlgebra, List) := (A, degList) -> ( + -- Normalize: each entry of degList should be a list compatible with + -- A.natural's degree group. Scalar ZZ is wrapped as {n}; too-short + -- lists are right-padded with zeros. + degList = apply(degList, d -> if instance(d, ZZ) then {d} else toList d); + if #degList > 0 and #(degrees A.natural) > 0 then ( + aDegLen := #(first degrees A.natural); + degList = apply(degList, d -> if #d < aDegLen then d | toList((aDegLen - #d):0) else d); + ); + F := if #degList == 0 then (A.natural)^0 else (A.natural)^(-degList); + result := new MutableHashTable; + result#(symbol dgAlgebra) = A; + result#(symbol ring) = A.ring; + result#(symbol natural) = F; + result#(symbol Degrees) = degList; + result#(symbol diff) = apply(#degList, i -> 0_F); + result#(symbol cache) = new CacheTable; + result.cache#(symbol diffs) = new MutableHashTable; + new DGModule from result +) -doc /// - Key - toComplex - (toComplex,DGAlgebra) - Headline - Converts a DGAlgebra to a Complex - Usage - C = toComplex A or C = toComplex(A,n) - Inputs - A:DGAlgebra - Outputs - C:Complex - The DG algebra A as a Complex - Description - Example - R = ZZ/101[x_1..x_10] - A = koszulComplexDGA(R) - C = toComplex A - Text - Warning: The term order that the internal command koszul uses to order the monomials is not GRevLex, and so the differentials - used in koszul and koszulComplexDGA will not match up exactly. Also, this command will only execute if all of the variables - of the @ TO DGAlgebra @ A are of odd homological degree. Otherwise, you need to use the function @ TO (toComplex, DGAlgebra, ZZ) @. -/// +-- setDiff(M, diffList): record the differential of each free generator. +-- diffList#i is a Vector in M.natural (or an element coercible to one) +-- living in hom-degree (d_i - 1) where d_i is the hom-degree of the i-th +-- generator. Invalidates all cached differentials. +setDiff (DGModule, List) := opts -> (M, diffList) -> ( + if not isFreeModule M.natural then + error "setDiff DGModule: currently only supported for free DG modules (freeDGModule output)."; + if #diffList != #M.Degrees then + error ("setDiff DGModule: expected " | toString(#M.Degrees) + | " differentials, got " | toString(#diffList) | "."); + ensureDGAlgebraCaches M; + invalidateDGAlgebraCache M; + -- Coerce each entry to a Vector in M.natural. Accept Vector as-is; + -- accept a 1-column Matrix by taking its first column; accept 0. + coerced := apply(diffList, e -> ( + if e === 0 then 0_(M.natural) + else if instance(e, Vector) then e + else if instance(e, Matrix) and numcols e == 1 then ( + -- fold the column into a vector in M.natural + rowDegs := degrees target e; + if rank M.natural != #rowDegs then + error "setDiff DGModule: matrix differential has wrong number of rows."; + sum apply(#rowDegs, j -> e_(j,0) * (M.natural)_j) + ) + else try (sub(e, M.natural)) else + error "setDiff DGModule: could not coerce differential entry to M.natural." + )); + M.diff = coerced; + M +) +-- setDiff returns the mutated DGModule for a DGModule input, even though +-- the method's default TypicalValue is DGAlgebra. Inform the documentation +-- system so per-signature doc nodes for (setDiff, DGModule, List) see the +-- right return type. +typicalValues#(setDiff, DGModule, List) = DGModule + +------------------------------------------------------------------------- +-- adjoinGenerators / killCycles / semifreeResolution for DGModule. +-- Module-theoretic analogs of adjoinVariables / killCycles / acyclicClosure +-- on DGAlgebra. +------------------------------------------------------------------------- + +-- adjoinGenerators(M, cycleList): produce a new free DG module M' that +-- agrees with M on its existing generators and adds one fresh generator +-- per element of cycleList, whose differential is the corresponding cycle. +-- Each cycle must be a Vector in M.natural of hom-degree d; the new +-- generator has hom-degree d+1. +-- Existing generator indices and differentials are preserved verbatim. +adjoinGenerators = method(TypicalValue => DGModule) +adjoinGenerators (DGModule, List) := (M, cycleList) -> ( + A := M.dgAlgebra; + if #cycleList == 0 then return M; + -- Derive new generator degrees: hom-degree = deg(z)#0 + 1; remaining + -- degree components copied from z (so the new generator's differential + -- is homogeneous w.r.t. the ambient multi-grading). + newDegs := apply(cycleList, z -> ( + d := degree z; + {first d + 1} | drop(d, 1) + )); + combinedDegs := M.Degrees | newDegs; + -- Build a fresh free DG module with the combined generator list. + Mnew := freeDGModule(A, combinedDegs); + -- Lift each existing differential from M.natural to Mnew.natural. + -- M.natural has rank r = #M.Degrees; Mnew.natural has rank r + #newDegs. + -- The first r generators of Mnew correspond to those of M. + r := #M.Degrees; + phi := v -> ( + if v === 0 then 0_(Mnew.natural) + else ( + vEntries := entries v; + sum apply(r, j -> (vEntries#j) * (Mnew.natural)_j) + ) + ); + oldDiffs := apply(M.diff, phi); + newDiffs := apply(cycleList, phi); + setDiff(Mnew, oldDiffs | newDiffs); + -- Cache migration: if all new gens sit at hom-degree >= minNewDeg, then + -- F_i(Mnew) = F_i(M) for all i < minNewDeg (new gens don't contribute + -- below their own hom-degree, assuming A is nonnegatively graded in + -- hom-deg). Reuse the already-computed d_i for those i. Also, since F_i + -- and F_{i-1} are literally equal, any downstream code that composes + -- the cached matrix still works. + -- setDiff above invalidated Mnew's cache, so do this after. + if M.cache.?diffs then ( + minNewDeg := min apply(newDegs, d -> first d); + scan(keys M.cache.diffs, n -> ( + if n < minNewDeg then Mnew.cache.diffs#n = M.cache.diffs#n; + )); + ); + Mnew +) -doc /// - Key - (toComplex,DGAlgebra,ZZ) - Headline - Converts a DGAlgebra to a Complex - Usage - C = toComplex A or C = toComplex(A,n) - Inputs - A:DGAlgebra - n:ZZ - Outputs - C:Complex - The DG algebra A as a Complex - Description - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} - A = acyclicClosure(R,EndDegree=>3) - Text - The above will be a resolution of the residue field over R, since R is a complete intersection. - Example - C = toComplex(A, 10) - apply(10, i -> prune HH_i(C)) -/// +-- killCycles M: find the smallest n (within the requested range) for which +-- H_n(M) is nonzero, and adjoin free generators in hom-degree n+1 whose +-- differentials are representative cycles of a minimal generating set of +-- H_n(M). Mirrors killCycles DGAlgebra. +killCycles DGModule := opts -> M -> ( + endDegree := if opts.EndDegree == -1 then opts.StartDegree else opts.EndDegree; + if opts.StartDegree > endDegree then + error "killCycles DGModule: StartDegree exceeds EndDegree."; + n := opts.StartDegree; + foundHomology := false; + nthHomology := null; + while n <= endDegree and not foundHomology do ( + nthHomology = prune homology(n, M); + if nthHomology == 0 then n = n + 1 else foundHomology = true; + ); + if not foundHomology then return M; + -- Lift minimal homology generators from H_n back to cycles in M_n. + -- moduleDifferential(n, M) is a map over A.ring from the free A.ring + -- basis of M at hom-degree n; cycles are in the kernel of that map. + -- We reconstruct cycles as Vectors in M.natural by pairing the kernel + -- coefficients with the hom-degree-n basis pairs (i, mon) of M. + pruningMap := nthHomology.cache.pruningMap; + homGensMat := gens image pruningMap; -- columns over A.ring + sourcePairs := moduleBasisPairs(M, n); + if #sourcePairs == 0 then return M; + moduleGens := apply(rank M.natural, i -> (M.natural)_i); + cycleList := apply(numcols homGensMat, c -> ( + sum apply(#sourcePairs, k -> ( + (i, mon) := sourcePairs#k; + cK := homGensMat_(k, c); + if cK == 0 then 0_(M.natural) else cK * mon * moduleGens#i + )) + )); + -- Drop any accidentally-zero cycles (shouldn't happen, but be safe). + cycleList = select(cycleList, z -> z != 0); + if #cycleList == 0 then return M; + adjoinGenerators(M, cycleList) +) +-- killCycles returns a DGModule when given a DGModule, even though +-- the method's default TypicalValue is DGAlgebra. Inform the documentation +-- system so per-signature doc nodes for (killCycles, DGModule) see the +-- right return type. +typicalValues#(killCycles, DGModule) = DGModule + +-- semifreeResolution(A, M, EndDegree => n): build a semifree DG module +-- over A approximating a resolution of (an R-module or H_0 shape) M up to +-- hom-degree n. Starts with a free DG module whose hom-degree-0 generators +-- match a generating set for M and whose hom-degree-1 differentials are +-- the presentation matrix columns; iteratively applies killCycles to kill +-- H_i for i = 1..n. +-- Mirrors acyclicClosure DGAlgebra: iteratively kills homology up to the +-- requested degree, producing a semifree DG module resolution. +semifreeResolution = method(TypicalValue => DGModule, Options => {StartDegree => 1, EndDegree => 3}) +semifreeResolution (DGAlgebra, Module) := opts -> (A, M) -> ( + R := A.ring; + if ring M =!= R then + error "semifreeResolution: module must be over the DG algebra's base ring."; + -- Free cover: one hom-degree-0 generator per generator of M. + numGensM := numgens M; + genDegs := apply(numGensM, i -> ( + rd := (degrees target presentation M)#i; + {0} | toList rd + )); + Mdg := freeDGModule(A, genDegs); + -- Kill the presentation relations: adjoin hom-degree-1 gens whose + -- differentials are the relations (viewed as elements of M.natural). + presM := presentation M; + if numcols presM > 0 then ( + natGens := apply(rank Mdg.natural, i -> (Mdg.natural)_i); + presOverA := sub(presM, A.natural); + relCycles := apply(numcols presOverA, c -> ( + sum apply(numGensM, j -> presOverA_(j, c) * natGens#j) + )); + relCycles = select(relCycles, z -> z != 0); + if #relCycles > 0 then Mdg = adjoinGenerators(Mdg, relCycles); + ); + -- Now iterate killCycles from StartDegree up to EndDegree. + n := opts.StartDegree; + while n <= opts.EndDegree do ( + Mdg = killCycles(Mdg, StartDegree => n); + n = n + 1; + ); + Mdg +) +semifreeResolution Module := opts -> M -> ( + R := ring M; + A := koszulComplexDGA R; + semifreeResolution(A, M, opts) +) -doc /// - Key - acyclicClosure - (acyclicClosure,DGAlgebra) - Headline - Compute the acyclic closure of a DGAlgebra. - Usage - B = acyclicClosure(A) - Inputs - A:DGAlgebra - Outputs - B:DGAlgebra - The acyclic closure of the DG Algebra A up to homological degree - provided in the EndDegree option (default value is 3). - Description - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} - A = koszulComplexDGA(R); - B = acyclicClosure(A,EndDegree=>3) - toComplex(B,8) - B.diff - SeeAlso - (acyclicClosure,Ring) -/// +------------------------------------------------------------------------- +-- minimalSemifreeResolution(A, M, EndDegree => n): MINIMAL semifree +-- resolution of M over A, up to hom-degree n. +-- +-- "Minimal" here means that when we tensor with the residue field k of +-- R along the augmentation A -> k (substituting T_i = 0 and reducing mod +-- m_R), every differential becomes zero. Equivalently, each generator's +-- differential lives in the augmentation ideal of A.natural. +-- +-- Strategy: +-- (1) Start from prune M, giving mu(M) hom-degree-0 generators (minimal +-- count over R). +-- (2) Check each column of presentation(prune M) against the DGA-induced +-- differential: x * e_0 may already be d(T_i * e_0) and thus already +-- a boundary in F_0. Using getBoundaryPreimage, we compute the +-- NON-BOUNDARY RESIDUE r of each relation column, and adjoin a +-- hom-deg-1 generator only when r is nonzero — with differential r, +-- not the original relation. This is the key step that makes the +-- resolution minimal OVER A (not just over R): blind adjoinment of +-- presentation columns duplicates Koszul-style relations and +-- produces extra cycles at later stages. +-- (3) Iterate killCycles. The existing killCycles DGModule uses +-- `prune homology(n, M).cache.pruningMap` to pick representative +-- cycles, which already gives a MINIMAL generating set of H_n over R +-- (prune over a graded-local ring = minimal presentation). So each +-- iteration contributes exactly mu(H_n(M)) new generators. +-- +-- Efficiency: relies on the cache migration in adjoinGenerators to avoid +-- recomputing low-degree differentials after each killCycles step. +-- +-- Precondition: A.ring should be (graded-)local so that prune gives a +-- minimal presentation. For A = koszulComplexDGA R with R a standard +-- graded quotient of a polynomial ring over a field, this is satisfied. +------------------------------------------------------------------------- +minimalSemifreeResolution = method(TypicalValue => DGModule, + Options => {StartDegree => 1, EndDegree => 3}) +minimalSemifreeResolution (DGAlgebra, Module) := opts -> (A, M) -> ( + R := A.ring; + if ring M =!= R then + error "minimalSemifreeResolution: module must be over the DG algebra's base ring."; + -- Replace M by a minimally-presented copy. prune gives a minimal + -- presentation over a (graded-)local ring; over a polynomial ring it + -- gives a minimal graded presentation. + Mmin := prune M; + numGensM := numgens Mmin; + presMin := presentation Mmin; + -- Generator degrees: copy from the target of the minimal presentation. + genDegs := if numGensM == 0 then {} + else apply(numGensM, i -> ( + rd := (degrees target presMin)#i; + {0} | toList rd + )); + Mdg := freeDGModule(A, genDegs); + -- Hom-deg 1 stage: each minimal relation of M becomes an element of + -- F_0(Mdg). If it is already a boundary under the DGA-induced + -- differential d_1 (from d(A_1 * e_j) terms), skip; otherwise adjoin + -- a hom-deg-1 gen killing the non-boundary RESIDUE (not the original + -- column), which is the minimal extension. + if numcols presMin > 0 and numGensM > 0 then ( + natGens := apply(rank Mdg.natural, i -> (Mdg.natural)_i); + presOverA := sub(presMin, A.natural); + relCycles := apply(numcols presOverA, c -> ( + sum apply(numGensM, j -> presOverA_(j, c) * natGens#j) + )); + nonZeroRels := select(relCycles, z -> z != 0); + if #nonZeroRels > 0 then ( + (liftOk, liftsOrResidues) := getBoundaryPreimage(Mdg, nonZeroRels); + toAdjoin := if liftOk then {} else + select(liftsOrResidues, r -> r != 0); + if #toAdjoin > 0 then Mdg = adjoinGenerators(Mdg, toAdjoin); + ); + ); + -- Iterate killCycles from StartDegree up to EndDegree. The existing + -- killCycles DGModule uses prune on H_n and takes representative cycles + -- from the pruning map's image — which is a minimal generating set. + n := opts.StartDegree; + while n <= opts.EndDegree do ( + Mdg = killCycles(Mdg, StartDegree => n); + n = n + 1; + ); + Mdg +) +minimalSemifreeResolution Module := opts -> M -> ( + R := ring M; + A := koszulComplexDGA R; + minimalSemifreeResolution(A, M, opts) +) -doc /// - Key - (acyclicClosure,Ring) - Headline - Compute the acyclic closure of the residue field of a ring up to a certain degree - Usage - A = acyclicClosure(R) - Inputs - R:Ring - Outputs - A:DGAlgebra - The acyclic closure of the ring R up to homological degree provided in the EndDegree option (default value is 3). - Description - Text - This package always chooses the Koszul complex on a generating set for the maximal ideal as a starting - point, and then computes from there, using the function @ TO (acyclicClosure,DGAlgebra) @. - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^4-d^3} - A = acyclicClosure(R,EndDegree=>3) - A.diff -/// +------------------------------------------------------------------------- +-- isMinimalSemifreeResolution(M): true iff each generator differential +-- lies in the augmentation ideal of A.natural. Equivalently: reducing +-- A.natural modulo (vars R, positive-hom-deg generators), each component +-- of each M.diff#i collapses to zero. +-- +-- Does NOT check acyclicity — use isAcyclic with an EndDegree for that. +------------------------------------------------------------------------- +isMinimalSemifreeResolution = method(TypicalValue => Boolean) +isMinimalSemifreeResolution DGModule := M -> ( + if not isValidDGModule M then return false; + if not isFreeModule M.natural then return false; + A := M.dgAlgebra; + R := A.ring; + -- Build the augmentation map A.natural -> k: + -- first set all T_i = 0 (sub to R), then reduce mod ideal vars R. + killT := map(R, A.natural, + matrix{apply(numgens A.natural, i -> 0_R)}); + mR := if numgens R > 0 then ideal vars R else ideal 0_R; + augIsZero := f -> ( + if f == 0 then true + else (killT f) % mR == 0 + ); + all(M.diff, v -> ( + if v == 0 then true + else all(entries v, c -> augIsZero c) + )) +) -doc /// - Key - (symbol **, DGAlgebra, Ring) - Headline - Tensor product of a DGAlgebra and another ring. - Usage - B = A ** S - Inputs - A:DGAlgebra - R:Ring - Outputs - B:DGAlgebra - Description - Text - Tensor product of a DGAlgebra and another ring (typically a quotient of A.ring). - Example - R = ZZ/101[a,b,c,d] - A = koszulComplexDGA(R) - S = R/ideal{a^3,a*b*c} - B = A ** S - Bdd = toComplex B - Bdd.dd -/// +------------------------------------------------------------------------- +-- DG module visualization: generatorTable, dgModuleSummary, +-- moduleBlockDiff, displayModuleBlockDiff. +-- +-- Parallels the DGAlgebra-side blockDiff / displayBlockDiff machinery but +-- augments the chunk-vector labels with a module-generator index so each +-- block knows which generator it belongs to. +------------------------------------------------------------------------- + +-- generatorTable(M): a per-generator table — i, hom-deg, ext-deg, d(e_i). +-- Useful for eyeballing a semifree resolution's generator list and +-- differentials at a glance. +generatorTable = method() +generatorTable DGModule := M -> ( + if #M.Degrees == 0 then return netList({{"no generators"}}, Alignment => Center); + hdrs := {"i", "hom-deg", "ext-deg", "d(e_i)"}; + rows := apply(#M.Degrees, i -> ( + d := M.Degrees#i; + hd := first d; + ed := drop(d, 1); + {i, hd, ed, M.diff#i} + )); + netList({hdrs} | rows, Alignment => Center, HorizontalSpace => 1, BaseRow => 0) +) -doc /// - Key - (symbol **, DGAlgebra, DGAlgebra) - Headline - Tensor product of a DGAlgebra and another ring. - Usage - C = A ** B - Inputs - A:DGAlgebra - B:DGAlgebra - Outputs - C:DGAlgebra - Description - Text - Tensor product of a pair of DGAlgebras. - Example - R = ZZ/101[a,b,c,d] - A = koszulComplexDGA({a,b}) - B = koszulComplexDGA({c,d}) - C = A ** B - Cdd = toComplex C - Cdd.dd - Caveat - Currently, the tensor product function does not create a block order on the variables from A and B. -/// +-- dgModuleSummary(M, n) / dgModuleSummary(M): +-- Report, for each hom-degree k in [0, n]: +-- * # A.ring-basis elements of F_k (#moduleBasisPairs) +-- * # generators of M.natural at hom-degree k (adjoined in that row) +-- When n is omitted, uses maxDegree(M) if finite, else errors. +dgModuleSummary = method() +dgModuleSummary (DGModule, ZZ) := (M, n) -> ( + if n < 0 then error "dgModuleSummary: degree bound must be nonnegative."; + genCounts := new MutableHashTable; + scan(M.Degrees, d -> ( + h := first d; + if not genCounts#?h then genCounts#h = 0; + genCounts#h = genCounts#h + 1; + )); + hdrs := {"hom-deg", "#gens adjoined", "rank F_n"}; + rows := apply(toList(0..n), k -> ( + g := if genCounts#?k then genCounts#k else 0; + rF := #moduleBasisPairs(M, k); + {k, g, rF} + )); + netList({hdrs} | rows, Alignment => Center, HorizontalSpace => 1, BaseRow => 0) +) +dgModuleSummary DGModule := M -> ( + d := maxDegree M; + if d === infinity then + error "dgModuleSummary: hom-degree is unbounded; supply an integer bound."; + dgModuleSummary(M, d) +) -doc /// - Key - killCycles - (killCycles,DGAlgebra) - Headline - Adjoins variables to make non-bounding cycles boundaries in the lowest positive degree with nontrivial homology. - Usage - B = killCycles(A) - Inputs - A:DGAlgebra - Outputs - B:DGAlgebra - Description - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3-d^4} - A = koszulComplexDGA(R) - A.diff - B = killCycles(A) - B.diff -/// +-- moduleBlockPhiMap(M, i, inVec, j, outVec): the (tgt <- src) block of d_n +-- where n = (first M.Degrees#i) + sum inVec, mapping +-- basis of chunk-vec-inVec monomials * e_i -> +-- basis of chunk-vec-outVec monomials * e_j. +-- Returns a matrix over A.ring (or null on degree mismatch). +moduleBlockPhiMap = (M, i, inVec, j, outVec) -> ( + A := M.dgAlgebra; + R := A.ring; + varsA := getVariableChunks A; + h := #varsA; + if h != #inVec or h != #outVec then return null; + srcHomDeg := sum inVec + first M.Degrees#i; + tgtHomDeg := sum outVec + first M.Degrees#j; + if srcHomDeg - 1 != tgtHomDeg then return null; + inBasisMat := if h == 0 then matrix{{1_(A.natural)}} + else tensor apply(h, k -> basis(inVec#k, A.natural, Variables => varsA#k)); + outBasisMat := if h == 0 then matrix{{1_(A.natural)}} + else tensor apply(h, k -> basis(outVec#k, A.natural, Variables => varsA#k)); + inMons := flatten entries inBasisMat; + outMons := flatten entries outBasisMat; + numSrc := #inMons; + numTgt := #outMons; + shifts := dd -> -drop(dd, 1); + srcDegs := apply(inMons, m -> shifts degree(m * (M.natural)_i)); + tgtDegs := apply(outMons, m -> shifts degree(m * (M.natural)_j)); + if numSrc == 0 or numTgt == 0 then + return map(R^tgtDegs, R^srcDegs, 0); + mat := mutableMatrix(R, numTgt, numSrc); + scan(numSrc, c -> ( + m := inMons#c; + db := moduleLeibniz(M, i, m); + dbEntries := entries db; + fj := dbEntries#j; + if fj == 0 then return; + coefMat := (coefficients(matrix{{fj}}, Monomials => matrix{outMons}))#1; + coefMatR := sub(coefMat, R); + scan(numTgt, a -> ( + cij := coefMatR_(a, 0); + if cij != 0 then mat_(a, c) = cij; + )); + )); + map(R^tgtDegs, R^srcDegs, matrix mat) +) -doc /// - Key - adjoinVariables - (adjoinVariables,DGAlgebra,List) - Headline - Adjoins variables to make the specified cycles boundaries. - Usage - B = adjoinVariables(A,cycleList) - Inputs - A:DGAlgebra - cycleList:List - Outputs - B:DGAlgebra - Description - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3-d^4} - A = koszulComplexDGA(R) - A.diff - prune homology(1,A) - B = adjoinVariables(A,{a^2*T_1}) - B.diff - prune homology(1,B) -/// +-- moduleDegreeLabels(M, n): sorted list of block labels (i, chunkVec) for +-- hom-degree n. A label (i, v) means "generator e_i times an A-monomial of +-- chunk-degree v". +moduleDegreeLabels = (M, n) -> ( + A := M.dgAlgebra; + sort flatten apply(#M.Degrees, i -> ( + di := first M.Degrees#i; + needed := n - di; + if needed < 0 then {} + else apply(degreeVecs(A, needed), v -> {i, v}) + )) +) -doc /// - Key - homologyAlgebra - (homologyAlgebra,DGAlgebra) - Headline - Compute the homology algebra of a DGAlgebra. - Usage - HA = homologyAlgebra(A) - Inputs - A:DGAlgebra - Outputs - HA:Ring - Description - Example - R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} - A = koszulComplexDGA(R) - apply(maxDegree A + 1, i -> numgens prune homology(i,A)) - HA = homologyAlgebra(A) - Text - Note that HA is a graded commutative polynomial ring (i.e. an exterior algebra) since R is a complete intersection. - Example - R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4,a^3*b^3*c^3*d^3} - A = koszulComplexDGA(R) - apply(maxDegree A + 1, i -> numgens prune homology(i,A)) - HA = homologyAlgebra(A) - numgens HA - HA.cache.cycles - Example - Q = ZZ/101[x,y,z] - I = ideal{y^3,z*x^2,y*(z^2+y*x),z^3+2*x*y*z,x*(z^2+y*x),z*y^2,x^3,z*(z^2+2*x*y)} - R = Q/I - A = koszulComplexDGA(R) - apply(maxDegree A + 1, i -> numgens prune homology(i,A)) - HA = homologyAlgebra(A) - Text - One can check that HA has Poincare duality since R is Gorenstein. - Text - If your DGAlgebra has generators in even degrees, then one must specify the options GenDegreeLimit and RelDegreeLimit. - Example - R = ZZ/101[a,b,c,d] - S = R/ideal{a^4,b^4,c^4,d^4} - A = acyclicClosure(R,EndDegree=>3) - B = A ** S - HB = homologyAlgebra(B,GenDegreeLimit=>7,RelDegreeLimit=>14) -/// +-- moduleBlockDiff(M, n): the hom-degree-n differential of M as a block +-- matrix indexed by module-gen-chunk labels. Parallels blockDiff(A, n). +-- Cached in M.cache#"moduleBlockDiffs". +moduleBlockDiff = method() +moduleBlockDiff (DGModule, ZZ) := (M, n) -> ( + ensureDGAlgebraCaches M; + if not M.cache#?"moduleBlockDiffs" then + M.cache#"moduleBlockDiffs" = new MutableHashTable; + if M.cache#"moduleBlockDiffs"#?n then return M.cache#"moduleBlockDiffs"#n; + A := M.dgAlgebra; + R := A.ring; + srcLabels := moduleDegreeLabels(M, n); + tgtLabels := moduleDegreeLabels(M, n - 1); + numSrc := #srcLabels; + numTgt := #tgtLabels; + -- Gather per-block matrices. + tempM := table(numTgt, numSrc, (a, b) -> ( + tgtLbl := tgtLabels#a; + srcLbl := srcLabels#b; + moduleBlockPhiMap(M, srcLbl#0, srcLbl#1, tgtLbl#0, tgtLbl#1) + )); + -- Helper: build the labeled source free module (one labeled summand per + -- src label); or labeled target free module. Used to assemble the + -- direct-sum structure even when tempM has no per-block maps to + -- derive the components from (empty one side). + varsA := getVariableChunks A; + labelFreeModule := (lbls, genIdxPos) -> ( + mods := apply(lbls, lbl -> ( + gi := lbl#(genIdxPos#0); + v := lbl#(genIdxPos#1); + basisMat := if #varsA == 0 then matrix{{1_(A.natural)}} + else tensor apply(#varsA, k -> basis(v#k, A.natural, Variables => varsA#k)); + mons := flatten entries basisMat; + R^(apply(mons, m -> -drop(degree(m * (M.natural)_gi), 1))) + )); + if #lbls == 0 then R^0 + else directSum apply(#lbls, a -> lbls#a => mods#a) + ); + srcLabelFM := labelFreeModule(srcLabels, (0, 1)); + tgtLabelFM := labelFreeModule(tgtLabels, (0, 1)); + result := if numSrc == 0 or numTgt == 0 then + map(tgtLabelFM, srcLabelFM, 0) + else + map(tgtLabelFM, srcLabelFM, matrix tempM); + M.cache#"moduleBlockDiffs"#n = result; + result +) -doc /// - Key - (homology,DGAlgebra) - Headline - Compute the homology algebra of a DGAlgebra. - Usage - HA = homology(A) - Inputs - A:DGAlgebra - Outputs - HA:Ring - SeeAlso - (homologyAlgebra,DGAlgebra) -/// +-- displayModuleBlockDiff(M, n): pretty-print moduleBlockDiff with block +-- labels in the header row / column. Mirrors displayBlockDiff(A, d). +displayModuleBlockDiff = method() +displayModuleBlockDiff (DGModule, ZZ) := (M, n) -> ( + dn := moduleBlockDiff(M, n); + inputLbls := indices source dn; + outputLbls := indices target dn; + if #inputLbls == 0 and #outputLbls == 0 then return "(empty)"; + firstRow := {"."} | inputLbls; + if #outputLbls == 0 then return netList({firstRow}, Alignment => Center); + body := apply(outputLbls, o -> ( + {o} | apply(inputLbls, i -> dn^[o]_[i]) + )); + netList({firstRow} | body, Alignment => Center) +) -doc /// - Key - torAlgebra - (torAlgebra,Ring) - Headline - Computes the Tor algebra of a ring - Usage - torR = torAlgebra(R) - Inputs - R:Ring - Outputs - torR:Ring - Description - Example - R = ZZ/101[a,b,c,d] - TorR = torAlgebra(R) - S = R/ideal{a^3,b^3,c^3,d^5} - TorS = torAlgebra(S, GenDegreeLimit => 3) - Text - The above example calculates the Tor algebra of R and S up to degree 3, by default. One can also specify the maximum degree - to compute generators of the Tor algebra by specifying the GenDegreeLimit option. - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3,a^2*b^2*c^3*d^2} - TorR = torAlgebra(R,GenDegreeLimit=>5) -/// +-- Expose the underlying label machinery for introspection. +displayModuleBlockDiff (DGModule, List, List) := (M, srcLbl, tgtLbl) -> ( + -- srcLbl is a single label {i, v}; tgtLbl is {j, w}. + n := (first M.Degrees#(srcLbl#0)) + sum(srcLbl#1); + dn := moduleBlockDiff(M, n); + dn_[srcLbl]^[tgtLbl] +) -doc /// - Key - (torAlgebra,Ring,Ring) - Headline - Computes Tor_R(S,k) up to a specified generating and relating degree. - Usage - TorRS = torAlgebra(R,S,GenDegreeLimit=>m,RelDegreeLimit=>n) - Inputs - R:Ring - S:Ring - Outputs - TorRS:Ring - Description - Example - R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} - M = coker matrix {{a^3*b^3*c^3*d^3}}; - S = R/ideal{a^3*b^3*c^3*d^3} - HB = torAlgebra(R,S,GenDegreeLimit=>4,RelDegreeLimit=>8) - numgens HB - apply(5,i -> #(flatten entries getBasis(i,HB))) - Mres = freeResolution(M, LengthLimit=>8) - Text - Note that in this example, $Tor_*^R(S,k)$ has trivial multiplication, since the - map from R to S is a Golod homomorphism by a theorem of Levin and Avramov. -/// +------------------------------------------------------------------------- +-- DG module element <-> coefficient-over-A.ring bridge. +-- +-- A free DG module F = (A.natural)^(-degList) has, at hom-degree n, an +-- A.ring-basis consisting of pairs (i, m) with m an A.natural monomial of +-- hom-degree n - d_i. moduleBasisPairs gives these pairs; the two helpers +-- below convert between Vectors in M.natural and their coefficient +-- columns over A.ring in that basis. +------------------------------------------------------------------------- +-- moduleCoefficients(M, v, n): column vector over A.ring of coefficients +-- of v (a Vector in M.natural, assumed at hom-degree n) in the F_n basis. +moduleCoefficients = method() +moduleCoefficients (DGModule, Vector, ZZ) := (M, v, n) -> ( + if not isFreeModule M.natural then + error "moduleCoefficients: requires a free DGModule (freeDGModule output)."; + A := M.dgAlgebra; + R := A.ring; + pairs := moduleBasisPairs(M, n); + if #pairs == 0 then return map(R^0, R^1, 0); + vEntries := entries v; + numNatGens := rank M.natural; + byGen := new MutableHashTable; + scan(numNatGens, i -> byGen#i = {}); + scan(#pairs, k -> ( + (i, mon) := pairs#k; + byGen#i = append(byGen#i, (k, mon)); + )); + col := mutableMatrix(R, #pairs, 1); + scan(numNatGens, i -> ( + fi := vEntries#i; + if fi == 0 then return; + rows := byGen#i; + if #rows == 0 then return; + tgtMons := apply(rows, pr -> pr#1); + coefMat := (coefficients(matrix{{fi}}, Monomials => matrix{tgtMons}))#1; + coefMatR := sub(coefMat, R); + scan(#rows, idx -> ( + rowIdx := (rows#idx)#0; + col_(rowIdx, 0) = coefMatR_(idx, 0); + )); + )); + matrix col +) -doc /// - Key - maxDegree - (maxDegree,DGAlgebra) - Headline - Computes the maximum homological degree of a DGAlgebra - Usage - mDegree = maxDegree(A) - Inputs - A:DGAlgebra - Outputs - mDegree:ZZ - The maximum degree of the DGAlgebra A (this can be infinite). - Description - Text - Note that if the DGAlgebra A has any generators of even degree, then maxDegree returns infinity. - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} - A = koszulComplexDGA(R) - B = acyclicClosure(A,EndDegree=>3) - maxDegree(A) - maxDegree(B) -/// +-- moduleFromCoefficients(M, col, n): reconstitute a Vector in M.natural +-- (at hom-degree n) from a column vector of A.ring coefficients. +moduleFromCoefficients = method() +moduleFromCoefficients (DGModule, Matrix, ZZ) := (M, col, n) -> ( + if not isFreeModule M.natural then + error "moduleFromCoefficients: requires a free DGModule."; + pairs := moduleBasisPairs(M, n); + if #pairs == 0 then return 0_(M.natural); + natGens := apply(rank M.natural, i -> (M.natural)_i); + sum apply(#pairs, k -> ( + (i, mon) := pairs#k; + c := col_(k, 0); + if c == 0 then 0_(M.natural) else c * mon * natGens#i + )) +) -doc /// - Key - isHomologyAlgebraTrivial - (isHomologyAlgebraTrivial,DGAlgebra) - Headline - Determines if the homology algebra of a DGAlgebra is trivial - Usage - isTriv = isHomologyAlgebraTrivial(A) - Inputs - A:DGAlgebra - Outputs - isTriv:Boolean - Description - Text - This function computes the homology algebra of the DGAlgebra A and determines if the multiplication on H(A) is trivial. - Example - R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} - S = R/ideal{a^3*b^3*c^3*d^3} - A = acyclicClosure(R,EndDegree=>3) - B = A ** S - isHomologyAlgebraTrivial(B,GenDegreeLimit=>6) - Text - The command returns true since R --> S is Golod. Notice we also used the option GenDegreeLimit here. - Example - R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} - A = koszulComplexDGA(R) - isHomologyAlgebraTrivial(A) - Text - The command returns false, since R is Gorenstein, and so HA has Poincare Duality, hence the multiplication - is far from trivial. -/// +-- diff(M, v): apply the DG module differential to a Vector v in M.natural. +-- Uses Leibniz directly (no basis reconstruction needed). +diff (DGModule, Vector) := (M, v) -> ( + A := M.dgAlgebra; + vEntries := entries v; + numGens := rank M.natural; + natGens := apply(numGens, i -> (M.natural)_i); + sum apply(numGens, i -> ( + fi := vEntries#i; + if fi == 0 then 0_(M.natural) + else ( + term1 := polyDifferential(A, fi) * natGens#i; + signExp := first degree fi; + sign := if odd signExp then -1 else 1; + term2 := sign * fi * M.diff#i; + term1 + term2 + ) + )) +) -doc /// - Key - isAcyclic - (isAcyclic,DGAlgebra) - Headline - Determines if a DGAlgebra is acyclic. - Usage - isAcyc = isAcyclic(A) - Inputs - A:DGAlgebra - Outputs - isAcyc:Boolean - Description - Text - This function determines if the DGAlgebra is acyclic. - Example - R = ZZ/101[a,b,c,d]/ideal{a^4+b^4+c^4+d^4} - isAcyclic(koszulComplexDGA R) - Example - Q = ZZ/101[a,b,c,d] - I = ideal {a^4,b^4,c^4,d^4} - isAcyclic(koszulComplexDGA I) -/// -doc /// - Key - isGolod - (isGolod,Ring) - Headline - Determines if a ring is Golod - Usage - isGol = isGolod(R) - Inputs - R:Ring - Outputs - isGol:Boolean - Description - Text - This function determines if the Koszul complex of a ring R admits a trivial Massey operation. If one exists, then R is Golod. - Example - R = ZZ/101[a,b,c,d]/ideal{a^4+b^4+c^4+d^4} - isGolod(R) - Text - Hypersurfaces are Golod, but - Example - R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} - isGolod(R) - Text - complete intersections of higher codimension are not. Here is another example: - Example - Q = ZZ/101[a,b,c,d] - R = Q/(ideal vars Q)^2 - isGolod(R) - Text - The above is a (CM) ring minimal of minimal multiplicity, hence Golod. The next example was found - by Lukas Katthan, and appears in his arXiv paper 1511.04883. It is the first known example - of an algebra that is not Golod, but whose Koszul complex has a trivial homology product. - Example - Q = ZZ/101[x_1,x_2,y_1,y_2,z,w] - I = ideal {x_1*x_2^2,z^2*w,y_1*y_2^2,x_2^2*z*w,y_2^2*z^2,x_1*x_2*y_1*y_2,x_2^2*y_2^2*z,x_1*y_1*z} - R = Q/I - isHomologyAlgebraTrivial koszulComplexDGA R - isGolod R - Text - Note that since the Koszul complex is zero in homological degree beyond the embedding dimension, there are only finitely - many Massey products that one needs to check to verify that a ring is Golod. -/// +------------------------------------------------------------------------- +-- getBoundaryPreimage(DGModule, ...): lift a boundary b in M to a +-- preimage x with d_M(x) = b. Returns a pair +-- (true, preimage) -- if b is a boundary +-- (false, residue) -- if not; residue is (b - d_M(liftAttempt)) +-- Parallels getBoundaryPreimage(DGAlgebra, ...). +------------------------------------------------------------------------- +getBoundaryPreimage (DGModule, List) := (M, boundaryList) -> ( + if not isFreeModule M.natural then + error "getBoundaryPreimage DGModule: requires a free DGModule."; + A := M.dgAlgebra; + R := A.ring; + nonzeros := select(boundaryList, b -> b != 0); + if nonzeros == {} then return (true, boundaryList); + homDegree := first degree first nonzeros; + if any(boundaryList, b -> b != 0 and first degree b != homDegree) then + error "getBoundaryPreimage DGModule: boundary elements must share a hom-degree."; + srcPairs := moduleBasisPairs(M, homDegree); + if #srcPairs == 0 then ( + -- No basis at this degree => only zero can be a boundary. + return (true, apply(#boundaryList, i -> 0_(M.natural))); + ); + coefCols := apply(boundaryList, b -> + if b == 0 then map(R^(#srcPairs), R^1, 0) else moduleCoefficients(M, b, homDegree) + ); + boundaryMat := matrix {coefCols}; + dnplus1 := moduleDifferential(homDegree + 1, M); + -- Align boundaryMat's target with dnplus1's target so that // sees + -- a common graded target (moduleCoefficients builds an ungraded + -- R^(#srcPairs) by default; retarget it to R^(tgtDegs) of dn). + dn := moduleDifferential(homDegree, M); + boundaryMat = map(source dn, source boundaryMat, boundaryMat); + liftMat := boundaryMat // dnplus1; + residual := boundaryMat - dnplus1 * liftMat; + if residual != 0 then ( + residueVectors := apply(numcols boundaryMat, j -> + moduleFromCoefficients(M, residual_{j}, homDegree)); + return (false, residueVectors); + ); + preimages := apply(numcols boundaryMat, j -> + moduleFromCoefficients(M, liftMat_{j}, homDegree + 1)); + (true, preimages) +) -doc /// - Key - isGolodHomomorphism - (isGolodHomomorphism,QuotientRing) - Headline - Determines if the canonical map from the ambient ring is Golod - Usage - isGol = isGolodHomomorphism(R) - Inputs - R:QuotientRing - Outputs - isGol:Boolean - Description - Text - This function determines if the canonical map from ambient R --> R is Golod. It does this by computing an acyclic closure of - ambient R (which is a @ TO DGAlgebra @), then tensors this with R, and determines if this DG Algebra has a trivial Massey operation - up to a certain homological degree provided by the option GenDegreeLimit. - Example - R = ZZ/101[a,b,c,d]/ideal{a^4+b^4+c^4+d^4} - isGolodHomomorphism(R,GenDegreeLimit=>5) - Text - If R is a Golod ring, then ambient R $\rightarrow$ R is a Golod homomorphism. - Example - Q = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} - R = Q/ideal (a^3*b^3*c^3*d^3) - isGolodHomomorphism(R,GenDegreeLimit=>5,TMOLimit=>3) - Text - The map from Q to R is Golod by a result of Avramov and Levin; we can only find the trivial Massey operations out to a given degree. -/// +getBoundaryPreimage (DGModule, Vector) := (M, b) -> ( + (lifted, myLift) := getBoundaryPreimage(M, {b}); + (lifted, first myLift) +) -doc /// - Key - getGenerators - (getGenerators,DGAlgebra) - Headline - Returns a list of cycles whose images generate HH(A) as an algebra - Usage - cycleList = getGenerators(A) - Inputs - A:DGAlgebra - Outputs - cycleList:List - Description - Text - This version of the function should only be used if all algebra generators of A are in odd homological degree, - provided in the EndDegree option. - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3,a^2*b^2*c^2} - A = koszulComplexDGA(R) - netList getGenerators(A) -/// +------------------------------------------------------------------------- +-- homologyClass(DGModule, Vector): given a cycle z of hom-degree d, +-- return an element of (prune homology(d, M)) representing [z]. Parallels +-- homologyClass(DGAlgebra, RingElement), but returns per-degree pruned +-- homology rather than the full H_*(A)-module (the latter requires a +-- richer cycle-representative cache; see homologyModule DGModule). +------------------------------------------------------------------------- +homologyClass (DGModule, Vector) := (M, z) -> ( + if not isFreeModule M.natural then + error "homologyClass DGModule: requires a free DGModule."; + if z == 0 then ( + -- Hom-degree of the zero vector is ambiguous; pick d=0 by convention. + return 0_(prune homology(0, M)); + ); + d := first degree z; + -- Verify z is a cycle (Leibniz-based). + if diff(M, z) != 0 then + error "homologyClass DGModule: expected a cycle."; + H := prune homology(d, M); + if H == 0 then return 0_H; + zCol := moduleCoefficients(M, z, d); + pruneMat := matrix H.cache.pruningMap; + boundaryMat := moduleDifferential(d + 1, M); + -- Solve zCol = pruneMat * hCoef + boundaryMat * t. + -- Concatenate and let M2 do the division. + combined := if numcols boundaryMat == 0 then pruneMat else pruneMat | boundaryMat; + sol := zCol // combined; + if combined * sol != zCol then + error "homologyClass DGModule: failed to express cycle in homology basis (cycle not in image of pruning map + boundaries)."; + hCoef := sol^{0 .. (numcols pruneMat - 1)}; + -- Build the element: sum h_i * H_i. pruneMat has numcols = numgens H. + sum apply(numcols pruneMat, i -> ( + c := hCoef_(i, 0); + if c == 0 then 0_H else c * H_i + )) +) -doc /// - Key - deviations - (deviations,Ring) - (deviations,Complex) - (deviations,RingElement,List) - Headline - Computes the deviations of the input ring, complex, or power series. - Usage - devTally = deviations(R) - Inputs - R:Ring - Outputs - devTally:Tally - Description - Text - This command computes the deviations of a @ TO Ring @, a @ TO Complex @, or a power series in the form of a @ TO RingElement @. - The deviations are the same as the degrees of the generators of the acyclic closure of R, or the degrees of the generators of the - Tor algebra of R. This function takes an option called Limit (default value 3) that specifies the largest deviation to compute. - Example - R = ZZ/101[a,b,c,d]/ideal {a^3,b^3,c^3,d^3} - deviations(R) - deviations(R,DegreeLimit=>4) - S = R/ideal{a^2*b^2*c^2*d^2} - deviations(S,DegreeLimit=>4) - T = ZZ/101[a,b]/ideal {a^2-b^3} - deviations(T,DegreeLimit=>4) - Text - Note that the deviations of T are not graded, since T is not graded. When calling deviations on a Complex, the - zeroth free module must be cyclic, and this is checked. The same goes for the case - of a RingElement. - Example - R = ZZ/101[a,b,c,d]/ideal {a^3,b^3,c^3,d^3} - A = degreesRing R - kRes = freeResolution(coker vars R, LengthLimit => 4) - pSeries = poincareN kRes - devA = deviations(R,DegreeLimit=>5) - devB = deviations(kRes,DegreeLimit=>5) - devC = deviations(pSeries,degrees R, DegreeLimit=>5) - devA === devB and devB === devC -/// +------------------------------------------------------------------------- +-- moduleMultMap(M, z): the chain map "left multiplication by z" on +-- toComplex M, where z is a cycle of hom-degree d in A.natural. Parallels +-- dgAlgebraMultMap. Source at deg i: F_i; target at deg i+d: F_{i+d}. +-- Leibniz guarantees this is a chain map when z is a cycle. +------------------------------------------------------------------------- +moduleMultMap = method() +moduleMultMap (DGModule, RingElement) := (M, z) -> ( + if not isFreeModule M.natural then + error "moduleMultMap: requires a free DGModule."; + A := M.dgAlgebra; + R := A.ring; + cxM := toComplex M; + if z == 0 then + return map(cxM, cxM, i -> map(cxM_i, cxM_i, 0), Degree => 0); + d := first degree z; + cxM2 := cxM ** R^(-(drop(degree z, 1))); + buildDegMap := i -> ( + srcPairs := moduleBasisPairs(M, i); + tgtPairs := moduleBasisPairs(M, i + d); + tgtMod := cxM_(i + d); + srcMod := cxM2_i; + if #srcPairs == 0 or #tgtPairs == 0 then + return map(tgtMod, srcMod, 0); + tgtByGen := new MutableHashTable; + scan(rank M.natural, j -> tgtByGen#j = {}); + scan(#tgtPairs, k -> ( + (j, mp) := tgtPairs#k; + tgtByGen#j = append(tgtByGen#j, (k, mp)); + )); + mat := mutableMatrix(R, #tgtPairs, #srcPairs); + scan(#srcPairs, c -> ( + (j, mon) := srcPairs#c; + zMon := z * mon; + if zMon == 0 then return; + rows := tgtByGen#j; + if #rows == 0 then return; + tgtMonsJ := apply(rows, pr -> pr#1); + coefMat := (coefficients(matrix{{zMon}}, Monomials => matrix{tgtMonsJ}))#1; + coefMatR := sub(coefMat, R); + scan(#rows, idx -> ( + rowIdx := (rows#idx)#0; + cij := coefMatR_(idx, 0); + if cij != 0 then mat_(rowIdx, c) = cij; + )); + )); + map(tgtMod, srcMod, matrix mat) + ); + map(cxM, cxM2, buildDegMap, Degree => d) +) -doc /// - Key - deviationsToPoincare - (deviationsToPoincare,HashTable) - [deviationsToPoincare,DegreeLimit] - Headline - Computes the power series corresponding to a set of deviations. - Usage - pSeries = deviationsToPoincare(devHash) - Inputs - devHash:HashTable - HashTable of the same form as the output from @ TO deviations @ - Outputs - pSeries:RingElement - Description - Text - This command takes a HashTable of the same form output from @ TO deviations @ and produces the Poincare series corresponding to it. - The (key,value) pairs must be of the form homologicalDegree=>number or (homologicalDegree,internalDegree)=>number. - Because - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} - RDevs = deviations(R,DegreeLimit=>6) - devPSeries = deviationsToPoincare(RDevs,DegreeLimit=>6) - pSeries = poincareN (freeResolution(coker vars R, LengthLimit=>6)) - substitute(devPSeries,ring pSeries) == pSeries -/// +------------------------------------------------------------------------- +-- moduleRelationsFromCycleActionDGM: DGModule analog of the +-- ModuleRelationsFromCycleAction helper used by homologyModule. Given a +-- DGModule M and a cycle z in A = M.dgAlgebra, build the action of the +-- homology class [z] on H_*(M) as a map over H_*(A), and return the +-- relation h - z.action (where h is left-mult by [z] in HA). +------------------------------------------------------------------------- +moduleRelationsFromCycleActionDGM = (M, z) -> ( + A := M.dgAlgebra; + R := A.ring; + HA := HH(A); + h := homologyClass(A, z); + d := first degree z; + zChainMap := moduleMultMap(M, z); + cxM := target zChainMap; + (lo, hi) := concentration cxM; + pruneHM := new MutableHashTable; + pruneMapMat := new MutableHashTable; + for i from lo to hi do ( + pruneHM#i = prune HH_i(cxM); + pruneMapMat#i = matrix (pruneHM#i).cache.pruningMap; + ); + actionOnPruned := new MutableHashTable; + for i from lo to hi - d do ( + hhMat := matrix HH_i(zChainMap); + composed := hhMat * pruneMapMat#i; + actionOnPruned#i = composed // pruneMapMat#(i + d); + ); + ll := hi - lo + 1; + degsHMTarget := flatten apply(toList(lo..hi), + p -> apply(degrees pruneHM#p, dd -> {p} | dd)); + degsHMSource := flatten apply(toList(lo..hi), + p -> apply(degrees pruneHM#p, dd -> ({p} | dd) + degree z)); + buildBlock := (iIdx, jIdx) -> ( + i := lo + iIdx; + j := lo + jIdx; + if j + d != i then map(pruneHM#i, pruneHM#j, 0) + else if not actionOnPruned#?j then map(pruneHM#i, pruneHM#j, 0) + else map(pruneHM#i, pruneHM#j, actionOnPruned#j) + ); + matActOfZ := map(HA^(-degsHMTarget), HA^(-degsHMSource), + tensor(map(HA, R), matrix table(ll, ll, buildBlock))); + map(HA^(-degsHMTarget), HA^(-degsHMSource), h) - matActOfZ +) + +------------------------------------------------------------------------- +-- homologyModule DGModule: generalized to arbitrary free DGModules. +-- Previously restricted to koszulComplexDGM form (via M.module); now +-- also supports freeDGModule-built M with general generator differentials. +-- +-- Fast path: when M.?module and d vanishes on every generator, defer to +-- homologyModule(A, M.module) (unchanged, uses existing Koszul-tensor logic). +-- +-- General path: iterate over HA.cache.cycles, use moduleMultMap to build +-- the action of each cycle class on toComplex M, and assemble the +-- resulting H_*(A)-module presentation. +------------------------------------------------------------------------- +homologyModule DGModule := M -> ( + A := M.dgAlgebra; + if M.?module and all(M.diff, z -> z == 0) then + return homologyModule(A, M.module); + if not isFreeModule M.natural then + error "homologyModule DGModule: non-free DGModules with nonzero differentials are not supported."; + HA := HH(A); + allActions := apply(HA.cache.cycles, z -> moduleRelationsFromCycleActionDGM(M, z)); + minimalPresentation coker matrix {allActions} +) + +------------------------------------------------------------------------- +-- DGModule ** Ring: base change along R -> S. For a free DGModule M +-- over a DGAlgebra A, builds M \otimes_R S = a free DG module over A \otimes S. +-- For koszulComplexDGM-built M, base-changes M.module and rebuilds. +-- Mirrors DGAlgebra ** Ring. +------------------------------------------------------------------------- +DGModule ** Ring := (M, S) -> ( + -- Cache M ** S so repeated base-changes agree (critical for + -- DGModuleMap ** Ring to produce source/target in the same world). + if M.cache#?"tensorWithRing" and (M.cache#"tensorWithRing")#?S then + return (M.cache#"tensorWithRing")#S; + A := M.dgAlgebra; + B := A ** S; + Mnew := null; + if M.?module and all(M.diff, z -> z == 0) then ( + -- koszulComplexDGM form: base-change the underlying R-module. + newUnderlying := M.module ** S; + Mnew = koszulComplexDGM(B, newUnderlying); + ) else ( + if not isFreeModule M.natural then + error "DGModule ** Ring: base change requires either koszulComplexDGM form or a free DGModule."; + -- Free path: lift each generator differential into B.natural. + Mnew = freeDGModule(B, M.Degrees); + numG := rank M.natural; + natGensNew := apply(numG, i -> (Mnew.natural)_i); + newDiffs := apply(M.diff, v -> ( + if v === 0 then 0_(Mnew.natural) + else ( + vEntries := entries v; + sum apply(numG, i -> ( + fi := vEntries#i; + if fi == 0 then 0_(Mnew.natural) + else (sub(fi, B.natural)) * natGensNew#i + )) + ) + )); + setDiff(Mnew, newDiffs); + ); + if not M.cache#?"tensorWithRing" then + M.cache#"tensorWithRing" = new MutableHashTable; + (M.cache#"tensorWithRing")#S = Mnew; + Mnew +) + +------------------------------------------------------------------------- +-- DGModule ** DGModule: EXTERIOR tensor product. +-- +-- Inputs: M a free DGModule over A, N a free DGModule over B, where +-- A.ring === B.ring. +-- Output: a free DGModule P over C = A ** B of rank (rank M)(rank N). +-- Generators: e_i ⊗ f_j for 0 ≤ i < rM, 0 ≤ j < rN, ordered +-- row-major (generator (i, j) has linear index i*rN + j). +-- Multidegree of e_i ⊗ f_j: M.Degrees#i + N.Degrees#j. +-- Differential (Koszul sign): +-- d(e_i ⊗ f_j) = d_M(e_i) ⊗ f_j + (-1)^{|e_i|} e_i ⊗ d_N(f_j) +-- where |e_i| is the hom-degree (first component) of e_i. +-- +-- Use cases: build small DGAs and modules over them separately, then +-- assemble their exterior product to get a DG module over A ** B. +-- This differs from the internal tensor product M ⊗_A N (which is +-- NOT what is built here). +-- +-- Restrictions: +-- * Both M and N must be free DGModules. koszulComplexDGM-style M +-- with M.module attached is not (yet) supported. +-- * A.ring === B.ring required (matches DGAlgebra **). +------------------------------------------------------------------------- +DGModule ** DGModule := (M, N) -> ( + A := M.dgAlgebra; + B := N.dgAlgebra; + if A.ring =!= B.ring then + error "DGModule **: factors must be DGModules over DGAlgebras sharing a common ground ring."; + if not isFreeModule M.natural or not isFreeModule N.natural then + error "DGModule **: both modules must be free DGModules (koszulComplexDGM/cokernel form not supported)."; + -- Cache M ** N on M so that repeated calls return the same DGModule + -- (important for UX: (F ** G) has source = M ** N, and we want a + -- fresh `M ** N` call to agree with the internal one). + if M.cache#?"tensorWith" and (M.cache#"tensorWith")#?N then + return (M.cache#"tensorWith")#N; + C := A ** B; + (iotaA, iotaB) := tensorInclusions C; + iA := iotaA.natural; -- RingMap A.natural -> C.natural + iB := iotaB.natural; -- RingMap B.natural -> C.natural + rM := rank M.natural; + rN := rank N.natural; + -- Combined generator multidegrees. + combinedDegrees := flatten apply(rM, i -> apply(rN, j -> ( + dM := M.Degrees#i; + dN := N.Degrees#j; + apply(#dM, k -> dM#k + dN#k) + ))); + P := freeDGModule(C, combinedDegrees); + Pgens := apply(rank P.natural, k -> (P.natural)_k); + idxOf := (i, j) -> i * rN + j; + -- Leibniz differential on each generator (i, j). + diffs := flatten apply(rM, i -> apply(rN, j -> ( + sign := (-1)^(first M.Degrees#i); + dMe := M.diff#i; + firstTerm := if dMe == 0 then 0_(P.natural) + else ( + entriesM := entries dMe; + sum apply(rM, k -> ( + coef := entriesM#k; + if coef == 0 then 0_(P.natural) + else (iA coef) * Pgens#(idxOf(k, j)) + )) + ); + dNf := N.diff#j; + secondTerm := if dNf == 0 then 0_(P.natural) + else ( + entriesN := entries dNf; + sum apply(rN, l -> ( + coef := entriesN#l; + if coef == 0 then 0_(P.natural) + else sign * (iB coef) * Pgens#(idxOf(i, l)) + )) + ); + firstTerm + secondTerm + ))); + setDiff(P, diffs); + P.cache#"tensorFactors" = (M, N); + if not M.cache#?"tensorWith" then + M.cache#"tensorWith" = new MutableHashTable; + (M.cache#"tensorWith")#N = P; + P +) + +-- ring DGModule / numgens DGModule: trivial accessors +ring DGModule := M -> M.ring + +-- maxDegree(M): the max hom-degree in the DG module is the max hom-degree +-- of the ambient DG algebra plus the largest hom-degree shift of a module +-- generator. If an even generator is present in A, A has infinite +-- hom-degree and so does M (return infinity). +maxDegree DGModule := M -> ( + A := M.dgAlgebra; + aMax := maxDegree A; + if aMax === infinity then return infinity; + if #M.Degrees == 0 then return aMax; + aMax + max(apply(M.Degrees, d -> first d)) +) + +-- getBasis(n, M): an A.ring-basis of the hom-degree-n piece of M.natural. +-- Delegates to basis on the underlying A.natural-module. +getBasis (ZZ, DGModule) := opts -> (n, M) -> ( + if opts.Limit == -1 then basis(n, M.natural, Variables => 0 .. numgens (M.dgAlgebra).natural - 1) + else basis(n, M.natural, Limit => opts.Limit, Variables => 0 .. numgens (M.dgAlgebra).natural - 1) +) + +-- toComplex(M, n): the chain complex of underlying A.ring-modules, +-- truncated at hom-degree n. +-- toComplex(M): use M's maxDegree (errors if infinite, matching DGAlgebra). +toComplex DGModule := M -> ( + maxDeg := maxDegree M; + if maxDeg === infinity then + error "Must specify an upper degree bound if an even generator exists in underlying DG algebra."; + toComplex(M, maxDeg) +) +toComplex (DGModule, ZZ) := (M, n) -> ( + if n <= 0 then complex({moduleDifferential(0, M)}) + else complex(apply(n, i -> moduleDifferential(i+1, M))) +) + +-- dgComplex(M): lazily-cached Complex of free A.ring-modules for M. +-- Mirrors dgComplex(DGAlgebra). Invalidation: clear M.cache.dgComplex. +dgComplex DGModule := M -> ( + ensureDGAlgebraCaches M; + if M.cache#?(symbol dgComplex) then return M.cache#(symbol dgComplex); + n := maxDegree M; + if n === infinity then ( + A := M.dgAlgebra; + n = sum select(degrees A.natural / first, i -> odd i) + 1; + ); + C := toComplex(M, max(n, 1)); + M.cache#(symbol dgComplex) = C; + C +) + +-- homology(M) unary: the full H_*(A)-module structure on H_*(M), matching +-- the DGAlgebra convention that HH with no integer returns the homology +-- algebra (line 544: `homology DGAlgebra := A -> homologyAlgebra(A)`). +-- For a per-degree R-module, use homology(n, M). +-- (homologyModule DGModule is defined below alongside the general-path +-- machinery for non-trivial generator differentials.) +homology DGModule := opts -> M -> homologyModule M + +-- isAcyclic(M): H_i(M) = 0 for i >= 1 up to a finite bound. Requires +-- EndDegree when M has infinite hom-degree, matching isAcyclic DGAlgebra. +isAcyclic DGModule := opts -> M -> ( + endDegree := maxDegree M; + if endDegree === infinity and opts.EndDegree == -1 then + error "Must supply an upper bound to check for acyclicity."; + if opts.EndDegree != -1 then endDegree = opts.EndDegree; + not any(1..endDegree, i -> prune homology(i, M) != 0) +) + +-- isValidDGModule(M): structural-invariant check on the stored object. +-- Parallels isValidDGAlgebra: required keys with expected types, and +-- generator-degree list length matches differential list length. Does +-- not check d_M^2 = 0 (see isWellDefinedDifferential DGModule). +isValidDGModule = method(TypicalValue => Boolean) +isValidDGModule DGModule := M -> ( + if not M#?(symbol dgAlgebra) or not instance(M#(symbol dgAlgebra), DGAlgebra) then return false; + if not isValidDGAlgebra M.dgAlgebra then return false; + if not M#?(symbol ring) or not instance(M#(symbol ring), Ring) then return false; + if not M#?(symbol natural) or not instance(M#(symbol natural), Module) then return false; + if not M#?(symbol Degrees) or not instance(M#(symbol Degrees), List) then return false; + if not M#?(symbol diff) or not instance(M#(symbol diff), List) then return false; + if #M.diff != #M.Degrees then return false; + true +) + +-- isWellDefined DGModule. Analog of isWellDefined DGAlgebra: checks +-- keys/types via isValidDGModule, then verifies the ambient DGAlgebra is +-- well-defined and d_M^2 = 0 up to maxDegree. Diagnostic messages +-- emitted when debugLevel > 0. +isWellDefined DGModule := M -> ( + if not isValidDGModule M then ( + if debugLevel > 0 then ( + missing := select( + {symbol dgAlgebra, symbol ring, symbol natural, symbol Degrees, symbol diff}, + s -> not M#?s); + if #missing > 0 then + << "-- DGModule: missing required key(s): " << toString missing << endl; + if M#?(symbol diff) and M#?(symbol Degrees) and #M.diff != #M.Degrees then + << "-- DGModule: #M.diff (" << #M.diff << ") != #M.Degrees (" << #M.Degrees << ")" << endl; + ); + return false; + ); + if not isWellDefined M.dgAlgebra then ( + if debugLevel > 0 then << "-- DGModule: M.dgAlgebra is not a well-defined DGAlgebra" << endl; + return false; + ); + if M.ring =!= M.dgAlgebra.ring then ( + if debugLevel > 0 then << "-- DGModule: M.ring does not equal M.dgAlgebra.ring" << endl; + return false; + ); + -- Each entry of M.diff must be an element of M.natural (or coercible). + for i from 0 to #M.diff - 1 do ( + d := M.diff#i; + try ( + -- Test coercion: the entry should be in M.natural's module structure. + if not (instance(d, Vector) or instance(d, Matrix) or d == 0_(M.natural)) then ( + if debugLevel > 0 then + << "-- DGModule: M.diff#" << i << " is not a module element of M.natural" << endl; + return false; + ); + ) else ( + if debugLevel > 0 then + << "-- DGModule: M.diff#" << i << " cannot be interpreted in M.natural" << endl; + return false; + ); + ); + -- Semantic: d_M^2 = 0 up to a finite bound. + if not isWellDefinedDifferential M then ( + if debugLevel > 0 then << "-- DGModule: d_M^2 fails to vanish" << endl; + return false; + ); + true +) + +-- isWellDefinedDifferential(M): semantic check d_M^2 = 0. +-- For a DG module we need this on every hom-degree up to some bound, +-- since Leibniz-via-A.diff does not reduce the check to generators alone: +-- d_M(a x) = d_A(a) x + (-1)^|a| a d_M(x) — even if d_M vanishes on x, +-- we must know d_A^2 = 0 on A to conclude d_M^2 = 0 globally, and in the +-- general case the generator differentials can mix. We therefore check +-- d_{n-1} d_n = 0 up to maxDegree. +isWellDefinedDifferential DGModule := M -> ( + if not isValidDGModule M then return false; + if not isWellDefinedDifferential M.dgAlgebra then return false; + n := maxDegree M; + if n === infinity then ( + A := M.dgAlgebra; + n = sum select(degrees A.natural / first, i -> odd i) + 1; + ); + all(1..max(n,1), i -> moduleDifferential(i-1, M) * moduleDifferential(i, M) == 0) +) + +-- ensureDGAlgebraCaches(M) / invalidateDGAlgebraCache(M): parallel to +-- the DGAlgebra versions. Creates the expected cache subtables so later +-- code can assume they exist; invalidation wipes derived state. +ensureDGAlgebraCaches DGModule := M -> ( + if not M#?(symbol cache) + or M#(symbol cache) === null + or not instance(M#(symbol cache), CacheTable) + then M#(symbol cache) = new CacheTable; + if not M.cache#?(symbol diffs) + or not instance(M.cache#(symbol diffs), MutableHashTable) + then M.cache#(symbol diffs) = new MutableHashTable; + M +) + +invalidateDGAlgebraCache DGModule := M -> ( + ensureDGAlgebraCaches M; + M.cache#(symbol diffs) = new MutableHashTable; + if M.cache#?(symbol dgComplex) then remove(M.cache, symbol dgComplex); + if M.cache#?"moduleBlockDiffs" then remove(M.cache, "moduleBlockDiffs"); + M +) + +------------------------------------------------------------------------- +-- DGModuleMap: morphisms of DG modules over a common DGAlgebra A. +-- +-- A DG module map f: M -> N of hom-degree 0 is: +-- * A-linear: f(a . m) = a . f(m) for a in A.natural, m in M +-- * chain map: d_N( f(m) ) = f( d_M(m) ) +-- Only same-DGA maps (M.dgAlgebra === N.dgAlgebra) are supported. Degree- +-- shifted variants and change-of-algebra maps are deferred (moduleMultMap +-- already provides the degree-shifted ComplexMap use case directly). +-- +-- A-linearity means the action on a general element is determined by the +-- images of the natural generators of M: +-- f( sum a_i e_i ) = sum a_i f(e_i) +-- So the underlying data is stored as a Matrix N.natural <- M.natural +-- (A.natural-linear), whose i-th column is f(e_i). +------------------------------------------------------------------------- + +net DGModuleMap := f -> ( + hdr := {net "Source => " | net f.source.natural}; + hdr = hdr | {net "Target => " | net f.target.natural}; + hdr = hdr | {net "Natural => " | net f.natural}; + horizontalJoin flatten ("{", stack hdr, "}") +) + +target DGModuleMap := f -> f.target +source DGModuleMap := f -> f.source + +-- Internal helper: build a Matrix N.natural <- (A.ring)^(#imgList) whose +-- columns are the given Vectors in N.natural. Handles zero entries. +vectorsToColumnMatrix = (N, imgList) -> ( + if #imgList == 0 then map(N.natural, (ring N.natural)^0, 0) + else ( + -- Each Vector's entries are elements of the ambient ring of N.natural + -- (i.e. A.natural); transposing makes them columns. + rows := apply(imgList, v -> if v === 0 then apply(rank ambient N.natural, i -> 0) else entries v); + transpose matrix rows + ) +) + +dgModuleMap = method(TypicalValue => DGModuleMap) + +-- Primary constructor: image matrix is an A.natural-linear Matrix +-- N.natural <- M.natural, whose i-th column gives f(e_i). +dgModuleMap (DGModule, DGModule, Matrix) := (N, M, img) -> ( + if M.dgAlgebra =!= N.dgAlgebra then + error "dgModuleMap: source and target must share the same DG algebra."; + A := M.dgAlgebra; + -- Coerce the caller-supplied matrix into a proper map N.natural <- M.natural. + natMap := try map(N.natural, M.natural, img) else + error "dgModuleMap: could not coerce matrix into a map N.natural <- M.natural."; + f := new MutableHashTable; + f#(symbol source) = M; + f#(symbol target) = N; + f#(symbol dgAlgebra) = A; + f#(symbol natural) = natMap; + f#(symbol cache) = new CacheTable; + new DGModuleMap from f +) + +-- Convenience constructor: imgList is a list of Vectors in N.natural (or +-- 0), one per M-generator. +dgModuleMap (DGModule, DGModule, List) := (N, M, imgList) -> ( + if M.dgAlgebra =!= N.dgAlgebra then + error "dgModuleMap: source and target must share the same DG algebra."; + if #imgList != #M.Degrees then + error ("dgModuleMap: expected " | toString(#M.Degrees) + | " image entries (one per source generator); got " | toString(#imgList) | "."); + -- Coerce entries to Vectors in N.natural. + coerced := apply(imgList, v -> ( + if v === 0 then 0_(N.natural) + else if instance(v, Vector) then v + else try (sub(v, N.natural)) else + error "dgModuleMap: could not coerce image entry to N.natural." + )); + mat := vectorsToColumnMatrix(N, coerced); + dgModuleMap(N, M, mat) +) + +-- identityDGModuleMap M: the identity map M -> M. Useful as the base of +-- recursive constructions and for testing isWellDefined. +identityDGModuleMap = method(TypicalValue => DGModuleMap) +identityDGModuleMap DGModule := M -> dgModuleMap(M, M, id_(M.natural)) + +-- zeroDGModuleMap(N, M): the zero map M -> N. +zeroDGModuleMap = method(TypicalValue => DGModuleMap) +zeroDGModuleMap (DGModule, DGModule) := (N, M) -> ( + if M.dgAlgebra =!= N.dgAlgebra then + error "zeroDGModuleMap: source and target must share the same DG algebra."; + dgModuleMap(N, M, map(N.natural, M.natural, 0)) +) + +-- Apply a DGModuleMap to an element of M.natural. Returns an element of +-- N.natural. Because f is A-linear, f(v) = f.natural * v. +DGModuleMap Vector := (f, v) -> f.natural * v + +-- Composition: for g: L -> M and f: M -> N, build f * g: L -> N. +DGModuleMap * DGModuleMap := (f, g) -> ( + if target g =!= source f then + error "DGModuleMap composition: target of right operand must equal source of left operand."; + dgModuleMap(target f, source g, f.natural * g.natural) +) + +-- Scalar multiplication: an element of A.ring scales a DGModuleMap. +RingElement * DGModuleMap := (r, f) -> ( + A := f.dgAlgebra; + if ring r =!= A.ring and ring r =!= A.natural then + error "RingElement * DGModuleMap: scalar ring must agree with A.ring or A.natural."; + rA := if ring r === A.natural then r else sub(r, A.natural); + dgModuleMap(target f, source f, rA * f.natural) +) + +-- Addition (same source, same target). +DGModuleMap + DGModuleMap := (f, g) -> ( + if source f =!= source g or target f =!= target g then + error "DGModuleMap +: both maps must have the same source and target."; + dgModuleMap(target f, source f, f.natural + g.natural) +) + +DGModuleMap - DGModuleMap := (f, g) -> ( + if source f =!= source g or target f =!= target g then + error "DGModuleMap -: both maps must have the same source and target."; + dgModuleMap(target f, source f, f.natural - g.natural) +) + +-- Negation. +- DGModuleMap := f -> dgModuleMap(target f, source f, - f.natural) + +-- DGModuleMap ** DGModuleMap: exterior tensor product of chain maps. +-- +-- Given F : M -> M' over A and G : N -> N' over B (with A.ring = B.ring), +-- build F ** G : M ** N -> M' ** N' over A ** B, acting on generators +-- by (e_i ⊗ f_j) -> F(e_i) ⊗ G(f_j). +-- +-- Concretely: F.natural is rM' × rM over A.natural, G.natural is rN' × rN +-- over B.natural. The tensor product has image vectors indexed by (k, l) +-- in the target's generator set of size rM' * rN', with entries +-- coef((e_i ⊗ f_j) -> (e'_k ⊗ f'_l)) = +-- iA(F_{k,i}) * iB(G_{l,j}) +-- where iA, iB are the tensor inclusions into the target's DG algebra. +DGModuleMap ** DGModuleMap := (F, G) -> ( + M := source F; Mp := target F; + N := source G; Np := target G; + if (M.dgAlgebra).ring =!= (N.dgAlgebra).ring or + (Mp.dgAlgebra).ring =!= (Np.dgAlgebra).ring then + error "DGModuleMap **: factors must share a common ground ring on each side."; + P := M ** N; + Pp := Mp ** Np; + Cp := Pp.dgAlgebra; + (iotaAp, iotaBp) := tensorInclusions Cp; + iAp := iotaAp.natural; + iBp := iotaBp.natural; + rM := rank M.natural; rMp := rank Mp.natural; + rN := rank N.natural; rNp := rank Np.natural; + Fmat := F.natural; + Gmat := G.natural; + -- Build the image columns. Column (i,j) of the tensor map sends + -- e_i ⊗ f_j to Σ_{k,l} iA(F_{k,i}) iB(G_{l,j}) (e'_k ⊗ f'_l). + Pgens := apply(rank Pp.natural, m -> (Pp.natural)_m); + idxOf := (k, l) -> k * rNp + l; + imgCols := flatten apply(rM, i -> apply(rN, j -> ( + sum apply(rMp, k -> sum apply(rNp, l -> ( + fki := Fmat_(k, i); + glj := Gmat_(l, j); + if fki == 0 or glj == 0 then 0_(Pp.natural) + else (iAp fki) * (iBp glj) * Pgens#(idxOf(k, l)) + ))) + ))); + dgModuleMap(Pp, P, imgCols) +) + +-- Equality: same source and target DGModules, and equal natural matrices. +DGModuleMap == DGModuleMap := (f, g) -> ( + source f === source g + and target f === target g + and f.natural == g.natural +) + +-- Right-scalar multiplication by a ring element (mirrors left version). +DGModuleMap * RingElement := (f, r) -> r * f + +-- ZZ / QQ / Number scalar multiplication (coerced through the DG algebra's +-- underlying polynomial ring so arithmetic lives in A.natural). +ZZ * DGModuleMap := (n, f) -> (sub(n, (f.dgAlgebra).natural)) * f +QQ * DGModuleMap := (q, f) -> (sub(q, (f.dgAlgebra).natural)) * f +Number * DGModuleMap := (n, f) -> (sub(n, (f.dgAlgebra).natural)) * f +DGModuleMap * ZZ := (f, n) -> n * f +DGModuleMap * QQ := (f, q) -> q * f +DGModuleMap * Number := (f, n) -> n * f + +-- Compare a DGModuleMap to 0 (zero map) or 1 (identity on source = target). +DGModuleMap == ZZ := (f, n) -> ( + if n == 0 then f.natural == 0 + else if n == 1 then (source f === target f and f.natural == id_((source f).natural)) + else error "DGModuleMap == ZZ: comparison only defined against 0 or 1." +) +ZZ == DGModuleMap := (n, f) -> f == n + +-- Base ring of the underlying DG algebra. +ring DGModuleMap := f -> (f.dgAlgebra).ring + +-- Chain-degree of a DGModuleMap: our DGModuleMaps are always degree-0 +-- chain maps (they commute with d strictly). +degree DGModuleMap := f -> 0 + +-- Injective / surjective / isomorphism tests go through the DGSubmodule +-- and DGQuotientModule machinery. +isInjective DGModuleMap := f -> isZero kernel f +isSurjective DGModuleMap := f -> isZero cokernel f +isIsomorphism DGModuleMap := f -> isInjective f and isSurjective f + +-- id_(M) for a DGModule M -- same dispatch pattern used by Complexes for +-- id_(C). Produces the identity DGModuleMap M -> M. +DGModule#id = M -> identityDGModuleMap M + +-- isHomogeneous: true iff f.natural is homogeneous as an A.natural-matrix. +isHomogeneous DGModuleMap := f -> isHomogeneous f.natural + +-- map(N, M, ZZ): degree-shifted identity / zero. Matches the ComplexMap +-- constructor map(D, C, i) where i=0 builds the identity (and requires +-- D == C) and i=1 is allowed only when both sides agree generator-by- +-- generator. For simplicity we support the two common cases: +-- map(M, M, 1) ~ identityDGModuleMap M +-- map(N, M, 0) ~ zeroDGModuleMap(M, N) +-- (Note the ComplexMap convention: the target comes first.) +map (DGModule, DGModule, ZZ) := o -> (N, M, i) -> ( + if i == 0 then zeroDGModuleMap(N, M) + else if i == 1 then ( + if N =!= M then + error "map(N, M, 1): identity requested but source and target differ."; + identityDGModuleMap M + ) + else error "map(DGModule, DGModule, ZZ): only 0 (zero map) and 1 (identity) are supported." +) + +-- isQuasiIsomorphism f: defer to the ComplexMap version applied to +-- toComplexMap f. Inherits the Concentration option from Complexes. +isQuasiIsomorphism DGModuleMap := o -> f -> isQuasiIsomorphism(toComplexMap f, o) + +-- isQuasiIsomorphism for DGAlgebraMap, same strategy. +isQuasiIsomorphism DGAlgebraMap := o -> f -> isQuasiIsomorphism(toComplexMap f, o) + +-- isWellDefined DGModuleMap. Modeled on isWellDefined ComplexMap +-- (Complexes/ChainComplexMap.m2 line 134). Checks: +-- (1) expected key shape (source, target, dgAlgebra, natural, cache), +-- (2) types of each slot, +-- (3) source/target share the same DGAlgebra, +-- (4) f.natural is a module map with the right source/target modules, +-- (5) hom-degrees are preserved (f is a hom-degree-0 chain map), +-- (6) chain-map condition on every generator: d_N(f(e_i)) == f(d_M(e_i)). +-- Diagnostic messages emitted when debugLevel > 0. +isWellDefined DGModuleMap := f -> ( + k := keys f; + expectedKeys := set {symbol source, symbol target, symbol dgAlgebra, symbol natural, symbol cache}; + if set k =!= expectedKeys then ( + if debugLevel > 0 then ( + added := toList(k - expectedKeys); + missing := toList(expectedKeys - k); + if #added > 0 then << "-- DGModuleMap: unexpected key(s): " << toString added << endl; + if #missing > 0 then << "-- DGModuleMap: missing key(s): " << toString missing << endl; + ); + return false; + ); + if not (instance(f.source, DGModule) or instance(f.source, DGQuotientModule)) then ( + if debugLevel > 0 then << "-- DGModuleMap: f.source is not a DGModule or DGQuotientModule" << endl; + return false; + ); + if not (instance(f.target, DGModule) or instance(f.target, DGQuotientModule)) then ( + if debugLevel > 0 then << "-- DGModuleMap: f.target is not a DGModule or DGQuotientModule" << endl; + return false; + ); + if instance(f.source, DGModule) and not isWellDefined f.source then ( + if debugLevel > 0 then << "-- DGModuleMap: f.source is not a well-defined DGModule" << endl; + return false; + ); + if instance(f.source, DGQuotientModule) and not isWellDefined f.source then ( + if debugLevel > 0 then << "-- DGModuleMap: f.source is not a well-defined DGQuotientModule" << endl; + return false; + ); + -- DGQuotientModule targets go through a slightly different path; we + -- still require the ambient to be well-defined. + if instance(f.target, DGModule) and not isWellDefined f.target then ( + if debugLevel > 0 then << "-- DGModuleMap: f.target is not a well-defined DGModule" << endl; + return false; + ); + if instance(f.target, DGQuotientModule) and not isWellDefined f.target then ( + if debugLevel > 0 then << "-- DGModuleMap: f.target is not a well-defined DGQuotientModule" << endl; + return false; + ); + if f.source.dgAlgebra =!= f.target.dgAlgebra then ( + if debugLevel > 0 then << "-- DGModuleMap: source and target must share the same DGAlgebra" << endl; + return false; + ); + if f.dgAlgebra =!= f.source.dgAlgebra then ( + if debugLevel > 0 then << "-- DGModuleMap: f.dgAlgebra disagrees with f.source.dgAlgebra" << endl; + return false; + ); + if not instance(f.natural, Matrix) then ( + if debugLevel > 0 then << "-- DGModuleMap: f.natural is not a Matrix" << endl; + return false; + ); + if source f.natural =!= (f.source).natural then ( + if debugLevel > 0 then << "-- DGModuleMap: source of f.natural is not f.source.natural" << endl; + return false; + ); + if target f.natural =!= (f.target).natural then ( + if debugLevel > 0 then << "-- DGModuleMap: target of f.natural is not f.target.natural" << endl; + return false; + ); + M := source f; + N := target f; + -- Hom-degree check: for each M-generator e_i, f(e_i) must sit in + -- hom-degree (first M.Degrees#i) of N.natural. Internal-degree shifts + -- are allowed (only the first component matters). + homDegOk := all(#M.Degrees, i -> ( + fei := f.natural * (M.natural)_i; + if fei == 0 then true + else first degree fei == first M.Degrees#i + )); + if not homDegOk then ( + if debugLevel > 0 then << "-- DGModuleMap: f fails to preserve homological degree on some generator" << endl; + return false; + ); + -- Chain-map condition, generator by generator. diff(N, ·) calls + -- on DGModule or DGQuotientModule (both supply the Leibniz formula). + ok := all(#M.Degrees, i -> ( + ei := (M.natural)_i; + fei := f.natural * ei; + dMei := M.diff#i; + dNfei := diff(N, fei); + fdMei := f.natural * dMei; + dNfei == fdMei + )); + if not ok and debugLevel > 0 then + << "-- DGModuleMap: chain-map condition d_N(f e_i) = f(d_M e_i) fails on some generator" << endl; + ok +) + +-- toComplexMap(f, n): the n-th component of f as an A.ring-linear map +-- F_n(M) -> F_n(N), where F_n(M) is the hom-deg-n piece of M.natural +-- expressed in its A.ring-basis (moduleBasisPairs). +-- +-- Strategy: enumerate srcPairs = basis of F_n(M); for each (i, mon) in +-- srcPairs, compute f(mon * e_i) = mon * (f.natural * e_i) in N.natural, +-- then decompose over the target basis. +toComplexMap (DGModuleMap, ZZ) := opts -> (f, n) -> ( + M := source f; + N := target f; + A := M.dgAlgebra; + R := A.ring; + srcPairs := moduleBasisPairs(M, n); + tgtPairs := moduleBasisPairs(N, n); + -- Source/target free R-modules are canonical via moduleDifferential's + -- source/target, but we build them independently to avoid forcing the + -- differential computation just for module shape. + srcDeg := p -> ( + (i, m) := p; + totalDeg := degree(m * (M.natural)_i); + -drop(totalDeg, 1) + ); + tgtDeg := p -> ( + (j, m) := p; + totalDeg := degree(m * (N.natural)_j); + -drop(totalDeg, 1) + ); + srcDegs := apply(srcPairs, srcDeg); + tgtDegs := apply(tgtPairs, tgtDeg); + srcMod := R^srcDegs; + tgtMod := R^tgtDegs; + if #srcPairs == 0 or #tgtPairs == 0 then + return map(tgtMod, srcMod, 0); + numSrc := #srcPairs; + numTgt := #tgtPairs; + -- Precompute f(e_i) as Vectors in N.natural. + fImages := apply(rank M.natural, i -> f.natural * (M.natural)_i); + -- Group target pairs by N-generator index. + tgtByGen := new MutableHashTable; + scan(rank N.natural, j -> tgtByGen#j = {}); + scan(numTgt, k -> ( + (j, mp) := tgtPairs#k; + tgtByGen#j = append(tgtByGen#j, (k, mp)); + )); + mat := mutableMatrix(R, numTgt, numSrc); + scan(numSrc, c -> ( + (i, mon) := srcPairs#c; + imgVec := mon * fImages#i; + if imgVec == 0 then return; + imgEntries := entries imgVec; + scan(rank N.natural, j -> ( + fj := imgEntries#j; + if fj == 0 then return; + rowsForJ := tgtByGen#j; + if #rowsForJ == 0 then return; + tgtMonsJ := apply(rowsForJ, pr -> pr#1); + coefMat := (coefficients(matrix{{fj}}, Monomials => matrix{tgtMonsJ}))#1; + coefMatR := sub(coefMat, R); + scan(#rowsForJ, idx -> ( + rowIdx := (rowsForJ#idx)#0; + cij := coefMatR_(idx, 0); + if cij != 0 then mat_(rowIdx, c) = cij; + )); + )); + )); + map(tgtMod, srcMod, matrix mat) +) +-- toComplexMap(f, n) returns a Matrix (per-degree component), while the +-- method's default TypicalValue is ComplexMap. Inform the documentation +-- system of the correct per-signature return type. +typicalValues#(toComplexMap, DGModuleMap, ZZ) = Matrix + +-- toComplexMap f: assemble all per-degree pieces into a ComplexMap +-- toComplex M -> toComplex N. An EndDegree bound is required when either +-- side has infinite hom-degree. +toComplexMap DGModuleMap := opts -> f -> ( + M := source f; + N := target f; + -- Specialized branch: target is a DGQuotientModule. At the DGModule + -- level a DGQuotientModule has no free basis for `moduleBasisPairs`, + -- so the generic per-degree assembly below would give the wrong + -- answer. For the canonical projection f = Q.projection the chain + -- map is exactly the induced quotient map from the ambient complex + -- to the cokernel of the inclusion. We only support that case. + if instance(N, DGQuotientModule) then ( + if not (N.?projection and f === N.projection) then + error "toComplexMap: into a DGQuotientModule only the canonical projection is supported."; + cxI := toComplexMap(N.subDGModule.inclusion, opts); + return inducedMap(cokernel cxI, target cxI); + ); + mDeg := maxDegree M; + nDeg := maxDegree N; + maxDeg := if mDeg === infinity or nDeg === infinity then infinity + else max(mDeg, nDeg); + if opts.EndDegree != -1 then maxDeg = opts.EndDegree; + if maxDeg === infinity then + error "toComplexMap DGModuleMap: hom-degrees are unbounded; supply EndDegree."; + if opts.AssertWellDefined then assert isWellDefined f; + cxM := toComplex(M, maxDeg); + cxN := toComplex(N, maxDeg); + map(cxN, cxM, i -> toComplexMap(f, i, opts), Degree => 0) +) -doc /// - Key - findTrivialMasseyOperation - findNaryTrivialMasseyOperation - (findTrivialMasseyOperation,DGAlgebra) - (findNaryTrivialMasseyOperation,DGAlgebra,List,HashTable,ZZ) - Headline - Finds a trivial Massey operation on a set of generators of H(A) - Usage - tmo = findTrivialMasseyOperation(A) - Inputs - A:DGAlgebra - Outputs - seq:Sequence - A sequence seq whose first entry reports whether a trivial Massey operation has been found, and the second - entry is a hash table with keys given by monomials in a generating set of the positive degree homology of - A and values the element that bounds the Massey product corresponding to that monomial. - Description - Text - This function the element that bounds all potentially nonzero Massey products (before taking homology class). - The maximum degree of a generating cycle is specified in the option GenDegreeLimit, if needed. - Text - Golod rings are defined by being those rings whose Koszul complex K^R has a trivial Massey operation. - Also, the existence of a trivial Massey operation on a DG algebra A forces the multiplication on H(A) - to be trivial. An example of a ring R such that H(K^R) has trivial multiplication, yet K^R does not admit - a trivial Massey operation is unknown. Such an example cannot be monomially defined, by a result of - Jollenbeck and Berglund. - Text - This is an example of a Golod ring. It is Golod since it is the Stanley-Reisner ideal of a flag complex - whose 1-skeleton is chordal [Jollenbeck-Berglund]. - Example - Q = ZZ/101[x_1..x_6] - I = ideal (x_3*x_5,x_4*x_5,x_1*x_6,x_3*x_6,x_4*x_6) - R = Q/I - A = koszulComplexDGA(R) - isHomologyAlgebraTrivial(A,GenDegreeLimit=>3) - cycleList = getGenerators(A) - (hasTMO, tmoSoFar) = findTrivialMasseyOperation(A) - assert(hasTMO) - Text - Below is an example of a Teter ring (Artinian Gorenstein ring modulo its socle), and the computation in Avramov and Levin's - paper shows that H(A) does not have trivial multiplication, hence no trivial Massey operation can exist. - Example - Q = ZZ/101[x,y,z] - I = ideal (x^3,y^3,z^3,x^2*y^2*z^2) - R = Q/I - A = koszulComplexDGA(R) - isHomologyAlgebraTrivial(A) - cycleList = getGenerators(A) - assert(not first findTrivialMasseyOperation(A)) - Text - The related function @ TO findNaryTrivialMasseyOperation @ find only the nth order trivial Massey operations. -/// +-- homology(f, n): the induced map H_n(M) -> H_n(N) as an R-module map. +homology (DGModuleMap, ZZ) := opts -> (f, n) -> ( + M := source f; + N := target f; + dMn := moduleDifferential(n, M); + dMnp1 := moduleDifferential(n + 1, M); + dNn := moduleDifferential(n, N); + dNnp1 := moduleDifferential(n + 1, N); + hM := homology(dMn, dMnp1); + hN := homology(dNn, dNnp1); + cm := toComplexMap(f, n); + inducedMap(hN, hM, cm) +) -doc /// - Key - masseyTripleProduct - (masseyTripleProduct,DGAlgebra,RingElement,RingElement,RingElement) - Headline - Computes the Massey triple product of a set of cycles or homology classes - Usage - h = masseyTripleProduct(A,h1,h2,h3) - Inputs - A:DGAlgebra - h1:RingElement - h2:RingElement - h3:RingElement - Outputs - h:RingElement - The return value is either the homology class of the Massey triple product defined - by the inputs or a cycle representing the homology class. - Description - Text - These functions compute the Massey triple product of either three homology classes - or three cycles that represent nonzero homology classes for which the Massey triple product - is defined. - Text - For an example, we return to an example due to Lukas Katthan which was discussed in @ TO isGolod @. - First, we define the algebra: - Example - Q = QQ[x_1,x_2,y_1,y_2,z] - I = ideal (x_1*x_2^2,y_1*y_2^2,z^3,x_1*x_2*y_1*y_2,y_2^2*z^2,x_2^2*z^2,x_1*y_1*z,x_2^2*y_2^2*z) - R = Q/I - KR = koszulComplexDGA R - Text - The following are cycles: - Example - z1 = z^2*T_5 - z2 = y_2^2*T_3 - z3 = x_2^2*T_1 - Text - and z1*z2, z2*z3 vanish in homology: - Example - (lifted12,lift12) = getBoundaryPreimage(KR,z1*z2) - (lifted23,lift23) = getBoundaryPreimage(KR,z2*z3) - Text - Note that the first return value of @ TO getBoundaryPreimage @ indicates that the inputs - are indeed boundaries, and the second value is the lift of the boundary along the differential. - Text - Given cycles z1,z2,z3 such that z1*z2 and z2*z3 are boundaries, - the Massey triple product of the homology classes represented by z1,z2 and z3 - is the homology class of lift12*z3 + z1*lift23. To see this, we compute and check: - Example - z123 = masseyTripleProduct(KR,z1,z2,z3) - z123 == lift12*z3 + z1*lift23 - Text - One may also compute Massey triple products directly on elements of the homology - algebra itself, as is seen with the command masseyTripleProduct: - Example - H = HH(KR) - h1 = homologyClass(KR,z1) - h2 = homologyClass(KR,z2) - h3 = homologyClass(KR,z3) - h123 = masseyTripleProduct(KR,h1,h2,h3) - h123 == homologyClass(KR,z123) -/// +-- homology f: the induced H_*(A)-module map H_*(M) -> H_*(N). +-- +-- Conventions mirror `homology DGAlgebraMap` (which returns a RingMap +-- HA -> HB). For f : M -> N a DGModuleMap, the underlying A.natural- +-- linear chain map induces an HA-linear map on H_*(-). We assemble that +-- map degree-by-degree from the pruned per-degree homologies of +-- toComplexMap f, then tensor up to HA to match the free HA-presentation +-- used by `homologyModule DGModule`. +-- +-- For users who want the bare ComplexMap of homologies (no HA-module +-- structure), use `HH toComplexMap f` directly. +-- +-- Restriction: currently requires both M.natural and N.natural to be +-- free A.natural-modules (this matches the general path of +-- homologyModule DGModule). The koszulComplexDGM-with-M.module fast +-- path is not yet handled here. +homology DGModuleMap := opts -> f -> ( + M := source f; + N := target f; + if M.dgAlgebra =!= N.dgAlgebra then + error "homology DGModuleMap: source and target must share a DG algebra."; + A := M.dgAlgebra; + R := A.ring; + HA := HH(A); + if not isFreeModule M.natural or not isFreeModule N.natural then + error "homology DGModuleMap: currently requires both source and target to be free DGModules (koszulComplexDGM form not yet supported)."; + cm := toComplexMap f; + cxM := source cm; + cxN := target cm; + (loM, hiM) := concentration cxM; + (loN, hiN) := concentration cxN; + -- Pruned per-degree homologies on each side (matches the basis used + -- by moduleRelationsFromCycleActionDGM, so degrees line up). + pruneHM := new MutableHashTable; + for i from loM to hiM do pruneHM#i = prune HH_i(cxM); + pruneHN := new MutableHashTable; + for j from loN to hiN do pruneHN#j = prune HH_j(cxN); + -- Pruned per-degree maps induced by f. + prunedMap := new MutableHashTable; + for i from max(loM, loN) to min(hiM, hiN) do + prunedMap#i = prune HH_i(cm); + -- Target-degree lists of the raw HA-free modules covering + -- homologyModule M and homologyModule N, respectively. + degsHMSource := flatten apply(toList(loM..hiM), + p -> apply(degrees pruneHM#p, dd -> {p} | dd)); + degsHNTarget := flatten apply(toList(loN..hiN), + p -> apply(degrees pruneHN#p, dd -> {p} | dd)); + rowsCount := hiN - loN + 1; + colsCount := hiM - loM + 1; + buildBlock := (rowIdx, colIdx) -> ( + i := loM + colIdx; + j := loN + rowIdx; + if i != j or not prunedMap#?i then + map(pruneHN#j, pruneHM#i, 0) + else ( + pm := prunedMap#i; + -- Make sure source / target agree with the pruneHM / pruneHN + -- objects (they usually do via M2's caching, but coerce to + -- be safe). + if source pm === pruneHM#i and target pm === pruneHN#j then pm + else map(pruneHN#j, pruneHM#i, matrix pm) + ) + ); + rawBlock := if rowsCount == 0 or colsCount == 0 then + map(R^(-degsHNTarget), R^(-degsHMSource), 0) + else + matrix table(rowsCount, colsCount, buildBlock); + -- Base-change to HA. + haBlock := map(HA^(-degsHNTarget), HA^(-degsHMSource), + tensor(map(HA, R), rawBlock)); + -- Build the raw cokers on each side (same construction as + -- homologyModule DGModule, but without minimalPresentation), then + -- induce the map and prune to match homologyModule's output. + HMraw := coker matrix {apply(HA.cache.cycles, + z -> moduleRelationsFromCycleActionDGM(M, z))}; + HNraw := coker matrix {apply(HA.cache.cycles, + z -> moduleRelationsFromCycleActionDGM(N, z))}; + raw := map(HNraw, HMraw, haBlock); + minimalPresentation raw +) -doc /// - Key - (masseyTripleProduct,DGAlgebra,ZZ,ZZ,ZZ) - Headline - Computes the matrix representing all triple Massey operations. - Usage - mat = masseyTripleProduct(A,l,m,n) - Inputs - A:DGAlgebra - l:ZZ - m:ZZ - n:ZZ - Outputs - mat:Matrix - Description - Text - Given a triple of homology classes h1,h2,h3, such that h1h2 = h2h3 = 0, - the Massey triple product of h1,h2 and h3 may be defined as in - @ TO masseyTripleProduct @. This command computes a basis of the homology - algebra of A in degrees l,m and n respectively, and expresses the triple - Massey operation of each triple, provided it is defined. If a triple product - is not defined (i.e. if either h1h2 or h2h3 is not zero) then the triple - product is reported as zero in the matrix. - Text - The following example appears in "On the Hopf algebra of a Local Ring" by Avramov - as an example of a nonvanishing Massey operation which an algebra generator: - Example - Q = QQ[t_1,t_2,t_3,t_4] - I = ideal (t_1^3,t_2^3,t_3^3-t_1*t_2^2,t_1^2*t_3^2,t_1*t_2*t_3^2,t_2^2*t_4,t_4^2) - R = Q/I - KR = koszulComplexDGA R - H = HH(KR) - masseys = masseyTripleProduct(KR,1,1,1); - rank masseys - Text - As you can see, this command is useful to determine the number of linearly independent - elements that arise as triple Massey products. - Text - For example, the following Massey triple product is nonvanishing and is an - algebra generator: - Example - masseyTripleProduct(KR,X_2,X_4,X_1) -/// +------------------------------------------------------------------------- +-- liftToDGModuleMap(N, M, h0): lift an R-module map h0 on the degree-0 +-- generator piece of M to a full DGModuleMap M -> N. +-- +-- Requires: +-- * M semifree (M.natural a free A.natural-module). +-- * N acyclic in positive hom-degrees up to EndDegree (so successive +-- Leibniz-style lifts exist). +-- h0 is a Matrix over A.natural with: +-- * rows indexing natural generators of N +-- * columns indexing the hom-degree-0 generators of M (in positional +-- order within M.Degrees) +-- For each hom-degree d >= 1, we lift generator-by-generator: given an +-- M-generator e_i of hom-degree d, compute bd = f_partial( d_M(e_i) ) in +-- hom-degree (d-1) of N.natural, then find an x in hom-degree d of +-- N.natural with d_N(x) = bd. x becomes f(e_i). +-- +-- EndDegree caps the lift; M-generators of hom-degree > EndDegree are +-- sent to 0 (and the result may fail isWellDefined beyond EndDegree). +------------------------------------------------------------------------- +liftToDGModuleMap = method(TypicalValue => DGModuleMap, Options => {EndDegree => -1}) +liftToDGModuleMap (DGModule, DGModule, Matrix) := opts -> (N, M, h0) -> ( + if M.dgAlgebra =!= N.dgAlgebra then + error "liftToDGModuleMap: source and target must share the same DG algebra."; + if not isFreeModule M.natural then + error "liftToDGModuleMap: source M must be semifree (use freeDGModule/adjoinGenerators)."; + numGenM := #M.Degrees; + genDegs := apply(M.Degrees, first); + maxDeg := if #genDegs == 0 then 0 else max genDegs; + if opts.EndDegree != -1 then maxDeg = opts.EndDegree; + -- Group M-generator indices by hom-degree. + genIdxByDeg := new MutableHashTable; + scan(numGenM, i -> ( + d := genDegs#i; + if not genIdxByDeg#?d then genIdxByDeg#d = {}; + genIdxByDeg#d = append(genIdxByDeg#d, i); + )); + deg0Indices := if genIdxByDeg#?0 then genIdxByDeg#0 else {}; + if numcols h0 != #deg0Indices then + error ("liftToDGModuleMap: h0 must have " | toString(#deg0Indices) + | " columns (one per hom-degree-0 M-generator); got " + | toString(numcols h0) | "."); + if numrows h0 != rank N.natural then + error ("liftToDGModuleMap: h0 must have " | toString(rank N.natural) + | " rows (one per natural generator of N); got " + | toString(numrows h0) | "."); + -- images#i = Vector in N.natural (current image of M-gen i), or null + images := new MutableList from apply(numGenM, i -> null); + -- Seed hom-deg-0 images from h0. + scan(#deg0Indices, k -> ( + col := apply(rank N.natural, j -> h0_(j, k)); + vec := sum apply(rank N.natural, j -> col#j * (N.natural)_j); + images#(deg0Indices#k) = vec; + )); + -- Helper: apply partial f to a Vector v in M.natural. + applyPartial := v -> ( + if v === 0 or v == 0 then return 0_(N.natural); + vEntries := entries v; + sum apply(numGenM, j -> ( + cj := vEntries#j; + if cj == 0 then 0_(N.natural) + else if images#j === null then ( + error "liftToDGModuleMap: encountered M-generator with unassigned image while lifting."; + ) + else cj * images#j + )) + ); + for d from 1 to maxDeg do ( + if not genIdxByDeg#?d then continue; + for i in genIdxByDeg#d do ( + bd := applyPartial(M.diff#i); + if bd == 0 then ( + images#i = 0_(N.natural); + ) + else ( + (ok, pre) := getBoundaryPreimage(N, bd); + if not ok then + error ("liftToDGModuleMap: cannot lift M-generator #" | toString i + | " at hom-degree " | toString d + | " (target N is not acyclic at this degree)."); + images#i = pre; + ); + ); + ); + -- Fill in zeros for any M-generator beyond EndDegree. + finalImgs := apply(numGenM, i -> if images#i === null then 0_(N.natural) else images#i); + dgModuleMap(N, M, finalImgs) +) -doc /// - Key - expandGeomSeries - (expandGeomSeries,List,ZZ) - (expandGeomSeries,RingElement,ZZ) - Headline - Expand a geometric series to a specified degree. - Usage - pSeries = expandGeomSeries(f,n) - Inputs - f:RingElement - Ratio of the geometric series to be expanded. - n:ZZ - Degree which to expand the geometric series. - Outputs - pSeries:RingElement - Power series representation of the geometric series. - Description - Text - If the user supplies a list instead of a RingElement as the first argument, the return - value is the product of all the each of the geometric series expanded to degree n obtained - by calling expandGeomSeries on each element of the list. - Example - A = ZZ[S,T_0,T_1] - f = expandGeomSeries(S^2*T_0^8,10) - g = expandGeomSeries(S^4*T_1^15,10) - h = expandGeomSeries({S^2*T_0^8,S^4*T_1^15},10) - B = A/(first gens A)^11 - substitute(f*g,B) == h -/// +-- Convenience: if M has exactly one hom-deg-0 generator mapping to a +-- Vector v in N.natural, accept v directly. +liftToDGModuleMap (DGModule, DGModule, Vector) := opts -> (N, M, v) -> ( + genDegs := apply(M.Degrees, first); + deg0Count := #select(genDegs, d -> d == 0); + if deg0Count != 1 then + error ("liftToDGModuleMap(Vector): M has " | toString deg0Count + | " hom-degree-0 generators; use the Matrix or List form instead."); + liftToDGModuleMap(N, M, {v}, opts) +) -doc /// - Key - torMap - (torMap,RingMap) - [torMap,GenDegreeLimit] - Headline - Compute the map of Tor algebras associated to a RingMap. - Usage - torPhi = torMap(phi) - Inputs - phi:RingMap - Outputs - torPhi:RingMap - Description - Text - The functor Tor_R(M,N) is also functorial in the ring argument. Therefore, a ring map phi from A to B induces an algebra map - from the Tor algebra of A to the Tor algebra of B. - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3,a^2*b^2*c^2} - S = R/ideal{a*b^2*c^2,a^2*b*c^2,a^2*b^2*c} - f = map(S,R) - fTor = torMap(f,GenDegreeLimit=>3) - matrix fTor - Text - In the following example, the map on Tor is surjective, which means that the ring homomorphism is large (Dress-Kramer). - Example - R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3,a*c,a*d,b*c,b*d} - S = ZZ/101[a,b]/ideal{a^3,b^3} - f = map(S,R,matrix{{a,b,0,0}}) - fTor = torMap(f,GenDegreeLimit=>4) - matrix fTor -/// +-- Convenience: pass a list of Vectors, one per hom-degree-0 M-generator. +liftToDGModuleMap (DGModule, DGModule, List) := opts -> (N, M, vList) -> ( + if M.dgAlgebra =!= N.dgAlgebra then + error "liftToDGModuleMap: source and target must share the same DG algebra."; + genDegs := apply(M.Degrees, first); + deg0Count := #select(genDegs, d -> d == 0); + if #vList != deg0Count then + error ("liftToDGModuleMap(List): M has " | toString deg0Count + | " hom-degree-0 generators; got " | toString(#vList) | " images."); + coerced := apply(vList, v -> ( + if v === 0 then 0_(N.natural) + else if instance(v, Vector) then v + else try (sub(v, N.natural)) else + error "liftToDGModuleMap(List): could not coerce image to N.natural." + )); + h0 := if #coerced == 0 then map(N.natural, (N.ring)^0, 0) + else vectorsToColumnMatrix(N, coerced); + liftToDGModuleMap(N, M, h0, opts) +) -doc /// - Key - (diff,DGAlgebra,RingElement) - Headline - Computes the differential of a ring element in a DGAlgebra - Usage - b = diff(A,a) - Inputs - A:DGAlgebra - a:RingElement - Outputs - b:RingElement -/// +------------------------------------------------------------------------- +-- v2: DGIdeal, DGSubmodule, DGQuotientModule, and +-- image / kernel / cokernel of DGModuleMap. +-- +-- Conventions. These types mirror M2's built-in Ideal / Module / quotient +-- conventions wherever possible, and add a differential-compatibility +-- layer on top: +-- +-- * DGIdeal -- a graded ideal of A.natural closed under d_A. +-- `ambient` returns A; `ideal I` returns the +-- underlying Ideal. `A / I` yields a new DGAlgebra. +-- +-- * DGSubmodule -- a DGModule S equipped with an inclusion +-- DGModuleMap S -> M. S.natural is a free A.natural- +-- module on the chosen generators; `inclusion S` +-- returns the chain-map S -> M. Because S inherits +-- from DGModule, generic DGModule operations apply. +-- +-- * DGQuotientModule -- quotient M / S of a DGModule M by a DGSubmodule S. +-- Q.natural is the cokernel of the inclusion matrix, +-- so it is NOT a free module; Q does NOT inherit +-- from DGModule (to avoid silently breaking free- +-- only operations). `ambient Q` returns M, +-- `subDGModule Q` returns S, `projection Q` returns +-- the DGModuleMap M -> Q. +-- +-- All three carry d-closure invariants; isWellDefined verifies them. +------------------------------------------------------------------------- + +DGIdeal = new Type of MutableHashTable +globalAssignment DGIdeal + +DGSubmodule = new Type of DGModule +globalAssignment DGSubmodule + +DGQuotientModule = new Type of MutableHashTable +globalAssignment DGQuotientModule + +-- Pretty-printing -------------------------------------------------------- + +net DGIdeal := I -> ( + hdr := net "DGIdeal of " | net I.dgAlgebra.natural; + gs := net I.generators; + stack {hdr, net "generators => " | gs} +) -doc /// - Key - getBoundaryPreimage - (getBoundaryPreimage,DGAlgebra,RingElement) - (getBoundaryPreimage,DGAlgebra,List) - Headline - Attempt to find a preimage of a boundary under the differential of a DGAlgebra. - Usage - (lifted,myLift) = getBoundaryPreimage(A,z) - Inputs - A:DGAlgebra - z:RingElement - Outputs - seq:Sequence - Description - Text - The first element in the return value is a boolean value indicating whether the - lift was possible. If true, the second coordinate of the return value is the lift. - If false, then the second coordinate of the return value is the reduction of the - input modulo the image. - Example - Q = QQ[x_1,x_2,y_1,y_2,z] - I = ideal (x_1*x_2^2,y_1*y_2^2,z^3,x_1*x_2*y_1*y_2,y_2^2*z^2,x_2^2*z^2,x_1*y_1*z,x_2^2*y_2^2*z) - R = Q/I - KR = koszulComplexDGA R - Text - The following are cycles: - Example - z1 = z^2*T_5 - z2 = y_2^2*T_3 - z3 = x_2^2*T_1 - {diff(KR,z1),diff(KR,z1),diff(KR,z1)} - Text - and z1*z2, z2*z3 vanish in homology: - Example - (lifted12,lift12) = getBoundaryPreimage(KR,z1*z2) - (lifted23,lift23) = getBoundaryPreimage(KR,z2*z3) - Text - We can check that the differential of the lift is the supposed boundary: - Example - diff(KR,lift23) == z2*z3 -/// +net DGSubmodule := S -> ( + stack { + net "DGSubmodule of ambient DGModule", + net "Degrees => " | net S.Degrees, + net "natural => " | net S.natural, + net "inclusion => " | net S.inclusion.natural + } +) -doc /// - Key - homologyClass - (homologyClass,DGAlgebra,RingElement) - Headline - Computes the element of the homology algebra corresponding to a cycle in a DGAlgebra. - Usage - h = homologyClass(A,z) - Inputs - A:DGAlgebra - z:RingElement - Outputs - h:RingElement - Description - Text - This function computes the element in the homology algebra of a cycle in a @ TO DGAlgebra @. - In order to do this, the @ TO homologyAlgebra @ is retrieved (or computed, if it hasn't been - already). - Example - Q = QQ[x,y,z] - I = ideal (x^3,y^3,z^3) - R = Q/I - KR = koszulComplexDGA R - z1 = x^2*T_1 - z2 = y^2*T_2 - H = HH(KR) - homologyClass(KR,z1*z2) -/// +net DGQuotientModule := Q -> ( + stack { + net "DGQuotientModule Q = M / S", + net "Q.natural = " | net Q.natural, + net "Degrees = " | net Q.Degrees + } +) -doc /// - Key - zerothHomology - (zerothHomology,DGAlgebra) - Headline - Compute the zeroth homology of the DGAlgebra A as a ring. - Usage - HA0 = zerothHomology A - Inputs - A:DGAlgebra - Outputs - HA0:Ring - HH_0(A) as a ring. -/// +------------------------------------------------------------------------- +-- DGIdeal +-- +-- dgIdeal(A, gens) takes a List or Matrix of homogeneous elements of +-- A.natural and returns the smallest DG ideal containing them, i.e. +-- the ideal of A.natural generated by {g, d(g), d^2(g), ...} iterated +-- until the ideal (as an A.natural-Ideal) stabilizes. +-- +-- isDGIdeal(A, I) (A:DGAlgebra, I:Ideal) checks d-closure without +-- saturation. +------------------------------------------------------------------------- + +dgIdeal = method(TypicalValue => DGIdeal) + +isDGIdeal = method(TypicalValue => Boolean) +isDGIdeal (DGAlgebra, Ideal) := (A, I) -> ( + if ring I =!= A.natural then + error "isDGIdeal: ideal must live in A.natural."; + all(flatten entries mingens I, g -> (polyDifferential(A, g)) % I == 0) +) -doc /// - Key - getDegNModule - (getDegNModule,ZZ,Ring,Ring) - Headline - Compute a presentation of M_i as an R-module - Usage - M' = getDegNModule(N,R,M) - M' = getDegNModule(N,R,A) - Inputs - N:ZZ - R:Ring - M:Module - M is a Module over a ring A, and A must be a graded R-algebra with A_0 = R - A:Ring - A must be a graded R-algebra, and A_0 = R - Outputs - M':Module - M_N as an R = A_0-module -/// +dgIdeal (DGAlgebra, Ideal) := (A, I) -> ( + if ring I =!= A.natural then + error "dgIdeal: ideal must live in A.natural."; + -- Iteratively d-close. + curr := I; + iter := 0; + while ( + iter = iter + 1; + if iter > 200 then error "dgIdeal: d-closure failed to stabilize."; + derivs := apply(flatten entries mingens curr, g -> polyDifferential(A, g)); + newIdeal := curr + ideal derivs; + newIdeal != curr + ) do curr = trim newIdeal; + J := trim curr; + result := new MutableHashTable; + result#(symbol dgAlgebra) = A; + result#(symbol ring) = A.ring; + result#(symbol natural) = J; + result#(symbol generators) = gens J; + result#(symbol cache) = new CacheTable; + new DGIdeal from result +) -doc /// - Key - DGAlgebraMap - ringMap - Headline - The class of all DG Algebra maps - Description - Text - A common way to create a DGAlgebraMap is via @ TO liftToDGMap @. -/// +dgIdeal (DGAlgebra, Matrix) := (A, m) -> ( + if ring m =!= A.natural then + error "dgIdeal(Matrix): matrix must have entries in A.natural."; + dgIdeal(A, ideal m) +) -doc /// - Key - dgAlgebraMap - (isWellDefined,DGAlgebraMap) - (dgAlgebraMap,DGAlgebra,DGAlgebra,Matrix) - Headline - Define a DG algebra map between DG algebras. - Usage - phi = dgAlgebraMap(B,A,M) - Inputs - A:DGAlgebra - Source - B:DGAlgebra - Target - M:Matrix - A matrix representing where the generators of A should be mapped to (akin to ringMap) - Outputs - phi:DGAlgebraMap - Description - Example - R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} - K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") - K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") - g = dgAlgebraMap(K1,K2,matrix{{Y_2,Y_3}}) - isWellDefined g - Text - The function does not check that the DG algebra map is well defined, however. - Example - f = dgAlgebraMap(K2,K1,matrix{{0,T_1,T_2}}) - isWellDefined f -/// +dgIdeal (DGAlgebra, List) := (A, gs) -> ( + if #gs == 0 then dgIdeal(A, ideal(0_(A.natural))) + else dgIdeal(A, ideal matrix {apply(gs, g -> sub(g, A.natural))}) +) -doc /// - Key - toComplexMap - (toComplexMap,DGAlgebraMap) - (toComplexMap,DGAlgebraMap,ZZ) - [toComplexMap,AssertWellDefined] - [toComplexMap,EndDegree] - Headline - Construct the ComplexMap associated to a DGAlgebraMap - Usage - psi = toComplexMap phi - Inputs - phi:DGAlgebraMap - Outputs - psi:ComplexMap - Description - Example - R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} - K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") - K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") - g = dgAlgebraMap(K1,K2,matrix{{Y_2,Y_3}}) - g' = toComplexMap g - Text - The option @ TO EndDegree @ must be specified if the source of phi has any algebra generators of even degree. The option @ TO AssertWellDefined @ - is used if one wishes to assert that the result of this computation is indeed a chain map. One can construct just the nth map in the - chain map by providing the second @ TO ZZ @ parameter. - Text - This function also works when working over different rings, such as the case when the @ TO DGAlgebraMap @ is produced via - @ TO liftToDGMap @ and in the next example. In this case, the target module is produced via @ TO pushForward @. - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} - S = R/ideal{a^2*b^2*c^2} - f = map(S,R) - A = acyclicClosure(R,EndDegree=>3) - B = acyclicClosure(S,EndDegree=>3) - phi = liftToDGMap(B,A,f) - toComplexMap(phi,EndDegree=>3) -/// +ambient DGIdeal := I -> I.dgAlgebra +ideal DGIdeal := I -> I.natural +generators DGIdeal := opts -> I -> I.generators +ring DGIdeal := I -> I.ring +numgens DGIdeal := I -> numgens I.natural -doc /// - Key - liftToDGMap - (liftToDGMap,DGAlgebra,DGAlgebra,RingMap) - [liftToDGMap,EndDegree] - Headline - Lift a ring homomorphism in degree zero to a DG algebra morphism - Usage - phiTilde = liftToDGMap(B,A,phi) - Inputs - B:DGAlgebra - Target - A:DGAlgebra - Source - phi:RingMap - Map from A in degree zero to B in degree zero - Outputs - phiTilde:DGAlgebraMap - DGAlgebraMap lifting phi to a map of DGAlgebras. - Description - Text - In order for phiTilde to be defined, phi of the image of the differential of A in degree 1 must lie in the image of the - differential of B in degree 1. At present, this condition is not checked. - Example - R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} - S = R/ideal{a^2*b^2*c^2} - f = map(S,R) - A = acyclicClosure(R,EndDegree=>3) - B = acyclicClosure(S,EndDegree=>3) - phi = liftToDGMap(B,A,f) - toComplexMap(phi,EndDegree=>3) -/// +-- Internal helper: assert two DGIdeals share an ambient DGAlgebra. +sameAmbientDG := (I, J, opname) -> ( + if I.dgAlgebra =!= J.dgAlgebra then + error(opname | ": DGIdeals must share the same ambient DGAlgebra."); +) -doc /// - Key - (homology,DGAlgebraMap) - Headline - Computes the homomorphism in homology associated to a DGAlgebraMap. - Usage - homologyPhi = homology(phi,n) - Inputs - phi:DGAlgebraMap - Outputs - homologyPhi:RingMap - The map on homology defined by phi. - Description - Example - R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} - K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") - K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") - f = dgAlgebraMap(K2,K1,matrix{{0,T_1,T_2}}) - g = dgAlgebraMap(K1,K2,matrix{{Y_2,Y_3}}) - toComplexMap g - HHg = HH g -/// +-- Wrap an Ideal of A.natural as a DGIdeal, running the d-closure +-- saturation machinery in dgIdeal. For operations that preserve +-- d-closure (sum, product, intersection, power, colon) this is an +-- identity-on-the-ideal pass but still re-creates the DGIdeal wrapper. +-- The saturation loop terminates immediately when the input is already +-- d-closed. +------------------------------------------------------------------------- +-- Ideal-algebra operations. Each returns a new DGIdeal whose .natural +-- is the corresponding Ideal operation result. d-closure is preserved +-- by each of these operations (sum: d(I+J) ⊂ dI+dJ ⊂ I+J; product: +-- d(IJ) ⊂ (dI)J + I(dJ) ⊂ IJ; intersection: direct; power: induction; +-- colon: if fJ ⊂ I then d(fJ) = (df)J + f(dJ) ⊂ I + f(dJ) ⊂ I + fJ ⊂ I, +-- so (df)J ⊂ I, i.e. df ∈ I:J). We run through dgIdeal() anyway so the +-- saturation loop certifies closure; for already-closed inputs this is +-- just a re-wrap. +------------------------------------------------------------------------- + +DGIdeal + DGIdeal := (I, J) -> ( + sameAmbientDG(I, J, "DGIdeal + DGIdeal"); + dgIdeal(I.dgAlgebra, I.natural + J.natural) +) --- moved this out of the documentation since it is not complete --- (homology,DGAlgebraMap,ZZ) +DGIdeal * DGIdeal := (I, J) -> ( + sameAmbientDG(I, J, "DGIdeal * DGIdeal"); + dgIdeal(I.dgAlgebra, I.natural * J.natural) +) --- Text --- One can also supply the second argument (a ZZ) in order to obtain the map on homology in a specified degree. --- (This is currently not available). +intersect(DGIdeal, DGIdeal) := DGIdeal => {} >> opts -> (I, J) -> ( + sameAmbientDG(I, J, "intersect DGIdeal"); + dgIdeal(I.dgAlgebra, intersect(I.natural, J.natural)) +) -doc /// - Key - dgAlgebraMultMap - (dgAlgebraMultMap,DGAlgebra,RingElement) - Headline - Returns the chain map corresponding to multiplication by a cycle. - Usage - phi = dgAlgebraMultMap(A,z) - Inputs - A:DGAlgebra - z:RingElement - Outputs - phi:ComplexMap - Description - Text - If A is a DGAlgebra, and z is a cycle of A, then left multiplication of A by z gives - a chain map from A to A. This command converts A to a complex using @ TO toComplex @, - and constructs a @ TO ComplexMap @ that represents left multiplication by z. - This command is used to determine the module structure that is computed in - @ TO homologyModule @. - Example - R = QQ[x,y,z]/ideal{x^3,y^3,z^3} - KR = koszulComplexDGA R - z1 = x^2*T_1 - phi = dgAlgebraMultMap(KR,z1) - Text - As you can see, the degree of phi is the homological degree of z: - Example - degree phi == first degree z - Text - Care is also taken to ensure the resulting map is homogeneous if R and z are: - Example - isHomogeneous phi - Text - One may then view the action of multiplication by the homology class of z upon - taking the induced map in homology: - Example - Hphi = prune HH(phi); (Hphi_0,Hphi_1,Hphi_2) -/// +DGIdeal : DGIdeal := (I, J) -> ( + sameAmbientDG(I, J, "DGIdeal : DGIdeal"); + dgIdeal(I.dgAlgebra, I.natural : J.natural) +) -doc /// - Key - homologyModule - (homologyModule,DGAlgebra,Module) - Headline - Compute the homology of a DGModule as a module over a DGAlgebra. - Usage - HM = homologyModule(A,M) - Inputs - A:DGAlgebra - M:Module - Outputs - HM:Module - Description - Text - Given a DGAlgebra A over a ring R, and an R-module M, A ** M carries the structure - of a left DG module over A. It follows that H(A ** M) is a module over H(A). - Although DGModules have yet to be implemented as objects in Macaulay2 in their own right, - the current infrastructure (with a little extra work) allows us to determine the module structure - of this type of DG module as a module over the homology algebra of A. - Text - Currently, this code will only work on DGAlgebras that are finite over their ring - of definition, such as Koszul complexes. (Truncations of) module structures in case - of non-finite DGAlgebras may be made available in a future update. - Text - For an example, we will compute the module structure of the Koszul homology of - the canonical module over the Koszul homology algebra. - Example - Q = QQ[x,y,z,w] - I = ideal (w^2, y*w+z*w, x*w, y*z+z^2, y^2+z*w, x*y+x*z, x^2+z*w) - R = Q/I - KR = koszulComplexDGA R - cxKR = toComplex KR - HKR = HH(KR) - Text - The following is the graded canonical module of R: - Example - degList = first entries vars Q / degree / first - M = Ext^4(Q^1/I,Q^{-(sum degList)}) ** R - Text - We obtain the Koszul homology module using the following command: - Example - HKM = homologyModule(KR,M); - Text - One may notice the duality of HKR and HKM by considering their Hilbert series: - Example - hsHKR = value numerator reduceHilbert hilbertSeries HKR - hsHKM = value numerator reduceHilbert hilbertSeries HKM - AA = ring hsHKR - e = numgens Q - hsHKR == T_0^e*T_1^e*sub(hsHKM, {T_0 => T_0^(-1), T_1 => T_1^(-1)}) -/// +DGIdeal ^ ZZ := (I, n) -> ( + if n < 0 then error "DGIdeal ^ ZZ: exponent must be nonnegative."; + if n == 0 then dgIdeal(I.dgAlgebra, ideal 1_(I.dgAlgebra.natural)) + else dgIdeal(I.dgAlgebra, (I.natural)^n) +) -doc /// - Key - (source,DGAlgebraMap) - Headline - Outputs the source of a DGAlgebraMap - Usage - A = source phi - Inputs - phi:DGAlgebraMap - Outputs - A: DGAlgebra -/// +-- Containment / equality. I ⊆ J iff I.natural ⊆ J.natural (both live +-- in the same ambient A.natural by sameAmbientDG). +isSubset(DGIdeal, DGIdeal) := (I, J) -> ( + sameAmbientDG(I, J, "isSubset DGIdeal"); + isSubset(I.natural, J.natural) +) -doc /// - Key - (target,DGAlgebraMap) - Headline - Outputs the target of a DGAlgebraMap - Usage - A = target phi - Inputs - phi:DGAlgebraMap - Outputs - A: DGAlgebra -/// +DGIdeal == DGIdeal := (I, J) -> ( + if I.dgAlgebra =!= J.dgAlgebra then false + else I.natural == J.natural +) + +-- mingens: return the A.natural-level minimal generators. (Note that +-- trim at the Ideal level doesn't enlarge the d-saturated closure; the +-- underlying ideal is already d-closed.) +mingens DGIdeal := opts -> I -> mingens I.natural + +-- Reduction of a ring element mod I. +RingElement % DGIdeal := (f, I) -> f % I.natural +ZZ % DGIdeal := (n, I) -> (sub(n, I.dgAlgebra.natural)) % I.natural + +-- isWellDefined DGIdeal. Key-shape + type checks, then the semantic +-- d-closure check (isDGIdeal). Diagnostic messages via debugLevel > 0. +isWellDefined DGIdeal := I -> ( + k := keys I; + expectedKeys := set {symbol dgAlgebra, symbol ring, symbol natural, symbol generators, symbol cache}; + if set k =!= expectedKeys then ( + if debugLevel > 0 then ( + added := toList(k - expectedKeys); + missing := toList(expectedKeys - k); + if #added > 0 then << "-- DGIdeal: unexpected key(s): " << toString added << endl; + if #missing > 0 then << "-- DGIdeal: missing key(s): " << toString missing << endl; + ); + return false; + ); + if not instance(I.dgAlgebra, DGAlgebra) then ( + if debugLevel > 0 then << "-- DGIdeal: I.dgAlgebra is not a DGAlgebra" << endl; + return false; + ); + if not isWellDefined I.dgAlgebra then ( + if debugLevel > 0 then << "-- DGIdeal: I.dgAlgebra is not well-defined" << endl; + return false; + ); + if not instance(I.natural, Ideal) then ( + if debugLevel > 0 then << "-- DGIdeal: I.natural is not an Ideal" << endl; + return false; + ); + if ring I.natural =!= I.dgAlgebra.natural then ( + if debugLevel > 0 then << "-- DGIdeal: I.natural is not an ideal in I.dgAlgebra.natural" << endl; + return false; + ); + ok := isDGIdeal(I.dgAlgebra, I.natural); + if not ok and debugLevel > 0 then + << "-- DGIdeal: I.natural is not d-closed (some generator has d(g) not in I)" << endl; + ok +) + +-- toComplex DGIdeal: build the short-exact-sequence-style complex of +-- I viewed as a DG submodule of the regular DG module A.natural. +-- Equivalent to the kernel of the canonical chain map A ->> A/I. +-- +-- NOTE: This requires viewing A.natural as a DGModule over A (the +-- "regular representation"). We do not yet have a canonical wrapper for +-- that view, so we defer implementation and emit a clear error. +-- +-- For now, users can access the ideal's underlying ring-theoretic data +-- via `ideal I`, the generating matrix via `gens I`, and the quotient +-- algebra's complex via `toComplex(A/I)`. +toComplex DGIdeal := I -> error( + "toComplex DGIdeal is not yet implemented. " + | "Use toComplex(ambient I / I) for the quotient's complex, " + | "or access underlying data via `ideal I` and `gens I`." +) + +-- A / I : quotient DGAlgebra. Builds a fresh DGAlgebra whose underlying +-- algebra is A.natural / I.natural, and whose differential is inherited +-- from A (d descends since I is d-closed). +DGAlgebra / DGIdeal := (A, I) -> ( + if I.dgAlgebra =!= A then + error "DGAlgebra / DGIdeal: ideal must be a DG ideal of the given DG algebra."; + Bnat := A.natural / I.natural; + proj := map(Bnat, A.natural); + result := new MutableHashTable; + result#(symbol ring) = A.ring; + result#(symbol natural) = Bnat; + result#(symbol Degrees) = A.Degrees; + -- Descend the differential: for each generator t of Bnat (same names as + -- gens A.natural), its image under d is proj(d_A t). A.diff is a + -- RingMap A.natural -> A.natural, so A.diff t applies it. Build + -- B.diff as a RingMap Bnat -> Bnat. + diffImgs := apply(gens A.natural, t -> proj(A.diff t)); + result#(symbol diff) = map(Bnat, Bnat, matrix {diffImgs}); + result#(symbol isHomogeneous) = A.isHomogeneous; + result#(symbol cache) = new CacheTable; + result.cache#(symbol diffs) = new MutableHashTable; + result#(symbol zerothHomology) = if A.?zerothHomology then A.zerothHomology else A.ring; + new DGAlgebra from result +) -doc /// - Key - AssertWellDefined - Headline - Option to check whether the lifted map on DGAlgebras is well defined. - Usage - liftToDGMap(...,AssertWellDefined=>true) -/// +------------------------------------------------------------------------- +-- DGSubmodule +-- +-- dgSubmodule(M, incMat): incMat is a Matrix whose target is M.natural +-- and whose source is a free A.natural-module; its columns are the +-- chosen A-generators of the submodule. The constructor d-closes by +-- iteratively adjoining derivatives of current generators until the +-- column span is d-stable. +-- +-- The resulting DGSubmodule S has: +-- S.natural -- source (or an extended free module) of the inclusion +-- S.inclusion -- DGModuleMap S -> M encoding the inclusion +-- S.Degrees -- degree list (one per S.natural generator) +-- S.diff -- list of Vectors in S.natural, one per S.natural +-- generator, giving d_S +------------------------------------------------------------------------- + +-- Internal helper: columnwise application of d_M, returning a Matrix +-- with target M.natural and source a free A.natural-module whose i-th +-- generator's hom-degree is (degrees source incMat)#i with its hom slot +-- decreased by 1 (d_M lowers hom-degree by 1). This makes the result a +-- degree-0 homogeneous matrix and ensures that when new columns get +-- appended during saturation (`incMat | dMat_newIdx`), the combined +-- source carries correct degrees for its generators. +applyDiffColumns := (M, incMat) -> ( + A := M.dgAlgebra; + n := rank M.natural; + ncols := numcols incMat; + srcDegs := degrees source incMat; + shiftedDegs := apply(srcDegs, d -> prepend(first d - 1, drop(d, 1))); + srcShifted := if #shiftedDegs == 0 then (A.natural)^0 + else (A.natural)^(-shiftedDegs); + if ncols == 0 then return map(M.natural, srcShifted, 0); + -- For each column, diff returns a Vector in M.natural. Collect the + -- entries of each and assemble a matrix by rows. + colEntries := apply(ncols, i -> entries diff(M, (incMat)_i)); + rowsList := apply(n, r -> apply(ncols, j -> (colEntries#j)#r)); + map(M.natural, srcShifted, rowsList) +) -doc /// - Key - StartDegree - Headline - Option to specify the degree to start computing the acyclic closure and killing cycles - Usage - acyclicClosure(...,StartDegree=>n) -/// +-- isDGSubmodule(M, incMat): test whether image(incMat) is d_M-closed +-- without saturating. Returns true/false. +isDGSubmodule = method(TypicalValue => Boolean) +isDGSubmodule (DGModule, Matrix) := (M, incMat) -> ( + if target incMat =!= M.natural then + error "isDGSubmodule: target of incMat must equal M.natural."; + if numcols incMat == 0 then return true; + -- Assemble d_M applied column-wise; check containment in the column + -- span of incMat via a single `//` solve. + dMat := applyDiffColumns(M, incMat); + residual := dMat - incMat * (dMat // incMat); + residual == 0 +) -doc /// - Key - EndDegree - Headline - Option to specify the degree to stop computing killing cycles and acyclic closure - Usage - killCycles(...,StartDegree=>n) -/// +dgSubmodule = method(TypicalValue => DGSubmodule) + +dgSubmodule (DGModule, Matrix) := (M, incMat0) -> ( + if target incMat0 =!= M.natural then + error "dgSubmodule: target of inclusion matrix must equal M.natural."; + A := M.dgAlgebra; + -- Step 1: d-saturate. Iteratively compute d of every current generator + -- and adjoin only those that are not already in the current column span. + incMat := incMat0; + iter := 0; + notDone := true; + while notDone do ( + iter = iter + 1; + if iter > 200 then error "dgSubmodule: d-closure failed to stabilize."; + dMat := applyDiffColumns(M, incMat); + if numcols dMat == 0 then (notDone = false; break); + residual := dMat - incMat * (dMat // incMat); + newIdx := select(numcols dMat, j -> residual_{j} != 0); + if #newIdx == 0 then (notDone = false; break); + incMat = incMat | dMat_newIdx; + ); + -- Step 2: compute degrees of the columns of the final incMat. + -- Invariant: for DGModule M, M.Degrees equals degrees M.natural (same + -- sign), matching freeDGModule's convention that F = (A.natural)^(-degList) + -- has degrees degList. So S.Degrees should likewise equal degrees F. + F := source incMat; + nGens := rank F; + subDegs := apply(nGens, i -> (degrees F)#i); + -- Step 3: compute d_S on each free generator by lifting d_M(col) back + -- through incMat. + -- Build a single matrix of d_M applied column-wise to incMat, then do + -- one bulk `//` solve. This avoids per-column Vector->Matrix coercion + -- and matches the style of the d-saturation loop above. + dFullMat := applyDiffColumns(M, incMat); + liftFull := dFullMat // incMat; + residualFull := dFullMat - incMat * liftFull; + if residualFull != 0 then + error("dgSubmodule: internal error -- d-closure appeared to fail after saturation."); + dSList := apply(nGens, i -> liftFull_i); + -- Step 4: package. + result := new MutableHashTable; + result#(symbol dgAlgebra) = A; + result#(symbol ring) = A.ring; + result#(symbol natural) = F; + result#(symbol Degrees) = subDegs; + result#(symbol diff) = dSList; + result#(symbol cache) = new CacheTable; + result.cache#(symbol diffs) = new MutableHashTable; + result#(symbol ambient) = M; + S := new DGSubmodule from result; + S#(symbol inclusion) = dgModuleMap(M, S, incMat); + S +) -doc /// - Key - GenDegreeLimit - Headline - Option to specify the maximum degree to look for generators - Usage - homologyAlgebra(...,GenDegreeLimit=>n) -/// +dgSubmodule (DGModule, List) := (M, gs) -> ( + if #gs == 0 then ( + -- The empty submodule: target is M.natural (over A.natural), and + -- source is a zero-rank free module over the same ring A.natural + -- (NOT M.ring = A.ring, which would be a different ring). + emptyMat := map(M.natural, (M.dgAlgebra.natural)^0, 0); + return dgSubmodule(M, emptyMat); + ); + -- Coerce each entry to a Vector in M.natural. + coerced := apply(gs, g -> ( + if instance(g, Vector) then g + else if instance(g, Matrix) and numcols g == 1 then (g)_0 + else try (g * 1_(M.natural)) else error("dgSubmodule(List): could not coerce generator to M.natural.") + )); + -- Build a matrix with these columns. + cols := matrix {coerced}; + dgSubmodule(M, cols) +) -doc /// - Key - RelDegreeLimit - Headline - Option to specify the maximum degree to look for relations - Usage - homologyAlgebra(...,RelDegreeLimit=>n) -/// +ambient DGSubmodule := S -> S.ambient +inclusion = method() +inclusion DGSubmodule := S -> S.inclusion +-- `module S` returns the underlying A.natural-module (free in this encoding); +-- for the set-theoretic submodule image, use `image S.inclusion.natural`. +module DGSubmodule := S -> S.natural + +-- isWellDefined DGSubmodule. Key-shape + type checks, then verify +-- d-closure: the column span of the inclusion matrix is stable under +-- d_M. Diagnostics via debugLevel > 0. +isWellDefined DGSubmodule := S -> ( + k := keys S; + expectedKeys := set { + symbol dgAlgebra, symbol ring, symbol natural, symbol Degrees, + symbol diff, symbol cache, symbol ambient, symbol inclusion + }; + if set k =!= expectedKeys then ( + if debugLevel > 0 then ( + added := toList(k - expectedKeys); + missing := toList(expectedKeys - k); + if #added > 0 then << "-- DGSubmodule: unexpected key(s): " << toString added << endl; + if #missing > 0 then << "-- DGSubmodule: missing key(s): " << toString missing << endl; + ); + return false; + ); + if not instance(S.ambient, DGModule) then ( + if debugLevel > 0 then << "-- DGSubmodule: S.ambient is not a DGModule" << endl; + return false; + ); + if not isWellDefined S.ambient then ( + if debugLevel > 0 then << "-- DGSubmodule: S.ambient is not a well-defined DGModule" << endl; + return false; + ); + if not instance(S.inclusion, DGModuleMap) then ( + if debugLevel > 0 then << "-- DGSubmodule: S.inclusion is not a DGModuleMap" << endl; + return false; + ); + -- Semantic: the column span of S.inclusion.natural must be d-stable + -- in M = S.ambient. The free presentation S.natural and its lifted + -- differential S.diff are derived from that; d-closure fails iff the + -- lift has nontrivial residual. + M := S.ambient; + incMat := S.inclusion.natural; + if target incMat =!= M.natural then ( + if debugLevel > 0 then << "-- DGSubmodule: target of inclusion matrix is not M.natural" << endl; + return false; + ); + if numcols incMat == 0 then return true; + dMat := applyDiffColumns(M, incMat); + residual := dMat - incMat * (dMat // incMat); + ok := residual == 0; + if not ok and debugLevel > 0 then + << "-- DGSubmodule: inclusion's column span is not d-closed in the ambient DGModule" << endl; + ok +) -doc /// - Key - TMOLimit - Headline - Option to specify the maximum arity of the trivial Massey operation - Usage - findTrivialMasseyOperation(...,TMOLimit=>n) -/// +------------------------------------------------------------------------- +-- DGQuotientModule +-- +-- dgQuotientModule(M, S) or `M / S` constructs the quotient Q. +-- Q.natural = coker S.inclusion.natural +-- Q.Degrees = M.Degrees +-- Q.projection = DGModuleMap M -> Q with natural = identity composed +-- with the quotient map (built via `map(Q.natural, ...)`). +------------------------------------------------------------------------- + +dgQuotientModule = method(TypicalValue => DGQuotientModule) +dgQuotientModule (DGModule, DGSubmodule) := (M, S) -> ( + if S.ambient =!= M then + error "dgQuotientModule: submodule's ambient must equal the given DGModule."; + A := M.dgAlgebra; + Qnat := cokernel S.inclusion.natural; + -- Projection natural matrix: M.natural -> Qnat, the canonical quotient. + projNat := map(Qnat, M.natural, id_(M.natural)); + result := new MutableHashTable; + result#(symbol dgAlgebra) = A; + result#(symbol ring) = A.ring; + result#(symbol natural) = Qnat; + result#(symbol Degrees) = M.Degrees; + -- The DGModule-like diff list: for each M-generator, push its image + -- in M.natural under d through proj to land in Qnat. + result#(symbol diff) = apply(#M.Degrees, i -> projNat * M.diff#i); + result#(symbol ambient) = M; + result#(symbol subDGModule) = S; + result#(symbol cache) = new CacheTable; + Q := new DGQuotientModule from result; + -- Build the projection DGModuleMap only after Q is instantiated so we + -- can point its `target` at Q. + -- Note: Q is NOT a DGModule, so we construct a bare DGModuleMap record. + projMap := new MutableHashTable; + projMap#(symbol source) = M; + projMap#(symbol target) = Q; + projMap#(symbol dgAlgebra) = A; + projMap#(symbol natural) = projNat; + projMap#(symbol cache) = new CacheTable; + Q#(symbol projection) = new DGModuleMap from projMap; + Q +) -doc /// - Key - [isAcyclic,EndDegree] - Headline - Option to specify the degree to finish checking acyclicity - Usage - isAcyclic(...,EndDegree=>n) -/// +DGModule / DGSubmodule := (M, S) -> dgQuotientModule(M, S) + +-- diff(Q, v) for DGQuotientModule Q: the same Leibniz formula used on +-- DGModule works verbatim because Q has .natural (a cokernel), .diff +-- (a list of columns in Q.natural), and .dgAlgebra. Used by +-- isWellDefined DGModuleMap to verify chain-map conditions when source +-- or target is a DGQuotientModule. +diff (DGQuotientModule, Vector) := (Q, v) -> ( + A := Q.dgAlgebra; + vEntries := entries v; + -- Use #vEntries (= presentation generator count) rather than rank, + -- which would try codim on a cokernel module and may fail over + -- non-affine graded ambients. + numGens := #vEntries; + natGens := apply(numGens, i -> (Q.natural)_i); + sum apply(numGens, i -> ( + fi := vEntries#i; + if fi == 0 then 0_(Q.natural) + else ( + term1 := polyDifferential(A, fi) * natGens#i; + signExp := first degree fi; + sign := if odd signExp then -1 else 1; + term2 := sign * fi * Q.diff#i; + term1 + term2 + ) + )) +) -doc /// - Key - [acyclicClosure,StartDegree] - Headline - Option to specify the degree to start computing the acyclic closure. - Usage - acyclicClosure(...,StartDegree=>n) - Description - Text - The default value is 1. -/// +ambient DGQuotientModule := Q -> Q.ambient +subDGModule = method() +subDGModule DGQuotientModule := Q -> Q.subDGModule +projection = method() +projection DGQuotientModule := Q -> Q.projection + +-- maxDegree on a DGQuotientModule: inherit from the ambient DGModule. +-- (DGQuotientModule is not currently a subtype of DGModule, so the +-- DGModule method at line 3204 does not fire; supply it here so that +-- toComplexMap can take a DGQuotientModule as target.) +maxDegree DGQuotientModule := Q -> maxDegree Q.ambient + +-- toComplex: build per-degree quotient complex directly from the ambient +-- DGModule and the submodule's inclusion-as-ComplexMap. If Q has been +-- through `prune` (see prune DGQuotientModule), return the cached +-- minimally-presented complex. +toComplex DGQuotientModule := Q -> ( + if Q.cache#?(symbol prunedComplex) then + return Q.cache#(symbol prunedComplex); + cxM := toComplex Q.ambient; + cxIncl := toComplexMap Q.subDGModule.inclusion; + cokernel cxIncl +) -doc /// - Key - [acyclicClosure,EndDegree] - Headline - Option to specify the degree to stop computing the acyclic closure - Usage - A = acyclicClosure(B,EndDegree=>n) - Inputs - B: DGAlgebra - n: ZZ - Description - Text - All generators of the acyclic closure will be found up to homological degree n. - Note that B can be an ordinary ring, a factor ring of a polynomial ring, treated as - a DGAlgebra generated in homological degree 0, as in the following example: - - The default value of EndDegree is 3. - Example - R = ZZ/101[x,y,z,w]/(ideal"x3,y3,z3,x2yz") - acyclicClosure R - acyclicClosure(R,EndDegree => 3) -/// +homology (ZZ, DGQuotientModule) := opts -> (n, Q) -> ( + HH_n (toComplex Q) +) -doc /// - Key - [getGenerators,StartDegree] - Headline - Option to specify the degree to start finding generators of HH(DGAlgebra) - Usage - getGenerators(...,StartDegree=>n) -/// +-- Unary homology on a DGQuotientModule: the graded HA-module obtained +-- by taking H_i(toComplex Q) at each hom-degree i ∈ [concentration cxQ] +-- and packaging the pieces as an HA = HH(A)-module via extension of +-- scalars along R → HA. +-- +-- This mirrors homologyModule DGModule, but uses toComplex(Q) directly +-- because Q.natural is a cokernel rather than a free A.natural-module, +-- so the moduleMultMap path (which requires a free natural module) +-- cannot be used generically. The result therefore captures the +-- R-module structure and the HA-action induced by R → HA only; any +-- nontrivial cycle-class action on the quotient must be descended +-- separately (not yet implemented). +homologyModule DGQuotientModule := Q -> ( + A := Q.dgAlgebra; + HA := HH A; + cxQ := toComplex Q; + (lo, hi) := concentration cxQ; + if lo > hi then return HA^0; + -- Per-degree homology as R-modules, then tensor up to HA-modules. + pieces := apply(toList(lo..hi), i -> HA ** prune HH_i cxQ); + directSum pieces +) -doc /// - Key - [getGenerators,DegreeLimit] - Headline - Option to specify the degree to stop finding generators of HH(DGAlgebra) - Usage - getGenerators(...,DegreeLimit=>n) -/// +homology DGQuotientModule := opts -> Q -> homologyModule Q + +-- isAcyclic on a DGQuotientModule: H_i(Q) = 0 for all i >= 1 up to the +-- finite bound implied by maxDegree Q.ambient (or the user-supplied +-- EndDegree). Matches isAcyclic DGModule's semantics. +isAcyclic DGQuotientModule := opts -> Q -> ( + endDegree := maxDegree Q; + if endDegree === infinity and opts.EndDegree == -1 then + error "Must supply an upper bound to check for acyclicity."; + if opts.EndDegree != -1 then endDegree = opts.EndDegree; + not any(1..endDegree, i -> prune homology(i, Q) != 0) +) -doc /// - Key - [killCycles,StartDegree] - Headline - Option to specify the degree to start looking for cycles - Usage - killCycles(...,StartDegree=>n) -/// +-- isWellDefined DGQuotientModule. Key-shape + type checks, then delegate +-- to the subDGModule's d-closure check (the quotient is well-defined +-- exactly when the sub is). Also verifies projection structure. +isWellDefined DGQuotientModule := Q -> ( + k := keys Q; + expectedKeys := set { + symbol dgAlgebra, symbol ring, symbol natural, symbol Degrees, + symbol diff, symbol cache, symbol ambient, symbol subDGModule, + symbol projection + }; + if set k =!= expectedKeys then ( + if debugLevel > 0 then ( + added := toList(k - expectedKeys); + missing := toList(expectedKeys - k); + if #added > 0 then << "-- DGQuotientModule: unexpected key(s): " << toString added << endl; + if #missing > 0 then << "-- DGQuotientModule: missing key(s): " << toString missing << endl; + ); + return false; + ); + if not instance(Q.ambient, DGModule) then ( + if debugLevel > 0 then << "-- DGQuotientModule: Q.ambient is not a DGModule" << endl; + return false; + ); + if not instance(Q.subDGModule, DGSubmodule) then ( + if debugLevel > 0 then << "-- DGQuotientModule: Q.subDGModule is not a DGSubmodule" << endl; + return false; + ); + if Q.subDGModule.ambient =!= Q.ambient then ( + if debugLevel > 0 then << "-- DGQuotientModule: Q.subDGModule.ambient does not match Q.ambient" << endl; + return false; + ); + if not instance(Q.projection, DGModuleMap) then ( + if debugLevel > 0 then << "-- DGQuotientModule: Q.projection is not a DGModuleMap" << endl; + return false; + ); + if source Q.projection =!= Q.ambient or target Q.projection =!= Q then ( + if debugLevel > 0 then << "-- DGQuotientModule: Q.projection has wrong source/target" << endl; + return false; + ); + -- Semantic: delegate to the submodule's well-definedness (which + -- verifies d-closure of the relations). + ok := isWellDefined Q.subDGModule; + if not ok and debugLevel > 0 then + << "-- DGQuotientModule: Q.subDGModule is not a well-defined DGSubmodule" << endl; + ok +) -doc /// - Key - [killCycles,EndDegree] - Headline - Option to specify the degree to stop looking for cycles - Usage - killCycles(...,EndDegree=>n) -/// +------------------------------------------------------------------------- +-- prune for DG types. +-- +-- Modeled on `prune Complex` (Complexes package, ChainComplex.m2 line +-- 662): that method minimalPresentation's each term, drops trivially-zero +-- pieces from the concentration, and transports differentials via +-- minimalPresentation at the ComplexMap level. We provide DG analogs: +-- +-- * prune DGModule : identity on a freeDGModule (M.natural +-- is already minimal). For non-free +-- DGModules (currently none), this would +-- minimalPresentation M.natural. +-- * prune DGSubmodule S : rebuild S using mingens of its inclusion +-- matrix (A.natural-level minimization). +-- d-closure preserved (span unchanged). +-- * prune DGQuotientModule Q : (1) prune Q.subDGModule; (2) cache the +-- complex-level pruned toComplex inside Q +-- so subsequent toComplex Q calls return +-- the minimally-presented complex. This +-- closes the commutation gap: on a pruned +-- Q we have toComplex Q == prune toComplex Q. +-- In particular, trivially-zero cokernels +-- now produce the zero complex. +-- * prune DGIdeal I : trim I.natural (ideal-level minimization). +-- +-- The DGQuotientModule branch uses the same transport idea as +-- `prune Complex` (minimalPresentation commutes with the differential) +-- without trying to rebuild Q.natural itself -- which would require +-- changing the ambient module M, a nontrivial refactor. +------------------------------------------------------------------------- + +-- minimalPresentation is the non-prune synonym; both keys installed to +-- match M2's convention (see ChainComplex.m2 line 661-662 for Complex). +-- +-- Following the Complex convention (ChainComplex.m2 ~line 685), every +-- prune method caches a `pruningMap` in the *output* object's cache, +-- encoding the canonical comparison map (pruned) --> (original). That +-- map is a well-defined DGModuleMap / DGAlgebraMap / DGIdealWrapper and +-- can be verified via isWellDefined. +-- +-- We do not materialize DGModule-level prune (prune DGModule = identity), +-- so the cached pruningMap on a DGModule is literally the identity. +minimalPresentation DGModule := o -> M -> M +prune DGModule := o -> M -> ( + M.cache#(symbol pruningMap) = identityDGModuleMap M; + M +) -doc /// - Key - [getBasis,Limit] - Headline - Option to specify the maximum number of basis elements to return - Usage - getBasis(...,Limit=>n) -/// +-- Trivial prune fallbacks for DGAlgebra and maps. These mirror the +-- Complexes pattern (prune is the identity on already-minimal objects) +-- and ensure that prune commutes with every constructor uniformly so +-- that calling code never has to guard against "prune not defined". +minimalPresentation DGAlgebra := o -> A -> A +prune DGAlgebra := o -> A -> A +minimalPresentation DGAlgebraMap := o -> f -> f +prune DGAlgebraMap := o -> f -> f +minimalPresentation DGModuleMap := o -> f -> f +prune DGModuleMap := o -> f -> f + +prune DGSubmodule := o -> S -> ( + M := S.ambient; + incMat := S.inclusion.natural; + -- No redundancy to trim if there are no generators or mingens is + -- already the same width: cache an identity pruningMap and return. + minIm := if numcols incMat == 0 then incMat else mingens image incMat; + if numcols minIm == numcols incMat then ( + S.cache#(symbol pruningMap) = identityDGModuleMap S; + return S; + ); + -- Build pruned Sp with fewer generators. + Sp := dgSubmodule(M, minIm); + -- Build pruningMap: Sp --> S as a DGModuleMap. Factor minIm = incMat * T + -- via `minIm // incMat` (solves the A.natural-linear system), then + -- coerce T to a map with the correct degree labels. + T := minIm // incMat; + Tcoerced := map(S.natural, Sp.natural, T); + pm := dgModuleMap(S, Sp, Tcoerced); + Sp.cache#(symbol pruningMap) = pm; + Sp +) +minimalPresentation DGSubmodule := o -> S -> prune S + +prune DGQuotientModule := o -> Q -> ( + -- Step 1: minimize the submodule presentation (A.natural-level prune). + prunedS := prune Q.subDGModule; + Qp := if prunedS === Q.subDGModule then Q else Q.ambient / prunedS; + -- Step 2: build pruningMap: Qp --> Q as a raw DGModuleMap. Both are + -- cokernels of inclusions of the same image(S) in M (equal sets), so + -- the identity on M.natural descends to a canonical map + -- Qp.natural --> Q.natural. Construct it directly (dgModuleMap does + -- not accept DGQuotientModule source/target because the type doesn't + -- inherit from DGModule). + idAmb := id_(Q.ambient.natural); + pmNat := try inducedMap(Q.natural, Qp.natural, idAmb) else + map(Q.natural, Qp.natural, idAmb); + pm := new DGModuleMap from { + symbol source => Qp, + symbol target => Q, + symbol dgAlgebra => Q.dgAlgebra, + symbol natural => pmNat, + symbol cache => new CacheTable + }; + Qp.cache#(symbol pruningMap) = pm; + -- Step 3: compute the complex-level prune and cache it. This mirrors + -- `prune Complex` and closes the commutation gap: after this, calling + -- toComplex on Qp returns the minimally-presented complex. + cxQ := toComplex Qp; + prunedCxQ := prune cxQ; + Qp.cache#(symbol prunedComplex) = prunedCxQ; + Qp +) -doc /// - Key - [homologyAlgebra,GenDegreeLimit] - Headline - Option to specify the maximum degree to look for generators - Usage - homologyAlgebra(...,GenDegreeLimit=>n) -/// +prune DGIdeal := o -> I -> ( + A := I.dgAlgebra; + minI := trim I.natural; + if numgens minI == numgens I.natural then ( + -- DGIdeals don't support maps directly (no DGIdealMap type); we + -- cache the identity as the pruningMap so downstream code can + -- share a uniform calling convention. The pruningMap for a DGIdeal is + -- recorded as the identity DGAlgebraMap on the ambient, which is + -- well-defined and trivially an iso. + I.cache#(symbol pruningMap) = identityDGAlgebraMap A; + return I; + ); + Ip := dgIdeal(A, (generators minI)_*); + -- For a pruned DGIdeal, the ambient DGAlgebra is unchanged and the + -- underlying ideal is equal (as a set) -- mingens just chooses a + -- smaller generating set. So the canonical comparison Ip --> I is + -- the identity on the ambient DG algebra. + Ip.cache#(symbol pruningMap) = identityDGAlgebraMap A; + Ip +) +minimalPresentation DGIdeal := o -> I -> prune I +minimalPresentation DGQuotientModule := o -> Q -> prune Q + +------------------------------------------------------------------------- +-- Basic module-like operations for DGModule / DGSubmodule / DGQuotientModule +-- +-- These mirror M2's standard Module/Ideal methods (numgens, rank, +-- degrees, isHomogeneous, isFreeModule, super, cover, relations). +-- They work uniformly across the DG-module type hierarchy so that +-- code written against Modules can often be reused on DG objects +-- without modification. +------------------------------------------------------------------------- + +numgens DGModule := M -> #M.Degrees +numgens DGSubmodule := S -> #S.Degrees +numgens DGQuotientModule := Q -> #Q.Degrees + +rank DGModule := M -> rank M.natural +rank DGSubmodule := S -> rank S.natural + +-- `rank DGQuotientModule` would ask for rank of a cokernel module, which +-- triggers codim computations that fail for non-affine graded ambients +-- (see the diff(DGQuotientModule, ...) rank-vs-numgens note). Instead, +-- expose it as numgens of the cokernel presentation. +numColumnsDGQ := Q -> numgens Q.natural + +degrees DGModule := M -> M.Degrees +degrees DGSubmodule := S -> S.Degrees +degrees DGQuotientModule := Q -> Q.Degrees + +isHomogeneous DGModule := M -> isHomogeneous M.natural +isHomogeneous DGSubmodule := S -> isHomogeneous S.natural +isHomogeneous DGQuotientModule := Q -> isHomogeneous Q.natural +isHomogeneous DGIdeal := I -> isHomogeneous I.natural + +-- `isFreeDGModule` — every DGModule in our encoding has M.natural a free +-- graded A.natural-module, so this returns true unless someone has +-- manually built one with a non-free natural (which isWellDefined would +-- still tolerate as long as the differential closes). We still check +-- to be safe. +isFreeDGModule = method(TypicalValue => Boolean) +isFreeDGModule DGModule := M -> isFreeModule M.natural +isFreeDGModule DGSubmodule := S -> isFreeModule S.natural + +-- M2-style structural accessors on DGSubmodule / DGQuotientModule. +-- super S : ambient DGModule of S. +-- cover Q : ambient DGModule M (of which Q is a quotient). +-- relations Q : inclusion matrix encoding the killed relations. +-- For a DGSubmodule the relations are (by construction) the zero +-- submodule of S.natural, since S.natural is a free A.natural-module. +super DGSubmodule := S -> S.ambient +super DGQuotientModule := Q -> Q.ambient +cover DGQuotientModule := Q -> Q.ambient +relations DGQuotientModule := Q -> Q.subDGModule.inclusion.natural + +-- isZero check: does the DG-thing have no generators (or equivalently +-- numgens == 0 / natural module is zero)? +isZero = method(TypicalValue => Boolean) +isZero DGModule := M -> numgens M.natural == 0 +isZero DGSubmodule := S -> numcols S.inclusion.natural == 0 or S.inclusion.natural == 0 +isZero DGQuotientModule := Q -> Q.natural == 0 +isZero DGIdeal := I -> numgens I.natural == 0 or I.natural == 0 + +------------------------------------------------------------------------- +-- DGSubmodule algebra: sum, intersection, containment, equality. +-- +-- All operations are in the same ambient DGModule. d-closure is +-- preserved because: +-- * (S + T) : d(s + t) = d(s) + d(t) ∈ S + T. +-- * (S ∩ T) : if x ∈ S ∩ T, then d(x) ∈ S and d(x) ∈ T. +-- * containment / equality are read off from the image submodules. +------------------------------------------------------------------------- + +sameAmbientDGSub := (S, T, opname) -> ( + if S.ambient =!= T.ambient then + error(opname | ": DGSubmodules must share the same ambient DGModule.") +) -doc /// - Key - [homologyAlgebra,RelDegreeLimit] - Headline - Option to specify the maximum degree to look for relations - Usage - homologyAlgebra(...,RelDegreeLimit=>n) -/// +DGSubmodule + DGSubmodule := (S, T) -> ( + sameAmbientDGSub(S, T, "DGSubmodule +"); + combined := S.inclusion.natural | T.inclusion.natural; + dgSubmodule(S.ambient, combined) +) -doc /// - Key - [torAlgebra,GenDegreeLimit] - Headline - Option to specify the maximum degree to look for generators - Usage - torAlgebra(...,GenDegreeLimit=>n) -/// +intersect(DGSubmodule, DGSubmodule) := DGSubmodule => {} >> opts -> (S, T) -> ( + sameAmbientDGSub(S, T, "intersect DGSubmodule"); + interMod := intersect(image S.inclusion.natural, image T.inclusion.natural); + dgSubmodule(S.ambient, generators interMod) +) -doc /// - Key - [torAlgebra,RelDegreeLimit] - Headline - Option to specify the maximum degree to look for relations - Usage - torAlgebra(...,RelDegreeLimit=>n) -/// +isSubset(DGSubmodule, DGSubmodule) := (S, T) -> ( + sameAmbientDGSub(S, T, "isSubset DGSubmodule"); + isSubset(image S.inclusion.natural, image T.inclusion.natural) +) -doc /// - Key - [findTrivialMasseyOperation,GenDegreeLimit] - Headline - Option to specify the maximum degree to look for generators - Usage - findTrivialMasseyOperation(...,GenDegreeLimit=>n) -/// +DGSubmodule == DGSubmodule := (S, T) -> ( + if S.ambient =!= T.ambient then false + else image S.inclusion.natural == image T.inclusion.natural +) -doc /// - Key - [findTrivialMasseyOperation,TMOLimit] - Headline - Option to specify the maximum arity of a trivial Massey operation, if one exists. - Usage - findTrivialMasseyOperation(...,TMOLimit=>n) -/// +------------------------------------------------------------------------- +-- Scalar ideal action: I * S and I * M. +-- +-- I * S has generators g · s for g a generator of I, s a generator of S +-- (viewed in the ambient M.natural). This is d-closed: +-- d(g · s) = d(g) · s ± g · d(s) ∈ I·S + I·S = I·S. +------------------------------------------------------------------------- + +DGIdeal * DGSubmodule := (I, S) -> ( + if S.ambient.dgAlgebra =!= I.dgAlgebra then + error "DGIdeal * DGSubmodule: DG algebras disagree between I and S.ambient."; + prodMod := I.natural * image S.inclusion.natural; + dgSubmodule(S.ambient, generators prodMod) +) -doc /// - Key - [isHomologyAlgebraTrivial,GenDegreeLimit] - Headline - Option to specify the maximum degree to look for generators - Usage - isHomologyAlgebraTrivial(...,GenDegreeLimit=>n) -/// +DGIdeal * DGModule := (I, M) -> ( + if M.dgAlgebra =!= I.dgAlgebra then + error "DGIdeal * DGModule: DG algebras disagree between I and M."; + -- Whole-ambient submodule, then scale. + I * dgSubmodule(M, id_(M.natural)) +) -doc /// - Key - [isGolodHomomorphism,GenDegreeLimit] - Headline - Option to specify the maximum degree to look for generators - Usage - isGolodHomomorphism(...,GenDegreeLimit=>n) -/// +------------------------------------------------------------------------- +-- Annihilator of a DGSubmodule / DGQuotientModule. +-- +-- Given S ⊂ M, ann(S) = { a ∈ A.natural : a · s = 0 for all s ∈ S }. +-- This is d-closed: if a · s = 0 for all s, then d(a · s) = 0 = d(a)·s +-- ± a·d(s), so d(a)·s = ∓ a·d(s). Since d(s) ∈ S, a·d(s) = 0, hence +-- d(a)·s = 0 for all s ∈ S, so d(a) ∈ ann(S). Same argument for Q. +------------------------------------------------------------------------- + +annihilator DGSubmodule := opts -> S -> ( + annIdeal := annihilator(image S.inclusion.natural, opts); + dgIdeal(S.ambient.dgAlgebra, annIdeal) +) -doc /// - Key - [deviations,DegreeLimit] - Headline - Option to specify the maximum degree to look for generators when computing the deviations - Usage - deviations(...,DegreeLimit=>n) -/// +annihilator DGQuotientModule := opts -> Q -> ( + annIdeal := annihilator(Q.natural, opts); + dgIdeal(Q.dgAlgebra, annIdeal) +) -doc /// - Key - [isGolodHomomorphism,TMOLimit] - Headline - Option to specify the maximum degree to look for generators when computing the deviations - Usage - isGolodHomomorphism(...,DegreeLimit=>n) -/// +------------------------------------------------------------------------- +-- DGIdeal: `module I` exposes the underlying Module-view of the ideal +-- (unifying with Ideal in M2, where `module I` returns I.module). +------------------------------------------------------------------------- + +module DGIdeal := I -> module I.natural + +------------------------------------------------------------------------- +-- Direct sum of DGModules: M ++ N. +-- +-- The natural module is M.natural ++ N.natural (M2 handles the degree +-- book-keeping). Hom-degrees are preserved component-wise. The new +-- differential acts block-diagonally: d(M ⊕ N)(m, n) = (d_M m, d_N n). +------------------------------------------------------------------------- + +DGModule ++ DGModule := (M, N) -> ( + if M.dgAlgebra =!= N.dgAlgebra then + error "DGModule ++ DGModule: DG algebras must agree."; + A := M.dgAlgebra; + sumNat := M.natural ++ N.natural; + mGens := #M.Degrees; + nGens := #N.Degrees; + -- Hom-degree/internal-degree labels concatenate: the first mGens + -- generators come from M (degrees M.Degrees), the remaining nGens + -- come from N (degrees N.Degrees). This mirrors how M2 handles + -- degrees on direct sums of free modules. + sumDegrees := M.Degrees | N.Degrees; + -- Differentials: d on M sits in the first mGens columns; d on N + -- sits in the last nGens columns; each column in sumNat is a + -- block vector (M-part, N-part). + -- sumNat == M.natural ++ N.natural, indexed so that gens 0..mGens-1 + -- are M's and gens mGens..mGens+nGens-1 are N's. Build the diff + -- list as per-generator Vectors in sumNat. + sumGens := apply(mGens + nGens, i -> (sumNat)_i); + mInj := map(sumNat, M.natural, (sumNat)_{0..mGens-1}); + nInj := map(sumNat, N.natural, (sumNat)_{mGens..mGens+nGens-1}); + sumDiff := apply(mGens + nGens, i -> ( + if i < mGens then mInj * M.diff#i + else nInj * N.diff#(i - mGens) + )); + result := new MutableHashTable; + result#(symbol dgAlgebra) = A; + result#(symbol ring) = A.ring; + result#(symbol natural) = sumNat; + result#(symbol Degrees) = sumDegrees; + result#(symbol diff) = sumDiff; + result#(symbol cache) = new CacheTable; + -- Initialize the `diffs` caching table that moduleDifferential + -- needs (otherwise isWellDefined / toComplex / homology all crash + -- on key-not-found). + result.cache#(symbol diffs) = new MutableHashTable; + -- Record summands for `components`. + result.cache#(symbol components) = {M, N}; + new DGModule from result +) -doc /// - Key - [homologyAlgebra,Verbosity] - Headline - Option to specify the maximum degree to look for generators when computing the deviations - Usage - homologyAlgebra(...,DegreeLimit=>n) -/// +-- components DGModule: when M was built via ++ or directSum, return the +-- summand list; otherwise return {M}. +components DGModule := M -> ( + if M.cache#?(symbol components) then M.cache#(symbol components) + else {M} +) -doc /// - Key - [getGenerators,Verbosity] - Headline - Option to specify the maximum degree to look for generators when computing the deviations - Usage - getGenerators(...,Verbosity=>n) -/// +-- directSum of a sequence/list of DGModules. Mirrors `directSum` for +-- Modules: fold ++ over the list. Empty list -> zero DGModule over the +-- first-available DGAlgebra (error if we cannot infer). +directSum DGModule := M -> M +DGModule.directSum = args -> ( + if #args == 0 then error "directSum DGModule: need at least one summand."; + fold((a, b) -> a ++ b, args) +) -doc /// - Key - (net,DGAlgebra) - Headline - Outputs the pertinent information about a DGAlgebra - Usage - net A - Inputs - A:DGAlgebra -/// +------------------------------------------------------------------------- +-- image / kernel / cokernel of a DGModuleMap +-- +-- image F : DGSubmodule of target F. +-- inclusion.natural = F.natural +-- S.natural = source F.natural (same as M.natural) +-- Since F is a chain map, F.natural's image is d-closed. +-- +-- kernel F : DGSubmodule of source F. +-- inclusion.natural = gens kernel F.natural +-- Closed under d since F is a chain map. +-- +-- cokernel F : DGQuotientModule target F / image F. +------------------------------------------------------------------------- + +image DGModuleMap := F -> ( + -- Construct image(F) as a DG submodule of target(F). F.natural is + -- automatically d-closed (F is a chain map), so no saturation is + -- needed -- we just need a good free presentation. + -- + -- Convention (matches M2's Matrix-level `image`): drop zero columns so + -- that numgens (image F) reflects the rank of the underlying image + -- rather than the arbitrary presentation width. For a 0 map this + -- yields the 0 submodule; for an injection the full source. + N := target F; + nat := F.natural; + nz := select(numcols nat, j -> nat_{j} != 0); + prunedMat := if #nz == numcols nat then nat + else if #nz == 0 then map(N.natural, (N.dgAlgebra.natural)^0, 0) + else nat_nz; + dgSubmodule(N, prunedMat) +) -doc /// - Key - (net,DGAlgebraMap) - Headline - Outputs the pertinent information about a DGAlgebraMap - Usage - net phi - Inputs - phi:DGAlgebraMap -/// +kernel DGModuleMap := opts -> F -> ( + M := source F; + kerMat := gens kernel F.natural; + dgSubmodule(M, kerMat) +) -doc/// - Key - displayBlockDiff - (displayBlockDiff, DGAlgebra, ZZ) - (displayBlockDiff, DGAlgebra, Array, Array) - (displayBlockDiff, DGAlgebra, List, List) - (displayBlockDiff, DGAlgebra, VisibleList) - Headline - Shows natural decomposition of a map in the Tate resolution - Usage - displayBlockDiff (A, n) - displayBlockDiff (A, LA, LA) - displayBlockDiff (A, L, L) - displayBlockDiff (A, V) - Inputs - A:DGAlgebra - of the sort produced by @TO acyclicClosure@ or @TO killCycles@ - n:ZZ - LA:Array - of the form [L] where L is a list representing a multi-index in the complex - L:List - of indices as above - V:VisibleList - a list, array or sequence representing a (single) multi-index in the complex defined by A - Description - Text - For example, consider the first five steps in the resolution of the residue field - in the following example: - Example - R = QQ[x,y,z]/(ideal(x^3,y^3,z^3,x*y*z)) - G = betti freeResolution (coker vars R, LengthLimit => 5) - Text - It is a free graded-commutative divided power algebra on generator of each degree starting with 1. - We can compute the beginning of the complex corresponding to the first 3 factors with as - Example - A = acyclicClosure(R,EndDegree => 2) - F = toComplex(A, 5); - betti F - betti F - Text - Since we gave @TO acyclicClosure @ the EndDegree 2, the complex produced is exact up to - step 2; that is betti F and betti G agree up to the column 3. There are 3 chunks of generators - in A, of homological degrees 1,2,3. - - From the betti table of F - we see the three generators of the exterior algebra K as the linear part of the resolution. - The second strand, 4,12,12,4 is the tensor product of K with the 4 generators of A that have - homological degree 2 and correspond to the 4 generators of I; thus they have internal degree 3. - We also see that A has three generators in homological degree 3 and internal degree 5. - K multiplied by these generators accounts for 3,9,9 of the 3rd strand, while the symmetric - square of the 4 generators of homological degree 2, account for 0,10,0 and the product of - these with the generators of degree 1 account for the remaining 0,0,30. Finally the 12 - in the 4th row represents the product of the 4 generators of A in homological degree with - the 3 in degree 3. - - The native display of the differentials of this complex does not distinguish these pieces, - but displayBlockDiff allows one to look at them in various ways: - Example - F.dd_3 - displayBlockDiff(A,3) - Text - Here the triples of numbers represent the number of factors from the generators of each of the three chunks of - variables: the first index gives the number from the Koszul complex, the second from the 4 variable in homological degree - 2 and the third the number from the 3 variables of homological degree 3. Thus the sum of the - three indices is the homological degree. The sources of the blocks are listed on the top row, the targets - are given by the columns. - We can see the lists of indices of the source and target with - Example - indices source blockDiff(A,5) - indices target blockDiff(A,5) - Text - We can extract one or several blocks from F.dd_3 as follows: - Example - R = QQ[x,y,z]/(ideal(x^3,y^3,z^3,x*y*z)) - A = acyclicClosure(R,EndDegree => 2) - Text - We can specify a lists of source and target degrees, either as lists representing an - index for th source and an index for the target: - Example - displayBlockDiff(A, {0,2,3}, {0,4,0}) - Text - or as a pair of arrays of such lists - Example - displayBlockDiff(A, [{0,2,3}], [{1,0,3},{0,4,0}]) - Text - or look at all the blocks with a given target summand with: - Example - displayBlockDiff(A, (1,0,3) ) - SeeAlso - blockDiff - acyclicClosure -/// +cokernel DGModuleMap := F -> ( + N := target F; + imgF := image F; + dgQuotientModule(N, imgF) +) -doc /// - Key - blockDiff - (blockDiff, DGAlgebra, ZZ) - Headline - prepares a map for display - Usage - X = blockDiff(A,n) - Inputs - A: DGAlgebra - n: ZZ - Outputs - X:Matrix - map between labeled direct sums - Description - Text - Use @TO displayBlockDiff@ to display the blocks in various ways. - SeeAlso - displayBlockDiff -/// - -------------------------------- --- Testing -- -------------------------------- -TEST /// -R = QQ[x,y,z]/(ideal(x^3,y^3,z^3,x*y*z)) -A = acyclicClosure(R,EndDegree => 2) -X = blockDiff(A,3) -Y = blockDiff(A,2) ---reconstruct a composite block: -assert(0 == Y_[{2,0,0}]^[{1,0,0}] *X^[{2,0,0}]_[{1,2,0}] + Y_[{0,2,0}]^[{1,0,0}] *X^[{0,2,0}]_[{1,2,0}] ) +-- In M2, `coker` and `cokernel` are aliases for the same MethodFunction +-- (cokernel = method(); coker = cokernel), so installing at one signature +-- also installs at the other. We do NOT add a separate `coker` alias line +-- because `coker DGModuleMap := cokernel` would silently overwrite the +-- method with the MethodFunction itself, creating infinite self-recursion. + +------------------------------------------------------------------------- +-- Backward-compatible tensor operations with non-DG types. +-- +-- Semantics overview: +-- * `** Ring` — base-change along the canonical substitute map +-- A.ring -> S. The result inherits the same DG shape +-- with coefficients pushed forward to S. When S is a +-- QuotientRing R/I, this is "reduction mod I." +-- * `** Module` — exterior tensor M ⊗_R N for M a DGModule and N a +-- free R-module. The differential acts as d_M ⊗ id_N. +-- * `** Ideal` — sugar for `** module I`. +-- +-- Functoriality requirements that every implementation satisfies: +-- * identity ** S is isomorphic to the identity on (... ** S). +-- * (f o g) ** S agrees with (f ** S) o (g ** S) at the natural +-- matrix level. +-- * isWellDefined holds on every tensor output (checked in regression). +------------------------------------------------------------------------- + +-- Helper: canonical substitute from (any ring containing the source +-- generator-names) into a fresh target ring. Errors out with a +-- contextualized message rather than the opaque M2 substitute error. +coerceAcrossRings := (src, tgt, opname) -> ( + try substitute(src, tgt) + else error (opname | ": cannot substitute across rings; ensure the target ring's generator names include the source's.") +) -/// -TEST /// --- test 0 : isHomogeneous, toComplex, maxDegree -R = ZZ/101[x,y,z] -A1 = freeDGAlgebra(R,{{1},{1},{1},{3}}) -setDiff(A1,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) -assert(not A1.isHomogeneous) -A1dd = toComplex(A1) -A1dd.dd - -A2 = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{3,3}}) -setDiff(A2,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) -assert(A2.isHomogeneous) -A2dd = toComplex(A2) -A2dd.dd - -B1 = koszulComplexDGA(R) -assert(B1.isHomogeneous) -B1dd = toComplex(B1) -B1dd.dd +-- DGSubmodule ** Ring: base-change of ambient and inclusion. Invariant: +-- if S' = S ** R is the base-change of S in ambient M, and M' = M ** R, +-- then S' is a well-defined DGSubmodule of M' with the pushed-forward +-- inclusion matrix. The differential stays d-closed because d (the +-- base-changed differential on M') sends the S'-image to itself by the +-- same columns that d(S) landed in. +DGSubmodule ** Ring := (Sub, S) -> ( + Mnew := (Sub.ambient) ** S; + Bnat := ring Mnew.natural; + incOld := Sub.inclusion.natural; + newEntries := apply(entries incOld, row -> + apply(row, f -> coerceAcrossRings(f, Bnat, "DGSubmodule ** Ring")) + ); + newInc := if numcols incOld == 0 then map(Mnew.natural, (Bnat)^0, 0) + else map(Mnew.natural, , matrix newEntries); + dgSubmodule(Mnew, newInc) +) -R = ZZ/101[x,y,z] -R2 = R/ideal {x^2-z^3} -B2 = koszulComplexDGA(R2) -assert(not B2.isHomogeneous) -B2dd = toComplex(B2) -B2dd.dd - -R = QQ[x,y,z] -B = koszulComplexDGA(R) -toComplex(B) -degrees B.natural -A = freeDGAlgebra(R,{{1},{1},{1},{3}}) -setDiff(A,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) -Add = toComplex(A) -assert(apply(maxDegree(A)+1, i -> prune HH_i(Add)) == {coker vars R,0,0,coker vars R,0,0,0}) -/// +-- QuotientRing inherits from the Ring method above. + +-- DGQuotientModule ** Ring: base-change the ambient and pushforward the +-- inclusion matrix onto the SAME freshly base-changed ambient, then +-- requote. (Calling `(Q.subDGModule) ** S` recursively would build a +-- second copy of the base-changed ambient and trip the "submodule's +-- ambient must equal the given DGModule" check in dgQuotientModule.) +DGQuotientModule ** Ring := (Q, S) -> ( + Mnew := (Q.ambient) ** S; + Bnat := ring Mnew.natural; + incOld := Q.subDGModule.inclusion.natural; + newEntries := apply(entries incOld, row -> + apply(row, f -> coerceAcrossRings(f, Bnat, "DGQuotientModule ** Ring"))); + newInc := if numcols incOld == 0 then map(Mnew.natural, (Bnat)^0, 0) + else map(Mnew.natural, , matrix newEntries); + Subnew := dgSubmodule(Mnew, newInc); + Mnew / Subnew +) -TEST /// --- test 1 : differential tests -R = ZZ/101[x,y,z, Degrees => {2,2,3}] -kRes = freeResolution coker vars R -kRes.dd_3 -A = koszulComplexDGA(R) -d3 = A.dd_3 -d2 = A.dd_2 -d1 = A.dd_1 -assert(source d1 == target d2) -assert(source d2 == target d3) -assert(d1*d2 == 0) -assert(d2*d3 == 0) -S1 = R/ideal (x^3-z^2) -B1 = koszulComplexDGA(S1) -d3 = B1.dd_3 -d2 = B1.dd_2 -d1 = B1.dd_1 -assert(source d1 == target d2) -assert(source d2 == target d3) -assert(d1*d2 == 0) -assert(d2*d3 == 0) -use R -S2 = R/ideal (x^4-z^2) -B2 = koszulComplexDGA(S2) -d3 = B2.dd_3 -d2 = B2.dd_2 -d1 = B2.dd_1 -assert(source d1 == target d2) -assert(source d2 == target d3) -assert(d1*d2 == 0) -assert(d2*d3 == 0) -/// +-- (QuotientRing handled via its Ring parent.) -TEST /// ---- test 2 : homology, homologyAlgebra, HH_ZZ, HH -R = ZZ/32003[a,b,x,y]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} -koszulR = koszul vars R -time apply(5,i -> numgens prune HH_i(koszulR)) -A = koszulComplexDGA(R) -HH_2(A) -HH(A) -hh2 = prune HH_2(koszulR) -hh2' = prune HH_2(A) -assert(hh2 == hh2') -/// +-- DGIdeal ** Ring: base-change the ambient DGAlgebra and pushforward the +-- ideal generators into the new .natural ring. +DGIdeal ** Ring := (I, S) -> ( + Anew := (I.dgAlgebra) ** S; + Bnat := Anew.natural; + oldGens := first entries generators I.natural; + newGens := apply(oldGens, g -> coerceAcrossRings(g, Bnat, "DGIdeal ** Ring")); + dgIdeal(Anew, newGens) +) -TEST /// --- test 3 : torAlgebra, deviations -R1 = QQ[x,y,z]/ideal{x^3,y^4,z^5} -TorR1 = torAlgebra(R1,GenDegreeLimit=>4) -devR1 = deviations(R1,DegreeLimit=>4) -use R1 -M = coker matrix {{x^2*y^3*z^4}} -Mres = freeResolution(M, LengthLimit => 7) -R2 = QQ[x,y,z]/ideal{x^3,y^4,z^5,x^2*y^3*z^4} -time TorR1R2 = torAlgebra(R1,R2,GenDegreeLimit=>5,RelDegreeLimit=>10) --- the multiplication is trivial, since the map R3 --> R4 is Golod -numgens TorR1R2 -numgens ideal TorR1R2 -apply(21, i -> #(flatten entries getBasis(i,TorR1R2))) -assert(sum oo - 1 == numgens TorR1R2) -/// +-- (QuotientRing handled via its Ring parent.) + +-- DGAlgebraMap ** Ring: base-change. Given phi : A -> B, produces +-- phi ⊗ id_S : A ** S -> B ** S whose natural matrix is the image +-- matrix pushed into (B ** S).natural. +DGAlgebraMap ** Ring := (phi, S) -> ( + Anew := (phi.source) ** S; + Bnew := (phi.target) ** S; + Bnat := Bnew.natural; + oldImgs := first entries matrix phi.natural; + newImgs := apply(oldImgs, f -> + coerceAcrossRings(f, Bnat, "DGAlgebraMap ** Ring")); + dgAlgebraMap(Bnew, Anew, matrix {newImgs}) +) -TEST /// --- test 4 : findEasyRelations -debug DGAlgebras -R1 = ZZ/32003[a,b,x,y]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} -R2 = ZZ/32003[a,b,x,y,Degrees=>{1,1,2,2}]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} -A1 = koszulComplexDGA(R1) -A2 = koszulComplexDGA(R2) -cycleList1 = getGenerators(A1,DegreeLimit=>4) -cycleList2 = getGenerators(A2,DegreeLimit=>4) -HAEasy1 = findEasyRelations(A1,cycleList1) -HAEasy2 = findEasyRelations(A2,cycleList2) -tally ((flatten entries basis HAEasy1) / degree) -pairs (tally ((flatten entries basis HAEasy1) / degree)) -myList1 = {({4,8},1),({3,4},1),({3,5},6),({3,6},6),({3,7},4),({2,3},4),({2,4},11),({2,5},8),({2,6},4),({1,2},4),({1,3},4),({1,4},1),({0,0},1)} -myList2 = {({0},1),({1},9),({2},27),({3},17),({4},1)} -tally ((flatten entries basis HAEasy1) / degree) -tally myList1 -assert(pairs tally((flatten entries basis HAEasy1) / degree) == myList1) -assert(pairs tally((flatten entries basis HAEasy2) / degree) == myList2) -/// +-- (QuotientRing handled via its Ring parent.) + +-- DGModuleMap ** Ring: base-change of a DGModuleMap. f : M -> N becomes +-- f ⊗ id_S : M ** S -> N ** S. The natural matrix entries push forward +-- via the canonical substitute. +DGModuleMap ** Ring := (f, S) -> ( + Mnew := (f.source) ** S; + Nnew := (f.target) ** S; + Bnat := ring Nnew.natural; + oldMat := f.natural; + newEntries := apply(entries oldMat, row -> + apply(row, g -> coerceAcrossRings(g, Bnat, "DGModuleMap ** Ring"))); + newMat := if numcols oldMat == 0 then map(Nnew.natural, Mnew.natural, 0) + else map(Nnew.natural, Mnew.natural, matrix newEntries); + dgModuleMap(Nnew, Mnew, newMat) +) -TEST /// --- test 5 : homology of a DGA whose H_0 is not a field -R = ZZ/32003[a,b] -I = ideal{a^6,b^6} -A = koszulComplexDGA(I) -HA = HH A -describe HA -use R -J = I + ideal {a^4*b^5,a^5*b^4} -B = koszulComplexDGA(J) -getGenerators(B) -apply(5, i -> numgens prune homology(i,B)) -apply(5, i -> prune homology(i,B)) -HB = HH B -HB2 = zerothHomology B -HB.cache.cycles -ideal HB --- looks right... -getDegNModule(0,HB2,HB) -getDegNModule(1,HB2,HB) -getDegNModule(2,HB2,HB) -getDegNModule(3,HB2,HB) -getDegNModule(4,HB2,HB) - -R = ZZ/32003[a,b,c] -I = (ideal vars R)^2 -A = koszulComplexDGA(I) -apply(10, i -> prune homology(i,A)) -time HA = HH A -HA2 = zerothHomology A -tally ((ideal HA)_* / degree / first) -select ((ideal HA)_*, f -> first degree f == 2) --- looks right... -getDegNModule(0,HA2,HA) -getDegNModule(1,HA2,HA) -getDegNModule(2,HA2,HA) -getDegNModule(3,HA2,HA) --- need to add asserts -/// +-- (QuotientRing handled via its Ring parent.) + +-- DGModule ** Module: exterior tensor M ⊗_R N where M is a DGModule over +-- A and N is a free R-module. The result is a free DGModule over A, +-- with generators {e_i ⊗ n_j}, hom-degree deg(e_i) (n_j contributes +-- internal degree), and differential d(e_i ⊗ n_j) = d(e_i) ⊗ n_j. +-- +-- We insist N be a free module because M.natural ** N needs to come out +-- free over A.natural for our DGModule semantics. +DGModule ** Module := (M, N) -> ( + -- Cache M ** N on M so repeated calls agree (needed for sub/quot + -- compatibility: `(Q.ambient) ** N` must match the ambient we + -- expect in consuming operations). + if M.cache#?"tensorWithModule" and (M.cache#"tensorWithModule")#?N then + return (M.cache#"tensorWithModule")#N; + A := M.dgAlgebra; + Anat := A.natural; + if not isFreeModule M.natural then + error "DGModule ** Module: M.natural must be free (koszulComplexDGM/cokernel form not supported)."; + if not isFreeModule N then + error "DGModule ** Module: N must be a free module."; + rM := rank M.natural; + rN := rank N; + Ndegs := degrees N; + -- Generator (i, j) of M ⊗_R N has hom-degree = first(M.Degrees#i) + -- (N contributes nothing to hom-degree; it's an ordinary R-module) + -- and internal degree = rest(M.Degrees#i) + Ndegs#j (component-wise). + -- We pad Ndegs#j with zeros if the internal-degree vectors of M are + -- longer than the R-degree vectors of N (e.g., multigraded A). + padAdd := (lst1, lst2) -> apply(#lst1, k -> ( + a := lst1#k; + b := if k < #lst2 then lst2#k else 0; + a + b + )); + combinedDegrees := flatten apply(rM, i -> apply(rN, j -> ( + dM := M.Degrees#i; + dN := Ndegs#j; + {first dM} | padAdd(drop(dM, 1), dN) + ))); + P := freeDGModule(A, combinedDegrees); + Pgens := apply(rank P.natural, k -> (P.natural)_k); + idxOf := (i, j) -> i * rN + j; + newDiffs := flatten apply(rM, i -> apply(rN, j -> ( + dMe := M.diff#i; + if dMe == 0 then 0_(P.natural) + else ( + dEnts := entries dMe; + sum apply(rM, k -> ( + coef := dEnts#k; + if coef == 0 then 0_(P.natural) + else coef * Pgens#(idxOf(k, j)) + )) + ) + ))); + setDiff(P, newDiffs); + if not M.cache#?"tensorWithModule" then + M.cache#"tensorWithModule" = new MutableHashTable; + (M.cache#"tensorWithModule")#N = P; + P +) -TEST /// --- test 6 : homologyAlgebra -R = ZZ/32003[a,b,x,y]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} -koszulR = koszul vars R -time apply(5,i -> numgens prune HH_i(koszulR)) -A = koszulComplexDGA(R) -time apply(5,i -> numgens prune homology(i,A)) --- ~2.15 seconds on mbp, with graded differentials -time HA = HH A -assert(numgens HA == 34) -assert(numgens ideal HA == 576) -assert(#(first degrees HA) == 2) - --- same example, but not graded because of the degree change. The homologyAlgebra function --- will then only return a graded algebra -R2 = ZZ/32003[a,b,x,y,Degrees=>{1,1,2,2}]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} -koszulR2 = koszul vars R2 -time apply(5,i -> numgens prune HH_i(koszulR2)) -A2 = koszulComplexDGA(R2) -time apply(5,i -> numgens prune homology(i,A2)) --- ~2.85 seconds on mbp, with ungraded differentials -time HA2 = homologyAlgebra A2 -assert(numgens HA2 == 34) -assert(numgens ideal HA2 == 576) --- should only be singly graded -assert(#(first degrees HA2) == 1) -/// +-- Commuted form: Module ** DGModule. By convention the "primary" factor +-- is the DG one, so we just forward. +Module ** DGModule := (N, M) -> M ** N + +-- DGModule ** Ideal is intentionally NOT defined, because `module I` is +-- typically non-free and we require free N. For quotient-style base +-- changes use `M ** (R/I)`; for I·M inside M use `I' * M` with a DGIdeal +-- I' whose generators are the ideal generators lifted to A.natural. + +-- DGSubmodule ** Module: base-change the ambient by exterior tensor with +-- N, then pushforward the inclusion to the new natural module. Column +-- e_â„“ of the old inclusion becomes (e_â„“ ⊗ n_0, e_â„“ ⊗ n_1, ...) in the +-- product indexing (flat index â„“ * rN + j) — i.e. one copy per generator +-- of N. d-closure is preserved because d does not touch the N-factor. +DGSubmodule ** Module := (Sub, N) -> ( + M := Sub.ambient; + Mnew := M ** N; + if not isFreeModule N then + error "DGSubmodule ** Module: N must be a free module."; + rN := rank N; + rM := rank M.natural; + incOld := Sub.inclusion.natural; + numCols := numcols incOld; + Bnat := ring Mnew.natural; + -- Build the (rM*rN) x (numCols*rN) coefficient matrix directly. + -- Column index (c, j) with c in 0..numCols-1, j in 0..rN-1 has + -- entries: row (i*rN + j') = (incOld)_(i,c) if j' == j, else 0. + newRows := apply(rM * rN, r -> ( + i := r // rN; + jprime := r % rN; + apply(numCols * rN, colIdx -> ( + c := colIdx // rN; + j := colIdx % rN; + if j != jprime then 0_Bnat + else sub(incOld_(i, c), Bnat) + )) + )); + newInc := if numCols == 0 then map(Mnew.natural, (Bnat)^0, 0) + else map(Mnew.natural, , matrix newRows); + dgSubmodule(Mnew, newInc) +) -TEST /// --- test 7 : acyclicClosure, isHomologyAlgebraTrivial, isGolod, isGolodHomomorphism -R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} -M = coker matrix {{a^3*b^3*c^3*d^3}}; -S = R/ideal{a^3*b^3*c^3*d^3} -time A = acyclicClosure(R,EndDegree=>6) -B = A ** S -assert(isHomologyAlgebraTrivial(B,GenDegreeLimit=>6)) -assert(isGolodHomomorphism(S,GenDegreeLimit=>6,TMOLimit=>3)) --- returns true since R --> S is Golod -R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} -A = koszulComplexDGA(R) -assert(not isHomologyAlgebraTrivial(A)) -assert(not isGolod R) --- false, since R is Gorenstein, and so HA has Poincare Duality -/// +-- DGSubmodule ** Ideal: see the note above `DGModule ** Ideal` — not +-- defined because the ideal's module is typically non-free. + +-- DGQuotientModule ** Module: base-change the ambient and pushforward +-- the killed submodule against the SAME base-changed ambient, then +-- requote. (Same subtlety as `** Ring`: two separate ambients would +-- trip the sub-ambient equality check in dgQuotientModule.) +DGQuotientModule ** Module := (Q, N) -> ( + if not isFreeModule N then + error "DGQuotientModule ** Module: N must be a free module."; + M := Q.ambient; + Mnew := M ** N; + rN := rank N; + rM := rank M.natural; + incOld := Q.subDGModule.inclusion.natural; + numCols := numcols incOld; + Bnat := ring Mnew.natural; + newRows := apply(rM * rN, r -> ( + i := r // rN; + jprime := r % rN; + apply(numCols * rN, colIdx -> ( + c := colIdx // rN; + j := colIdx % rN; + if j != jprime then 0_Bnat + else sub(incOld_(i, c), Bnat) + )) + )); + newInc := if numCols == 0 then map(Mnew.natural, (Bnat)^0, 0) + else map(Mnew.natural, , matrix newRows); + Subnew := dgSubmodule(Mnew, newInc); + Mnew / Subnew +) -TEST /// --- test 8 : DGAlgebra ** DGAlgebra - need to add in an assert -R = ZZ/101[a,b,c,d] -I = ideal(a,b) -J = ideal(c,d) -A = koszulComplexDGA(I) -B = koszulComplexDGA(J) -Cdd = toComplex(A ** B) -Cdd.dd -/// +-- DGQuotientModule ** Ideal: see the `** Ideal` note above. -TEST /// --- test 9 : isHomologyAlgebraTrivial, getGenerators, findTrivialMasseyOperation -Q = ZZ/101[x_1..x_6] -I = ideal (x_3*x_5,x_4*x_5,x_1*x_6,x_3*x_6,x_4*x_6) -R = Q/I -A = koszulComplexDGA(R) -isHomologyAlgebraTrivial(A,GenDegreeLimit=>3) -cycleList = getGenerators(A) -assert(first findTrivialMasseyOperation(A)) +-- Commuted forms for sub/quot-module ** Module. +Module ** DGSubmodule := (N, Sub) -> Sub ** N +Module ** DGQuotientModule := (N, Q) -> Q ** N --- this is a Teter ring, and the computation in Avramov and Levin's paper shows --- H(A) does not have trivial multiplication. -Q = ZZ/101[x,y,z] -I = ideal (x^3,y^3,z^3,x^2*y^2*z^2) -R = Q/I -A = koszulComplexDGA(R) -assert(not isHomologyAlgebraTrivial(A,GenDegreeLimit=>3)) -cycleList = getGenerators(A) -prodList = apply(subsets(cycleList,2), l -> (first degree l#0 + first degree l#1,l#0*l#1)); -assert(not first findTrivialMasseyOperation(A)) -/// +-------------------- +-- Documentation -- +-------------------- -TEST /// --- test 10 : isAcyclic -R = ZZ/101[a,b,c,d] -A = koszulComplexDGA(R) -B = koszulComplexDGA({a^4,b^4,c^4,d^4}) -C = koszulComplexDGA((ideal vars R)^2) -assert(isAcyclic A) -assert(isAcyclic B) -assert(not isAcyclic C) -/// +beginDocumentation() -TEST /// --- test 11 : isGolod and isHomologyAlgebraTrivial example --- Interesting case due to Katthan. -Q = ZZ/101[x_1,x_2,y_1,y_2,z,w] -I = ideal {x_1*x_2^2,z^2*w,y_1*y_2^2,x_2^2*z*w,y_2^2*z^2,x_1*x_2*y_1*y_2,x_2^2*y_2^2*z,x_1*y_1*z} -R = Q/I -assert(isHomologyAlgebraTrivial koszulComplexDGA R == true) -assert(isGolod R == false) -/// +load "./DGAlgebras/doc.m2" -TEST /// ---- test 12: isGolod and isHomologyAlgebraTrivial example again ---- This example is due to Roos -S = QQ[x,y,z,u] -I = ideal(u^3, x*y^2, (x+y)*z^2, x^2*u+z*u^2, y^2*u+x*z*u, y^2*z+y*z^2) - -- you can see that the mult on the koszul homology will be trivial -betti (A = freeResolution I) -R = S/I -assert(isHomologyAlgebraTrivial koszulComplexDGA R == true) -assert(isGolod R == false) -/// +------------------------------- +-- Testing -- +------------------------------- +load "./DGAlgebras/tests.m2" end-- @@ -4507,7 +6189,7 @@ needsPackage "DGAlgebras" R = ZZ/101[x,y,z] A = freeDGAlgebra(R,{{1},{1},{1},{3}}) A.natural -setDiff(A,{x,y,z,x*T_2*T_3-y*T_1*T_3+z*T_1*T_2}) +setDiff(A,{x,y,z,x*T_(1,2)*T_(1,3)-y*T_(1,1)*T_(1,3)+z*T_(1,1)*T_(1,2)}) isHomogeneous(A) @@ -4587,6 +6269,3 @@ displayBlockDiff(A, {1,0,3}) displayBlockDiff(A, [{1,0,3}]) /// -displayBlockDiff(DGAlgebra, BasicList) := (A,L) -> displayBlockDiff(A,L) - -new Array from [{1}] diff --git a/M2/Macaulay2/packages/DGAlgebras/doc.m2 b/M2/Macaulay2/packages/DGAlgebras/doc.m2 new file mode 100644 index 00000000000..d47604e34d1 --- /dev/null +++ b/M2/Macaulay2/packages/DGAlgebras/doc.m2 @@ -0,0 +1,9992 @@ +doc /// + Key + DGAlgebras + Headline + Data types and basic functions on differential graded (DG) Algebras. + Description + Text + This package is used to define and manipulate DG algebras. + Contributors + Some documentation was added by Daniel Rostamloo and David Eisenbud. + Subnodes + "Basic operations on DG Algebras" + "The Koszul complex as a DG Algebra" + "Basic operations on DG Algebra Maps" + "Basic operations on DG Module Maps" + "Base change and tensor with non-DG types" + "Operations on DG Ideals" + "Building DG modules, submodules, and quotients" + "Module-like operations on DG modules" + "Operations on DG Submodules" + "Image, kernel, and cokernel of DG module maps" + "Homology of DG modules and DG module maps" + "Pruning DG modules, submodules, quotients, and maps" + "Well-definedness, acyclicity, and quasi-isomorphism" + "Semifree resolutions of DG modules" + "Computing module differentials and visualizing DG modules" + "Building DG algebras from existing DG algebras" + "Low-level differential computations and validity checks" + "Accessors and cache management" + "DG module primitives and chain-complex export" +/// + +doc /// + Key + "Basic operations on DG Algebras" + Headline + Outlines some basic operations on DG Algebras + Description + Text + There are several ways to define a DGAlgebra. One can start by defining one 'from scratch'. One does + this by specifying the ring over which the DGAlgebra is defined and the degrees of the generators. The + name of the generators of the DGAlgebra by default is $T_i$, but one may change this by specifying the + optional (string) argument 'Variable'. + Example + R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} + A = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{1,1}}) + Text + The command freeDGAlgebra only defines the underlying algebra of A, and not the differential. To set the differential of A, + one uses the command setDiff. + Example + setDiff(A, gens R) + Text + Note that the above is the (graded) Koszul complex on a set of generators of R. A much easier way to define this is to use the + function koszulComplexDGA. + Example + B = koszulComplexDGA(R, Variable=>"S") + Text + One can compute the homology algebra of a DGAlgebra using the homology (or HH) command. + Example + HB = HH B + describe HB + degrees HB + Text + Note that since R is a complete intersection, its Koszul homology algebra is an exterior algebra, which is a + free graded commutative algebra. Note that the internal degree is preserved in the computation of the homology algebra + of B. + Text + One can also adjoin variables to kill cycles in homology. The command killCycles looks for the first positive degree + nonzero homology (say i), and adjoins variables in homological degree i+1 that differentiate to a minimal generating set of this homology, so that the + resulting DGAlgebra now only has homology in degree greater than i (note of course this could introduce new homology in higher degrees). + The command adjoinVariables allows finer control over this procedure. See @ TO adjoinVariables @ for an example. + Example + HB.cache.cycles + C = adjoinVariables(B,{first HB.cache.cycles}) + homologyAlgebra(C,GenDegreeLimit=>4,RelDegreeLimit=>4) + C = killCycles(B) + homologyAlgebra(C,GenDegreeLimit=>4,RelDegreeLimit=>4) + Text + Again, note that since R is a complete intersection, once we adjoin the variables in homological degree two to kill the cycles in degree one, + we obtain a minimal DG Algebra resolution of the residue field of R. Also, note that since C has generators in even degree, one must specify the + optional arguments GenDegreeLimit and RelDegreeLimit to specify the max degree of the computation. To do this, one uses the homologyAlgebra command + rather than the HH command. + Text + This computation could have also been done with the command acyclicClosure. The command acyclicClosure performs the command killCycles sequentially to ensure that the + result has homology in higher and higher degrees, thereby computing (part of) a minimal DG Algebra resolution of the residue field. acyclicClosure has an optional + argument EndDegree that allows the user to specify the maximum homological degree with which to perform this adjunction of variables. The default value of this is 3, since if there + are any variables of degree 3 that need to be added, then each subsequent homological degree will require some variables to be adjoined (Halperin's rigidity theorem). + Example + D = acyclicClosure R + R' = ZZ/101[x,y,z]/ideal{x^2,y^2,z^2,x*y*z} + E = acyclicClosure(R',EndDegree=>5) + tally degrees E.natural + Text + As you can see, since R' is not a complete intersection, the acyclic closure of E requires infinitely many variables; we display the degrees of the first 6 here. + The tally that is displayed gives the deviations of the ring R. One can compute the deviations directly from any minimal free resolution of the residue field + of R', so that using the one provided by res coker vars R is faster. To do this, use the command @ TO deviations @. + Example + deviations(R,DegreeLimit=>6) + deviations(R',DegreeLimit=>6) + Text + As a brief warning, the command @ TO poincareN @ which is used in @ TO deviations @ uses the symbols S and T internally, and may cause problems referring to such rings at the Macaulay2 prompt. +/// + +doc /// + Key + "The Koszul complex as a DG Algebra" + Headline + an example + Description + Text + The Koszul complex on a sequence of elements $f_1,\dots,f_r$ is a complex of R-modules whose underlying graded R-module + is the exterior algebra on R^r generated in homological degree one. This algebra structure also respects the boundary map + of the complex in the sense that it satisfies the Leibniz rule. That is, $d(ab) = d(a)b + (-1)^{deg a}ad(b)$. When one + speaks of 'the' Koszul complex of a ring, one means the Koszul complex on a minimal set of generators of the maximal ideal of R. + Example + R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} + KR = koszulComplexDGA R + Text + One can specify the name of the variable to easily handle multiple Koszul complexes at once. + Example + S = ZZ/101[x,y,z]/ideal{x^3,y^3,z^3,x^2*y^2,y^2*z^2} + KS = koszulComplexDGA(S,Variable=>"U") + Text + To obtain the chain complex associated to the Koszul complex, one may use toComplex. One can also obtain this complex + directly without using the DGAlgebras package by using the command @ TO koszul @. + Example + cxKR = toComplex KR + prune HH cxKR + Text + Since the Koszul complex is a DG algebra, its homology is itself an algebra. One can obtain this algebra using the command + homology, homologyAlgebra, or HH (all commands work). This algebra structure can detect whether or not the ring is a complete + intersection or Gorenstein. + Example + HKR = HH KR + ideal HKR + R' = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3,a*c,a*d,b*c,b*d,a^2*b^2-c^2*d^2} + HKR' = HH koszulComplexDGA R' + numgens HKR' + ann ideal gens HKR' + Text + Note that since the socle of HKR' is one dimensional, HKR' has Poincare duality, and hence R' is Gorenstein. + Text + One can also consider the Koszul complex of an ideal, or a sequence of elements. + Example + Q = ambient R + I = ideal {a^3,b^3,c^3,d^3} + KI = koszulComplexDGA I + HKI = HH KI + describe HKI + use Q + I' = I + ideal{a^2*b^2*c^2*d^2} + KI' = koszulComplexDGA I' + HKI' = HH KI' + describe HKI' + HKI'.cache.cycles + Text + Note that since I is a Q-regular sequence, the Koszul complex is acyclic, and that both homology algebras are algebras over the zeroth homology + of the Koszul complex. +/// + +doc /// + Key + "Basic operations on DG Algebra Maps" + Headline + Outlines some basic operations on DGAlgebraMaps + Description + Text + An algebra map between the underlying graded algebras that satisfies the Leibniz rule is a morphism of DG algebras. Such objects + are created using the DGAlgebraMap class. As with DGAlgebras, one can define a DGAlgebraMap 'from scratch' using @ TO dgAlgebraMap @. + Example + R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} + K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") + K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") + f = dgAlgebraMap(K2,K1,matrix{{0,T_(1,1),T_(1,2)}}) + Text + Once we define the DGAlgebraMap, it is a good idea to check to see if it indeed satisfies the Leibniz rule. This can be checked by using + isWellDefined. + Example + isWellDefined f + Text + Oops! Let's try that again. + Example + g = dgAlgebraMap(K1,K2,matrix{{Y_(1,2),Y_(1,3)}}) + isWellDefined g + Text + One can lift a ring homomorphism in degree zero to a map of DGAlgebras (up to a specified degree) using liftToDGMap. This is helpful + in some of the internal functions of the DGAlgebras package, such as computing the map induced on Tor algebras by a RingMap. + Example + R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} + S = R/ideal{a^2*b^2*c^2} + f = map(S,R) + A = acyclicClosure(R,EndDegree=>3) + B = acyclicClosure(S,EndDegree=>3) + phi = liftToDGMap(B,A,f) + Text + Once one has a DGAlgebraMap, one can also obtain the underlying map of complexes via toComplexMap. + Example + cmPhi = toComplexMap(phi,EndDegree=>3) + Text + There are also some auxiliary commands associated with DGAlgebraMaps + Example + source phi + target phi + Text + One can also obtain the map on homology induced by a DGAlgebra map. + Example + HHg = HH g + matrix HHg +/// + +doc /// + Key + "Basic operations on DG Module Maps" + Headline + Outlines some basic operations on DGModuleMaps + Description + Text + A morphism of DG modules over a fixed DG algebra A is an A-linear map of underlying + graded A.natural-modules that commutes with the differentials. Such objects are + created using the DGModuleMap class. The workhorse constructor is @ TO dgModuleMap @, + which accepts either a full Matrix encoding of the map or a list of image Vectors + (one per natural generator of the source). + Example + R = QQ[x,y]/ideal(x^2,y^2) + A = koszulComplexDGA R + k = R^1 / ideal(x, y) + Mdg = minimalSemifreeResolution(A, k, EndDegree => 2) + idM = identityDGModuleMap Mdg + isWellDefined idM + Text + As with DGAlgebraMaps, once you have a candidate map it is a good idea to check + @ TO isWellDefined @. The check verifies (per natural generator of the source) both + the hom-degree condition and the chain-map condition $d_N \circ f = f \circ d_M$. + Example + natGens = apply(rank Mdg.natural, i -> (Mdg.natural)_i) + xMap = dgModuleMap(Mdg, Mdg, apply(natGens, g -> x * g)) + isWellDefined xMap + Text + A DGModuleMap induces a chain map between the underlying complexes via + @ TO toComplexMap @, and a module map on every homology degree via + @ TO homology @. + Example + H0 = homology(xMap, 0) + H0 == map(target H0, source H0, 0) + Text + Classical construction: given a hom-degree-0 seed (the images of the hom-deg-0 + generators of M in N.natural), @ TO liftToDGModuleMap @ inductively solves + $d_N(x) = f(d_M(e))$ degree by degree, producing a full chain map M -> N. + Requires N to be acyclic up to the requested EndDegree. + Example + f = liftToDGModuleMap(Mdg, Mdg, {(Mdg.natural)_0}, EndDegree => 2) + isWellDefined f + h0 = homology(f, 0) + h0 == id_(source h0) + Text + Auxiliary operations: addition, subtraction, scalar multiplication by elements of + A.ring, negation, and composition via $*$ are all supported, mirroring the + matrix-level operations on f.natural. + Example + (2_R * idM).natural == 2 * idM.natural + (idM * idM).natural == idM.natural + SeeAlso + DGModuleMap + dgModuleMap + identityDGModuleMap + zeroDGModuleMap + liftToDGModuleMap + isWellDefined + toComplexMap + homology +/// + +doc /// + Key + DGModuleMap + Headline + The class of morphisms of DG modules over a common DG algebra + Description + Text + A DGModuleMap encodes an A-linear chain map between two DGModules sharing the + same underlying DG algebra A. Create one via @ TO dgModuleMap @. See + @ TO "Basic operations on DG Module Maps" @ for a guided tour. + SeeAlso + DGModule + dgModuleMap + "Basic operations on DG Module Maps" +/// + +doc /// + Key + dgModuleMap + (dgModuleMap, DGModule, DGModule, Matrix) + (dgModuleMap, DGModule, DGModule, List) + Headline + Construct a DGModuleMap from a matrix or from a list of image Vectors + Usage + f = dgModuleMap(N, M, img) + Inputs + N:DGModule + The target DGModule. + M:DGModule + The source DGModule; must share the same DG algebra as N. + img: + Either a Matrix whose i-th column encodes the image of the i-th natural + generator of M in N.natural, or a List of Vectors of the same length as + M.Degrees (one Vector per source generator). + Outputs + f:DGModuleMap + Description + Text + The map @ TT "f" @ satisfies the @ TT "A" @-linearity constraint by + construction: the image of a general element @ TT "\\sum a_i e_i" @ is + @ TT "\\sum a_i f(e_i)" @, where @ TT "f(e_i)" @ is the @ TT "i" @-th entry + supplied. Whether @ TT "f" @ is also a chain map (i.e.\ commutes with + the differentials) can be verified with @ TO isWellDefined @. + Text + A particularly useful application is to build multiplication-by-a-ring-element + chain maps on a minimal semifree resolution, whose induced action on + homology recovers the action of the element on @ TT "Tor" @. Over the + complete intersection @ TT "R = k[x, y]/(x^2, y^2)" @, the variable + @ TT "y" @ acts as zero on the residue field, and indeed the induced map + on homology of the multiplication-by-@ TT "y" @ chain map is zero in + every degree: + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 3) + natGens = apply(rank Mdg.natural, i -> (Mdg.natural)_i) + fy = dgModuleMap(Mdg, Mdg, apply(natGens, g -> y * g)) + isWellDefined fy + cmy = toComplexMap fy + apply(0..3, n -> prune HH_n cmy) + Text + The same constructor also accepts a @ TO Matrix @ whose columns give + the images of the natural generators. For instance, supplying the + identity matrix of @ TT "Mdg.natural" @ reproduces the identity chain + map: + Example + idmat = id_(Mdg.natural) + g = dgModuleMap(Mdg, Mdg, idmat) + isWellDefined g + g == identityDGModuleMap Mdg + SeeAlso + identityDGModuleMap + zeroDGModuleMap + liftToDGModuleMap + (isWellDefined, DGModuleMap) + "Basic operations on DG Module Maps" +/// + +doc /// + Key + identityDGModuleMap + (identityDGModuleMap, DGModule) + Headline + The identity DGModuleMap on a DG module + Usage + idM = identityDGModuleMap M + Inputs + M:DGModule + Outputs + idM:DGModuleMap + Description + Text + Returns the identity endomorphism of M as a DGModuleMap. Its underlying + natural matrix is $\mathrm{id}_{M.natural}$ and its induced map on every + homology degree is the identity. + Text + The shorthand @ TT "id_M" @ (mirroring the @ TO "Complexes::Complexes" @ + convention @ TT "id_C" @ for a Complex @ TT "C" @) is an alias, and both + forms compare equal to the scalar @ TT "1" @: + Example + R = ZZ/101[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + id_KM == identityDGModuleMap KM + id_KM == 1 + Text + The identity is the neutral element for composition of DG module maps. + On a semifree resolution of the residue field, the mult-by-y chain map + is idempotent under pre- and post-composition with the identity: + Example + Mdg = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 2) + natGens = apply(rank Mdg.natural, i -> (Mdg.natural)_i) + fy = dgModuleMap(Mdg, Mdg, apply(natGens, v -> y * v)) + idMdg = identityDGModuleMap Mdg + idMdg * fy == fy + fy * idMdg == fy + idMdg * idMdg == idMdg + SeeAlso + dgModuleMap + zeroDGModuleMap +/// + +doc /// + Key + zeroDGModuleMap + (zeroDGModuleMap, DGModule, DGModule) + Headline + The zero DGModuleMap between two DG modules over a common algebra + Usage + zM = zeroDGModuleMap(N, M) + Inputs + N:DGModule + M:DGModule + Outputs + zM:DGModuleMap + The zero map M -> N. Its natural matrix is zero, and its induced map on + every homology degree is zero. + Description + Text + The zero endomorphism is the neutral element for addition and the + absorbing element for composition of DG module maps: + Example + R = ZZ/101[x] / ideal(x^2) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, R^1 / ideal(x), EndDegree => 2) + idM = identityDGModuleMap Mdg + zM = zeroDGModuleMap(Mdg, Mdg) + idM + zM == idM + zM * idM == zM + idM * zM == zM + Text + Taking the cokernel of the zero endomorphism recovers the original DG + module, while taking its kernel yields the full module as a DG + submodule: + Example + Q = cokernel zM + rank Q.natural == rank Mdg.natural + K = kernel zM + instance(K, DGSubmodule) + SeeAlso + dgModuleMap + identityDGModuleMap +/// + +doc /// + Key + liftToDGModuleMap + (liftToDGModuleMap, DGModule, DGModule, Matrix) + (liftToDGModuleMap, DGModule, DGModule, List) + (liftToDGModuleMap, DGModule, DGModule, Vector) + [liftToDGModuleMap, EndDegree] + Headline + Lift an image of hom-degree-0 generators to a full DGModuleMap + Usage + f = liftToDGModuleMap(N, M, h0) + Inputs + N:DGModule + The target DGModule. Must be acyclic in positive hom-degrees up to + EndDegree so that lifts exist. + M:DGModule + The source DGModule. Must be semifree (M.natural a free module). + h0: + A Matrix with one column per hom-degree-0 generator of M and + (rank N.natural)-many rows whose entries lie in A.natural, or a List of + Vectors (in N.natural), or a single Vector when M has exactly one + hom-degree-0 generator. + Outputs + f:DGModuleMap + Description + Text + Starting from the supplied hom-degree-0 assignment, the routine inductively + constructs images for each M-generator of hom-degree $d \geq 1$ by solving + $d_N(x) = f(d_M(e))$ in N.natural at hom-degree $d$. Existence of a lift + at every step is what requires N to be acyclic. The lift is not unique in + general: different choices of preimage at each step produce chain-homotopic + lifts. + Example + R = QQ[x,y]/ideal(x^2,y^2) + A = koszulComplexDGA R + Mmin = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 2) + Mnon = semifreeResolution(A, R^1 / ideal(x, y), EndDegree => 2) + quism = liftToDGModuleMap(Mnon, Mmin, {(Mnon.natural)_0}, EndDegree => 2) + isWellDefined quism + SeeAlso + dgModuleMap + identityDGModuleMap + "Basic operations on DG Module Maps" +/// + +doc /// + Key + (homology, DGModuleMap) + Headline + The induced map on graded homology modules over HH(A) + Usage + h = homology f + Inputs + f:DGModuleMap + A morphism M -> N of DG modules over a common DG algebra A. Both + M.natural and N.natural must be free (the free / semifree case). + Outputs + h: + The induced A.homology-module map H_*(M) -> H_*(N), where H_*(M) and + H_*(N) carry their natural HH(A)-module structures via the action of + cycles of A on M and N. The result lives over the ring HH(A). + Description + Text + The per-degree restrictions of h are given by @ TO (homology, DGModuleMap, ZZ) @. + The present method assembles them into a single map of HH(A)-modules by + forming the direct sum of the per-degree pieces and imposing the + cycle-action relations on both sides. Functorial in f. + Example + R = QQ[x,y]/ideal(x^2, y^2) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, R^1 / ideal(x,y), EndDegree => 2) + idM = identityDGModuleMap Mdg + h = homology idM + ring source h === HH A + SeeAlso + (homology, DGModuleMap, ZZ) + "Basic operations on DG Module Maps" +/// + +doc /// + Key + (symbol ==, DGModuleMap, DGModuleMap) + (isHomogeneous, DGModuleMap) + (map, DGModule, DGModule, ZZ) + (isQuasiIsomorphism, DGModuleMap) + Headline + Elementary predicates and constructors for DGModuleMaps + Description + Text + Auxiliary operations mirroring those on ComplexMap. + For maps f, g between DG modules over a common DG algebra: + Text + @ TT "f == g" @ -- equality of source, target, and underlying natural matrix. + Text + @ TT "isHomogeneous f" @ -- agrees with @ TT "isHomogeneous f.natural" @. + Text + @ TT "map(N, M, 0)" @ -- returns the zero map M -> N. + @ TT "map(M, M, 1)" @ -- returns the identity on M. + Text + @ TT "isQuasiIsomorphism f" @ -- returns true iff the induced chain map on + @ TO toComplexMap @ is a quasi-isomorphism. + Example + R = QQ[x,y]/ideal(x^2, y^2) + Mdg = minimalSemifreeResolution(koszulComplexDGA R, R^1 / ideal(x,y), EndDegree => 2) + idM = identityDGModuleMap Mdg + zM = zeroDGModuleMap(Mdg, Mdg) + idM == idM + map(Mdg, Mdg, 0) == zM + map(Mdg, Mdg, 1) == idM + isQuasiIsomorphism idM + SeeAlso + DGModuleMap + identityDGModuleMap + zeroDGModuleMap + "Basic operations on DG Module Maps" +/// + +doc /// + Key + identityDGAlgebraMap + (identityDGAlgebraMap, DGAlgebra) + Headline + The identity DGAlgebraMap on a DG algebra + Usage + idA = identityDGAlgebraMap A + Inputs + A:DGAlgebra + Outputs + idA:DGAlgebraMap + Description + Text + Returns the identity self-map of A as a DGAlgebraMap. Under the hood the + underlying RingMap A.natural -> A.natural is the identity map on every + generator (including the base-ring generators). + Example + R = QQ[x,y]/ideal(x^2,y^2) + A = koszulComplexDGA R + idA = identityDGAlgebraMap A + (idA * idA) == idA + SeeAlso + dgAlgebraMap + (symbol *, DGAlgebraMap, DGAlgebraMap) +/// + +doc /// + Key + (symbol *, DGAlgebraMap, DGAlgebraMap) + Headline + Composition of DG algebra maps + Usage + h = g * f + Inputs + g:DGAlgebraMap + A map with source = target of f. + f:DGAlgebraMap + Outputs + h:DGAlgebraMap + The composite DGAlgebraMap whose underlying RingMap is g.natural * f.natural. + SeeAlso + identityDGAlgebraMap + dgAlgebraMap +/// + +doc /// + Key + (symbol **, DGAlgebra, DGAlgebra) + tensorFactors + (tensorFactors, DGAlgebra) + tensorInclusions + (tensorInclusions, DGAlgebra) + Headline + Exterior tensor product of DG algebras over a common ground ring + Usage + C = A ** B + (A1, A2) = tensorFactors C + (iotaA, iotaB) = tensorInclusions C + Inputs + A:DGAlgebra + B:DGAlgebra + Must satisfy A.ring === B.ring. + Outputs + C:DGAlgebra + A DGAlgebra over A.ring whose underlying algebra has #A.Degrees + #B.Degrees + generators, with the obvious multi-degrees, and whose differential is + d_A on the A-generators and d_B on the B-generators (transported via the + canonical inclusions). + Description + Text + This is the exterior tensor product of A and B as DGAs: both are viewed + as augmented DG algebras over their common ground ring, and the tensor + is taken componentwise in multi-degrees. Internally the result is cached + on A.cache, so C1 = A ** B and C2 = A ** B return the SAME DGAlgebra + object, which is essential for composition identities to hold downstream. + Text + The helpers @ TO tensorFactors @ and @ TO tensorInclusions @ recover the + pair (A, B) and the canonical inclusions A -> C and B -> C (as DGAlgebraMaps). + Example + R = QQ[x,y] + A = koszulComplexDGA R + C = A ** A + numgens C.natural + (iotaA, iotaB) = tensorInclusions C + SeeAlso + (symbol **, DGAlgebraMap, DGAlgebraMap) + (symbol **, DGModule, DGModule) +/// + +doc /// + Key + (symbol **, DGAlgebraMap, DGAlgebraMap) + Headline + Tensor product of DG algebra maps + Usage + h = f ** g + Inputs + f:DGAlgebraMap + A:A -> A' + g:DGAlgebraMap + B:B -> B' + Outputs + h:DGAlgebraMap + The map A ** B -> A' ** B' that restricts to f on the A-generators and + g on the B-generators, transported via the canonical inclusions. + Description + Text + Functorial with the DGA tensor product: identityDGAlgebraMap(A) ** identityDGAlgebraMap(B) + equals identityDGAlgebraMap(A ** B). + SeeAlso + (symbol **, DGAlgebra, DGAlgebra) + identityDGAlgebraMap +/// + +doc /// + Key + (symbol **, DGModule, DGModule) + Headline + Exterior tensor product of DG modules over DG algebras sharing a ground ring + Usage + P = M ** N + Inputs + M:DGModule + A (free) DG module over some DG algebra A. + N:DGModule + A (free) DG module over some DG algebra B, with A.ring === B.ring. + Outputs + P:DGModule + A (free) DG module over A ** B whose natural generators are indexed + lexicographically by (i, j) with i ranging over M-generators and j over + N-generators. The multi-degree of (i, j) is M.Degrees#i + N.Degrees#j + coordinate-wise, and the differential is + $d(e_i \otimes f_j) = d_M(e_i) \otimes f_j + (-1)^{|e_i|}\, e_i \otimes d_N(f_j)$. + Description + Text + This is the EXTERIOR tensor product: we do not quotient by an A- or B-action. + The resulting DGModule lives over A ** B via the canonical inclusions. + The result is cached on M.cache, so M ** N returns the same object on repeat. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + ng = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * ng#0}) + P = M ** M + P.dgAlgebra === (A ** A) + SeeAlso + (symbol **, DGAlgebra, DGAlgebra) + (symbol **, DGModuleMap, DGModuleMap) + freeDGModule +/// + +doc /// + Key + (symbol **, DGModuleMap, DGModuleMap) + Headline + Tensor product of DG module maps + Usage + h = F ** G + Inputs + F:DGModuleMap + M -> M' + G:DGModuleMap + N -> N' + Outputs + h:DGModuleMap + The map M ** N -> M' ** N' acting by F on the first factor and G on the second factor. + Description + Text + Functorial: idM ** idN equals identityDGModuleMap(M ** N), and tensor with + a zero map is a zero map. + SeeAlso + (symbol **, DGModule, DGModule) + identityDGModuleMap + zeroDGModuleMap +/// + +doc /// + Key + DGAlgebra + Headline + The class of all DGAlgebras + Description + Text + Some common ways to create DGAlgebras include @ TO koszulComplexDGA @, @ TO freeDGAlgebra @, @ TO setDiff @, and @ TO acyclicClosure @. + SeeAlso + "Basic operations on DG Algebras" +/// + +doc /// + Key + freeDGAlgebra + (freeDGAlgebra,Ring,List) + [freeDGAlgebra,Variable] + Headline + Construct a free DG algebra with given generator degrees + Usage + A = freeDGAlgebra(R, degreeList) + A = freeDGAlgebra(R, degreeList, Variable => "U") + Inputs + R:Ring + The base ring over which the DG algebra is defined. + degreeList:List + One degree per algebra generator. Each degree is itself a + @ TO List @ of integers; the @ EM "first" @ entry is interpreted + as the homological degree, and any remaining entries are internal + gradings carried through to the underlying algebra. + Variable => String + Either a base name (@ TO String @ or @ TO Symbol @) or an explicit + list of symbols. Defaults to @ TT "\"T\"" @. See below. + Outputs + A:DGAlgebra + A DG algebra over @ TT "R" @ whose @ TT "A.natural" @ is a + graded-commutative polynomial ring on @ TT "#degreeList" @ + generators. The differential is not set by this constructor; call + @ TO setDiff @ on the result to install it. + Description + Text + The output is a @ TO DGAlgebra @ whose underlying ring is a + polynomial ring in graded-commutative generators: generators of + @ EM "odd" @ homological degree are skew-commutative and square + to zero (i.e.\ exterior), while generators of @ EM "even" @ + homological degree are fully commutative. The resulting algebra + is always free as a graded @ TT "R" @-algebra; the current version + of the package does @ EM "not" @ handle quotient DG algebras whose + underlying ring is a non-polynomial quotient. + Text + @ BOLD "Variable-naming convention." @ Generators are named + @ TT "base_(i, j)" @, where @ TT "i" @ is the homological degree + (the first entry of the degree vector) and @ TT "j" @ is a + @ EM "1-indexed" @ counter among generators at that hom-degree. + So @ TT "freeDGAlgebra(R, {{1}, {1}, {1}, {3}})" @ produces four + generators @ TT "T_(1,1), T_(1,2), T_(1,3), T_(3,1)" @. The + @ TT "Variable" @ option changes the base name: + Example + R = ZZ/101[x, y, z] + A = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}, Variable => "U") + gens A.natural + Text + Passing a @ TO List @ of symbols for @ TT "Variable" @ overrides + the doubly-indexed naming entirely and uses the given names + verbatim. The list length must match @ TT "#degreeList" @: + Example + B = freeDGAlgebra(R, {{1}, {1}, {1}}, Variable => {getSymbol "P", getSymbol "Q", getSymbol "RR"}) + gens B.natural + Text + This is the mode used internally by @ TO adjoinVariables @ and + @ TO acyclicClosure @ to preserve the identities of existing + generators when extending a DG algebra. + Text + @ BOLD "Multi-grading." @ Any degree entries beyond the first are + carried through as additional gradings of the underlying ring + @ TT "A.natural" @. To make the differential homogeneous with + respect to both the homological and internal gradings, pair each + hom-degree with the matching internal-degree of its image under + @ TO setDiff @. Here, @ TT "T_(1,i)" @ has degree @ TT "(1,1)" @ + (hom-degree 1, internal-degree 1) matching @ TT "x, y, z" @, and + @ TT "T_(3,1)" @ has degree @ TT "(3,3)" @ matching its + cubic-monomial differential: + Example + Bmulti = freeDGAlgebra(R, {{1, 1}, {1, 1}, {1, 1}, {3, 3}}) + degrees Bmulti.natural + setDiff(Bmulti, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}) + isHomogeneous Bmulti + Text + Dropping the internal grading (hom-degree only) breaks homogeneity + because the differential mixes @ EM "different" @ internal + polynomial degrees of @ TT "R" @: + Example + Bhomo = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}) + setDiff(Bhomo, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}) + isHomogeneous Bhomo + Text + The differential is set after construction (via @ TO setDiff @) + rather than passed to @ TT "freeDGAlgebra" @ directly: the + differential's ring is @ TT "A.natural" @, which does not exist + until after the constructor runs. For common DG algebras arising + in commutative algebra (Koszul complex, Tate resolution, + acyclic closure, etc.) dedicated constructors are available; see + @ TO koszulComplexDGA @, @ TO acyclicClosure @, @ TO minimalModel @. + Caveat + There is currently a bug handling DG algebras that have no + generators in some homological degree but some in a later + homological degree; for example @ TT "freeDGAlgebra(R, {{1}, {5}})" @ + may produce incorrect behavior if no hom-degree 3 generators are + adjoined first. The safer workflow is @ TT "freeDGAlgebra" @ with + hom-degrees only up to what is needed, followed by + @ TO adjoinVariables @ to extend incrementally. + SeeAlso + koszulComplexDGA + acyclicClosure + minimalModel + adjoinVariables + setDiff + DGAlgebra +/// + +doc /// + Key + koszulComplexDGA + (koszulComplexDGA,Ring) + [koszulComplexDGA,Variable] + Headline + The Koszul complex on the variables of a ring, as a DG algebra + Usage + A = koszulComplexDGA R + A = koszulComplexDGA(R, Variable => "S") + Inputs + R:Ring + A polynomial ring or quotient of one; the Koszul complex is built on + @ TT "gens R" @. + Variable => String + Base name for the Koszul generators. Defaults to @ TT "\"T\"" @. + Both @ TT "String" @ and @ TT "Symbol" @ values are accepted. + Outputs + A:DGAlgebra + The Koszul complex on @ TT "gens R" @ equipped with the structure of + a DG algebra over @ TT "R" @. + Description + Text + Given a ring @ TT "R" @ with @ TT "n = numgens R" @ generators + @ TT "x_1, ..., x_n" @, the returned DG algebra has underlying + graded-commutative polynomial ring + @ TT "R[T_(1,1), ..., T_(1,n)]" @ on @ TT "n" @ exterior (odd, + square-zero) generators in hom-degree 1, with differential + @ TT "d(T_(1,i)) = x_i" @ extended by the Leibniz rule. In the + @ TT "R" @-module direction, the complex is identical to the + classical Koszul complex @ TT "K_\\bullet(x_1, ..., x_n; R)" @. + Text + @ BOLD "Variable naming convention." @ Generators are named + @ TT "T_(i, j)" @, where @ TT "i" @ is the homological degree (here + always @ TT "1" @) and @ TT "j" @ is a @ EM "1-indexed" @ counter + among generators at that hom-degree. Pass @ TT "Variable => \"S\"" @ + to use base name @ TT "S" @ instead. + Example + R = ZZ/101[a, b, c] / ideal(a^3, b^3, c^3) + A = koszulComplexDGA R + gens A.natural + flatten entries matrix A.diff + Text + Converting to a @ TO Complex @ gives the ordinary Koszul complex on + the variables (up to a choice of monomial order on the exterior + product); taking homology recovers the classical Koszul homology: + Example + complexA = toComplex A + complexA.dd_1 + ranks = apply(4, i -> numgens prune HH_i complexA) + ranks == apply(4, i -> numgens prune HH_i koszul vars R) + Text + The homology can be computed directly from @ TT "A" @ via + @ TO (homology,ZZ,DGAlgebra) @, or as an algebra via + @ TO homologyAlgebra @. + Text + To rename the Koszul generators, pass the @ TT "Variable" @ option: + Example + S = ZZ/101[a, b] + AS = koszulComplexDGA(S, Variable => "U") + gens AS.natural + Caveat + The Koszul complex is built on @ EM "all" @ elements of @ TT "gens R" @, + not on a minimal generating set of a possibly non-irrelevant ideal. + To build a Koszul complex on a specific sequence of ring elements, use + @ TO (koszulComplexDGA, List) @; for a full generating set of an ideal, + use @ TO (koszulComplexDGA, Ideal) @. Also, @ TO toComplex @ uses a + different monomial order than the @ TO "Complexes::koszul" @ command, + so individual differentials may differ by a basis permutation even + though the complexes are isomorphic. + SeeAlso + (koszulComplexDGA, Ideal) + (koszulComplexDGA, List) + freeDGAlgebra + acyclicClosure + koszulComplexDGM + (homology, ZZ, DGAlgebra) + homologyAlgebra +/// + +doc /// + Key + (koszulComplexDGA,Ideal) + Headline + The Koszul complex on the generators of an ideal, as a DG algebra + Usage + A = koszulComplexDGA I + Inputs + I:Ideal + An ideal of a ring @ TT "R" @. + Outputs + A:DGAlgebra + The Koszul complex over @ TT "R" @ on the chosen generators of + @ TT "I" @. + Description + Text + Given generators @ TT "f_1, ..., f_r" @ of @ TT "I" @, the output is + a DG algebra over @ TT "R" @ whose underlying graded algebra is + @ TT "R[T_(1,1), ..., T_(1,r)]" @ on @ TT "r" @ exterior generators + in hom-degree 1, with differential @ TT "d(T_(1,i)) = f_i" @ + extended by the Leibniz rule. The variable naming convention and + @ TT "Variable" @ option mirror @ TO (koszulComplexDGA, Ring) @. + Example + R = ZZ/101[a, b, c] + I = ideal(a^3, b^3, c^3, a^2 * b^2 * c^2) + A = koszulComplexDGA I + gens A.natural + flatten entries matrix A.diff + Text + The resulting complex coincides with @ TO "Complexes::koszul" @ + applied to @ TT "gens I" @, up to monomial order: + Example + complexA = toComplex A + ranks = apply(5, i -> numgens prune HH_i complexA) + ranks == apply(5, i -> numgens prune HH_i koszul gens I) + Text + In particular, @ TT "I" @ contains the redundant generator + @ TT "a^2 b^2 c^2" @ (it is in @ TT "(a^3, b^3, c^3)" @), so the + Koszul complex is not exact at @ TT "H_1" @; nonzero higher + Koszul homology encodes the relations and syzygies between the + chosen generators. + SeeAlso + (koszulComplexDGA, Ring) + (koszulComplexDGA, List) + (homology, ZZ, DGAlgebra) +/// + +doc /// + Key + (koszulComplexDGA,List) + Headline + The Koszul complex on a list of ring elements, as a DG algebra + Usage + A = koszulComplexDGA diffList + Inputs + diffList:List + A nonempty list of @ TO RingElement @ s, all lying in a common ring + @ TT "R" @. These become the images of the Koszul generators under + the differential. + Outputs + A:DGAlgebra + The Koszul complex over @ TT "R" @ on the sequence @ TT "diffList" @. + Description + Text + This is the lowest-level form: the generators of @ TT "I" @ or + @ TT "R" @ are replaced by an arbitrary user-supplied list of ring + elements. Useful for building a Koszul complex on a partial + regular sequence or on arbitrary elements that may not generate an + ideal of interest. The variable naming convention follows + @ TO (koszulComplexDGA, Ring) @: generators are named + @ TT "T_(1, j)" @ with @ TT "j = 1, ..., #diffList" @. + Example + R = ZZ/101[x, y, z] + A = koszulComplexDGA({x^2, y*z}) + gens A.natural + flatten entries matrix A.diff + apply(3, i -> numgens prune HH_i toComplex A) + Text + Here the sequence @ TT "(x^2, y z)" @ is regular in + @ TT "ZZ/101[x, y, z]" @, so the Koszul complex is acyclic in + positive homology and @ TT "H_0" @ is the quotient + @ TT "R/(x^2, y z)" @ (of dimension 1 as an @ TT "R" @-module). + SeeAlso + (koszulComplexDGA, Ring) + (koszulComplexDGA, Ideal) +/// + +doc /// + Key + (homology,ZZ,DGAlgebra) + Headline + Computes the homology of a DG algebra as a module + Usage + H = homology(n,A) + Inputs + n:ZZ + A:DGAlgebra + Outputs + H:Module + The nth homology of A. + Description + Example + R = ZZ/32003[x,y,z] + A = koszulComplexDGA(R) + apply(numgens R+1, i -> numgens prune homology(i,A)) +/// + +doc /// + Key + setDiff + (setDiff,DGAlgebra,List) + InitializeComplex + [setDiff,InitializeComplex] + InitializeDegreeZeroHomology + [setDiff,InitializeDegreeZeroHomology] + Headline + Install or replace the differential on a DG algebra + Usage + A = setDiff(A, diffList) + A = setDiff(A, diffList, InitializeComplex => false) + Inputs + A:DGAlgebra + diffList:List + A list of elements of @ TT "A.natural" @, one per algebra generator, + given in the @ EM "same order" @ as @ TT "gens A.natural" @ (which is + the order of @ TT "A.Degrees" @). The @ TT "i" @-th entry becomes + @ TT "d((gens A.natural)_i)" @ and is extended to all of + @ TT "A.natural" @ by @ TT "R" @-linearity and the Leibniz rule. + Each entry must have homological degree one less than its generator. + InitializeComplex => Boolean + Whether to eagerly compute all differential matrices up to + @ TT "maxDegree A" @. Defaults to @ TT "true" @; set to + @ TT "false" @ to skip the precomputation when only a low-degree + fragment is needed. + InitializeDegreeZeroHomology => Boolean + Whether to compute the quotient ring @ TT "H_0(A)" @ (needed by + @ TO homologyAlgebra @). Defaults to @ TT "true" @; set to + @ TT "false" @ to skip the Grobner-basis computation when not + needed. + Outputs + A:DGAlgebra + The same @ TT "A" @, now with its @ TT "diff" @ field populated; + returned for convenience in a chained call. + Description + Text + Because the ring @ TT "A.natural" @ does not exist until after + @ TO freeDGAlgebra @ runs, the differential cannot be passed to + the constructor and must be set afterwards with @ TT "setDiff" @. + The list @ TT "diffList" @ supplies the image of each algebra + generator; Leibniz and @ TT "R" @-linearity extend this to every + element of @ TT "A.natural" @. + Example + R = ZZ/101[x, y, z] + A = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}) + gens A.natural + setDiff(A, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}) + flatten entries matrix A.diff + Text + Here @ TT "d(T_(1,i)) = x_i" @ (so the Koszul piece on + @ TT "T_(1,*)" @ is installed) and @ TT "d(T_(3,1))" @ is the + Koszul-relation 2-cycle @ TT "x T_(1,2) T_(1,3) - y T_(1,1) T_(1,3) + + z T_(1,1) T_(1,2)" @, turning @ TT "A" @ into a Tate-resolution + fragment for @ TT "R/(x, y, z)" @. Converting to a @ TO Complex @ + shows the installed differentials: + Example + Acomplex = toComplex A + Acomplex.dd_1 + Acomplex.dd_2 + Text + @ BOLD "Validity." @ The package does @ EM "not" @ check that the + installed differential squares to zero; a user-supplied + @ TT "diffList" @ that violates @ TT "d^2 = 0" @ yields a malformed + DG algebra with silently wrong downstream behavior. The chain-map + conditions can be checked after the fact via + @ TO (isWellDefined, DGAlgebra) @. + Text + @ BOLD "Options." @ @ TT "InitializeComplex" @ controls whether + @ TT "setDiff" @ eagerly builds all differential matrices up to + @ TT "maxDegree A" @ (the sum of the hom-degrees of the + odd-hom-degree generators). Set it to @ TT "false" @ to defer this + computation when only low-degree information is needed: + Example + Abig = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}) + setDiff(Abig, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}, InitializeComplex => false) + Text + @ TT "InitializeDegreeZeroHomology" @ controls whether the quotient + ring @ TT "H_0(A) = A_0 / image(d_1)" @ is computed on the spot + (a Grobner-basis computation, deferred to @ TO homologyAlgebra @ + if turned off). Default: @ TT "true" @. Turn it off if you have + many hom-degree-1 generators and are not about to compute + @ TT "HH A" @ as a DG algebra. + Caveat + The order of entries in @ TT "diffList" @ must match the order of + generators in @ TT "gens A.natural" @ (equivalently, of + @ TT "A.Degrees" @). For constructors that adjoin generators + incrementally (@ TO adjoinVariables @, @ TO acyclicClosure @, + @ TO killCycles @), the new generators come after all existing + ones in this ordering. + SeeAlso + freeDGAlgebra + koszulComplexDGA + adjoinVariables + (isWellDefined, DGAlgebra) + homologyAlgebra +/// + +doc /// + Key + (isHomogeneous, DGAlgebra) + Headline + Determine if the DGAlgebra respects the gradings of the ring it is defined over. + Usage + isHom = isHomogeneous(A) + Inputs + A:DGAlgebra + Outputs + isHom:Boolean + Whether or not the DGA respects the grading + Description + Example + R = ZZ/101[x,y,z] + A = freeDGAlgebra(R,{{1},{1},{1},{3}}) + setDiff(A,{x,y,z,x*T_(1,2)*T_(1,3)-y*T_(1,1)*T_(1,3)+z*T_(1,1)*T_(1,2)}) + isHomogeneous A + B = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{3,3}}) + setDiff(B,{x,y,z,x*T_(1,2)*T_(1,3)-y*T_(1,1)*T_(1,3)+z*T_(1,1)*T_(1,2)}) + isHomogeneous B +/// + +doc /// + Key + natural + Headline + The underlying graded-commutative ring of a DGAlgebra or DGModule + Usage + Anat = A.natural + Description + Text + For a @ TO DGAlgebra @ @ TT "A" @, the key @ TT "A.natural" @ holds + the underlying graded-commutative polynomial ring of @ TT "A" @ + with the differential @ EM "forgotten" @. This is the ambient ring + in which algebra generators live, in which cycles and boundaries + are expressed, and against which differentials are compared. It is + always a polynomial ring over @ TT "A.ring" @ (with the + skew-commutative flag set on odd-hom-degree generators), never a + quotient. + Example + R = ZZ/101[a, b, c, d] + A = koszulComplexDGA R + A.natural + A.ring === R + gens A.natural + Text + For a @ TO DGModule @ @ TT "M" @, the same key holds the underlying + free @ TT "A.natural" @-module with the differential forgotten. The + differential is recorded separately in @ TT "M.diff" @ and the + multi-degrees of the natural generators in @ TT "M.Degrees" @: + Example + B = freeDGModule(A, {0, 1, 2}) + B.natural + rank B.natural + SeeAlso + DGAlgebra + DGModule + ring +/// + +doc /// + Key + cycles + Headline + Chosen cycle representatives for homology generators of a DG algebra + Usage + A.cycles + Description + Text + When @ TO acyclicClosure @, @ TO killCycles @, or + @ TO homologyAlgebra @ adjoin new generators to kill homology of a + DG algebra @ TT "A" @, the chosen cycle representatives are cached + on the output so the exact Tate construction can be inspected + afterwards. The key is @ TT "A.cycles" @ on a DG algebra, or + @ TT "HA.cache.cycles" @ on the output of @ TO homologyAlgebra @. + Example + R = ZZ/101[a, b, c, d] / ideal(a^3, b^4, c^5, d^6) + A = koszulComplexDGA R + apply(maxDegree A + 1, i -> numgens prune homology(i, A)) + HA = homologyAlgebra A + numgens HA + HA.cache.cycles + Text + The entries of @ TT "HA.cache.cycles" @ are elements of + @ TT "A.natural" @: cycles chosen to represent the generators of + @ TT "HA" @ as an algebra. These are the same representatives + that would be used as the differentials of newly adjoined + generators in @ TO acyclicClosure @. + SeeAlso + homologyAlgebra + acyclicClosure + killCycles +/// + +doc /// + Key + getBasis + (getBasis,ZZ,DGAlgebra) + Headline + Get a basis for a particular homological degree of a DG algebra. + Usage + M = getBasis(n,A) + Inputs + n:ZZ + A:DGAlgebra + Outputs + M:Matrix + The basis of the desired homological degree of the DG Algebra. + Description + Text + This function is to allow for the retrieval of a basis of a particular homological degree of a @ TO DGAlgebra @ + when the underlying algebra A.natural is multigraded. In the code, the homological grading is always the first + integer in the degree tuple, and so this function returns a matrix consisting of all monomials in homological + degree n. + Example + R = ZZ/101[a..d, Degrees=>{1,1,1,2}] + A = koszulComplexDGA(R) + getBasis(3,A) +/// + +doc /// + Key + (getBasis,ZZ,Ring) + Headline + Get a basis for a degree of a ring. + Usage + M = getBasis(n,R) + Inputs + n:ZZ + R:Ring + Outputs + M:Matrix + The basis of the desired degree + Description + Text + This function was not meant for general use, but it fixes the first degree in the degree tuple + of the ring R, and finds a basis of that 'slice' of the ring. It does this by using a cached + version of the ring that forgets all other degrees. A Ring object in Macaulay2 will not have this + cached ring by default, but the rings used internally in the DGAlgebras package will. +/// + +doc /// + Key + toComplex + (toComplex,DGAlgebra) + Headline + Converts a DGAlgebra to a Complex + Usage + C = toComplex A or C = toComplex(A,n) + Inputs + A:DGAlgebra + Outputs + C:Complex + The DG algebra A as a Complex + Description + Example + R = ZZ/101[x_1..x_10] + A = koszulComplexDGA(R) + C = toComplex A + Text + Warning: The term order that the internal command koszul uses to order the monomials is not GRevLex, and so the differentials + used in koszul and koszulComplexDGA will not match up exactly. Also, this command will only execute if all of the variables + of the @ TO DGAlgebra @ A are of odd homological degree. Otherwise, you need to use the function @ TO (toComplex, DGAlgebra, ZZ) @. +/// + +doc /// + Key + (toComplex,DGAlgebra,ZZ) + Headline + Converts a DGAlgebra to a Complex + Usage + C = toComplex A or C = toComplex(A,n) + Inputs + A:DGAlgebra + n:ZZ + Outputs + C:Complex + The DG algebra A as a Complex + Description + Example + R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} + A = acyclicClosure(R,EndDegree=>3) + Text + The above will be a resolution of the residue field over R, since R is a complete intersection. + Example + C = toComplex(A, 10) + apply(10, i -> prune HH_i(C)) +/// + +doc /// + Key + acyclicClosure + (acyclicClosure,DGAlgebra) + Headline + Tate's construction of a semifree acyclic DG algebra extension + Usage + B = acyclicClosure A + B = acyclicClosure(A, EndDegree => n) + Inputs + A:DGAlgebra + The starting DG algebra. + StartDegree => ZZ + First hom-degree at which to begin killing homology. Defaults to + @ TT "1" @. + EndDegree => ZZ + Last hom-degree at which homology is killed. Defaults to + @ TT "3" @. + Variable => String + Base name used for any new adjoined generators (Strings and Symbols + are both accepted). Defaults to reusing the base symbol already in + use by @ TT "A" @, or @ TT "\"T\"" @ if A has no generators. + Outputs + B:DGAlgebra + A semifree extension of @ TT "A" @ with the same ring and the same + degree-zero homology, whose positive-degree homology has been killed + in every hom-degree from @ TT "StartDegree" @ through @ TT "EndDegree" @. + Description + Text + @ BOLD "Tate's construction." @ Starting from @ TT "A" @, + @ TT "acyclicClosure" @ iterates @ TO killCycles @ from + @ TT "StartDegree" @ up through @ TT "EndDegree" @. At each step + @ TT "n" @, a minimal set of cycle representatives of @ TT "H_n(A)" @ + is computed and a new DG algebra generator is adjoined for each, + placed in hom-degree @ TT "n + 1" @ and whose differential is the + chosen cycle. Adjoining follows the graded-commutative convention: + generators of odd hom-degree are exterior (skew-commutative, + square-zero) while generators of even hom-degree are polynomial, so + the result is a free DG algebra in the sense of Tate/Gulliksen. If + @ TT "A" @ is the Koszul complex on a regular sequence, no + homology exists to kill and @ TT "acyclicClosure" @ returns @ TT "A" @ + unchanged; otherwise new generators appear, recording the deviations + of @ TT "A" @. + Text + @ BOLD "Variable-naming convention." @ Both the existing generators of + @ TT "A" @ and the newly adjoined generators use a doubly-indexed + naming scheme @ TT "base_(i, j)" @, where @ TT "i" @ is the + homological degree (the first entry of the multi-degree) and + @ TT "j" @ is a @ EM "1-indexed" @ counter among generators at that + hom-degree. Counters continue past those already present in + @ TT "A" @. For example, if @ TT "A" @ already has three generators + @ TT "T_(1,1), T_(1,2), T_(1,3)" @ in hom-degree 1 and two new + hom-degree-2 cycles need killing, the output has new generators + @ TT "T_(2,1)" @ and @ TT "T_(2,2)" @. The @ TT "Variable" @ option + only affects the base symbol for @ EM "new" @ generators; existing + names are preserved. + Text + Over a complete intersection @ TT "R = k[a, b, c]/(a^3, b^3, c^3)" @, + the Koszul complex has three nonzero hom-degree-2 cycles (one per + relation) which must be killed, introducing three new polynomial + generators @ TT "T_(2,1), T_(2,2), T_(2,3)" @: + Example + R = ZZ/101[a, b, c] / ideal(a^3, b^3, c^3) + A = koszulComplexDGA R + gens A.natural + B = acyclicClosure(A, EndDegree => 2) + gens B.natural + take(flatten entries matrix B.diff, 6) + Text + Here the new generators satisfy + @ TT "d(T_(2,i)) = a_i^2 T_(1,i)" @, the cycle representatives of + @ TT "H_2" @ for each element of the regular sequence. The result + is quasi-isomorphic to @ TT "R/(a,b,c)" @ in hom-degrees + @ TT "<= 2" @. + Text + For larger @ TT "EndDegree" @, the construction keeps going; over a + c.i. no further generators appear because the Koszul-plus-polynomial + DG algebra is already a resolution of the residue field (this is + Tate's theorem): + Example + B2 = acyclicClosure(A, EndDegree => 4) + numgens B2.natural == numgens B.natural + Text + Over a non-c.i., higher deviations are nontrivial and + @ TT "acyclicClosure" @ adjoins further generators at each stage. + Tallying by hom-degree gives the deviation sequence of the ring + (excluding hom-degree 0, where the ring itself sits): + Example + S = QQ[x, y] / ideal(x^2, x*y, y^2) + C = acyclicClosure(koszulComplexDGA S, EndDegree => 3) + tally apply(C.Degrees, d -> first d) + Text + The hom-degree-1 count of @ TT "2" @ comes from the two Koszul + exterior generators (one per variable of @ TT "S" @); hom-degree + @ TT "2" @ has three polynomial generators killing the three + relations; and hom-degrees @ TT "3" @ and beyond have nontrivial + deviations, signaling that @ TT "S" @ is @ EM "not" @ a complete + intersection. Over a complete intersection, all hom-degrees + @ TT ">= 3" @ would be empty. + Text + To choose a different base symbol for the newly adjoined generators, + use the @ TT "Variable" @ option. Existing generators of @ TT "A" @ + keep their names: + Example + D = acyclicClosure(A, EndDegree => 2, Variable => "U") + gens D.natural + Caveat + For a DG algebra @ TT "A" @ whose degree-zero part already has + inhomogeneous structure (e.g. one built via @ TO freeDGAlgebra @ + with a hand-written differential that is not square-zero), the + package assumes d^2 = 0 without checking; the resulting + @ TT "acyclicClosure" @ is only meaningful when this holds. + SeeAlso + (acyclicClosure,Ring) + killCycles + adjoinVariables + minimalModel + koszulComplexDGA + StartDegree + EndDegree +/// + +doc /// + Key + (acyclicClosure,Ring) + Headline + Tate's acyclic closure of the residue field, starting from the Koszul complex + Usage + A = acyclicClosure R + A = acyclicClosure(R, EndDegree => n) + Inputs + R:Ring + A local or graded ring (in practice, a quotient of a polynomial ring + by a homogeneous ideal). + Outputs + A:DGAlgebra + A semifree DG algebra over @ TT "R" @ whose degree-zero homology is + @ TT "R/m = R_0" @ (the residue field, or more generally + @ TT "R/" @ @ TO (ideal, Ring) @@ TT " R" @) and whose positive + homology is killed through hom-degree @ TT "EndDegree" @. + Description + Text + This form is shorthand for + @ TT "acyclicClosure(koszulComplexDGA R, ...)" @: the Koszul complex + on a chosen generating set of the maximal/irrelevant ideal is used + as the starting DG algebra, and @ TO (acyclicClosure, DGAlgebra) @ + iteratively adjoins new generators to kill homology up to + @ TT "EndDegree" @. The output is a truncation of Avramov's + acyclic closure of @ TT "R" @ over itself -- equivalently, a free + DG algebra resolution of the residue field in hom-degrees + @ TT "0" @ through @ TT "EndDegree" @. + Text + @ BOLD "Variable convention." @ The hom-degree-1 exterior generators + come from @ TO koszulComplexDGA @ and are named @ TT "T_(1,j)" @, + one for each generator of the maximal/irrelevant ideal. Higher + generators (adjoined by @ TO killCycles @ at each stage) are named + @ TT "T_(n,j)" @, with @ TT "n" @ the homological degree and + @ TT "j" @ a 1-indexed counter. Generators of odd hom-degree are + exterior; generators of even hom-degree are polynomial. Pass + @ TT "Variable => \"S\"" @ (or any string/symbol) to rename the base. + Example + R = ZZ/101[a, b, c, d] / ideal(a^3, b^3, c^4 - d^3) + A = acyclicClosure(R, EndDegree => 3) + tally apply(A.Degrees, d -> first d) + take(flatten entries matrix A.diff, numgens A.natural) + Text + Four hom-degree-1 exterior generators @ TT "T_(1,1), ..., T_(1,4)" @ + (one per variable of @ TT "R" @) and three hom-degree-2 polynomial + generators (one for each defining relation, since @ TT "R" @ is a + complete intersection of codimension 3). In the resulting DG + algebra the residue field has been resolved through hom-degree 3. + Text + For a complete intersection, the Koszul complex already has zero + higher homology beyond what the Tate polynomial closure kills at + hom-degree 2, so the number of generators stabilizes quickly: + Example + S = QQ[x, y, z] / ideal(x^2, y^2, z^2) + C = acyclicClosure(S, EndDegree => 4) + tally apply(C.Degrees, d -> first d) + Text + For a non-c.i., further deviations appear: + Example + T = QQ[x, y] / ideal(x^2, x*y, y^2) + D = acyclicClosure(T, EndDegree => 3) + tally apply(D.Degrees, d -> first d) + SeeAlso + (acyclicClosure, DGAlgebra) + koszulComplexDGA + minimalModel + killCycles + StartDegree + EndDegree +/// + +doc /// + Key + minimalModel + (minimalModel, Ideal) + (minimalModel, QuotientRing) + [minimalModel, StartDegree] + [minimalModel, EndDegree] + [minimalModel, Variable] + Headline + Build the minimal DG algebra resolution of a quotient ring + Usage + A = minimalModel I + A = minimalModel R + A = minimalModel(I, EndDegree => n) + Inputs + I:Ideal + An ideal in a polynomial ring @ TT "Q" @. + R:QuotientRing + A quotient @ TT "Q/I" @, where @ TT "Q" @ is a polynomial ring. + The defining ideal @ TT "ideal R" @ (in @ TT "ambient R = Q" @) + is used. + StartDegree => ZZ + First hom-degree at which to start killing homology. Defaults + to @ TT "1" @. + EndDegree => ZZ + Last hom-degree at which to kill homology. Defaults to + @ TT "3" @. + Variable => String + Name of the divided-power generators; defaults to + @ TT "\"T\"" @. + Outputs + A:DGAlgebra + A DG algebra over @ TT "Q" @ whose degree-zero homology is + @ TT "Q/I" @ and whose positive-degree homology is killed + through hom-degree @ TT "EndDegree" @. In other words, a + truncation of the minimal DG algebra resolution of + @ TT "Q/I" @ as a @ TT "Q" @-algebra. + Description + Text + Given an ideal @ TT "I" @ in a polynomial ring @ TT "Q" @, + @ TT "minimalModel I" @ is a shorthand for + Text + @ TT "acyclicClosure(koszulComplexDGA I)" @ . + Text + The Koszul DG algebra on a generating set of @ TT "I" @ has + @ TT "H_0 = Q/I" @, and acyclicClosure adjoins divided-power + variables in successively higher hom-degrees to kill all + positive homology, following Tate's construction. The result + is a semifree DG algebra resolution of @ TT "Q/I" @ over + @ TT "Q" @ in the sense of Avramov, minimal in each hom-degree. + When @ TT "R = Q/I" @ is passed, the defining ideal of + @ TT "R" @ inside its ambient polynomial ring is used. + Text + For a complete intersection, the Koszul complex on a minimal + generating set is already acyclic in positive degrees, so + @ TT "minimalModel" @ returns just that Koszul DG algebra: + Example + Q = ZZ/101[x, y] + I = ideal(x^2, y^2) + A = minimalModel(I, EndDegree => 3) + apply(0..3, n -> numcols getBasis(n, A)) + Text + The @ TT "QuotientRing" @ form is equivalent: + Example + R = Q / I + A' = minimalModel(R, EndDegree => 3) + numgens A.natural == numgens A'.natural + Text + For a non-complete-intersection ideal the acyclic closure + genuinely adjoins higher divided-power generators: + Example + J = ideal(x^2, x*y, y^2) + B = minimalModel(J, EndDegree => 3) + apply(0..3, n -> numcols getBasis(n, B)) + Text + As with @ TO acyclicClosure @, @ TT "EndDegree" @ truncates + the construction and @ TT "Variable" @ renames the adjoined + generators: + Example + C = minimalModel(I, EndDegree => 2, Variable => "S") + gens C.natural + Text + @ BOLD "Deviations and Gulliksen's theorem." @ The rank of + @ TT "A" @ in hom-degree @ TT "n" @ is the @ TT "n" @-th + @ EM "deviation" @ of @ TT "Q/I" @, a classical invariant + which vanishes for @ TT "n >= 2" @ if and only if @ TT "Q/I" @ + is a complete intersection (Gulliksen-Avramov). Compare the + two shapes: + Example + A1 = minimalModel(ideal(x^2, y^2), EndDegree => 4); + apply(0..4, n -> numcols getBasis(n, A1)) + A2 = minimalModel(ideal(x^2, x*y, y^2), EndDegree => 4); + apply(0..4, n -> numcols getBasis(n, A2)) + Text + @ TT "A1" @ concentrates in hom-degrees @ TT "0, 1" @ (the + hallmark of a c.i.), while @ TT "A2" @ keeps acquiring + divided-power generators, showing that @ TT "Q/(x^2, xy, y^2)" @ + is not a c.i. Running + @ TO minimalSemifreeResolution @ on the residue field over the + minimal model then gives a test of Gulliksen's theorem, which + states that over a codimension-@ TT "c" @ complete intersection + the Betti numbers of any finitely generated module are + eventually polynomial in the hom-degree, of degree at most + @ TT "c - 1" @: + Example + A = minimalModel(ideal(x^2, y^2), EndDegree => 8); + k = Q^1 / ideal(x, y); + Mdg = minimalSemifreeResolution(A, k, EndDegree => 8); + b = apply(0..8, n -> numcols moduleDifferential(n, Mdg)) + apply(0..7, n -> b#(n+1) - b#n) + apply(0..6, n -> b#(n+2) - 2*b#(n+1) + b#n) + Text + The Betti sequence is @ TT "(1, 4, 8, 12, 16, 20, 24, 28, 32)" @; + first differences stabilize to a constant and second + differences are eventually @ TT "0" @, so the numbers are + eventually linear -- a degree-@ TT "1" @ polynomial, matching + codimension @ TT "2" @. A codimension-@ TT "3" @ c.i. such as + @ TT "(x^2, y^2, z^2)" @ would instead show second differences + stabilizing nonzero and third differences vanishing. + Caveat + The ring of @ TT "A" @ is always the polynomial ring containing + the relations -- that is, @ TT "ring I" @ or @ TT "ambient R" @, + not the quotient itself. This matches the standard convention + that the minimal DG algebra resolution of @ TT "Q/I" @ lives + over @ TT "Q" @. + SeeAlso + acyclicClosure + (acyclicClosure, Ring) + koszulComplexDGA + (koszulComplexDGA, Ideal) +/// + +doc /// + Key + (symbol **, DGAlgebra, Ring) + Headline + Base change of a DG algebra to another ring + Usage + B = A ** S + Inputs + A:DGAlgebra + S:Ring + A ring that admits substitution from A.ring. The common case is + @ TT "S = A.ring / I" @ for some ideal I, but any ring into which the + variables of A.natural substitute is allowed. + Outputs + B:DGAlgebra + A DG algebra over S with the same generator degrees as A and with + differential obtained by substituting every entry of the differential + matrix of A into B.natural. + Description + Text + Base change: the underlying graded algebra is the same exterior / symmetric + shape over S, and the differential is transported by @ TO substitute @. + When S = A.ring / I, this models the DG algebra "A mod I" used, for + instance, in building the Koszul complex of a quotient ring. + Text + The result is cached on @ TT "A.cache" @: repeated calls + @ TT "A ** S" @ with the same pair (A, S) return the SAME DGAlgebra + object. This identity is essential for functoriality of base change on + DGAlgebraMaps and DGModules — see @ TO "Base change and tensor with non-DG types" @. + Example + R = ZZ/101[a,b,c,d] + A = koszulComplexDGA(R) + S = R/ideal{a^3,a*b*c} + B = A ** S + B === A ** S + Bdd = toComplex B + Bdd.dd + Text + Base change along the trivial quotient @ TT "R / ideal 0_R" @ is structure- + preserving: the result is a well-defined DGAlgebra over a ring isomorphic + to R. + Example + R' = ZZ/101[x] + A' = koszulComplexDGA R' + QR' = R' / ideal(0_(R')) + isWellDefined(A' ** QR') + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGModule, Ring) + (symbol **, DGAlgebraMap, Ring) +/// + +doc /// + Key + "Base change and tensor with non-DG types" + Headline + Tensoring DG objects with ordinary rings and modules + Description + Text + The DGAlgebras package extends @ TO2((symbol **, DGAlgebra, Ring), "** ") @ + so that every DG object (@ TO DGAlgebra @, @ TO DGModule @, @ TO DGSubmodule @, + @ TO DGQuotientModule @, @ TO DGIdeal @, @ TO DGAlgebraMap @, @ TO DGModuleMap @) + can be base-changed along any ring map out of the ground ring. It also defines + the exterior tensor of a DGModule with an ordinary @ TO Module @, subsuming the + usual ring-of-scalars base change as the special case @ TT "M ** S^1" @. + Text + Two design choices make these operations usable in larger constructions. + Text + @BOLD "Caching (object identity)."@ Both @ TO (symbol **, DGAlgebra, Ring) @ and + @ TO (symbol **, DGModule, Ring) @ cache their result, so repeated calls with the + same operands return the SAME object. This matters because a DGAlgebraMap or + DGModuleMap is tagged by its source and target DG-objects, and without identity + the source of @ TT "id_M ** S" @ and the target of @ TT "id_M ** S" @ would be two + distinct (though isomorphic) DGModules. + Text + @BOLD "Functoriality."@ These operations are functorial: if @ TT "id" @ is the + identity DGModuleMap on M, then @ TT "id ** S" @ is the identity DGModuleMap on + @ TT "M ** S" @; likewise for DGAlgebraMaps. Composition is preserved. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + QR = R / ideal(x^2) + MQ = M ** QR + MQ === M ** QR + idM = identityDGModuleMap M + (idM ** QR).source === MQ + (idM ** QR).target === MQ + isWellDefined(idM ** QR) + Text + For a DGModule M and an ordinary free module N, @ TT "M ** N" @ forms the + exterior tensor product over @ TT "A.natural" @. This lets you tensor by + copies of a ring, by direct sums, or by arbitrary graded shifts without + leaving the DG world. + Example + N = R^{0, -1} + MN = M ** N + class MN + numgens MN + rank MN.natural == (rank M.natural) * (rank N) + Text + The tensor is associative in the natural way: you can chain + @ TT "(M ** N) ** K" @ for a sequence of ordinary free modules, producing + a DGModule whose natural rank is the product of the ranks. + Example + K = R^3 + MNK = (M ** R^2) ** K + isWellDefined MNK + numgens MNK == 2 * 2 * 3 + Text + A deliberate omission: there is no @ TT "** Ideal" @ method. For an ideal + I, the underlying module @ TT "module I" @ is almost never free, so exterior + tensor with a DGModule is not well-defined in our framework. Use + @ TT "** (R/I)" @ for base change along the quotient instead. + Subnodes + (symbol **, DGAlgebra, Ring) + (symbol **, DGModule, Ring) + (symbol **, DGSubmodule, Ring) + (symbol **, DGQuotientModule, Ring) + (symbol **, DGIdeal, Ring) + (symbol **, DGAlgebraMap, Ring) + (symbol **, DGModuleMap, Ring) + (symbol **, DGModule, Module) + (symbol **, DGSubmodule, Module) + (symbol **, DGQuotientModule, Module) +/// + +doc /// + Key + (symbol **, DGModule, Ring) + Headline + Base change of a DG module along a ring map + Usage + MS = M ** S + Inputs + M:DGModule + A (free) DG module over some DG algebra A. + S:Ring + A ring admitting substitution from A.ring. Typically S = A.ring / I. + Outputs + MS:DGModule + A DG module over @ TT "A ** S" @ with the same generator multi-degrees as M + and with differential obtained by substituting every entry of each + differential column into the base-changed algebra. + Description + Text + This is the base change of M along the ring map A.ring -> S, lifted to the + DG level. Since A ** S and M ** S are both cached, repeated tensoring + with S is idempotent up to object identity: + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + QR = R / ideal(x^2) + MS = M ** QR + MS === M ** QR + MS.dgAlgebra === A ** QR + isWellDefined MS + Text + When M is the Koszul complex on a sequence and the map A.ring -> S is a + quotient map, @ TT "M ** S" @ is the Koszul complex of the same sequence + computed over S. + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGAlgebra, Ring) + (symbol **, DGModuleMap, Ring) +/// + +doc /// + Key + (symbol **, DGSubmodule, Ring) + Headline + Base change of a DG submodule along a ring map + Usage + SubS = Sub ** S + Inputs + Sub:DGSubmodule + A DG submodule of some ambient DG module M. + S:Ring + A ring admitting substitution from the ground ring of Sub. + Outputs + SubS:DGSubmodule + The DG submodule of @ TT "M ** S" @ whose inclusion matrix is obtained by + substituting every entry of the original inclusion into the base-changed + ring. + Description + Text + The ambient is @ TT "(Sub.ambient) ** S" @, so that for any DGQuotientModule + built from Sub the quotient @ TT "(M/Sub) ** S" @ equals + @ TT "(M ** S) / (Sub ** S) " @ up to canonical equality. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + Sub = dgSubmodule(M, matrix {{x_Anat, y_Anat}}) + QR = R / ideal(x^2) + SubQ = Sub ** QR + isWellDefined SubQ + SubQ.ambient === M ** QR + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGModule, Ring) + (symbol **, DGQuotientModule, Ring) +/// + +doc /// + Key + (symbol **, DGQuotientModule, Ring) + Headline + Base change of a DG quotient module along a ring map + Usage + QS = Q ** S + Inputs + Q:DGQuotientModule + S:Ring + Outputs + QS:DGQuotientModule + The quotient @ TT "(M ** S) / (Sub ** S)" @ where M = Q.ambient and + Sub = Q.subDGModule. + Description + Text + Internally, the submodule is rebuilt directly inside the base-changed + ambient (rather than constructing @ TT "Sub ** S" @ separately and then + quotienting); this is what ensures the ambient DGModule of the result is + the cached @ TT "M ** S" @, not a distinct isomorphic copy. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + Sub = dgSubmodule(M, matrix {{x_Anat}}) + Q = M / Sub + QR = R / ideal(x^2) + QS = Q ** QR + isWellDefined QS + QS.ambient === M ** QR + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGSubmodule, Ring) + (symbol **, DGModule, Ring) +/// + +doc /// + Key + (symbol **, DGIdeal, Ring) + Headline + Base change of a DG ideal along a ring map + Usage + IS = I ** S + Inputs + I:DGIdeal + A DG ideal of some DG algebra A. + S:Ring + Outputs + IS:DGIdeal + The DG ideal of @ TT "A ** S" @ generated by the images of the original + generators of I under @ TO substitute @. + Description + Text + Unlike @ TT "** Ideal" @ (which is not defined — see the note in + @ TO "Base change and tensor with non-DG types" @), this operation does + land inside the DG world: DGIdeal is always generated by elements, so + substitution along the ring map of base change produces a well-formed + DGIdeal of @ TT "A ** S" @. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat, y_Anat}) + QR = R / ideal(x^2) + IQ = I ** QR + isWellDefined IQ + IQ.dgAlgebra === A ** QR + Text + Base change preserves the zero and unit DG ideals: the zero ideal stays + zero, and the unit ideal stays the whole algebra. + Example + Z = dgIdeal(A, {}) + ZQ = Z ** QR + isWellDefined ZQ and isZero ZQ + U = dgIdeal(A, {1_Anat}) + UQ = U ** QR + isWellDefined UQ and not isZero UQ + Text + Multivariate quotients and multi-generator DG ideals interact as expected. + Example + R3 = ZZ/101[x,y,z] + A3 = koszulComplexDGA R3 + Anat3 = A3.natural + I3 = dgIdeal(A3, {x_Anat3, y_Anat3, z_Anat3}) + QR3 = R3 / ideal(x*y, y*z) + isWellDefined(I3 ** QR3) + SeeAlso + "Base change and tensor with non-DG types" + dgIdeal + (symbol **, DGAlgebra, Ring) +/// + +doc /// + Key + (symbol **, DGAlgebraMap, Ring) + Headline + Base change of a DG algebra map along a ring map + Usage + phiS = phi ** S + Inputs + phi:DGAlgebraMap + A DG algebra map A -> B. + S:Ring + Outputs + phiS:DGAlgebraMap + A DG algebra map @ TT "A ** S -> B ** S" @ whose underlying RingMap + sends each generator of A.natural to the image under phi, substituted + into @ TT "(B ** S).natural" @. + Description + Text + Functorial and compatible with object identity: because @ TT "A ** S" @ + and @ TT "B ** S" @ are cached, repeated evaluations of @ TT "phi ** S" @ + land in the same source/target, and + @ TT "identityDGAlgebraMap(A) ** S" @ equals + @ TT "identityDGAlgebraMap(A ** S)" @. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + idA = identityDGAlgebraMap A + QR = R / ideal(x^2) + idAQ = idA ** QR + isWellDefined idAQ + (idAQ.source) === (A ** QR) + (idAQ.target) === (A ** QR) + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGAlgebra, Ring) + identityDGAlgebraMap +/// + +doc /// + Key + (symbol **, DGModuleMap, Ring) + Headline + Base change of a DG module map along a ring map + Usage + fS = f ** S + Inputs + f:DGModuleMap + A DG module map M -> N over a DG algebra A. + S:Ring + Outputs + fS:DGModuleMap + A DG module map @ TT "M ** S -> N ** S" @ (over @ TT "A ** S" @) whose + underlying matrix is obtained entry-wise by substituting each entry of + f.natural into the base-changed ring. + Description + Text + Functorial: @ TT "identityDGModuleMap(M) ** S" @ equals + @ TT "identityDGModuleMap(M ** S)" @, and composition is preserved. + Cache-identity is what ensures the source and target of the result are + the same cached DG modules as @ TT "M ** S" @ and @ TT "N ** S" @. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + idM = identityDGModuleMap M + QR = R / ideal(x^2) + idMQ = idM ** QR + isWellDefined idMQ + (idMQ.source) === (M ** QR) + (idMQ.target) === (M ** QR) + Text + Functoriality is literal at the matrix level: the natural matrix of + @ TT "idM ** QR" @ is the identity matrix on @ TT "(M ** QR).natural" @. + Example + MQ = M ** QR + idMQ.natural == id_(MQ.natural) + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGModule, Ring) + identityDGModuleMap +/// + +doc /// + Key + (symbol **, DGModule, Module) + (symbol **, Module, DGModule) + Headline + Exterior tensor product of a DG module with an ordinary free module + Usage + P = M ** N + P = N ** M + Inputs + M:DGModule + Must be semifree: M.natural must be a free A.natural-module. + N:Module + An ordinary module over A.ring (or a compatible ring) that is also free. + Outputs + P:DGModule + A DG module over the same DG algebra A, with + @ TT "rank P.natural = (rank M.natural) * (rank N)" @ generators. The + multi-degree of the (i, j)-th natural generator is + @ TT "M.Degrees#i + (0, (degrees N)#j)" @ (homological degree from M, + internal degrees shifted by the internal degree of the j-th generator of N). + The differential is @ TT "d_M \\otimes id_N" @. + Description + Text + This is the exterior tensor product of DG-modules, where @ TT "N" @ is + viewed as a complex concentrated in homological degree 0 with zero + differential. The commuted form @ TT "N ** M" @ is defined as + @ TT "M ** N" @. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + N = R^2 + MN = M ** N + class MN + numgens MN + isWellDefined MN + Text + The special case @ TT "M ** R^1" @ is canonically isomorphic to M, and + @ TT "M ** R^{d}" @ is a shift of M by the internal degree d. + Text + Multi-degree M tensors predictably: if M has multiple homological degrees + and N is free of rank r, the result has @ TT "(rank M.natural) * r" @ + natural generators and is well-defined. + Example + M2m = freeDGModule(A, {0, 1}) + N3 = R^3 + M2N = M2m ** N3 + numgens M2N == 2 * 3 + isWellDefined M2N + Text + Edge case: tensoring with the zero module @ TT "R^0" @ gives a well-defined + zero DGModule. + Example + MZ = M ** R^0 + isWellDefined MZ + numgens MZ == 0 + Caveat + Both M and N must be free (semifree on the DG side). For the non-free case, + take an initial free resolution and then tensor. + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGModule, Ring) + (symbol **, DGModule, DGModule) +/// + +doc /// + Key + (symbol **, DGSubmodule, Module) + (symbol **, Module, DGSubmodule) + Headline + Exterior tensor product of a DG submodule with an ordinary free module + Usage + SubN = Sub ** N + SubN = N ** Sub + Inputs + Sub:DGSubmodule + N:Module + A free module over a compatible ring. + Outputs + SubN:DGSubmodule + The DG submodule of @ TT "(Sub.ambient) ** N" @ whose inclusion acts as + @ TT "inclusion(Sub) \\otimes id_N" @ on natural generators indexed by + (i, j). + Description + Text + Equivalently: for each natural generator of N, embed a copy of Sub's + inclusion matrix into the corresponding block of the tensored ambient. + The commuted form @ TT "N ** Sub" @ is defined as @ TT "Sub ** N" @. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + Sub = dgSubmodule(M, matrix {{x_Anat, y_Anat}}) + N = R^2 + SubN = Sub ** N + isWellDefined SubN + SubN.ambient === M ** N + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGModule, Module) + (symbol **, DGSubmodule, Ring) +/// + +doc /// + Key + (symbol **, DGQuotientModule, Module) + (symbol **, Module, DGQuotientModule) + Headline + Exterior tensor product of a DG quotient module with an ordinary free module + Usage + QN = Q ** N + QN = N ** Q + Inputs + Q:DGQuotientModule + N:Module + A free module over a compatible ring. + Outputs + QN:DGQuotientModule + The DG quotient module @ TT "(Q.ambient ** N) / (Q.subDGModule ** N)" @, + built directly inside the tensored ambient so that the ambient of the + result equals @ TT "Q.ambient ** N" @ on the nose. + Description + Text + The commuted form @ TT "N ** Q" @ is defined as @ TT "Q ** N" @. + Example + R = ZZ/101[x,y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + Sub = dgSubmodule(M, matrix {{x_Anat}}) + Q = M / Sub + N = R^2 + QN = Q ** N + isWellDefined QN + QN.ambient === M ** N + SeeAlso + "Base change and tensor with non-DG types" + (symbol **, DGModule, Module) + (symbol **, DGQuotientModule, Ring) +/// + +doc /// + Key + "Operations on DG Ideals" + Headline + Sum, product, power, intersection, colon, and other ideal-algebra operations on DG ideals + Description + Text + A DG ideal of a DG algebra A is a graded ideal of @ TT "A.natural" @ that is + closed under the differential d of A. The @ TO DGIdeal @ type wraps such an + ideal and exposes the standard M2 @ TO Ideal @ operations, each of which + preserves d-closure and returns a new @ TO DGIdeal @. + Text + Constructor: @ TO dgIdeal @ takes a List, Matrix, or Ideal of elements of + @ TT "A.natural" @ and iteratively d-saturates the ideal they generate + until it is closed under d. Warning: if an input element is not a cycle, + the d-saturation enlarges the ideal (possibly to the unit ideal). + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat}) + J = dgIdeal(A, {y_Anat}) + isWellDefined I + isDGIdeal(A, I.natural) + Text + Ideal-algebra operations all preserve d-closure by elementary arguments + (sum: @ TT "d(I+J) \\subset dI + dJ \\subset I + J" @; product: by the + Leibniz rule; intersection: direct; power: induction; colon: Leibniz + + @ TT "f J \\subset I" @ implies @ TT "(df) J \\subset I + f(dJ) \\subset I" @). + Concretely, they all return well-defined DGIdeals. + Example + isWellDefined(I + J) + isWellDefined(I * J) + isWellDefined(I^3) + isWellDefined intersect(I, J) + isWellDefined(I : J) + Text + Equality, containment, and reduction mirror the usual behavior on @ TO Ideal @. + Example + isSubset(I, I + J) + I != I + J + (x_Anat^2 % I) == 0 + (y_Anat^2 % I) == y_Anat^2 + Text + Quotient DG algebras: @ TT "A / I" @ builds the DG algebra whose + underlying ring is @ TT "A.natural / I.natural" @, with differential + descending from d (well-defined precisely because I is d-closed). + Example + B = A / I + class B + isWellDefined B + Text + Compatibility: any operation that takes an @ TO Ideal @ and lives on + @ TT "I.natural" @ can be reached via @ TT "I.natural" @, but the + wrapped operations on DGIdeal return DGIdeals (so d-closure is + certified on the result by running through @ TO dgIdeal @ once more). + Subnodes + DGIdeal + dgIdeal + isDGIdeal + (isWellDefined, DGIdeal) + (symbol +, DGIdeal, DGIdeal) + (symbol *, DGIdeal, DGIdeal) + (symbol ^, DGIdeal, ZZ) + (intersect, DGIdeal, DGIdeal) + (symbol :, DGIdeal, DGIdeal) + (symbol ==, DGIdeal, DGIdeal) + (symbol %, RingElement, DGIdeal) + (mingens, DGIdeal) + (prune, DGIdeal) + (symbol /, DGAlgebra, DGIdeal) + (isHomogeneous, DGIdeal) + SeeAlso + DGIdeal + dgIdeal + (symbol **, DGIdeal, Ring) +/// + +doc /// + Key + DGIdeal + Headline + The class of d-closed graded ideals of a DG algebra + Description + Text + A @ TT "DGIdeal" @ wraps a graded ideal of @ TT "A.natural" @ that is + closed under the differential d of A. It records the ambient DG + algebra, the underlying Ideal, and a chosen set of generators. Create + one via @ TO dgIdeal @. + Text + The standard ideal-algebra operations (@ TO (symbol +, DGIdeal, DGIdeal) @, + @ TO (symbol *, DGIdeal, DGIdeal) @, @ TO (symbol ^, DGIdeal, ZZ) @, + @ TO (intersect, DGIdeal, DGIdeal) @, @ TO (symbol :, DGIdeal, DGIdeal) @) + all preserve d-closure and return new DGIdeals. See + @ TO "Operations on DG Ideals" @ for a guided tour. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat * y_Anat}) + class I + ambient I === A + ring I === R + numgens I + SeeAlso + dgIdeal + "Operations on DG Ideals" + (symbol /, DGAlgebra, DGIdeal) +/// + +doc /// + Key + dgIdeal + (dgIdeal, DGAlgebra, List) + (dgIdeal, DGAlgebra, Matrix) + (dgIdeal, DGAlgebra, Ideal) + Headline + Construct a DG ideal from a list, matrix, or ideal of generators + Usage + I = dgIdeal(A, gs) + Inputs + A:DGAlgebra + gs: + A List of elements of @ TT "A.natural" @, a Matrix of such, or an + @ TO Ideal @ of @ TT "A.natural" @. + Outputs + I:DGIdeal + The smallest d-closed ideal of @ TT "A.natural" @ containing the given + generators. Constructed by iteratively adjoining derivatives until the + ideal stabilizes. + Description + Text + If the inputs are already cycles, no new generators are introduced and + @ TT "I.natural" @ is the ordinary ideal they generate. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + xA = sub(x, Anat) + I = dgIdeal(A, {xA}) + numgens I.natural == 1 + isDGIdeal(A, I.natural) + Text + If a seed element is not a cycle, d-saturation may enlarge the ideal. + For the Koszul complex on {x}, @ TT "d(T_1) = x" @, so + @ TT "dgIdeal(A, {T_1}) " @ ends up containing both @ TT "T_1" @ and + @ TT "x" @. + Example + R' = ZZ/101[x] + A' = koszulComplexDGA R' + T = (A'.natural)_0 + I' = dgIdeal(A', {T}) + numgens I'.natural + isDGIdeal(A', I'.natural) + Text + A more interesting multi-variable example: over the Koszul algebra of + @ TT "QQ[x, y]" @, the top-degree exterior product @ TT "T_1 T_2" @ has + @ TT "d(T_1 T_2) = -y T_1 + x T_2" @, and d-saturation records both the + seed and its differential as minimal generators of the resulting + d-closed ideal: + Example + R2 = QQ[x, y] + A2 = koszulComplexDGA R2 + T1 = (A2.natural)_0 + T2 = (A2.natural)_1 + polyDifferential(A2, T1 * T2) + J = dgIdeal(A2, {T1 * T2}) + mingens J.natural + Text + An empty seed or a list of zero seeds yields the zero DG ideal. + Example + Z = dgIdeal(A, {}) + isWellDefined Z + isZero Z + Z' = dgIdeal(A, {0_Anat, 0_Anat}) + isZero Z' + Text + A seed containing @ TT "1" @ gives the unit DG ideal. + Example + U = dgIdeal(A, {1_Anat, x_Anat}) + U.natural == ideal 1_Anat + SeeAlso + DGIdeal + isDGIdeal + "Operations on DG Ideals" +/// + +doc /// + Key + isDGIdeal + (isDGIdeal, DGAlgebra, Ideal) + Headline + Test whether an ideal of A.natural is closed under the differential of A + Usage + b = isDGIdeal(A, I) + Inputs + A:DGAlgebra + I:Ideal + An ideal of @ TT "A.natural" @. + Outputs + b:Boolean + True iff every minimal generator g of I satisfies + @ TT "polyDifferential(A, g) % I == 0" @. + Description + Text + Unlike @ TO dgIdeal @ (which d-saturates), @ TT "isDGIdeal" @ simply + checks d-closure without modifying the input. Use it to validate that + an externally-produced Ideal is already a DG ideal. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + isDGIdeal(A, ideal(x_Anat, y_Anat)) + T = Anat_0 -- first Koszul variable T_{1,1}; d(T) = x + isDGIdeal(A, ideal(T)) + Text + The second test returns false: the ideal @ TT "(T)" @ is not d-closed + because @ TT "d(T) = x" @ is not in it. Passing the same seed to + @ TO dgIdeal @ produces the d-closure. + Example + J = dgIdeal(A, {T}) + isDGIdeal(A, J.natural) + SeeAlso + dgIdeal + DGIdeal +/// + +doc /// + Key + (isWellDefined, DGIdeal) + Headline + Verify that a DGIdeal has correct structure and is d-closed + Usage + b = isWellDefined I + Inputs + I:DGIdeal + Outputs + b:Boolean + Description + Text + Performs (1) a key-shape check — the expected fields (@ TT "dgAlgebra, ring, + natural, generators, cache" @) are present and have the correct types — + and (2) the semantic d-closure check via @ TO isDGIdeal @. Set + @ TT "debugLevel > 0" @ to see a diagnostic message on failure. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat, y_Anat}) + isWellDefined I + SeeAlso + dgIdeal + isDGIdeal + DGIdeal +/// + +doc /// + Key + (symbol +, DGIdeal, DGIdeal) + Headline + Sum of two DG ideals + Usage + K = I + J + Inputs + I:DGIdeal + J:DGIdeal + With @ TT "I.dgAlgebra === J.dgAlgebra" @. + Outputs + K:DGIdeal + The DGIdeal with @ TT "K.natural = I.natural + J.natural" @. + Description + Text + Preserves d-closure because @ TT "d(I + J) \\subset dI + dJ \\subset I + J" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat}) + J = dgIdeal(A, {y_Anat}) + K = I + J + isWellDefined K + (I + J) == (J + I) + Text + The zero DGIdeal is a two-sided identity for @ TT "+" @. + Example + Z = dgIdeal(A, {}) + (Z + I) == I + SeeAlso + "Operations on DG Ideals" + (symbol *, DGIdeal, DGIdeal) + (intersect, DGIdeal, DGIdeal) +/// + +doc /// + Key + (symbol *, DGIdeal, DGIdeal) + Headline + Product of two DG ideals + Usage + K = I * J + Inputs + I:DGIdeal + J:DGIdeal + With @ TT "I.dgAlgebra === J.dgAlgebra" @. + Outputs + K:DGIdeal + The DGIdeal with @ TT "K.natural = I.natural * J.natural" @. + Description + Text + Preserves d-closure by the Leibniz rule: @ TT "d(ab) = d(a) b \\pm a d(b)" @ + shows @ TT "d(IJ) \\subset (dI) J + I (dJ) \\subset IJ" @. When the ambient + ring is commutative, multiplication is commutative. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat}) + J = dgIdeal(A, {y_Anat}) + K = I * J + isWellDefined K + (I * J) == (J * I) + Text + The unit DGIdeal is a two-sided identity for @ TT "*" @ (as ideals). + Example + U = dgIdeal(A, {1_Anat}) + (U * I) == I + SeeAlso + "Operations on DG Ideals" + (symbol +, DGIdeal, DGIdeal) + (symbol ^, DGIdeal, ZZ) +/// + +doc /// + Key + (symbol ^, DGIdeal, ZZ) + Headline + Nonnegative integer power of a DG ideal + Usage + K = I^n + Inputs + I:DGIdeal + n:ZZ + A nonnegative integer. + Outputs + K:DGIdeal + @ TT "I^0" @ is the unit DGIdeal @ TT "(1)" @; @ TT "I^n" @ for + @ TT "n >= 1" @ is the n-fold product @ TT "I * I * ... * I" @. + Description + Text + Distributes with multiplication: @ TT "I^2 * I == I^3" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat, y_Anat}) + isWellDefined(I^2) + isWellDefined(I^3) + (I^2 * I) == I^3 + Text + @ TT "I^0" @ always returns the unit DGIdeal @ TT "(1)" @. Taking + powers of the zero DG ideal is well-defined at every exponent. + Example + I^0 + Z = dgIdeal(A, {}) + isWellDefined(Z^0) and isWellDefined(Z^5) + Caveat + Negative exponents are rejected with an error. + SeeAlso + "Operations on DG Ideals" + (symbol *, DGIdeal, DGIdeal) +/// + +doc /// + Key + (intersect, DGIdeal, DGIdeal) + Headline + Intersection of two DG ideals + Usage + K = intersect(I, J) + Inputs + I:DGIdeal + J:DGIdeal + With @ TT "I.dgAlgebra === J.dgAlgebra" @. + Outputs + K:DGIdeal + The DGIdeal with @ TT "K.natural = intersect(I.natural, J.natural)" @. + Description + Text + Intersections preserve d-closure directly: if g lies in both I and J, + then d(g) lies in both (by d-closure of each), hence in their intersection. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat}) + J = dgIdeal(A, {y_Anat}) + K = intersect(I, J) + isWellDefined K + Text + Intersecting with the zero DGIdeal yields the zero DGIdeal. + Example + Z = dgIdeal(A, {}) + ZI = intersect(Z, I) + isWellDefined ZI + numgens ZI.natural == 0 + SeeAlso + "Operations on DG Ideals" + (symbol +, DGIdeal, DGIdeal) + (symbol *, DGIdeal, DGIdeal) +/// + +doc /// + Key + (symbol :, DGIdeal, DGIdeal) + Headline + Ideal quotient (colon) of two DG ideals + Usage + K = I : J + Inputs + I:DGIdeal + J:DGIdeal + With @ TT "I.dgAlgebra === J.dgAlgebra" @. + Outputs + K:DGIdeal + The DGIdeal with @ TT "K.natural = I.natural : J.natural" @ — i.e. + @ TT "{ f \\in A.natural | f J \\subset I }" @. + Description + Text + Preserves d-closure: if @ TT "f J \\subset I" @ then by Leibniz + @ TT "d(f) J = d(fJ) - f d(J) \\subset dI + f J \\subset I + I = I" @, + so @ TT "d(f) \\in I : J" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat * y_Anat}) + J = dgIdeal(A, {y_Anat}) + K = I : J + isWellDefined K + SeeAlso + "Operations on DG Ideals" + (symbol *, DGIdeal, DGIdeal) +/// + +doc /// + Key + (symbol ==, DGIdeal, DGIdeal) + (isSubset, DGIdeal, DGIdeal) + Headline + Equality and containment of DG ideals + Usage + b = I == J + b = isSubset(I, J) + Inputs + I:DGIdeal + J:DGIdeal + Outputs + b:Boolean + Description + Text + Both compare DGIdeals via their underlying Ideals @ TT "I.natural" @ + and @ TT "J.natural" @. Equality additionally requires the DG algebras + to be the same object (@ TT "=== " @ — object identity, not just + isomorphism). Containment also asserts that I and J share an ambient + DG algebra. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat}) + J = dgIdeal(A, {x_Anat, y_Anat}) + isSubset(I, J) + I != J + I == I + SeeAlso + "Operations on DG Ideals" + (symbol +, DGIdeal, DGIdeal) +/// + +doc /// + Key + (symbol %, RingElement, DGIdeal) + (symbol %, ZZ, DGIdeal) + Headline + Reduce a ring element modulo a DG ideal + Usage + r = f % I + Inputs + f: + A RingElement of @ TT "I.dgAlgebra.natural" @, or an integer (coerced + into @ TT "I.dgAlgebra.natural" @ before reduction). + I:DGIdeal + Outputs + r:RingElement + The normal form of f modulo @ TT "I.natural" @. + Description + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat}) + (x_Anat^2 % I) == 0 + (y_Anat^2 % I) == y_Anat^2 + SeeAlso + "Operations on DG Ideals" + (symbol ==, DGIdeal, DGIdeal) +/// + +doc /// + Key + (mingens, DGIdeal) + (numgens, DGIdeal) + (module, DGIdeal) + (ring, DGIdeal) + (generators, DGIdeal) + Headline + Basic accessors on a DG ideal + Description + Text + Thin wrappers around the corresponding @ TO Ideal @ operations on + @ TT "I.natural" @, with the exception of @ TT "ambient I" @, which + returns the ambient @ TO DGAlgebra @ (not its underlying ring). + Text + @ TT "mingens I" @ returns the minimal-generator matrix of + @ TT "I.natural" @. Note that the d-saturation in @ TO dgIdeal @ may + have produced redundant generators; @ TT "mingens I" @ re-extracts a + minimal set. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat, x_Anat * y_Anat}) + mingens I + numgens I + Text + @ TT "numgens I" @ equals @ TT "numgens I.natural" @ — the number of + (stored, possibly redundant) generators of the underlying ideal. + Example + numgens I == numgens I.natural + Text + @ TT "module I" @ returns the underlying Module-view of the ideal, and + @ TT "ring I, ambient I, generators I" @ expose the ground ring, the + ambient DG algebra, and the chosen generator matrix, respectively. + Example + ring I === R + ambient I === A + module I + SeeAlso + "Operations on DG Ideals" + dgIdeal + (prune, DGIdeal) +/// + +doc /// + Key + (prune, DGIdeal) + (minimalPresentation, DGIdeal) + Headline + Trim a DG ideal down to a minimal generating set + Usage + J = prune I + J = minimalPresentation I + Inputs + I:DGIdeal + Outputs + J:DGIdeal + A DGIdeal with @ TT "J.natural == I.natural" @ (as ideals) but with a + possibly smaller generator list obtained by @ TO trim @. + Description + Text + @ TT "prune" @ computes @ TT "trim I.natural" @ and wraps the result. + It caches a @ TT "pruningMap" @ in @ TT "J.cache" @ giving the canonical + comparison map @ TT "J -> I" @; since DGIdeals have the same ambient + DG algebra before and after pruning, this is recorded as the identity + DGAlgebraMap on @ TT "I.dgAlgebra" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat, x_Anat * y_Anat, x_Anat^2}) + J = prune I + isWellDefined J + J.natural == I.natural + Text + Idempotent: pruning an already-minimal DGIdeal is a no-op at the + ideal level (the stored generator list already agrees with + @ TT "mingens" @). Repeated pruning is safe. + Example + isWellDefined prune prune prune J + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @ on + DGIdeals. + SeeAlso + "Operations on DG Ideals" + (mingens, DGIdeal) + dgIdeal +/// + +doc /// + Key + (symbol /, DGAlgebra, DGIdeal) + Headline + Quotient DG algebra by a DG ideal + Usage + B = A / I + Inputs + A:DGAlgebra + I:DGIdeal + A DG ideal of A. Must satisfy @ TT "I.dgAlgebra === A" @. + Outputs + B:DGAlgebra + A DG algebra whose underlying ring is @ TT "A.natural / I.natural" @ + and whose differential is the (well-defined) descent of d_A. + Description + Text + The descent is well-defined precisely because I is d-closed: for any + lift g' of a class g in B, the difference @ TT "g - g'" @ lies in I, + so @ TT "d_A(g) - d_A(g') \\in d_A(I) \\subset I" @ and the image + @ TT "[d_A(g)] \\in B" @ is independent of the chosen lift. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + Anat = A.natural + T = Anat_0 + I = dgIdeal(A, {T}) + B = A / I + isWellDefined B + B.natural + SeeAlso + "Operations on DG Ideals" + DGIdeal + dgIdeal +/// + +doc /// + Key + (isHomogeneous, DGIdeal) + (isZero, DGIdeal) + Headline + Predicates on a DG ideal + Description + Text + @ TT "isHomogeneous I" @ returns @ TT "isHomogeneous I.natural" @ — + i.e. tests whether the underlying ideal is homogeneous with respect to + the grading of @ TT "A.natural" @. + Text + @ TT "isZero I" @ returns true iff @ TT "I.natural" @ is the zero + ideal (equivalently, @ TT "numgens I.natural == 0" @). + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + I = dgIdeal(A, {x_Anat, y_Anat}) + isHomogeneous I + isZero I + Z = dgIdeal(A, {}) + isZero Z + SeeAlso + "Operations on DG Ideals" + DGIdeal +/// + +doc /// + Key + "Building DG modules, submodules, and quotients" + Headline + The DGModule / DGSubmodule / DGQuotientModule hierarchy and its constructors + Description + Text + The package provides three related types of DG-module-like objects + over a fixed @ TO DGAlgebra @ A: + Text + @ TO DGModule @ — a free graded @ TT "A.natural" @-module equipped + with a differential that satisfies the Leibniz rule against A. + Built via @ TO freeDGModule @, with differentials set by + @ TO setDiff @. + Text + @ TO DGSubmodule @ — a subobject of a DGModule M, represented by a + matrix of generators whose column span is closed under the + differential of M. Built via @ TO dgSubmodule @, which + @ TT "d" @-saturates the seed generators automatically. + Text + @ TO DGQuotientModule @ — the cokernel M / S of the inclusion + S → M, with the induced differential. Built via @ TO dgQuotientModule @ + or the shorthand @ TT "M / S" @. + Text + Every object in this hierarchy carries an underlying graded + @ TT "A.natural" @-module accessible via @ TT "M.natural" @, a list of + multi-degrees @ TT "M.Degrees" @, and a list of differentials + @ TT "M.diff" @ (one per natural generator for DGModule and + DGSubmodule; per quotient-presentation generator for + DGQuotientModule). Structural correctness can be checked with + @ TO isWellDefined @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + isWellDefined M + Anat = A.natural + S = dgSubmodule(M, (id_(M.natural))_{0}) + isWellDefined S + Q = M / S + isWellDefined Q + Subnodes + DGModule + DGSubmodule + DGQuotientModule + freeDGModule + dgSubmodule + dgQuotientModule + (symbol /, DGModule, DGSubmodule) + SeeAlso + "Module-like operations on DG modules" + "Operations on DG Submodules" + DGAlgebra +/// + +doc /// + Key + DGModule + Headline + The class of DG modules over a DG algebra + Description + Text + A @ TT "DGModule" @ M over a @ TO DGAlgebra @ A is a free graded + @ TT "A.natural" @-module together with a differential that squares + to zero and satisfies the Leibniz rule against A. Internally M is + a hashtable with keys: + Text + @ TT "M.dgAlgebra" @ — the ambient @ TO DGAlgebra @. + Text + @ TT "M.natural" @ — the underlying graded @ TT "A.natural" @-module. + Text + @ TT "M.Degrees" @ — the list of multi-degrees of the natural generators. + Text + @ TT "M.diff" @ — a list of differentials, one per natural generator, + each living in @ TT "M.natural" @ in the appropriate homological degree. + Text + New DGModules are constructed by @ TO freeDGModule @; the differential + on generators is set via @ TO setDiff @. Every + @ TO DGSubmodule @ is also a DGModule (as a subtype); by contrast a + @ TO DGQuotientModule @ is a separate type with a compatible presentation. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + isWellDefined M + M.Degrees + SeeAlso + "Building DG modules, submodules, and quotients" + freeDGModule + setDiff + DGSubmodule + DGQuotientModule +/// + +doc /// + Key + DGSubmodule + Headline + The class of d-closed submodules of a DG module + Description + Text + A @ TT "DGSubmodule" @ S of a @ TO DGModule @ M is a free + A.natural-module together with an inclusion + @ TT "S.inclusion : S → M" @ of DGModules whose column span in + @ TT "M.natural" @ is closed under the differential of M. + @ TT "DGSubmodule" @ is a subtype of @ TO DGModule @, so every + DG-module operation applies to submodules as well. + Text + Beyond the DGModule keys, a DGSubmodule carries: + Text + @ TT "S.ambient" @ — the enclosing DGModule M. + Text + @ TT "S.inclusion" @ — a @ TO DGModuleMap @ S → M; use + @ TO inclusion @ to access it. + Text + New DGSubmodules are constructed by @ TO dgSubmodule @, which + takes either an inclusion matrix or a list of generators, and + d-saturates the input automatically. See + @ TO "Operations on DG Submodules" @ for the lattice operations + (sum, intersection, equality, containment). + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + S = dgSubmodule(M, id_(M.natural)) + ambient S === M + source inclusion S === S + target inclusion S === M + SeeAlso + "Building DG modules, submodules, and quotients" + "Operations on DG Submodules" + dgSubmodule + DGModule + inclusion +/// + +doc /// + Key + DGQuotientModule + Headline + The class of quotient DG modules M / S + Description + Text + A @ TT "DGQuotientModule" @ Q = M / S is the cokernel of the + inclusion @ TT "S.inclusion : S → M" @, equipped with the induced + differential. Unlike @ TO DGSubmodule @, @ TT "DGQuotientModule" @ + is NOT a subtype of @ TO DGModule @: its @ TT ".natural" @ is a + cokernel module, which does not support the free-module operations + (rank, basis by index) that DG algorithms assume on a generic + DGModule. + Text + Beyond the DGModule-like keys (@ TT ".dgAlgebra" @, @ TT ".natural" @, + @ TT ".Degrees" @, @ TT ".diff" @), a DGQuotientModule carries: + Text + @ TT "Q.ambient" @ — the DGModule M from which Q was built; + accessible via @ TO ambient @. + Text + @ TT "Q.subDGModule" @ — the killed submodule S; accessible via + @ TO subDGModule @. + Text + @ TT "Q.projection" @ — the quotient map M → Q as a + @ TO DGModuleMap @; accessible via @ TO projection @. + Text + New DGQuotientModules are built by @ TO dgQuotientModule @ or the + shorthand @ TT "M / S" @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Z = dgSubmodule(M, {}) + Q = M / Z + instance(Q, DGQuotientModule) + ambient Q === M + subDGModule Q === Z + source projection Q === M + target projection Q === Q + SeeAlso + "Building DG modules, submodules, and quotients" + "Module-like operations on DG modules" + dgQuotientModule + (symbol /, DGModule, DGSubmodule) +/// + +doc /// + Key + freeDGModule + (freeDGModule, DGAlgebra, List) + Headline + Construct a free DG module over a DG algebra + Usage + M = freeDGModule(A, degList) + Inputs + A:DGAlgebra + degList:List + of multi-degrees. Each entry is either an integer (interpreted as + a homological degree with trivial internal degree) or a list whose + first entry is the homological degree and whose remaining entries + are internal degrees. Short lists are right-padded with zeros to + match the length of a degree in @ TT "degrees A.natural" @. + Outputs + M:DGModule + A DGModule whose @ TT ".natural" @ is the graded free + @ TT "A.natural" @-module of rank @ TT "#degList" @ with the + specified generator degrees, and whose differential is initialized + to zero on every generator. + Description + Text + The differential is initially set to zero on every generator, and is + customized afterward via @ TO setDiff @. Because the constructor + returns a free @ TO DGModule @, the result can serve as a building block + for semifree resolutions (see @ TO adjoinGenerators @ and @ TO semifreeResolution @) + or as the starting point for a hand-built DG module, as illustrated below. + Text + Here we build the 2-periodic minimal free resolution of the residue field + over the hypersurface @ TT "R = k[x]/(x^2)" @ — the prototypical Eisenbud + matrix factorization — as an infinite semifree DG module over the Koszul + DG algebra: + Example + R = QQ[x] / ideal(x^2) + A = koszulComplexDGA R + F = freeDGModule(A, toList(0..4)) + natGens = apply(rank F.natural, i -> (F.natural)_i) + setDiff(F, {0, x * natGens#0, x * natGens#1, x * natGens#2, x * natGens#3}) + apply(1..4, n -> moduleDifferential(n, F)) + Text + The differential alternates between multiplication by @ TT "x" @ in + every degree, matching the classical 2-periodic resolution of the + residue field. Its homology is concentrated in degree zero: + Example + apply(0..3, n -> prune homology(n, F)) + Text + More elaborate differentials are possible once multiple generators + sit in overlapping hom-degrees. Here is a rank-3 toy example where + @ TT "F_1" @ has two generators and @ TT "d_1" @ sends them to + @ TT "x" @ and @ TT "y" @ respectively, recovering the truncation of + the Koszul complex on @ TT "(x, y)" @: + Example + S = QQ[x, y] + AS = koszulComplexDGA S + G = freeDGModule(AS, {0, 1, 1}) + natG = apply(rank G.natural, i -> (G.natural)_i) + setDiff(G, {0, x * natG#0, y * natG#0}) + moduleDifferential(1, G) + Text + Internal degrees from a multi-graded base ring are inherited; a + plain homological degree is padded with zeros to match. + Example + Rg = ZZ/101[x, y, Degrees => {{1, 0}, {0, 1}}] + Ag = koszulComplexDGA Rg + Mg = freeDGModule(Ag, {{0, 0, 0}, {1, 0, 0}}) + degrees Mg + Text + The empty-list form gives the zero DG module. + Example + M0 = freeDGModule(A, {}) + isZero M0 + SeeAlso + "Building DG modules, submodules, and quotients" + DGModule + setDiff + adjoinGenerators + semifreeResolution + (isWellDefined, DGModule) +/// + +doc /// + Key + dgSubmodule + (dgSubmodule, DGModule, Matrix) + (dgSubmodule, DGModule, List) + Headline + Construct a d-closed submodule of a DG module + Usage + S = dgSubmodule(M, incMat) + S = dgSubmodule(M, gs) + Inputs + M:DGModule + incMat:Matrix + with target @ TT "M.natural" @, whose columns are proposed + generators of the submodule. + gs:List + of elements of @ TT "M.natural" @ (as Vectors, 1-column Matrices, + or anything coercible to @ TT "M.natural" @). The empty list gives + the zero submodule. + Outputs + S:DGSubmodule + The smallest d-closed submodule of M containing the columns of + @ TT "incMat" @ (respectively the elements of @ TT "gs" @). + Description + Text + The constructor iteratively adjoins the M-differentials of the + current generators until the column span is closed, then builds + the lifted differential on the resulting free module. If d-closure + fails to stabilize within 200 iterations, an error is raised — in + practice this only happens if the ambient module has unbounded + homological degree and the seed is chosen poorly. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + S = dgSubmodule(M, id_(M.natural)) + instance(S, DGSubmodule) + ambient S === M + isWellDefined S + Text + The list form coerces each entry to an element of + @ TT "M.natural" @; passing the empty list produces the zero + submodule. + Example + Z = dgSubmodule(M, {}) + numcols (inclusion Z).natural == 0 + isZero Z + Text + d-closure is performed automatically. Seeding with a non-closed + generator pulls in its M-differential: + Example + M2 = freeDGModule(A, {0, 1}) + natGens = apply(rank M2.natural, i -> (M2.natural)_i) + setDiff(M2, {0, x * natGens#0}) + Anat = A.natural + seed = (id_(M2.natural))_{1} + S2 = dgSubmodule(M2, seed) + xe0 = (id_(M2.natural))_{0} * matrix {{x_Anat}} + incMat = (inclusion S2).natural + (xe0 - incMat * (xe0 // incMat)) == 0 + Text + In a richer setting, consider the minimal semifree resolution of the + residue field over the complete intersection + @ TT "R = QQ[x, y]/(x^2, y^2)" @. Seeding the submodule with the + hom-degree-2 natural generators, d-closure automatically pulls in the + hom-degree-4 generators that sit below them in the Koszul-style + differential: + Example + S = QQ[x, y] / ideal(x^2, y^2) + B = koszulComplexDGA S + Mdg = minimalSemifreeResolution(B, S^1 / ideal(x, y), EndDegree => 3) + homDegs = apply(Mdg.Degrees, d -> d#0) + pos2 = positions(Mdg.Degrees, d -> d#0 == 2) + seedHi = (id_(Mdg.natural))_pos2 + Shi = dgSubmodule(Mdg, seedHi) + apply(Shi.Degrees, d -> d#0) + isWellDefined Shi + Caveat + The target of the inclusion matrix must be @ TT "M.natural" @ on the + nose (object identity, not just isomorphism). This catches common + shape mistakes up front — for example, building + @ TT "matrix {{a, b}}" @ for a rank-n ambient produces a 1×2 matrix + whose target is rank 1, not rank n. + SeeAlso + "Building DG modules, submodules, and quotients" + "Operations on DG Submodules" + DGSubmodule + inclusion + (isWellDefined, DGModule) +/// + +doc /// + Key + dgQuotientModule + (dgQuotientModule, DGModule, DGSubmodule) + Headline + Construct the quotient DG module M / S + Usage + Q = dgQuotientModule(M, S) + Inputs + M:DGModule + S:DGSubmodule + With @ TT "S.ambient === M" @. + Outputs + Q:DGQuotientModule + The cokernel of @ TT "S.inclusion.natural" @ equipped with the + induced differential. + Description + Text + The shorthand @ TT "M / S" @ calls this constructor. The resulting + Q records the ambient M (via @ TO ambient @), the killed submodule + S (via @ TO subDGModule @), and the quotient map M → Q (via + @ TO projection @). + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Z = dgSubmodule(M, {}) + Q = dgQuotientModule(M, Z) + instance(Q, DGQuotientModule) + ambient Q === M + subDGModule Q === Z + Text + Quotienting by the zero submodule gives a DGQuotientModule whose + underlying cokernel is isomorphic to @ TT "M.natural" @; quotienting + by the whole ambient yields the zero DGQuotientModule. + Example + Sfull = dgSubmodule(M, id_(M.natural)) + Qzero = M / Sfull + isZero Qzero + Caveat + @ TT "S.ambient" @ must equal @ TT "M" @ as an object (===), not + merely be isomorphic. This enforces a strict lattice-of-submodules + discipline on the resulting quotient. + SeeAlso + "Building DG modules, submodules, and quotients" + (symbol /, DGModule, DGSubmodule) + DGQuotientModule + projection + subDGModule +/// + +doc /// + Key + (symbol /, DGModule, DGSubmodule) + Headline + Quotient DG module: M / S + Usage + Q = M / S + Inputs + M:DGModule + S:DGSubmodule + With @ TT "S.ambient === M" @. + Outputs + Q:DGQuotientModule + Description + Text + Shorthand for @ TT "dgQuotientModule(M, S)" @. See + @ TO dgQuotientModule @ for semantics and invariants. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Z = dgSubmodule(M, {}) + Q = M / Z + ambient Q === M + isWellDefined Q + Text + The zero and top-dimensional boundary cases are handled + consistently: @ TT "M / 0" @ is a DGQuotientModule wrapping a + cokernel isomorphic to M, and @ TT "M / M" @ is the zero + DGQuotientModule. + Example + Sfull = dgSubmodule(M, id_(M.natural)) + isZero (M / Sfull) + SeeAlso + "Building DG modules, submodules, and quotients" + dgQuotientModule + DGQuotientModule +/// + +doc /// + Key + ambient + (ambient, DGIdeal) + (ambient, DGSubmodule) + (ambient, DGQuotientModule) + inclusion + (inclusion, DGSubmodule) + projection + (projection, DGQuotientModule) + subDGModule + (subDGModule, DGQuotientModule) + Headline + Accessors for the ambient object, inclusion, projection, and killed submodule + Description + Text + These accessors navigate between the DG types in the + DGModule / DGSubmodule / DGQuotientModule hierarchy: + Text + @ TT "ambient" @ on a @ TO DGIdeal @ returns the enclosing + @ TO DGAlgebra @; on a @ TO DGSubmodule @ or + @ TO DGQuotientModule @ it returns the enclosing @ TO DGModule @. + Text + @ TT "inclusion" @ on a @ TO DGSubmodule @ S returns the + @ TO DGModuleMap @ S → ambient(S). + Text + @ TT "projection" @ on a @ TO DGQuotientModule @ Q returns the + @ TO DGModuleMap @ ambient(Q) → Q. + Text + @ TT "subDGModule" @ on a @ TO DGQuotientModule @ Q returns the + @ TO DGSubmodule @ that was killed to form Q. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + S = dgSubmodule(M, id_(M.natural)) + Q = M / S + ambient S === M + ambient Q === M + source inclusion S === S + target inclusion S === M + source projection Q === M + target projection Q === Q + subDGModule Q === S + Text + For a DGIdeal the analogous accessor returns the DGAlgebra: + Example + I = dgIdeal(A, {x_(A.natural)}) + ambient I === A + SeeAlso + "Building DG modules, submodules, and quotients" + DGSubmodule + DGQuotientModule + DGIdeal +/// + +doc /// + Key + "Module-like operations on DG modules" + Headline + Basic queries (generators, rank, degrees, homogeneity) extended to DG modules and their sub- and quotient modules + Description + Text + Every @ TO DGModule @ (and its @ TO DGSubmodule @ / @ TO DGQuotientModule @ variants) + carries an underlying graded @ TT "A.natural" @-module, accessible via + @ TT "M.natural" @. The standard @ TO Module @ methods — @ TO numgens @, + @ TO rank @, @ TO degrees @, @ TO isHomogeneous @, @ TO super @, + @ TO cover @, @ TO relations @ — are lifted directly onto the DG types, + so code written against ordinary modules can often be reused on DG + objects unchanged. + Text + Two DG-specific predicates are added: + Text + @ TO isFreeDGModule @ — is the underlying @ TT "M.natural" @ a free + graded @ TT "A.natural" @-module? Every DGModule built via + @ TO freeDGModule @ satisfies this; user-built DGModules with + non-free @ TT ".natural" @ slots are tolerated by @ TO isWellDefined @ + as long as the differential closes. + Text + @ TO isZero @ — does the DG object have zero natural generators? + Consistent across DGModule, DGSubmodule, DGQuotientModule, and + DGIdeal, so predicates can be written generically. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1, 2}) + numgens M + rank M + degrees M + isHomogeneous M + isFreeDGModule M + isZero M + Text + On quotient DG modules, @ TT "super" @ and @ TT "cover" @ both return + the ambient DGModule, and @ TT "relations" @ returns the matrix of + the killed inclusion — mirroring M2's cokernel encoding of a quotient. + Example + Anat = A.natural + Mone = freeDGModule(A, {0}) + S = dgSubmodule(Mone, matrix {{x_Anat, y_Anat}}) + Q = Mone / S + super Q === Mone + cover Q === Mone + relations Q + Subnodes + (numgens, DGModule) + (rank, DGModule) + (degrees, DGModule) + (isHomogeneous, DGModule) + isFreeDGModule + (isZero, DGModule) + (super, DGSubmodule) + SeeAlso + "Operations on DG Submodules" + "Operations on DG Ideals" + DGModule +/// + +doc /// + Key + (numgens, DGModule) + (numgens, DGSubmodule) + (numgens, DGQuotientModule) + Headline + Number of natural generators of a DG module (or sub- or quotient module) + Usage + n = numgens M + Inputs + M: + A DGModule, DGSubmodule, or DGQuotientModule. + Outputs + n:ZZ + The number of natural generators of the underlying graded @ TT "A.natural" @-module, + equal to the length of its list of degrees. + Description + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1, 2}) + numgens M == 3 + Text + For a DGSubmodule the count is the number of columns of the inclusion + matrix (= number of generators being included). For a DGQuotientModule + it is the number of natural generators of the quotient presentation, + which by construction equals @ TT "numgens (cover Q).natural" @. + Example + Anat = A.natural + M1 = freeDGModule(A, {0}) + S = dgSubmodule(M1, matrix {{x_Anat, y_Anat}}) + numgens S == 2 + Q = M1 / S + numgens Q == numgens M1 + Text + Boundary case: a DGModule freely generated on an empty list has zero + generators and is @ TO isZero @. + Example + M0 = freeDGModule(A, {}) + numgens M0 == 0 + isZero M0 + SeeAlso + "Module-like operations on DG modules" + (rank, DGModule) + (degrees, DGModule) + isZero +/// + +doc /// + Key + (rank, DGModule) + (rank, DGSubmodule) + Headline + Rank of the underlying free A.natural-module + Usage + r = rank M + Inputs + M: + A DGModule or DGSubmodule whose @ TT ".natural" @ is free. + Outputs + r:ZZ + @ TT "rank M.natural" @. + Description + Text + For a free DGModule, @ TT "rank" @ and @ TO numgens @ coincide. + For a DGSubmodule, @ TT "rank" @ reports the rank of the included + free module (which is @ TT "numgens S" @ when the inclusion is + injective on natural generators — always the case for submodules + built by @ TO dgSubmodule @). + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + rank M == numgens M + Anat = A.natural + M1 = freeDGModule(A, {0}) + S = dgSubmodule(M1, matrix {{x_Anat, y_Anat}}) + rank S + Text + After pruning a DGSubmodule with redundant inclusions, @ TT "rank" @ + equals @ TT "numgens" @ on the pruned object. + Example + Sbig = dgSubmodule(M1, matrix {{1_Anat, x_Anat, y_Anat}}) + Sp = prune Sbig + numgens Sp == rank Sp + Caveat + @ TT "rank" @ is not defined on @ TO DGQuotientModule @: the rank of a + cokernel module triggers codim computations that fail over non-affine + graded ambients. Use @ TO numgens @ for the generator count. + SeeAlso + "Module-like operations on DG modules" + (numgens, DGModule) +/// + +doc /// + Key + (degrees, DGModule) + (degrees, DGSubmodule) + (degrees, DGQuotientModule) + Headline + Multi-degrees of the natural generators of a DG module + Usage + ds = degrees M + Inputs + M: + A DGModule, DGSubmodule, or DGQuotientModule. + Outputs + ds:List + A list of multi-degrees, one per natural generator. Each multi-degree + is a list whose first entry is the homological degree and whose + remaining entries are the internal degrees. + Description + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1, 2}) + degrees M == {{0,0},{1,0},{2,0}} + Text + Internal degrees come from the ambient ring's grading and are + concatenated onto the homological degree. + Example + Rg = ZZ/101[x, y, Degrees => {{1,0}, {0,1}}] + Ag = koszulComplexDGA Rg + Mg = freeDGModule(Ag, {{0,0,0}, {1,0,0}}) + degrees Mg + SeeAlso + "Module-like operations on DG modules" + (numgens, DGModule) + (isHomogeneous, DGModule) +/// + +doc /// + Key + (isHomogeneous, DGModule) + (isHomogeneous, DGSubmodule) + (isHomogeneous, DGQuotientModule) + Headline + Test whether the underlying graded module is homogeneous + Usage + b = isHomogeneous M + Inputs + M: + A DGModule, DGSubmodule, or DGQuotientModule. + Outputs + b:Boolean + @ TT "isHomogeneous M.natural" @. + Description + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 2}) + isHomogeneous M + Anat = A.natural + M1 = freeDGModule(A, {0}) + S = dgSubmodule(M1, matrix {{x_Anat, y_Anat}}) + isHomogeneous S + Q = M1 / S + isHomogeneous Q + SeeAlso + "Module-like operations on DG modules" + (degrees, DGModule) +/// + +doc /// + Key + isFreeDGModule + (isFreeDGModule, DGModule) + (isFreeDGModule, DGSubmodule) + Headline + Is the underlying A.natural-module free? + Usage + b = isFreeDGModule M + Inputs + M: + A DGModule or DGSubmodule. + Outputs + b:Boolean + @ TT "isFreeModule M.natural" @. + Description + Text + Every DGModule built via @ TO freeDGModule @ (and every DGSubmodule of + one) has free @ TT ".natural" @, so this returns true in the typical + case. The check exists because it is possible to build a DGModule + directly whose underlying @ TT ".natural" @ module is non-free, and @ TO isWellDefined @ will + tolerate such an object as long as the differential closes. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 2}) + isFreeDGModule M + Anat = A.natural + M1 = freeDGModule(A, {0}) + S = dgSubmodule(M1, matrix {{x_Anat}}) + isFreeDGModule S + Text + Tensor products, base changes, and direct sums constructed by this + package all land in the free case, so @ TT "isFreeDGModule" @ remains + true after any of those operations. + Example + MNK = (M ** R^2) ** R^2 + isFreeDGModule MNK + SeeAlso + "Module-like operations on DG modules" + freeDGModule + (isWellDefined, DGModule) +/// + +doc /// + Key + (isZero, DGModule) + (isZero, DGSubmodule) + (isZero, DGQuotientModule) + isZero + Headline + Does the DG object have no natural generators? + Usage + b = isZero X + Inputs + X: + A DGModule, DGSubmodule, DGQuotientModule, or @ TO DGIdeal @. + Outputs + b:Boolean + True iff the natural presentation is zero. Concretely: + @ TT "numgens M.natural == 0" @ for a DGModule, no inclusion columns + (or a zero inclusion matrix) for a DGSubmodule, and + @ TT "Q.natural == 0" @ for a DGQuotientModule. For a DGIdeal, + see @ TO (isZero, DGIdeal) @. + Description + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M0 = freeDGModule(A, {}) + isZero M0 + M = freeDGModule(A, {0}) + not isZero M + Text + A zero DGSubmodule can be built explicitly via a zero inclusion matrix. + Example + Anat = A.natural + Z = dgSubmodule(M, map(M.natural, (Anat)^0, 0)) + isZero Z + Text + Quotienting a DGModule by its whole ambient yields a zero + DGQuotientModule. + Example + Full = dgSubmodule(M, id_(M.natural)) + Q = M / Full + isZero Q + SeeAlso + "Module-like operations on DG modules" + (numgens, DGModule) + (isZero, DGIdeal) +/// + +doc /// + Key + (super, DGSubmodule) + (super, DGQuotientModule) + (cover, DGQuotientModule) + (relations, DGQuotientModule) + Headline + Ambient DGModule, cover, and relation matrix of a DG sub- or quotient module + Description + Text + These mirror the analogous M2 Module accessors. + Text + @ TT "super S" @ (for a DGSubmodule S) and @ TT "super Q, cover Q" @ + (for a DGQuotientModule Q) all return the ambient DGModule M from + which the sub- or quotient was built. + Text + @ TT "relations Q" @ returns the inclusion matrix of the killed + submodule, encoding the cokernel presentation of Q. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Anat = A.natural + S = dgSubmodule(M, matrix {{x_Anat, y_Anat}}) + super S === M + Q = M / S + super Q === M + cover Q === M + numcols relations Q == 2 + SeeAlso + "Module-like operations on DG modules" + DGSubmodule + DGQuotientModule +/// + +doc /// + Key + "Operations on DG Submodules" + Headline + Sum, intersection, equality, and containment of DG submodules + Description + Text + DGSubmodules of a common ambient DGModule M form a lattice under sum, + intersection, and inclusion — each operation preserves d-closure by + elementary arguments paralleling the @ TO "Operations on DG Ideals" @ + story: + Text + @BOLD "Sum"@: @ TT "d(s + t) = d(s) + d(t) \\in S + T" @ when + @ TT "s \\in S" @ and @ TT "t \\in T" @. + Text + @BOLD "Intersection"@: if @ TT "x \\in S \\cap T" @ then + @ TT "d(x) \\in S" @ (by d-closure of S) and @ TT "d(x) \\in T" @ + (by d-closure of T), so @ TT "d(x) \\in S \\cap T" @. + Text + @BOLD "Containment / equality"@: read off directly from the images of + the inclusions in @ TT "M.natural" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Anat = A.natural + S = dgSubmodule(M, matrix {{x_Anat}}) + T = dgSubmodule(M, matrix {{y_Anat}}) + ST = S + T + isWellDefined ST + isSubset(S, ST) and isSubset(T, ST) + cap = intersect(S, T) + isWellDefined cap + isSubset(cap, S) and isSubset(cap, T) + Text + Sum is idempotent (@ TT "S + S == S" @) and intersecting with the + zero submodule yields the zero submodule. + Example + (S + S) == S + Z = dgSubmodule(M, map(M.natural, (Anat)^0, 0)) + isZero intersect(S, Z) + Text + All four operations require the DGSubmodules to share an ambient + DGModule (via @ TT "===" @ — object identity, not just isomorphism), + and raise an error otherwise. + Subnodes + (symbol +, DGSubmodule, DGSubmodule) + (intersect, DGSubmodule, DGSubmodule) + (symbol ==, DGSubmodule, DGSubmodule) + SeeAlso + DGSubmodule + "Operations on DG Ideals" + "Module-like operations on DG modules" +/// + +doc /// + Key + (symbol +, DGSubmodule, DGSubmodule) + Headline + Sum of two DG submodules of a common ambient + Usage + U = S + T + Inputs + S:DGSubmodule + T:DGSubmodule + With @ TT "S.ambient === T.ambient" @. + Outputs + U:DGSubmodule + The DGSubmodule of @ TT "S.ambient" @ whose inclusion matrix is the + horizontal concatenation of @ TT "S.inclusion.natural" @ and + @ TT "T.inclusion.natural" @. + Description + Text + Preserves d-closure and contains both summands. Idempotent: + @ TT "S + S == S" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Anat = A.natural + S = dgSubmodule(M, matrix {{x_Anat}}) + T = dgSubmodule(M, matrix {{y_Anat}}) + ST = S + T + isWellDefined ST + isSubset(S, ST) and isSubset(T, ST) + (S + S) == S + Caveat + If the two ambients are distinct objects (even if isomorphic), this + raises an error — the operation is about lattice structure within a + fixed ambient. + SeeAlso + "Operations on DG Submodules" + (intersect, DGSubmodule, DGSubmodule) + (isSubset, DGSubmodule, DGSubmodule) +/// + +doc /// + Key + (intersect, DGSubmodule, DGSubmodule) + Headline + Intersection of two DG submodules of a common ambient + Usage + U = intersect(S, T) + Inputs + S:DGSubmodule + T:DGSubmodule + With @ TT "S.ambient === T.ambient" @. + Outputs + U:DGSubmodule + The DGSubmodule of @ TT "S.ambient" @ whose image is + @ TT "image(S.inclusion.natural) \\cap image(T.inclusion.natural)" @. + Description + Text + Preserves d-closure directly. Intersecting with the zero submodule + yields the zero submodule; intersecting with the same submodule is + idempotent (@ TT "intersect(S, S) == S" @ as submodules). + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Anat = A.natural + S = dgSubmodule(M, matrix {{x_Anat}}) + T = dgSubmodule(M, matrix {{x_Anat * y_Anat}}) + cap = intersect(S, T) + isWellDefined cap + isSubset(cap, S) and isSubset(cap, T) + Text + Boundary case: intersection with the zero submodule is zero. + Example + Z = dgSubmodule(M, map(M.natural, (Anat)^0, 0)) + isZero intersect(S, Z) + SeeAlso + "Operations on DG Submodules" + (symbol +, DGSubmodule, DGSubmodule) +/// + +doc /// + Key + (symbol ==, DGSubmodule, DGSubmodule) + (isSubset, DGSubmodule, DGSubmodule) + Headline + Equality and containment of DG submodules + Usage + b = S == T + b = isSubset(S, T) + Inputs + S:DGSubmodule + T:DGSubmodule + Outputs + b:Boolean + Description + Text + Equality requires the ambients to be the same object + (@ TT "===" @) and the images of the inclusions to agree. + Containment further requires the DGSubmodules share an ambient and + tests the image inclusions. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + Anat = A.natural + S = dgSubmodule(M, matrix {{x_Anat}}) + U = dgSubmodule(M, matrix {{x_Anat, y_Anat}}) + isSubset(S, U) + not isSubset(U, S) + S == S + Text + Two DGSubmodules built from distinct ambient DGModules are never + equal, even if the ambient modules are abstractly isomorphic. + Example + M' = freeDGModule(A, {0}) + S' = dgSubmodule(M', matrix {{x_Anat}}) + not (S == S') + SeeAlso + "Operations on DG Submodules" + (symbol +, DGSubmodule, DGSubmodule) + (intersect, DGSubmodule, DGSubmodule) +/// + +doc /// + Key + "Image, kernel, and cokernel of DG module maps" + Headline + Building DG submodules and DG quotient modules from a DG module map + Description + Text + For a DG module map @ TT "f : M -> N" @ of DG modules over a common + DG algebra A, the package provides three chain-level constructions: + Text + @ TO (image, DGModuleMap) @ — the image of f, built as a + @ TO DGSubmodule @ of the target N. A chain map sends cycles to + cycles and boundaries to boundaries, so the column span of + @ TT "f.natural" @ is automatically d-closed in @ TT "N.natural" @; + no d-saturation is needed. + Text + @ TO (kernel, DGModuleMap) @ — the kernel of f, built as a + @ TO DGSubmodule @ of the source M. Again chain-map-ness gives + d-closure for free. + Text + @ TO (cokernel, DGModuleMap) @ — the cokernel of f, built as a + @ TO DGQuotientModule @ equal to @ TT "target f / image f" @. + Text + These three operations fit together in the usual short exact + sequence: for any f, we have + @ TT "rank(source f) == numgens(kernel f) + numgens(image f)" @ + and @ TT "rank(target f) == numgens(image f) + numgens(cokernel f)" @ + whenever the natural modules are free. + Text + A genuinely informative example: let + @ TT "R = k[x, y]/(x^2, y^2)" @ and let + @ TT "KM = koszulComplexDGM R^1" @ be its Koszul DG module. + Multiplication by @ TT "x" @ is a degree-0 endomorphism of + @ TT "KM" @ because @ TT "x" @ is central in the underlying + ring: + Example + R = ZZ/101[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + mx = dgModuleMap(KM, KM, x * id_(KM.natural)) + isWellDefined mx + Text + Since @ TT "x^2 = 0" @ in @ TT "R" @, the composition + @ TT "mx o mx" @ is the zero map, which chain-level is the + statement @ TT "image mx \\subseteq kernel mx" @. The three + constructions expose this directly: + Example + imgMx = image mx; + kerMx = kernel mx; + cokMx = cokernel mx; + all({imgMx, kerMx, cokMx}, isWellDefined) + numcols (inclusion imgMx).natural + numcols (inclusion kerMx).natural + isSubset(imgMx, kerMx) + Text + The cokernel is the quotient DG module + @ TT "KM / x \\cdot KM" @; at the natural level it is + @ TT "KM \\otimes_R R/(x) = k[y]/(y^2) \\otimes_k \\Lambda(T_1, T_2)" @, + presented by the @ TT "1 x 1" @ matrix @ TT "[x]" @ per + generator: + Example + presentation cokMx.natural + Text + The three operations are used throughout the package -- notably + by @ TO2((homology, DGModuleMap, ZZ), "homology at degree n") @, + which computes @ TT "kernel(d_n) / image(d_{n+1})" @ via these + primitives. + Text + Finally, each of @ TT "image mx" @, @ TT "kernel mx" @, + @ TT "cokernel mx" @ is itself a DG module, so + @ TO (toComplex, DGModule) @ (or its + @ TO DGSubmodule @ / @ TO DGQuotientModule @ variants) converts + them into ordinary chain complexes one can hand to + @ TO "Complexes::Complexes" @. For the cokernel in particular, + the underlying complex is @ TT "KM / x \\cdot KM" @ and its + homology picks up the classes that become new cycles when the + @ TT "x" @-direction is killed: + Example + CcokMx = toComplex cokMx + apply(0..(maxDegree cokMx), n -> prune HH_n CcokMx) + Subnodes + (image, DGModuleMap) + (kernel, DGModuleMap) + (cokernel, DGModuleMap) + SeeAlso + DGModuleMap + "Building DG modules, submodules, and quotients" + "Basic operations on DG Module Maps" +/// + +doc /// + Key + (image, DGModuleMap) + Headline + Image of a DG module map as a DG submodule of its target + Usage + S = image f + Inputs + f:DGModuleMap + A morphism @ TT "M -> N" @ of DG modules over a common DG algebra. + Outputs + S:DGSubmodule + The DG submodule of @ TT "N = target f" @ whose column span in + @ TT "N.natural" @ is the image of @ TT "f.natural" @. Zero + columns of @ TT "f.natural" @ are dropped, so the number of + generators of the result reflects the rank of the image rather + than the width of the presentation. + Description + Text + Since f is a chain map, the column span of @ TT "f.natural" @ is + already d-closed in @ TT "N.natural" @; @ TO dgSubmodule @ is + called without a d-saturation loop. + Text + A substantive example: over + @ TT "R = k[x, y]/(x^2, y^2)" @ take the Koszul DG module + @ TT "KM = koszulComplexDGM R^1" @ and the endomorphism + "multiplication by @ TT "x" @." Its image is a proper + DG submodule -- it cannot be all of @ TT "KM" @ because + @ TT "x" @ annihilates the class of @ TT "1 \\in R" @ modulo + its own image (since @ TT "x^2 = 0" @): + Example + R = ZZ/101[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + mx = dgModuleMap(KM, KM, x * id_(KM.natural)) + imgMx = image mx + isWellDefined imgMx + numcols (inclusion imgMx).natural + isSubset(imgMx, dgSubmodule(KM, id_(KM.natural))) + Text + The image of the inclusion of a DG submodule + @ TT "S \\subset M" @ recovers @ TT "S" @ itself: + Example + Sfull = dgSubmodule(KM, id_(KM.natural)); + (image (inclusion Sfull)) == Sfull + SeeAlso + "Image, kernel, and cokernel of DG module maps" + (kernel, DGModuleMap) + (cokernel, DGModuleMap) + DGSubmodule +/// + +doc /// + Key + (kernel, DGModuleMap) + Headline + Kernel of a DG module map as a DG submodule of its source + Usage + S = kernel f + Inputs + f:DGModuleMap + A morphism @ TT "M -> N" @ of DG modules over a common DG algebra. + Outputs + S:DGSubmodule + The DG submodule of @ TT "M = source f" @ generated by the kernel + of @ TT "f.natural" @ as a module over @ TT "M.dgAlgebra.natural" @. + Description + Text + A chain map sends cycles to cycles, so the kernel of + @ TT "f.natural" @ at the module level is already d-closed. The + constructor takes the natural-level kernel generators and wraps + them with @ TO dgSubmodule @. + Text + Mathematically interesting example: over + @ TT "R = k[x, y]/(x^2, y^2)" @ the endomorphism + "multiplication by @ TT "x" @" of the Koszul DG module + @ TT "KM" @ squares to zero, so its kernel contains its image: + Example + R = ZZ/101[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + mx = dgModuleMap(KM, KM, x * id_(KM.natural)) + kerMx = kernel mx + imgMx = image mx + isWellDefined kerMx + numcols (inclusion kerMx).natural + isSubset(imgMx, kerMx) + Text + The inclusion of a DG submodule is injective, so its kernel is + zero: + Example + Sfull = dgSubmodule(KM, id_(KM.natural)); + isZero kernel (inclusion Sfull) + SeeAlso + "Image, kernel, and cokernel of DG module maps" + (image, DGModuleMap) + (cokernel, DGModuleMap) + DGSubmodule +/// + +doc /// + Key + (cokernel, DGModuleMap) + Headline + Cokernel of a DG module map as a DG quotient module of its target + Usage + Q = cokernel f + Inputs + f:DGModuleMap + A morphism @ TT "M -> N" @ of DG modules over a common DG algebra. + Outputs + Q:DGQuotientModule + The DG quotient module @ TT "N / image(f)" @ with the induced + differential. + Description + Text + Equivalently, this is @ TT "dgQuotientModule(target f, image f)" @. + The @ TT "coker" @ spelling is an alias for @ TT "cokernel" @ and + invokes the same method. + Text + A nontrivial example: over @ TT "R = k[x, y]/(x^2, y^2)" @, + multiplication by @ TT "x" @ on the Koszul DG module + @ TT "KM" @ has cokernel equal to @ TT "KM / x \\cdot KM" @, + which at the natural level is @ TT "KM \\otimes_R R/(x)" @ -- + the base change of @ TT "KM" @ mod @ TT "x" @: + Example + R = ZZ/101[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + mx = dgModuleMap(KM, KM, x * id_(KM.natural)) + Q = cokernel mx + isWellDefined Q + ambient Q === KM + presentation Q.natural + Text + The induced differential on @ TT "Q" @ is well-defined because + @ TT "mx" @ commutes with the differential, and it can be + realized as an ordinary chain complex via + @ TO (toComplex, DGQuotientModule) @: + Example + C = toComplex Q + apply(0..(maxDegree Q), n -> prune HH_n C) + Text + For a DG submodule inclusion @ TT "S \\hookrightarrow M" @, the + cokernel is exactly the DG quotient module @ TT "M / S" @: + Example + Sfull = dgSubmodule(KM, id_(KM.natural)); + isZero cokernel (inclusion Sfull) + SeeAlso + "Image, kernel, and cokernel of DG module maps" + (image, DGModuleMap) + (kernel, DGModuleMap) + DGQuotientModule + (symbol /, DGModule, DGSubmodule) +/// + +doc /// + Key + "Homology of DG modules and DG module maps" + Headline + Computing H_n and H_* for DG modules, DG submodules, DG quotient modules, and DG module maps + Description + Text + For a DG module @ TT "M" @ over a DG algebra @ TT "A" @, the package + supports two parallel views of homology: + Text + @ TO (homology, ZZ, DGModule) @ returns a single degree + @ TT "H_n(M)" @ as a module over the base ring @ TT "A.ring" @. + This is the natural per-degree shape and matches the convention of + @ TO (homology, ZZ, DGAlgebra) @. + Text + @ TO (homology, DGModule) @ (equivalently @ TO homologyModule @) + returns the full graded object @ TT "H_*(M)" @ assembled as a + module over the homology algebra @ TT "HH(A)" @, with the + @ TT "HH(A)" @-action coming from multiplication by cycle classes. + Text + Maps @ TT "f : M -> N" @ of DG modules induce maps on homology at + each level: @ TO (homology, DGModuleMap, ZZ) @ returns the induced + map @ TT "H_n(M) -> H_n(N)" @ as a map of @ TT "A.ring" @-modules, + and @ TO (homology, DGModuleMap) @ assembles those pieces into a + map of @ TT "HH(A)" @-modules. + Text + For quotients, @ TO (homology, ZZ, DGQuotientModule) @ and + @ TO (homology, DGQuotientModule) @ (equivalently + @ TO (homologyModule, DGQuotientModule) @) work the same way, + using @ TO toComplex @ to reduce to an + underlying complex. A @ TO DGSubmodule @ is a @ TO DGModule @, + so its homology is computed by the DG module methods directly. + Text + To extract an explicit representative of a given homology class, + use @ TO (homologyClass, DGModule, Vector) @; given a cycle in + @ TT "M.natural" @ it returns the corresponding element of + @ TT "prune homology(n, M)" @. + Example + R = QQ[x, y]/ideal(x^2, y^2) + A = koszulComplexDGA R + k = R^1 / ideal(x, y) + M = minimalSemifreeResolution(A, k, EndDegree => 2) + H0 = prune homology(0, M) + numgens H0 == 1 + idM = identityDGModuleMap M + h0 = homology(idM, 0) + h0 == id_(source h0) + Text + The identity on @ TT "M" @ induces the identity on every + @ TT "H_n(M)" @, a baseline sanity check that each of the pieces + fits together. + Subnodes + (homology, ZZ, DGModule) + (homology, DGModule) + (homology, DGModuleMap, ZZ) + (homology, ZZ, DGQuotientModule) + (homology, DGQuotientModule) + (homologyClass, DGModule, Vector) + SeeAlso + (homology, DGModuleMap) + (homology, ZZ, DGAlgebra) + (homology, DGAlgebra) + homologyModule + homologyClass +/// + +doc /// + Key + (homology, ZZ, DGModule) + Headline + The degree-n homology of a DG module as a module over the base ring + Usage + H = homology(n, M) + Inputs + n:ZZ + A homological degree. + M:DGModule + A DG module over a DG algebra @ TT "A" @. + Outputs + H:Module + The module @ TT "H_n(M) = ker(d_n) / image(d_{n+1})" @, presented + as a module over @ TT "A.ring" @. + Description + Text + This is the per-degree homology: the differential + @ TT "d_n : M_n -> M_{n-1}" @ is built via + @ TT "moduleDifferential" @, and @ TT "H_n(M)" @ is obtained by + applying the built-in M2 @ TT "homology" @ method to the adjacent + pair @ TT "(d_n, d_{n+1})" @. The resulting module lives over + @ TT "A.ring" @; use @ TO (prune, Module) @ to minimize the + presentation. + Text + Because @ TO DGSubmodule @ is a subtype of @ TO DGModule @, this + method also handles DG submodules directly. + Example + R = QQ[x, y, z] + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + prune homology(0, KM) + prune homology(1, KM) == 0 + prune homology(2, KM) == 0 + Text + For the Koszul complex on a regular sequence, only + @ TT "H_0 = R/(x,y,z)" @ is nonzero; this recovers the standard + fact. + Example + R2 = ZZ/101[x] + A2 = koszulComplexDGA R2 + M = freeDGModule(A2, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + numgens prune homology(0, M) + numgens prune homology(1, M) + SeeAlso + "Homology of DG modules and DG module maps" + (homology, DGModule) + (homology, DGModuleMap, ZZ) + (homology, ZZ, DGQuotientModule) + (homology, ZZ, DGAlgebra) + moduleDifferential +/// + +doc /// + Key + (homology, DGModule) + (homologyModule, DGModule) + Headline + The graded homology of a DG module as a module over HH(A) + Usage + HM = homology M + HM = homologyModule M + Inputs + M:DGModule + A DG module over a DG algebra @ TT "A" @. + Outputs + HM:Module + The graded homology @ TT "H_*(M)" @ packaged as a module over the + homology algebra @ TT "HH(A)" @. The @ TT "HH(A)" @-action comes + from multiplication by cycle classes of @ TT "A" @ on @ TT "M" @. + Description + Text + The two names @ TT "homology M" @ and @ TT "homologyModule M" @ + produce the same object and are provided as aliases, matching the + analogous convention for DG algebras in which + @ TO (homology, DGAlgebra) @ returns @ TO homologyAlgebra @. + Text + For per-degree homology as an @ TT "A.ring" @-module, use + @ TO (homology, ZZ, DGModule) @. + Text + A fast path is taken when @ TT "M" @ came from + @ TO koszulComplexDGM @ and has vanishing generator differentials: + in that case the result is computed directly from + @ TT "M.module" @ via @ TO (homologyModule, DGAlgebra, Module) @. + Otherwise the method builds the @ TT "HH(A)" @-module presentation + degree by degree, using the action of each cycle class of + @ TT "A" @ on @ TT "M" @. + Example + R = QQ[x, y]/ideal(x^2, y^2) + A = koszulComplexDGA R + k = R^1 / ideal(x, y) + M = minimalSemifreeResolution(A, k, EndDegree => 2) + HM = homology M + ring HM === HH A + HM' = homologyModule M + HM == HM' + Text + The ring of the result is @ TT "HH(A)" @, the homology algebra of + the underlying DG algebra. + Caveat + The general path currently requires @ TT "M.natural" @ to be a free + @ TT "A.natural" @-module (or @ TT "M" @ to be a + @ TO koszulComplexDGM @ with zero generator differentials). + SeeAlso + "Homology of DG modules and DG module maps" + (homology, ZZ, DGModule) + (homology, DGAlgebra) + homologyModule + homologyAlgebra +/// + +doc /// + Key + (homology, DGModuleMap, ZZ) + Headline + The induced map on degree-n homology of a DG module map + Usage + h = homology(f, n) + Inputs + f:DGModuleMap + A map @ TT "M -> N" @ of DG modules over a common DG algebra + @ TT "A" @. + n:ZZ + A homological degree. + Outputs + h:Matrix + The induced map @ TT "H_n(f) : H_n(M) -> H_n(N)" @, presented as + a morphism of @ TT "A.ring" @-modules. + Description + Text + Computed via @ TO inducedMap @ from the chain map + @ TT "toComplexMap(f, n)" @ applied to the pair of per-degree + homology modules. Since a chain map sends cycles to cycles and + boundaries to boundaries, the induced map is well defined. + Text + The identity DG module map induces the identity on each + @ TT "H_n" @; the zero DG module map induces the zero map on each + @ TT "H_n" @. Composition respects homology: + @ TT "homology(g * f, n) == homology(g, n) * homology(f, n)" @. + Example + R = QQ[x, y]/ideal(x^2, y^2) + A = koszulComplexDGA R + k = R^1 / ideal(x, y) + M = minimalSemifreeResolution(A, k, EndDegree => 3) + idM = identityDGModuleMap M + zM = zeroDGModuleMap(M, M) + h0id = homology(idM, 0) + h0id == id_(source h0id) + h0z = homology(zM, 0) + h0z == map(target h0z, source h0z, 0) + Text + Multiplication by @ TT "x" @ on a minimal semifree resolution of + @ TT "k = R/(x, y)" @ induces the zero map on @ TT "H_0 = k" @, + because @ TT "x" @ kills @ TT "k" @. + Example + natGens = apply(rank M.natural, i -> (M.natural)_i) + xMap = dgModuleMap(M, M, apply(natGens, g -> x * g)) + h0x = homology(xMap, 0) + h0x == map(target h0x, source h0x, 0) + SeeAlso + "Homology of DG modules and DG module maps" + (homology, DGModuleMap) + (homology, ZZ, DGModule) + toComplexMap +/// + +doc /// + Key + (homology, ZZ, DGQuotientModule) + Headline + The degree-n homology of a DG quotient module + Usage + H = homology(n, Q) + Inputs + n:ZZ + A homological degree. + Q:DGQuotientModule + A DG quotient module @ TT "M / S" @ over a DG algebra @ TT "A" @. + Outputs + H:Module + The module @ TT "H_n(Q)" @, presented as a module over + @ TT "A.ring" @. + Description + Text + Implemented via @ TT "HH_n (toComplex Q)" @. Since the underlying + natural module is a cokernel rather than a free module, the + differentials of @ TT "Q" @ are expressed through + @ TO toComplex @ and the standard M2 + @ TT "HH_n" @ is applied. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + S = dgSubmodule(M, (id_(M.natural))_{1}) + Q = M / S + h0 = prune homology(0, Q) + h1 = prune homology(1, Q) + h2 = prune homology(2, Q) + numgens h0 == 1 + numgens h1 == 1 + h2 == 0 + Text + With @ TT "S" @ acyclic and @ TT "H_0(M) = R/x" @, the quotient + @ TT "Q = M/S" @ has @ TT "H_0(Q) = R/x" @ and + @ TT "H_1(Q) = R/x" @, both equal to the residue field + @ TT "k = ZZ/101" @ (one generator each, torsion), and + @ TT "H_2(Q) = 0" @. + SeeAlso + "Homology of DG modules and DG module maps" + (homology, DGQuotientModule) + (homology, ZZ, DGModule) + (toComplex, DGQuotientModule) +/// + +doc /// + Key + (homology, DGQuotientModule) + (homologyModule, DGQuotientModule) + Headline + The graded homology of a DG quotient module + Usage + HQ = homology Q + HQ = homologyModule Q + Inputs + Q:DGQuotientModule + A DG quotient module @ TT "M / S" @ over a DG algebra @ TT "A" @. + Outputs + HQ:Module + The graded homology @ TT "H_*(Q)" @ packaged as a module over + @ TT "HH(A)" @. + Description + Text + Implemented by taking @ TO toComplex @ and + assembling the per-degree homologies + @ TT "HH_i(toComplex Q)" @ into a direct sum, then tensoring each + piece with @ TT "HH(A)" @ along @ TT "A.ring -> HH(A)" @. + The two names @ TT "homology Q" @ and @ TT "homologyModule Q" @ + are aliases, matching @ TO (homology, DGModule) @. + Text + Because @ TT "Q.natural" @ is a cokernel rather than a free + @ TT "A.natural" @-module, the general cycle-action construction + used for @ TO (homologyModule, DGModule) @ does not apply; the + result captures the R-module structure and the @ TT "HH(A)" @ + action coming from @ TT "A.ring -> HH(A)" @ only. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + S = dgSubmodule(M, (id_(M.natural))_{1}) + Q = M / S + HQ = homology Q + ring HQ === HH A + HQ' = homologyModule Q + HQ == HQ' + SeeAlso + "Homology of DG modules and DG module maps" + (homology, ZZ, DGQuotientModule) + (homology, DGModule) + (toComplex, DGQuotientModule) + homologyModule +/// + +doc /// + Key + (homologyClass, DGModule, Vector) + Headline + The homology class of a cycle in a DG module + Usage + h = homologyClass(M, z) + Inputs + M:DGModule + A free DG module over a DG algebra @ TT "A" @. + z:Vector + A cycle in @ TT "M.natural" @; that is, an element @ TT "z" @ of + homological degree @ TT "d" @ with @ TT "diff(M, z) == 0" @. + Outputs + h: + The corresponding element of @ TT "prune homology(d, M)" @. + Boundaries go to zero; the zero vector returns + @ TT "0_(prune homology(0, M))" @ by convention. + Description + Text + This is the DG module analogue of + @ TO (homologyClass, DGAlgebra, RingElement) @. It expresses the + cycle @ TT "z" @ in the basis of @ TT "prune homology(d, M)" @ by + solving @ TT "zCol = pruneMat * hCoef + boundaryMat * t" @, where + @ TT "pruneMat" @ is the pruning map of @ TT "H_d(M)" @ and + @ TT "boundaryMat" @ is the degree-@ TT "d+1" @ differential. + Text + An input that is not a cycle raises an error. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + z = natGens#0 + H = prune homology(0, M) + h = homologyClass(M, z) + h != 0_H + Text + On a boundary, @ TT "homologyClass" @ returns zero. + Example + b = x * natGens#0 + hb = homologyClass(M, b) + hb == 0_(prune homology(0, M)) + Caveat + Requires @ TT "M" @ to be a free DG module. + SeeAlso + "Homology of DG modules and DG module maps" + (homologyClass, DGAlgebra, RingElement) + (homology, ZZ, DGModule) +/// + +doc /// + Key + "Pruning DG modules, submodules, quotients, and maps" + Headline + Minimizing presentations of DG types via prune and minimalPresentation + Description + Text + For each DG type @ TT "T" @ (DG algebra, DG module, DG submodule, + DG quotient module, DG ideal, DG algebra map, DG module map), the + package provides @ TT "prune" @ and its synonym + @ TT "minimalPresentation" @. The result is an object of the + same type whose underlying presentation has had redundancy + removed, where possible. + Text + Every call to @ TT "prune" @ caches a @ TT "pruningMap" @ in the + @ TT "cache" @ table of the pruned output, encoding the canonical + comparison map @ TT "(pruned) -> (original)" @. For types where + pruning does nothing (see below), the cached pruningMap is the + identity; for types where it does something, the pruningMap is a + well-defined @ TO DGModuleMap @ (or @ TO DGAlgebraMap @) whose + source is the pruned object and whose target is the original. + This mirrors the convention used by + @ TT "prune" @ applied to @ TT "Complex" @ objects from the Complexes package for @ TT "Complex" @ objects. + Text + The two active cases are: + Text + @ TO (prune, DGSubmodule) @ trims the inclusion matrix to a + minimal generating set of its column span. When the inclusion + matrix is already minimal, @ TT "prune" @ is the identity. + Text + @ TO (prune, DGQuotientModule) @ first prunes the relations + submodule and then additionally prunes + @ TT "toComplex Qp" @ at the complex level, caching the result so + that @ TT "toComplex Qp" @ returns a minimally presented complex. + Text + The no-op cases, installed so that pruning always commutes with + every constructor, are @ TO (prune, DGModule) @, + @ TO (prune, DGAlgebra) @, @ TO (prune, DGAlgebraMap) @, + @ TO (prune, DGModuleMap) @, and (already documented) + @ TO (prune, DGIdeal) @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + S = dgSubmodule(M, matrix {{1_Anat, x_Anat, y_Anat}}) + Sp = prune S + numcols (inclusion S).natural + numcols (inclusion Sp).natural + image (inclusion S).natural == image (inclusion Sp).natural + Text + The image submodule of @ TT "M.natural" @ is unchanged; the + presentation of @ TT "S" @ is reduced from three generators to + one. + Subnodes + (prune, DGSubmodule) + (prune, DGQuotientModule) + (prune, DGModule) + (prune, DGAlgebra) + (prune, DGAlgebraMap) + (prune, DGModuleMap) + SeeAlso + (prune, DGIdeal) + (mingens, DGIdeal) +/// + +doc /// + Key + (prune, DGSubmodule) + (minimalPresentation, DGSubmodule) + Headline + Trim a DG submodule to a minimal generating set of its inclusion + Usage + Sp = prune S + Sp = minimalPresentation S + Inputs + S:DGSubmodule + Outputs + Sp:DGSubmodule + A DG submodule of the same ambient DG module whose inclusion + matrix has had @ TT "A.natural" @-linearly redundant columns + removed. + Description + Text + @ TT "prune S" @ computes + @ TT "mingens image S.inclusion.natural" @ and builds a new + DG submodule from that reduced inclusion matrix. The + @ TT "A.natural" @-span of the new inclusion matrix equals the + original: @ TT "image Sp.inclusion.natural == image S.inclusion.natural" @. + Text + A @ TT "pruningMap" @ is cached in @ TT "Sp.cache" @ as a + @ TO DGModuleMap @ from @ TT "Sp" @ to @ TT "S" @; when the + inclusion matrix is already minimal this is the identity on + @ TT "S" @ (and @ TT "prune S === S" @). + Text + Well-definedness (d-closure) and the underlying natural-level + image are preserved by pruning. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + S = dgSubmodule(M, matrix {{1_Anat, x_Anat, y_Anat}}) + Sp = prune S + numcols (inclusion Sp).natural + numcols (inclusion S).natural + image (inclusion S).natural == image (inclusion Sp).natural + isWellDefined Sp + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @ on + DG submodules. + SeeAlso + "Pruning DG modules, submodules, quotients, and maps" + (prune, DGQuotientModule) + (prune, DGModule) + dgSubmodule +/// + +doc /// + Key + (prune, DGQuotientModule) + (minimalPresentation, DGQuotientModule) + Headline + Minimize the presentation of a DG quotient module + Usage + Qp = prune Q + Qp = minimalPresentation Q + Inputs + Q:DGQuotientModule + Outputs + Qp:DGQuotientModule + A DG quotient module of the same ambient DG module whose + relations submodule has been pruned, and whose + @ TT "toComplex Qp" @ has additionally been pruned at the + complex level. + Description + Text + @ TT "prune Q" @ proceeds in two steps. First, it prunes the + relations submodule @ TT "Q.subDGModule" @ using + @ TO (prune, DGSubmodule) @; this trims redundant columns from + the inclusion matrix. If the inclusion was already minimal, + @ TT "Qp === Q" @. + Text + Second, it computes @ TT "prune (toComplex Qp)" @ at the + complex level and caches the result in @ TT "Qp.cache" @ under + the symbol @ TT "prunedComplex" @. After this, calling + @ TT "toComplex Qp" @ returns a minimally presented complex and + pruning commutes with @ TT "toComplex" @: the complex-level + prune of @ TT "toComplex Qp" @ is idempotent. + Text + A @ TT "pruningMap" @ from @ TT "Qp" @ to @ TT "Q" @ is cached + in @ TT "Qp.cache" @ as a @ TO DGModuleMap @ induced by the + identity on @ TT "M.natural" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + S = dgSubmodule(M, matrix {{1_Anat, x_Anat, y_Anat}}) + Q = M / S + Qp = prune Q + image (inclusion Qp.subDGModule).natural == image (inclusion S).natural + Text + If the cokernel is secretly zero, pruning collapses + @ TT "toComplex Qp" @ to the zero complex, matching + @ TT "prune" @ applied to @ TT "Complex" @ objects from the Complexes package on @ TT "Complex" @. + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @ on + DG quotient modules. + SeeAlso + "Pruning DG modules, submodules, quotients, and maps" + (prune, DGSubmodule) + dgQuotientModule + (symbol /, DGModule, DGSubmodule) +/// + +doc /// + Key + (prune, DGModule) + (minimalPresentation, DGModule) + Headline + Pruning a DG module is the identity + Usage + Mp = prune M + Mp = minimalPresentation M + Inputs + M:DGModule + Outputs + Mp:DGModule + The same DG module @ TT "M" @. A @ TT "pruningMap" @ equal to + the identity DG module map on @ TT "M" @ is cached in + @ TT "M.cache" @. + Description + Text + DG modules as stored in this package (built from + @ TO freeDGModule @ or @ TO koszulComplexDGM @) carry no + auxiliary presentation data that @ TT "prune" @ could minimize; + the method is installed as the identity so that calling code can + use @ TT "prune" @ uniformly across DG types. + Text + Calling @ TT "prune M" @ has the side effect of caching an + identity @ TO DGModuleMap @ under the symbol @ TT "pruningMap" @ + in @ TT "M.cache" @. + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + (prune M) === M + (minimalPresentation M) === M + SeeAlso + "Pruning DG modules, submodules, quotients, and maps" + (prune, DGSubmodule) + (prune, DGQuotientModule) +/// + +doc /// + Key + (prune, DGAlgebra) + (minimalPresentation, DGAlgebra) + Headline + Pruning a DG algebra is the identity + Usage + Ap = prune A + Ap = minimalPresentation A + Inputs + A:DGAlgebra + Outputs + Ap:DGAlgebra + The same DG algebra @ TT "A" @. + Description + Text + DG algebras as stored in this package carry a fixed underlying + graded algebra and differential; there is no redundancy for + @ TT "prune" @ to eliminate. The method is installed as the + identity so that pruning commutes uniformly with every + constructor. + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + (prune A) === A + (minimalPresentation A) === A + SeeAlso + "Pruning DG modules, submodules, quotients, and maps" + (prune, DGIdeal) +/// + +doc /// + Key + (prune, DGAlgebraMap) + (minimalPresentation, DGAlgebraMap) + Headline + Pruning a DG algebra map is the identity + Usage + fp = prune f + fp = minimalPresentation f + Inputs + f:DGAlgebraMap + Outputs + fp:DGAlgebraMap + The same DG algebra map @ TT "f" @. + Description + Text + Since @ TO (prune, DGAlgebra) @ is the identity, and a + @ TO DGAlgebraMap @ is stored as a ring map between the + underlying graded algebras of its source and target, there is + nothing for @ TT "prune" @ to minimize. The method is installed + as the identity so that pruning commutes uniformly. + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + phi = identityDGAlgebraMap A + (prune phi) === phi + (minimalPresentation phi) === phi + SeeAlso + "Pruning DG modules, submodules, quotients, and maps" + (prune, DGAlgebra) + DGAlgebraMap +/// + +doc /// + Key + (prune, DGModuleMap) + (minimalPresentation, DGModuleMap) + Headline + Pruning a DG module map is the identity + Usage + fp = prune f + fp = minimalPresentation f + Inputs + f:DGModuleMap + Outputs + fp:DGModuleMap + The same DG module map @ TT "f" @. + Description + Text + Since @ TO (prune, DGModule) @ is the identity, and a + @ TO DGModuleMap @ is stored as an @ TT "A.natural" @-linear + matrix between the naturals of its source and target, there is + nothing for @ TT "prune" @ to minimize. The method is installed + as the identity so that pruning commutes uniformly. + Text + @ TT "minimalPresentation" @ is a synonym for @ TT "prune" @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + idM = identityDGModuleMap M + (prune idM) === idM + (minimalPresentation idM) === idM + SeeAlso + "Pruning DG modules, submodules, quotients, and maps" + (prune, DGModule) + DGModuleMap +/// + +doc /// + Key + "Well-definedness, acyclicity, and quasi-isomorphism" + Headline + Predicates that check DG-theoretic conditions on DG types + Description + Text + The package exposes three families of predicates for checking + conditions that distinguish DG objects from their underlying + graded / module-theoretic data. + Text + @ TO isWellDefined @ — verifies that an object satisfies the DG + axioms appropriate to its type. For a @ TO DGAlgebra @ or + @ TO DGModule @, the central check is that the stored + differential squares to zero on every generator. For a + @ TO DGSubmodule @ or @ TO DGQuotientModule @, the check is + d-closure of the defining submodule; for a @ TO DGAlgebraMap @ + or @ TO DGModuleMap @, the check is the chain-map condition + @ TT "d \\circ f = f \\circ d" @ on every generator. + Text + @ TO isAcyclic @ — returns true when a DG algebra or DG module + has @ TT "H_i = 0" @ for all @ TT "i >= 1" @ up to a finite + bound. For objects with infinite hom-degree the user must + supply @ TO EndDegree @. + Text + @ TO isQuasiIsomorphism @ — returns true when a DG algebra map + or DG module map induces an isomorphism on @ TT "H_*" @. The + underlying check is performed on @ TO toComplexMap @ and + inherits the @ TT "Concentration" @ option from the Complexes + package. + Text + All three predicates accept the option @ TO EndDegree @ where + applicable; for finite-degree objects the default bound is + @ TT "maxDegree" @ of the object and no option is required. + Example + R = QQ[x, y, z] + A = koszulComplexDGA R + isWellDefined A + isAcyclic A + Anat = A.natural + M = freeDGModule(A, {0}) + S = dgSubmodule(M, matrix {{1_Anat}}) + Q = M / S + isWellDefined S + isWellDefined Q + phi = identityDGAlgebraMap A + isWellDefined phi + isQuasiIsomorphism phi + Text + For a regular polynomial ring the Koszul DG algebra is acyclic + and its identity map is a quasi-isomorphism. Both + @ TT "S" @ and @ TT "Q = M / S" @ are well-defined because + @ TT "S" @ is d-closed. + Subnodes + (isWellDefined, DGAlgebra) + (isWellDefined, DGModule) + (isWellDefined, DGSubmodule) + (isWellDefined, DGQuotientModule) + (isWellDefined, DGModuleMap) + (isAcyclic, DGModule) + (isAcyclic, DGQuotientModule) + (isQuasiIsomorphism, DGAlgebraMap) + (annihilator, DGSubmodule) + (annihilator, DGQuotientModule) + SeeAlso + (isWellDefined, DGIdeal) + (isAcyclic, DGAlgebra) + (isQuasiIsomorphism, DGModuleMap) +/// + +doc /// + Key + (isWellDefined, DGAlgebra) + Headline + Check that a DG algebra has correct structure and a differential that squares to zero + Usage + b = isWellDefined A + Inputs + A:DGAlgebra + Outputs + b:Boolean + True when @ TT "A" @ has all expected structural keys with the + correct types, the base ring of @ TT "A.natural" @ is compatible + with @ TT "A.ring" @, the differential lowers homological + degree by one on every generator, and @ TT "d^2 = 0" @ holds on + every generator of @ TT "A.natural" @. + Description + Text + Checks three conditions in order. First, a structural + validation: the DG algebra has keys @ TT "ring" @, + @ TT "natural" @, @ TT "diff" @, and @ TT "Degrees" @ with the + right types, and the length of @ TT "A.Degrees" @ equals the + number of generators of @ TT "A.natural" @. Second, every + generator @ TT "g" @ of @ TT "A.natural" @ has + @ TT "d(g)" @ of homological degree @ TT "|g| - 1" @. Third, + @ TT "d^2(g) = 0" @ on every generator — Leibniz then extends + this to all of @ TT "A.natural" @. + Text + When @ TT "debugLevel > 0" @ the routine prints a diagnostic + line for each failing check. + Example + R = QQ[x, y, z] + A = koszulComplexDGA R + isWellDefined A + Text + A DG algebra with @ TT "A.diff == {}" @ (no differential set) + passes the check trivially — the second and third conditions + apply only to generators with a stored differential. + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + DGAlgebra + setDiff + (isWellDefined, DGIdeal) + (isAcyclic, DGAlgebra) +/// + +doc /// + Key + (isWellDefined, DGModule) + Headline + Check that a DG module has correct structure and that its differential squares to zero + Usage + b = isWellDefined M + Inputs + M:DGModule + Outputs + b:Boolean + True when @ TT "M" @ has all expected structural keys, its base + DG algebra @ TT "M.dgAlgebra" @ is itself well-defined, the + ring of @ TT "M" @ matches that of its DG algebra, each stored + differential is a module element of @ TT "M.natural" @, and + @ TT "d_M^2 = 0" @ on every generator. + Description + Text + Performs a structural check (required keys and list-length + agreement @ TT "#M.diff == #M.Degrees" @), then verifies that + @ TT "M.dgAlgebra" @ is a well-defined DG algebra, checks that + each entry of @ TT "M.diff" @ is a vector or matrix over + @ TT "M.natural" @, and finally calls the internal + @ TT "isWellDefinedDifferential" @ routine to verify + @ TT "d_M^2 = 0" @ up to the natural bound. + Text + Diagnostic messages are emitted when @ TT "debugLevel > 0" @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + isWellDefined M + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + DGModule + freeDGModule + setDiff + (isWellDefined, DGSubmodule) + (isWellDefined, DGModuleMap) +/// + +doc /// + Key + (isWellDefined, DGSubmodule) + Headline + Check that a DG submodule is d-closed in its ambient DG module + Usage + b = isWellDefined S + Inputs + S:DGSubmodule + Outputs + b:Boolean + True when @ TT "S" @ has all expected structural keys, its + ambient is a well-defined DG module, and the column span of + @ TT "S.inclusion.natural" @ in @ TT "S.ambient.natural" @ is + stable under @ TT "d_{S.ambient}" @. + Description + Text + After a structural key check, verifies that + @ TT "S.ambient" @ is a well-defined DG module, then checks + d-closure: for every column @ TT "v" @ of the inclusion matrix, + @ TT "d_M(v)" @ lies in the column span of the inclusion + matrix modulo further columns. This is the one nontrivial + condition that distinguishes a DG submodule from a general + A.natural-submodule of @ TT "M.natural" @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + S = dgSubmodule(M, (id_(M.natural))_{1}) + isWellDefined S + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + DGSubmodule + dgSubmodule + (isWellDefined, DGModule) + (isWellDefined, DGQuotientModule) +/// + +doc /// + Key + (isWellDefined, DGQuotientModule) + Headline + Check that a DG quotient module has a well-defined differential + Usage + b = isWellDefined Q + Inputs + Q:DGQuotientModule + Outputs + b:Boolean + True when @ TT "Q" @ has all expected structural keys, its + projection map has the right source and target, and its + relations submodule @ TT "Q.subDGModule" @ is itself a + well-defined DG submodule of @ TT "Q.ambient" @. + Description + Text + After a structural key-shape check, verifies that + @ TT "Q.ambient" @ is a @ TO DGModule @, that + @ TT "Q.subDGModule" @ is a @ TO DGSubmodule @ with ambient + equal to @ TT "Q.ambient" @, and that @ TT "Q.projection" @ is + a @ TO DGModuleMap @ from @ TT "Q.ambient" @ to @ TT "Q" @. + The substantive check then delegates to + @ TO (isWellDefined, DGSubmodule) @ on the relations + submodule: the quotient is well-defined exactly when the + relations are d-closed in the ambient. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + S = dgSubmodule(M, (id_(M.natural))_{1}) + Q = M / S + isWellDefined Q + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + DGQuotientModule + dgQuotientModule + (isWellDefined, DGSubmodule) + (symbol /, DGModule, DGSubmodule) +/// + +doc /// + Key + (isWellDefined, DGModuleMap) + Headline + Check that a DG module map has correct structure and is a chain map + Usage + b = isWellDefined f + Inputs + f:DGModuleMap + Outputs + b:Boolean + True when @ TT "f" @ has all expected structural keys, source + and target are DG modules (or DG quotient modules) over a + common DG algebra, the natural matrix has the correct source + and target, @ TT "f" @ preserves hom-degree, and + @ TT "d_N(f(e_i)) = f(d_M(e_i))" @ on every natural generator + @ TT "e_i" @ of the source. + Description + Text + Six checks in order: structural key shape; source and target + types are @ TO DGModule @ or @ TO DGQuotientModule @; source + and target share the same DG algebra; + @ TT "f.natural" @ is a module map with the correct source and + target; the hom-degree of @ TT "f" @ is zero; and the chain-map + condition holds on every generator of the source. + Text + Modeled on @ TT "isWellDefined ComplexMap" @ from the + Complexes package. Diagnostic messages are emitted when + @ TT "debugLevel > 0" @. + Example + R = QQ[x, y]/ideal(x^2, y^2) + A = koszulComplexDGA R + M = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 2) + idM = identityDGModuleMap M + zM = zeroDGModuleMap(M, M) + isWellDefined idM + isWellDefined zM + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + DGModuleMap + dgModuleMap + identityDGModuleMap + zeroDGModuleMap + (isWellDefined, DGModule) +/// + +doc /// + Key + (isAcyclic, DGModule) + [(isAcyclic, DGModule), EndDegree] + Headline + Determine whether a DG module has vanishing positive-degree homology + Usage + b = isAcyclic M + b = isAcyclic(M, EndDegree => n) + Inputs + M:DGModule + EndDegree => ZZ + An upper bound for the range of degrees checked. Required when + @ TT "M" @ has infinite @ TO maxDegree @ (for example, when its + DG algebra is an @ TO acyclicClosure @). When omitted and + @ TT "M" @ is finite-dimensional, @ TT "maxDegree M" @ is used. + Outputs + b:Boolean + True when @ TT "H_i(M) = 0" @ for all @ TT "1 <= i <= n" @. + Description + Text + Checks per-degree homology via @ TO (homology, ZZ, DGModule) @ + and @ TO prune @. Degree zero is intentionally excluded: + acyclicity concerns only positive-degree homology. + Example + R = QQ[x, y, z] + A = koszulComplexDGA R + KM = koszulComplexDGM R^1 + isAcyclic KM + Text + For the Koszul DG module on a regular sequence, all + positive-degree homology vanishes (@ TT "H_0 = R / (x, y, z)" @ + is the only nonzero piece). + Example + R' = QQ[x, y]/ideal(x^2, y^2) + KM' = koszulComplexDGM R'^1 + not isAcyclic(KM', EndDegree => 3) + Text + Over a non-regular ring the Koszul DG module picks up nontrivial + positive-degree homology. + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + (isAcyclic, DGAlgebra) + (isAcyclic, DGQuotientModule) + (homology, ZZ, DGModule) + EndDegree +/// + +doc /// + Key + (isAcyclic, DGQuotientModule) + [(isAcyclic, DGQuotientModule), EndDegree] + Headline + Determine whether a DG quotient module has vanishing positive-degree homology + Usage + b = isAcyclic Q + b = isAcyclic(Q, EndDegree => n) + Inputs + Q:DGQuotientModule + EndDegree => ZZ + An upper bound for the range of degrees checked. Required when + @ TT "Q.ambient" @ has infinite @ TO maxDegree @. + Outputs + b:Boolean + True when @ TT "H_i(Q) = 0" @ for all @ TT "1 <= i <= n" @. + Description + Text + Semantics match @ TO (isAcyclic, DGModule) @: positive-degree + homology only, computed via @ TO (homology, ZZ, DGQuotientModule) @ + and @ TO prune @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + Sfull = dgSubmodule(M, id_(M.natural)) + Q0 = M / Sfull + isAcyclic Q0 + Text + When the quotient is the zero DG module, @ TT "isAcyclic" @ + holds trivially. + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + (isAcyclic, DGModule) + (homology, ZZ, DGQuotientModule) + EndDegree +/// + +doc /// + Key + (isQuasiIsomorphism, DGAlgebraMap) + Headline + Determine whether a DG algebra map induces an isomorphism on homology + Usage + b = isQuasiIsomorphism phi + Inputs + phi:DGAlgebraMap + Outputs + b:Boolean + True when the induced chain map @ TT "toComplexMap phi" @ is a + quasi-isomorphism on the underlying complexes. + Description + Text + Computed by forwarding to @ TT "isQuasiIsomorphism(toComplexMap phi)" @ + from the Complexes package. The underlying check inspects + @ TT "H_n" @ of the induced chain map at every degree in the + concentration of the source and target. The + @ TT "Concentration" @ option of the Complexes version is + accepted and passed through. + Example + R = QQ[x, y, z] + A = koszulComplexDGA R + phi = identityDGAlgebraMap A + isQuasiIsomorphism phi + Text + The identity DG algebra map is trivially a quasi-isomorphism. + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + (isQuasiIsomorphism, DGModuleMap) + identityDGAlgebraMap + toComplexMap +/// + +doc /// + Key + (annihilator, DGSubmodule) + Headline + The DG ideal of A annihilating a DG submodule + Usage + I = annihilator S + Inputs + S:DGSubmodule + Outputs + I:DGIdeal + The DG ideal of @ TT "S.ambient.dgAlgebra" @ whose underlying + ideal is the annihilator of @ TT "image S.inclusion.natural" @ + as a submodule of @ TT "S.ambient.natural" @. + Description + Text + Computes @ TT "annihilator(image S.inclusion.natural)" @ at the + @ TT "A.natural" @ level, then wraps the result as a DG ideal. + The result is a DG ideal: if @ TT "a" @ annihilates @ TT "S" @ + and @ TT "s \\in S" @, then + @ TT "d(a s) = d(a) s \\pm a d(s)" @ vanishes because both + terms vanish (the second since @ TT "d(s) \\in S" @), so + @ TT "d(a) s = 0" @ for every @ TT "s \\in S" @ and + @ TT "d(a)" @ is again in the annihilator. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + S = dgSubmodule(M, matrix {{x_Anat}}) + I = annihilator S + isWellDefined I + Text + The annihilator of the zero submodule is the unit ideal. + Example + S0 = dgSubmodule(M, map(M.natural, (Anat)^0, 0)) + annihilator S0 == dgIdeal(A, {1_Anat}) + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + (annihilator, DGQuotientModule) + DGIdeal + dgIdeal + DGSubmodule +/// + +doc /// + Key + (annihilator, DGQuotientModule) + Headline + The DG ideal of A annihilating a DG quotient module + Usage + I = annihilator Q + Inputs + Q:DGQuotientModule + Outputs + I:DGIdeal + The DG ideal of @ TT "Q.dgAlgebra" @ whose underlying ideal is + the annihilator of @ TT "Q.natural" @ (a cokernel presentation) + as an @ TT "A.natural" @-module. + Description + Text + Computes @ TT "annihilator Q.natural" @ and wraps it as a DG + ideal. As with @ TO (annihilator, DGSubmodule) @ the result is + automatically d-closed: the argument showing closure uses + Leibniz together with the fact that @ TT "d" @ on the ambient + descends to @ TT "d" @ on the quotient. + Example + R = ZZ/101[x, y] + A = koszulComplexDGA R + Anat = A.natural + M = freeDGModule(A, {0}) + S = dgSubmodule(M, matrix {{x_Anat}}) + Q = M / S + I = annihilator Q + isWellDefined I + isSubset(dgIdeal(A, {x_Anat}), I) + SeeAlso + "Well-definedness, acyclicity, and quasi-isomorphism" + (annihilator, DGSubmodule) + DGIdeal + dgQuotientModule + DGQuotientModule +/// + +doc /// + Key + "Semifree resolutions of DG modules" + Headline + Building semifree DG module resolutions via koszulComplexDGM, adjoinGenerators, killCycles, and semifreeResolution + Description + Text + A @ TO DGModule @ @ TT "F" @ over a @ TO DGAlgebra @ @ TT "A" @ is + @ EM "semifree" @ if its underlying graded @ TT "A.natural" @-module + is free with a homogeneous basis. Semifree DG modules play the + role of free modules in ordinary homological algebra: every + @ TT "A" @-module (or @ TT "A.ring" @-module lifted along the + augmentation @ TT "A -> A.ring" @) admits a semifree resolution, + unique up to DG module homotopy. + Text + The package provides four building blocks for assembling semifree + resolutions by hand or automatically: + Text + @ TO koszulComplexDGM @ constructs the Koszul DG module + @ TT "K^R \\otimes_R M" @ as a DG module over + @ TT "koszulComplexDGA(ring M)" @. On a regular ring this is + already a free resolution of @ TT "M" @; over a singular ring it + provides a starting point that needs further generators adjoined + to kill higher homology. + Text + @ TO adjoinGenerators @ takes a free DG module @ TT "M" @ and a + list of cycles in @ TT "M.natural" @ and returns a new free DG + module with one additional generator per cycle, whose differential + is that cycle. This is the module-theoretic analog of + @ TO adjoinVariables @ on a DG algebra. + Text + @ TO (killCycles, DGModule) @ scans for the smallest hom-degree + @ TT "n" @ in the requested range at which @ TT "H_n(M)" @ is + nonzero, then adjoins one new hom-degree-@ TT "(n+1)" @ generator + per minimal homology class. Iterating this from + @ TT "StartDegree" @ up to @ TT "EndDegree" @ produces a DG module + with vanishing homology in that range. + Text + @ TO semifreeResolution @ automates the full construction: build a + free cover of @ TT "M" @, adjoin hom-degree-1 generators for the + relations of @ TT "M" @, then iterate + @ TO (killCycles, DGModule) @. Its refined cousin + @ TO minimalSemifreeResolution @ applies + @ TO getBoundaryPreimage @ at each stage to subtract off + boundaries before adjoining, producing a resolution that is + minimal over @ TT "A" @ in the sense that every generator's + differential lands in the augmentation ideal of + @ TT "A.natural" @. The predicate + @ TO isMinimalSemifreeResolution @ tests this minimality + condition. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 3) + isMinimalSemifreeResolution Mdg + apply(0..3, n -> prune homology(n, Mdg)) + Text + Over a complete intersection of codimension two, the rank of + @ TT "F_n" @ in the minimal semifree resolution of the residue + field matches the @ TT "n" @-th Betti number @ TT "n+1" @, as + expected from the Tate construction. + Subnodes + koszulComplexDGM + adjoinGenerators + (killCycles, DGModule) + semifreeResolution + [semifreeResolution, StartDegree] + [semifreeResolution, EndDegree] + minimalSemifreeResolution + [minimalSemifreeResolution, StartDegree] + [minimalSemifreeResolution, EndDegree] + isMinimalSemifreeResolution + SeeAlso + acyclicClosure + killCycles + adjoinVariables + getBoundaryPreimage +/// + +doc /// + Key + koszulComplexDGM + (koszulComplexDGM, Module) + (koszulComplexDGM, DGAlgebra, Module) + Headline + The Koszul complex of a module as a DG module + Usage + KM = koszulComplexDGM M + KM = koszulComplexDGM(A, M) + Inputs + A:DGAlgebra + A DG algebra over @ TT "R = ring M" @. If omitted, + @ TT "koszulComplexDGA(ring M)" @ is used. + M:Module + A module over a polynomial or quotient ring @ TT "R" @. + Outputs + KM:DGModule + The Koszul DG module @ TT "K^R \\otimes_R M" @ viewed as a DG + module over @ TT "A" @. + Description + Text + The module generators of @ TT "KM" @ are one copy of each + generator of @ TT "M" @, placed in hom-degree @ TT "0" @. The + differential vanishes on these generators, so the full + differential of @ TT "KM" @ is determined by the Leibniz rule + together with the differential on @ TT "A.natural" @. In the + one-argument form the DG algebra is built on demand via + @ TT "koszulComplexDGA(ring M)" @; in the two-argument form the + caller supplies @ TT "A" @, whose @ TT "ring" @ must equal + @ TT "ring M" @. + Text + On a regular ring @ TT "R" @, @ TT "K^R" @ resolves the residue + field @ TT "k" @, so @ TT "koszulComplexDGM M" @ resolves @ TT "M" @ + via @ TT "k \\otimes_R M" @-style homology. In particular + @ TT "koszulComplexDGM R^1" @ recovers the Koszul complex of + @ TT "R" @ itself. + Text + Over a regular ring, the Koszul DG module on @ TT "R^1" @ is a + free resolution of the residue field, so its higher homology + vanishes: + Example + R = QQ[x, y, z] + KM = koszulComplexDGM R^1 + apply(0..3, n -> prune homology(n, KM)) + Text + Over a complete intersection, the higher homology of + @ TT "koszulComplexDGM R^1" @ is no longer zero — it is the Koszul + homology algebra of @ TT "R" @, which records the deviations of + @ TT "R" @. For the codimension-2 complete intersection + @ TT "k[x,y]/(x^2, y^2)" @, @ TT "HH_i(KM)" @ has rank equal to the + @ TT "i" @-th Betti number of the residue field: + Example + S = QQ[x, y] / ideal(x^2, y^2) + KS = koszulComplexDGM S^1 + apply(0..3, n -> numgens prune homology(n, KS)) + Text + Applying @ TO koszulComplexDGM @ to a nontrivial quotient module + produces its Koszul resolution, now tensored against the given + module. Here we resolve @ TT "S / (x)" @ over @ TT "S = k[x, y]" @ + and read off its free ranks: + Example + S = QQ[x, y] + KQ = koszulComplexDGM(S^1 / ideal(x)) + C = toComplex KQ + apply(0..2, n -> rank C_n) + apply(0..2, n -> prune homology(n, KQ)) + Text + In the two-argument form the caller-supplied DG algebra must share + the base ring of the module. This is the usual way to build a + Koszul DG module that lives over the @ TO DGAlgebra @ you will pass + to @ TO semifreeResolution @ or @ TO minimalSemifreeResolution @: + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 3) + apply(0..3, n -> rank (toComplex Mdg)_n) + Caveat + The two-argument form raises an error when + @ TT "A.ring =!= ring M" @. + SeeAlso + "Semifree resolutions of DG modules" + koszulComplexDGA + DGModule + freeDGModule + semifreeResolution + minimalSemifreeResolution +/// + +doc /// + Key + adjoinGenerators + (adjoinGenerators, DGModule, List) + Headline + Adjoin new free generators to a DG module with prescribed differentials + Usage + Mnew = adjoinGenerators(M, cycleList) + Inputs + M:DGModule + A free DG module (built by @ TO freeDGModule @ or an earlier call + to @ TO adjoinGenerators @). + cycleList:List + A list of Vectors in @ TT "M.natural" @; each entry should be a + cycle under the differential of @ TT "M" @, homogeneous with + respect to the ambient multi-grading. + Outputs + Mnew:DGModule + A free DG module whose generator list is @ TT "M.Degrees" @ + followed by one new generator per cycle in @ TT "cycleList" @, + shifted up by one hom-degree, with differential equal to the + corresponding cycle. + Description + Text + This is the module-theoretic analog of @ TO adjoinVariables @ on a + DG algebra. For each @ TT "z" @ in @ TT "cycleList" @ of + hom-degree @ TT "d" @, the output has a fresh generator in + hom-degree @ TT "d + 1" @ whose differential is @ TT "z" @. If + @ TT "z" @ is indeed a cycle then the new generator witnesses its + homology class as a boundary, so homology classes supported on + @ TT "cycleList" @ are killed. + Text + Existing generator indices and their differentials are preserved + verbatim: the first @ TT "#M.Degrees" @ generators of + @ TT "Mnew" @ correspond to those of @ TT "M" @, so code that + references @ TT "(M.natural)_i" @ for old indices @ TT "i" @ + continues to work when lifted to @ TT "Mnew" @. + Example + R = QQ[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + Mnew = adjoinGenerators(M, {x * natGens#0}) + #Mnew.Degrees + first Mnew.Degrees#1 + Text + The new generator sits in hom-degree @ TT "1" @ and its + differential is the cycle @ TT "x * e_0" @ in @ TT "M.natural" @. + Caveat + Only supported when @ TT "M.natural" @ is a free + @ TT "A.natural" @-module (i.e. @ TT "M" @ was built by + @ TO freeDGModule @ or a previous @ TO adjoinGenerators @ call). + Each entry of @ TT "cycleList" @ must be a cycle; otherwise the + output differential will not satisfy @ TT "d^2 = 0" @. + SeeAlso + "Semifree resolutions of DG modules" + freeDGModule + (killCycles, DGModule) + adjoinVariables +/// + +doc /// + Key + (killCycles, DGModule) + [(killCycles, DGModule), StartDegree] + [(killCycles, DGModule), EndDegree] + Headline + Adjoin free generators to kill the lowest nonvanishing homology of a DG module + Usage + Mnew = killCycles M + Mnew = killCycles(M, StartDegree => s, EndDegree => e) + Inputs + M:DGModule + A free DG module. + StartDegree => ZZ + The smallest hom-degree at which to look for nontrivial homology. + Defaults to @ TT "1" @. + EndDegree => ZZ + The largest hom-degree at which to look; defaults to + @ TT "StartDegree" @ (so by default a single degree is scanned). + Outputs + Mnew:DGModule + Either @ TT "M" @ itself (if homology vanishes throughout the + requested range) or a free DG module with additional hom-degree + @ TT "n+1" @ generators, where @ TT "n" @ is the smallest degree + in the range with @ TT "H_n(M) != 0" @; the new generators' + differentials are representative cycles of a minimal generating + set for @ TT "H_n(M)" @. + Description + Text + The construction mirrors @ TO (killCycles, DGAlgebra) @ for DG + modules. Scanning from @ TT "StartDegree" @ upwards, the method + computes @ TT "prune homology(n, M)" @ until it finds the first + degree @ TT "n" @ with nontrivial homology. It then lifts the + minimal homology generators provided by the pruning map back to + cycles in @ TT "M_n" @ and hands them to @ TO adjoinGenerators @, + producing a new DG module in which those classes become + boundaries. + Text + A single call kills homology at one degree. Iterating with + successive @ TT "StartDegree" @ values is how + @ TO semifreeResolution @ and + @ TO minimalSemifreeResolution @ build resolutions. + Example + R = QQ[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + M2 = adjoinGenerators(M, {x * natGens#0}) + prune homology(0, M2) + M3 = killCycles(M2, StartDegree => 1, EndDegree => 2) + prune homology(1, M3) == 0 + Text + @ TT "M2" @ has @ TT "H_0 = R/(x) = k" @ and no higher homology + to kill, so @ TT "M3" @ agrees with @ TT "M2" @ in this example. + In settings where @ TT "H_1" @ is nontrivial, @ TT "killCycles" @ + adjoins one hom-degree-2 generator per minimal @ TT "H_1" @ + class. + Caveat + Raises an error when @ TT "StartDegree > EndDegree" @. The input + must be a free DG module for the underlying homology computation + to extract representative cycles. + SeeAlso + "Semifree resolutions of DG modules" + adjoinGenerators + (killCycles, DGAlgebra) + semifreeResolution + minimalSemifreeResolution + StartDegree + EndDegree +/// + +doc /// + Key + semifreeResolution + (semifreeResolution, DGAlgebra, Module) + (semifreeResolution, Module) + Headline + Build a semifree DG module resolving a module over the base ring + Usage + F = semifreeResolution M + F = semifreeResolution(A, M) + F = semifreeResolution(A, M, EndDegree => n) + Inputs + A:DGAlgebra + A DG algebra over @ TT "R = ring M" @. If omitted, + @ TT "koszulComplexDGA(ring M)" @ is used. + M:Module + A module over @ TT "A.ring" @. + StartDegree => ZZ + First hom-degree at which to start killing homology. Defaults + to @ TT "1" @. + EndDegree => ZZ + Last hom-degree at which to kill homology. Defaults to + @ TT "3" @. Homology of @ TT "F" @ in the range + @ TT "[StartDegree, EndDegree]" @ will vanish. + Outputs + F:DGModule + A free DG module over @ TT "A" @ with + @ TT "H_0(F) = M" @ and @ TT "H_n(F) = 0" @ for + @ TT "StartDegree <= n <= EndDegree" @. + Description + Text + Starts from a free DG module with one hom-degree-0 generator per + generator of @ TT "M" @, adjoins hom-degree-1 generators whose + differentials are the columns of @ TT "presentation M" @ lifted + to @ TT "A.natural" @ (killing the presentation relations), and + then iterates @ TO (killCycles, DGModule) @ from + @ TT "StartDegree" @ through @ TT "EndDegree" @. + Text + This is the module-theoretic analog of + @ TO acyclicClosure @ on a DG algebra. The output is a semifree + DG module but is not guaranteed to be minimal over @ TT "A" @: + relation columns are adjoined even when they are already + boundaries under the DG algebra differential. For a minimal + resolution, use @ TO minimalSemifreeResolution @. + Example + R = QQ[x] + M = R^1 / ideal(x) + Mdg = semifreeResolution(koszulComplexDGA R, M, EndDegree => 3) + prune homology(0, Mdg) + all(1..3, n -> prune homology(n, Mdg) == 0) + Text + Over a complete intersection, the resolution is infinite but the + truncation up to any requested @ TT "EndDegree" @ is acyclic in + positive hom-degrees up to that bound. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + Mdg = semifreeResolution(koszulComplexDGA R, k, EndDegree => 3) + all(1..3, i -> prune homology(i, Mdg) == 0) + Text + The @ TT "Module" @-only form infers the DG algebra from + @ TT "ring M" @: + Example + R = QQ[x] + M = R^1 / ideal(x) + Mdg = semifreeResolution(M, EndDegree => 2) + Mdg.ring === R + Mdg.dgAlgebra.ring === R + Caveat + The output is not minimal in general; use + @ TO minimalSemifreeResolution @ when a minimal presentation is + required. The base ring of @ TT "M" @ must agree with + @ TT "A.ring" @ in the two-argument form. + SeeAlso + "Semifree resolutions of DG modules" + minimalSemifreeResolution + (killCycles, DGModule) + acyclicClosure + koszulComplexDGM +/// + +doc /// + Key + [semifreeResolution, StartDegree] + Headline + Option to specify the degree at which semifreeResolution starts killing cycles + Usage + semifreeResolution(..., StartDegree => n) + Description + Text + Controls the first hom-degree at which + @ TO semifreeResolution @ attempts to kill homology. + Hom-degrees strictly below @ TT "StartDegree" @ are populated + only by the initial free cover and the adjoinment of + presentation relations at hom-degree @ TT "1" @. The default + value is @ TT "1" @. + SeeAlso + semifreeResolution + [semifreeResolution, EndDegree] +/// + +doc /// + Key + [semifreeResolution, EndDegree] + Headline + Option to specify the last degree at which semifreeResolution kills cycles + Usage + semifreeResolution(..., EndDegree => n) + Description + Text + Controls the last hom-degree at which + @ TO semifreeResolution @ iterates + @ TO (killCycles, DGModule) @. Homology of the output in the + range @ TT "[StartDegree, EndDegree]" @ vanishes; higher degrees + are not resolved. The default value is @ TT "3" @. + SeeAlso + semifreeResolution + [semifreeResolution, StartDegree] +/// + +doc /// + Key + minimalSemifreeResolution + (minimalSemifreeResolution, DGAlgebra, Module) + (minimalSemifreeResolution, Module) + Headline + Build a minimal semifree DG module resolution over a DG algebra + Usage + F = minimalSemifreeResolution M + F = minimalSemifreeResolution(A, M) + F = minimalSemifreeResolution(A, M, EndDegree => n) + Inputs + A:DGAlgebra + A DG algebra over a (graded-)local ring @ TT "R = ring M" @. If + omitted, @ TT "koszulComplexDGA(ring M)" @ is used. + M:Module + A module over @ TT "A.ring" @. + StartDegree => ZZ + First hom-degree at which to start killing homology. Defaults + to @ TT "1" @. + EndDegree => ZZ + Last hom-degree at which to kill homology. Defaults to + @ TT "3" @. + Outputs + F:DGModule + A semifree DG module resolving @ TT "M" @ over @ TT "A" @, + minimal in the sense that every generator's differential lands + in the augmentation ideal of @ TT "A.natural" @. + Description + Text + Produces a semifree resolution whose generator count in each + hom-degree is minimal among all semifree resolutions of + @ TT "M" @ over @ TT "A" @. Two ingredients make the output + minimal: + Text + First, @ TT "M" @ is replaced by @ TT "prune M" @, giving a + minimal presentation over the (graded-)local base ring. The + number of hom-degree-0 generators is therefore the minimal + number of generators of @ TT "M" @ over @ TT "R" @. + Text + Second, each column of @ TT "presentation(prune M)" @ is tested + with @ TO getBoundaryPreimage @ against the DG-algebra-induced + differential. If a relation is already a boundary, it is not + adjoined; if only a residue is a non-boundary, the adjoined + generator's differential is that residue rather than the + original relation. This is what makes the output minimal + @ EM "over A" @, not merely over @ TT "R" @. + Text + The iterative @ TO (killCycles, DGModule) @ passes then use the + canonical pruning map of @ TT "homology(n, F)" @ to pick a + minimal generating set of representative cycles at each stage. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 3) + isMinimalSemifreeResolution Mdg + all(1..3, i -> prune homology(i, Mdg) == 0) + Text + Over the c.i. @ TT "R = k[x, y]/(x^2, y^2)" @ the minimal + semifree resolution of @ TT "k" @ has @ TT "rank F_n = n+1" @, + matching the Betti numbers of @ TT "k" @ over @ TT "R" @. + Example + apply(0..3, n -> numcols moduleDifferential(n, Mdg)) + Text + As with @ TO semifreeResolution @, the @ TT "Module" @-only + form infers @ TT "koszulComplexDGA(ring M)" @ as the DG + algebra: + Example + R = QQ[x] + M = R^1 / ideal(x) + Mdg = minimalSemifreeResolution(M, EndDegree => 2) + Mdg.ring === R + isMinimalSemifreeResolution Mdg + Text + A more substantial example: take the Koszul DG algebra on a + regular sequence cutting out a complete intersection, and + resolve a module over the resulting DG algebra. Here + @ TT "A = koszulComplexDGA(I_*)" @ with + @ TT "I = (x^2, y^2)" @ is a DG algebra over + @ TT "Q = ZZ/101[x, y]" @ whose degree-zero homology is the + complete intersection @ TT "Q/I" @, and + @ TT "A" @ itself is the minimal DG algebra resolution of + @ TT "Q/I" @ over @ TT "Q" @. We resolve the + @ TT "Q/I" @-module @ TT "M = Q/(x^2, x*y, y^2)" @ as a DG + @ TT "A" @-module: + Example + Q = ZZ/101[x, y] + I = ideal(x^2, y^2) + A = koszulComplexDGA(I_*) + M = Q^1 / ideal(x^2, x*y, y^2) + Mdg = minimalSemifreeResolution(A, M, EndDegree => 5) + isMinimalSemifreeResolution Mdg + all(1..5, i -> prune homology(i, Mdg) == 0) + apply(0..5, n -> numcols moduleDifferential(n, Mdg)) + Text + @ BOLD "Matrix factorizations on hypersurfaces." @ Over a + hypersurface @ TT "R = Q/(f)" @ every maximal Cohen-Macaulay + module's free resolution is eventually @ EM "2-periodic" @ + with differentials forming a matrix factorization + @ TT "(phi, psi)" @ of @ TT "f" @ (Eisenbud). The minimal + semifree resolution over @ TT "A = koszulComplexDGA(ideal f)" @ + exhibits this periodicity directly in its printed + differentials. Here is the classic example @ TT "f = x^5" @: + Example + Q = ZZ/101[x] + A = koszulComplexDGA(ideal(x^5)) + Mdg = minimalSemifreeResolution(A, Q^1/ideal(x), EndDegree => 6) + apply(1..6, n -> flatten entries moduleDifferential(n, Mdg)) + Text + The 2x2 blocks alternate between an @ TT "x" @-pattern and an + @ TT "x^4" @-pattern; their product is @ TT "x^5" @ times an + identity matrix, which is exactly the matrix factorization + @ TT "x * x^4 = x^5" @. Similarly, for the quadric form + @ TT "f = x^2 + y^2" @ the module + @ TT "coker [[x, y], [y, -x]]" @ is already a matrix + factorization of @ TT "f" @, so its minimal semifree + resolution stabilizes in hom-degree one: + Example + Q = ZZ/101[x, y] + A = koszulComplexDGA(ideal(x^2 + y^2)) + M = coker matrix{{x, y}, {y, -x}} + Mdg = minimalSemifreeResolution(A, M, EndDegree => 6) + apply(0..6, n -> numcols moduleDifferential(n, Mdg)) + Text + For the three-variable quadric @ TT "f = x^2 + y^2 + z^2" @, + resolving the residue field stabilizes at rank @ TT "8" @, + reflecting the 8-dimensional Clifford algebra of a + nondegenerate ternary quadratic form: + Example + Q = ZZ/101[x, y, z] + A = koszulComplexDGA(ideal(x^2 + y^2 + z^2)) + Mdg = minimalSemifreeResolution(A, Q^1/ideal(x, y, z), EndDegree => 6) + apply(0..6, n -> numcols moduleDifferential(n, Mdg)) + Caveat + Minimality relies on @ TT "A.ring" @ being (graded-)local so that + @ TT "prune" @ returns a minimal presentation. For + @ TT "A = koszulComplexDGA R" @ with @ TT "R" @ a standard graded + quotient of a polynomial ring over a field, this is satisfied. + SeeAlso + "Semifree resolutions of DG modules" + semifreeResolution + isMinimalSemifreeResolution + getBoundaryPreimage + (killCycles, DGModule) +/// + +doc /// + Key + [minimalSemifreeResolution, StartDegree] + Headline + Option to specify the degree at which minimalSemifreeResolution starts killing cycles + Usage + minimalSemifreeResolution(..., StartDegree => n) + Description + Text + Controls the first hom-degree at which + @ TO minimalSemifreeResolution @ attempts to kill homology. + The default is @ TT "1" @. + SeeAlso + minimalSemifreeResolution + [minimalSemifreeResolution, EndDegree] +/// + +doc /// + Key + [minimalSemifreeResolution, EndDegree] + Headline + Option to specify the last degree at which minimalSemifreeResolution kills cycles + Usage + minimalSemifreeResolution(..., EndDegree => n) + Description + Text + Controls the last hom-degree at which + @ TO minimalSemifreeResolution @ iterates + @ TO (killCycles, DGModule) @. Homology of the output in the + range @ TT "[StartDegree, EndDegree]" @ vanishes. The default + is @ TT "3" @. + SeeAlso + minimalSemifreeResolution + [minimalSemifreeResolution, StartDegree] +/// + +doc /// + Key + isMinimalSemifreeResolution + (isMinimalSemifreeResolution, DGModule) + Headline + Test whether a semifree DG module is minimal over its DG algebra + Usage + b = isMinimalSemifreeResolution M + Inputs + M:DGModule + A free DG module (the intended use is on the output of + @ TO semifreeResolution @ or + @ TO minimalSemifreeResolution @). + Outputs + b:Boolean + @ TT "true" @ when each generator's differential lies in the + augmentation ideal of @ TT "A.natural" @, i.e. its reduction + along @ TT "A -> k" @ (sending @ TT "T_i -> 0" @ and then mod + @ TT "(vars R)" @) is zero. + Description + Text + Minimality over a DG algebra @ TT "A" @ means: after base change + along the augmentation @ TT "A -> k" @, every differential of + @ TT "F" @ becomes the zero map. Equivalently, each + @ TT "M.diff#i" @ has all of its coefficients (in the free + basis of @ TT "M.natural" @) lying in the augmentation ideal of + @ TT "A.natural" @ generated by @ TT "(vars R)" @ and the + positive-hom-degree generators @ TT "T_i" @. + Text + This predicate checks only the structural minimality condition; + it does not verify acyclicity. Use + @ TO (isAcyclic, DGModule) @ with an @ TT "EndDegree" @ option + for that. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 3) + isMinimalSemifreeResolution Mdg + Text + A DG module built with a unit differential between free + generators is not minimal: + Example + R = QQ[x] + A = koszulComplexDGA R + Mbad = freeDGModule(A, {0, 1}) + natGens = apply(rank Mbad.natural, i -> (Mbad.natural)_i) + setDiff(Mbad, {0, natGens#0}) + isMinimalSemifreeResolution Mbad + Caveat + Returns @ TT "false" @ on DG modules whose underlying + @ TT "A.natural" @-module is not free (in that case the + generator differentials are not the right data to test). + SeeAlso + "Semifree resolutions of DG modules" + minimalSemifreeResolution + semifreeResolution + (isAcyclic, DGModule) +/// + +doc /// + Key + "Computing module differentials and visualizing DG modules" + Headline + Differential matrices, element-wise differentials, and visualization helpers for DG modules + Description + Text + Once a @ TO DGModule @ @ TT "M" @ over a @ TO DGAlgebra @ + @ TT "A" @ is built, the package supports two levels of access + to the differential. + Text + The @ EM "matrix-level" @ view is @ TO moduleDifferential @: the + call @ TT "moduleDifferential(n, M)" @ returns the hom-degree-@ TT "n" @ + piece of @ TT "d_M" @ as a homogeneous matrix over + @ TT "A.ring" @ whose source and target are the free + @ TT "A.ring" @-modules on the respective monomial bases of + @ TT "M_n" @ and @ TT "M_{n-1}" @. The block-structured variant + @ TO moduleBlockDiff @ partitions this matrix into one summand + per @ TT "(i, v)" @ label, where @ TT "i" @ indexes a generator + of @ TT "M.natural" @ and @ TT "v" @ is a chunk-degree vector + on the variables of @ TT "A.natural" @. The pretty-printer + @ TO displayModuleBlockDiff @ renders this labeled block matrix + with one label per row and column. + Text + The @ EM "element-level" @ view is @ TT "diff(M, v)" @: apply the + differential directly to a homogeneous element + @ TT "v" @ of @ TT "M.natural" @ via the Leibniz rule, returning + a Vector in @ TT "M.natural" @ of hom-degree one less. This is + the entry point used internally by chain-map checks and by + @ TO (homologyClass, DGModule, Vector) @. + Text + Two lightweight inspection helpers summarize the generator + layout: @ TO generatorTable @ prints a one-row-per-generator + table with hom-degree, external degree, and differential; and + @ TO dgModuleSummary @ tabulates, for each hom-degree in a + requested range, the number of freshly adjoined generators at + that degree and the rank of @ TT "F_n" @ as an + @ TT "A.ring" @-module. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 2) + d2 = moduleDifferential(2, Mdg) + d1 = moduleDifferential(1, Mdg) + d1 * d2 == 0 + Text + Over a complete intersection, the semifree resolution of the + residue field has well-defined differentials matching + @ TT "d^2 = 0" @ in every degree. + Subnodes + moduleDifferential + (diff, DGModule, Vector) + (diff, DGQuotientModule, Vector) + generatorTable + dgModuleSummary + moduleBlockDiff + displayModuleBlockDiff + SeeAlso + "Semifree resolutions of DG modules" + (homology, ZZ, DGModule) +/// + +doc /// + Key + moduleDifferential + (moduleDifferential, ZZ, DGModule) + Headline + The hom-degree-n differential of a DG module as a matrix over the base ring + Usage + dn = moduleDifferential(n, M) + Inputs + n:ZZ + A hom-degree. + M:DGModule + A DG module over a DG algebra @ TT "A" @; must be free (as in + @ TO freeDGModule @ output) or in @ TO koszulComplexDGM @ + form. + Outputs + dn:Matrix + The component @ TT "d_n : F_n -> F_{n-1}" @ of the + differential of @ TT "M" @, as a homogeneous matrix over + @ TT "A.ring" @. Columns are indexed by the pairs + @ TT "(i, m)" @ of @ TT "moduleBasisPairs(M, n)" @; rows by + the pairs of @ TT "moduleBasisPairs(M, n-1)" @. + Description + Text + Two code paths are used internally: + Text + When @ TT "M" @ comes from @ TO koszulComplexDGM @ (so + @ TT "M.module" @ is set) and every generator differential is + zero, @ TT "d_n" @ is the tensor product + @ TT "polyDifferential(n, A) \\otimes id_{M.module}" @. This + is the fast path. + Text + For general free DG modules, @ TT "d_n" @ is computed + monomial-by-monomial via the Leibniz rule, extracting + @ TT "A.ring" @-linear coefficients in the target basis. + Results are cached per hom-degree in + @ TT "M.cache.diffs" @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 2) + d1 = moduleDifferential(1, Mdg) + d2 = moduleDifferential(2, Mdg) + d1 * d2 == 0 + Text + Every column of @ TT "d_{n-1} d_n" @ is zero, which is + the @ TT "d^2 = 0" @ condition. + Caveat + Raises an error on non-free DG modules (for example, the + natural module of a @ TO DGQuotientModule @). In that setting, + compute the differential at the element level via + @ TT "diff(Q, v)" @ or work with @ TO dgComplex @. + SeeAlso + "Computing module differentials and visualizing DG modules" + (diff, DGModule, Vector) + moduleBlockDiff + polyDifferential + (homology, ZZ, DGModule) +/// + +doc /// + Key + (diff, DGModule, Vector) + Headline + Apply the DG module differential to an element + Usage + dv = diff(M, v) + Inputs + M:DGModule + A DG module. + v:Vector + A homogeneous element of @ TT "M.natural" @. + Outputs + dv:Vector + The image @ TT "d_M(v)" @ in @ TT "M.natural" @, computed via + the Leibniz rule. + Description + Text + Applies the Leibniz rule coordinate by coordinate. If + @ TT "v = sum_i f_i \\cdot e_i" @ where @ TT "e_i" @ are the + free generators of @ TT "M.natural" @ and + @ TT "f_i \\in A.natural" @, then + @ TT "d_M(v) = sum_i (d_A(f_i) \\cdot e_i + (-1)^{|f_i|} f_i \\cdot d_M(e_i))" @ + where @ TT "|f_i|" @ is the hom-degree of @ TT "f_i" @. + Text + This element-level interface is the entry point for checking + the cycle condition on a candidate homology class and for + defining @ TT "DGModuleMap" @ objects without committing to the + matrix-level bookkeeping of + @ TO moduleDifferential @. + Example + R = QQ[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + diff(M, natGens#1) + diff(M, natGens#0) + Text + Here @ TT "d(e_1) = x \\cdot e_0" @ and @ TT "d(e_0) = 0" @. + SeeAlso + "Computing module differentials and visualizing DG modules" + moduleDifferential + (diff, DGQuotientModule, Vector) + (diff, DGAlgebra, RingElement) +/// + +doc /// + Key + (diff, DGQuotientModule, Vector) + Headline + Apply the induced differential on a DG quotient module to an element + Usage + dv = diff(Q, v) + Inputs + Q:DGQuotientModule + A DG quotient module @ TT "Q = M / S" @. + v:Vector + A homogeneous element of @ TT "Q.natural" @. + Outputs + dv:Vector + The image @ TT "d_Q(v)" @ in @ TT "Q.natural" @. + Description + Text + Because the inclusion of @ TT "S" @ into @ TT "M" @ is a DG + submodule (its columns are d-closed in @ TT "M" @), the + differential of @ TT "M" @ descends to @ TT "Q" @. This method + applies the Leibniz rule to a representative in + @ TT "Q.natural" @ without going through a matrix-level + differential, which is convenient because @ TT "Q.natural" @ is + a cokernel rather than a free module. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + S = dgSubmodule(M, (id_(M.natural))_{1}) + Q = M / S + projection Q + -- apply d to a natural-module generator of Q + diff(Q, (Q.natural)_0) + SeeAlso + "Computing module differentials and visualizing DG modules" + (diff, DGModule, Vector) + DGQuotientModule + dgQuotientModule +/// + +doc /// + Key + generatorTable + (generatorTable, DGModule) + Headline + Display the generator list of a DG module with hom-degrees and differentials + Usage + t = generatorTable M + Inputs + M:DGModule + Outputs + t:Net + A centered table with one row per generator. Columns are the + generator index @ TT "i" @, the hom-degree, the remaining + external degree, and the differential + @ TT "M.diff#i" @. + Description + Text + A convenient overview of the generator structure of a + semifree DG module. Particularly useful for inspecting the + output of @ TO minimalSemifreeResolution @ or + @ TO semifreeResolution @, where each iteration of + @ TO (killCycles, DGModule) @ appends further generators. + Example + R = QQ[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + instance(generatorTable M, Net) + Text + When @ TT "M" @ has no generators, the result is a single-row + placeholder. + SeeAlso + "Computing module differentials and visualizing DG modules" + dgModuleSummary + moduleBlockDiff +/// + +doc /// + Key + dgModuleSummary + (dgModuleSummary, DGModule) + (dgModuleSummary, DGModule, ZZ) + Headline + Tabulate hom-degree-wise generator counts and free-rank counts for a DG module + Usage + t = dgModuleSummary M + t = dgModuleSummary(M, n) + Inputs + M:DGModule + A free DG module (for example from @ TO freeDGModule @ or + @ TO minimalSemifreeResolution @). + n:ZZ + Upper bound on the hom-degree range to display. When omitted, + @ TT "maxDegree M" @ is used; if that is @ TT "infinity" @ the + method raises an error asking for an explicit bound. + Outputs + t:Net + A centered table with one row per hom-degree in @ TT "0..n" @. + Columns are the hom-degree @ TT "k" @, the number of + generators of @ TT "M" @ placed at hom-degree @ TT "k" @ (i.e. + adjoined in that degree), and the rank of @ TT "F_k" @ as an + @ TT "A.ring" @-module. + Description + Text + A one-call overview of how the generators of @ TT "M" @ + distribute across hom-degrees and how that translates into + base-ring ranks. Matches the data fed into + @ TO moduleDifferential @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 3) + instance(dgModuleSummary(Mdg, 3), Net) + Caveat + The one-argument form is only applicable when the underlying + hom-grading of @ TT "M" @ is bounded (for example when + @ TT "M" @ is a @ TO koszulComplexDGM @ output over a polynomial + ring). For arbitrary semifree DG modules, pass an explicit + upper bound. + SeeAlso + "Computing module differentials and visualizing DG modules" + generatorTable + moduleBlockDiff + moduleDifferential +/// + +doc /// + Key + moduleBlockDiff + (moduleBlockDiff, DGModule, ZZ) + Headline + The hom-degree-n differential of a DG module as a labeled block matrix + Usage + b = moduleBlockDiff(M, n) + Inputs + M:DGModule + A DG module over a DG algebra @ TT "A" @; must be free. + n:ZZ + A hom-degree. + Outputs + b:Matrix + The hom-degree-@ TT "n" @ differential of @ TT "M" @, assembled + as a map between direct sums indexed by pairs + @ TT "{i, v}" @ (generator index plus variable-chunk-degree + vector). + Description + Text + A block-structured refinement of @ TO moduleDifferential @. + The source of the resulting map is a direct sum of small free + @ TT "A.ring" @-modules, one per pair + @ TT "{i, v}" @, where @ TT "i" @ is the generator index of + @ TT "M.natural" @ and @ TT "v" @ is a tuple of exponents + giving the chunk-degrees of @ TT "A.natural" @-monomials + contributing to @ TT "F_n" @. Composing with + @ TT "matrix" @ recovers exactly + @ TT "moduleDifferential(n, M)" @. + Text + Results are cached in + @ TT "M.cache#\"moduleBlockDiffs\"" @ per @ TT "n" @, so + repeated calls are cheap. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 3) + b2 = moduleBlockDiff(Mdg, 2) + matrix b2 == moduleDifferential(2, Mdg) + -- Each source/target index is a {genIdx, chunkVec} pair. + srcIdx = indices source b2 + all(srcIdx, l -> #l == 2 and instance(l#0, ZZ) and instance(l#1, List)) + SeeAlso + "Computing module differentials and visualizing DG modules" + displayModuleBlockDiff + moduleDifferential +/// + +doc /// + Key + displayModuleBlockDiff + (displayModuleBlockDiff, DGModule, ZZ) + (displayModuleBlockDiff, DGModule, List, List) + Headline + Pretty-print the labeled block matrix of a DG module differential + Usage + tab = displayModuleBlockDiff(M, n) + entry = displayModuleBlockDiff(M, srcLbl, tgtLbl) + Inputs + M:DGModule + A DG module; must be free. + n:ZZ + A hom-degree. + srcLbl:List + A pair @ TT "{i, v}" @ identifying a single source block (a + generator index and a chunk-degree vector). + tgtLbl:List + A pair @ TT "{j, w}" @ identifying a single target block. + Outputs + tab:Net + A centered table whose first row lists source labels and whose + first column lists target labels; inner entries are the + corresponding blocks of @ TT "moduleBlockDiff(M, n)" @. + entry:Matrix + In the three-argument form, returns the single block + @ TT "b^[tgtLbl]_[srcLbl]" @ extracted from the labeled block + matrix. + Description + Text + The two-argument form lays out @ TO moduleBlockDiff @ as a + labeled table, parallel to @ TT "displayBlockDiff" @ for DG + algebras. The three-argument form bypasses the table and + returns a single block directly, which is handy for + programmatic inspection. + Example + R = QQ[x, y] / ideal(x^2, y^2) + k = R^1 / ideal(x, y) + A = koszulComplexDGA R + Mdg = minimalSemifreeResolution(A, k, EndDegree => 2) + instance(displayModuleBlockDiff(Mdg, 2), Net) + SeeAlso + "Computing module differentials and visualizing DG modules" + moduleBlockDiff + moduleDifferential +/// + +doc /// + Key + "Building DG algebras from existing DG algebras" + Headline + Base change, generator restriction, truncation, and targeted cycle-killing for DG algebras + Description + Text + Given an existing @ TO DGAlgebra @ @ TT "A" @, the package + supports several ways to derive new DG algebras from it. All of + these constructions preserve the graded-commutative and Leibniz + structure by design; most perform safety checks before returning. + Text + @ TO baseChange @ moves a DG algebra along a ring map on its + base: given @ TT "A" @ over @ TT "R" @ and a ring map + @ TT "phi: R -> S" @ (or simply a target ring @ TT "S" @, in + which case @ TT "map(S, R)" @ is used), @ TT "baseChange" @ + returns a DG algebra over @ TT "S" @ whose generator list and + differentials are obtained by transporting scalars along + @ TT "phi" @. + Text + @ TO subDGAlgebra @ produces a sub-DG algebra of @ TT "A" @ + generated by a chosen subset of the DG generators. The subset + must be d-closed: it is an error if the differential of a kept + generator uses a dropped one. + @ TO restrictDifferential @ is a synonym. + @ TO truncateGenerators @ is a convenience wrapper: + @ TT "truncateGenerators(A, n)" @ calls + @ TT "subDGAlgebra" @ with the generators of hom-degree + strictly greater than @ TT "n" @. + Text + @ TO killHomologyAtDegree @ targets a specific hom-degree + @ TT "n" @: it computes minimal representatives of + @ TT "H_n(A)" @ and adjoins variables in hom-degree + @ TT "n+1" @ whose differentials are those representatives, + producing a new DG algebra with @ TT "H_n = 0" @. + Text + All four constructions take optional arguments + @ TO InitializeDegreeZeroHomology @ and + @ TO InitializeComplex @ controlling how much of the + downstream cache is eagerly populated by the internal call to + @ TO setDiff @. + Text + Two low-level predicates round out the family: + @ TO isValidDGAlgebra @ performs a structural-invariant check + (keys and types), and + @ TO isWellDefinedDifferential @ performs the semantic + @ TT "d^2 = 0" @ check on every generator. Both are called + internally by @ TO (isWellDefined, DGAlgebra) @. + Example + R = QQ[x, y] + S = R / ideal(x^2, y^2) + A = koszulComplexDGA R + B = baseChange(A, S) + underlyingRing B === S + isWellDefinedDifferential B + Text + Moving a Koszul DG algebra from a polynomial ring to a + complete intersection factor preserves + @ TT "d^2 = 0" @ because the differential expressions + @ TT "d(T_i) = x_i" @ remain valid in @ TT "S" @. + Subnodes + baseChange + subDGAlgebra + restrictDifferential + truncateGenerators + killHomologyAtDegree + isValidDGAlgebra + isWellDefinedDifferential + SeeAlso + freeDGAlgebra + koszulComplexDGA + setDiff + (isWellDefined, DGAlgebra) + adjoinVariables + killCycles +/// + +doc /// + Key + baseChange + (baseChange, DGAlgebra, Ring) + (baseChange, DGAlgebra, RingMap) + [baseChange, InitializeDegreeZeroHomology] + [baseChange, InitializeComplex] + Headline + Transport a DG algebra along a ring map on its base + Usage + B = baseChange(A, S) + B = baseChange(A, phi) + Inputs + A:DGAlgebra + A DG algebra over some ring @ TT "R" @. + S:Ring + A target ring. Shorthand for @ TT "baseChange(A, map(S, R))" @. + phi:RingMap + A ring map with source @ TT "R = underlyingRing A" @; its target + becomes the base ring of @ TT "B" @. + InitializeDegreeZeroHomology => Boolean + Whether to compute @ TT "H_0" @ of the result eagerly. Default + @ TT "true" @. + InitializeComplex => Boolean + Whether to build the underlying complex of @ TT "B" @ + eagerly. Default @ TT "false" @. + Outputs + B:DGAlgebra + A DG algebra over @ TT "target phi" @ with the same generator + hom-degrees as @ TT "A" @, and whose differentials are the + images of @ TT "A.diff" @ under the obvious extension of + @ TT "phi" @ that sends each @ TT "A" @-generator to the + corresponding @ TT "B" @-generator. + Description + Text + Useful for lifting a Koszul DG algebra over a polynomial ring + to one over a factor ring, or for specialization (via a ring + map that kills some variables). + Example + R = QQ[x, y] + A = koszulComplexDGA R + S = R / ideal(x^2, y^2) + B = baseChange(A, S) + underlyingRing B === S + isWellDefinedDifferential B + Text + An explicit ring map gives more flexibility: + Example + R = QQ[x, y] + A = koszulComplexDGA R + phi = map(R, R, {x, 0}) + Bphi = baseChange(A, phi) + underlyingRing Bphi === R + Caveat + Raises an error if @ TT "source phi" @ is not equal to + @ TT "underlyingRing A" @. + SeeAlso + "Building DG algebras from existing DG algebras" + (symbol **, DGAlgebra, Ring) + InitializeDegreeZeroHomology + InitializeComplex +/// + +doc /// + Key + subDGAlgebra + (subDGAlgebra, DGAlgebra, List) + [subDGAlgebra, InitializeDegreeZeroHomology] + [subDGAlgebra, InitializeComplex] + Headline + The sub-DG algebra generated by a chosen subset of DG generators + Usage + B = subDGAlgebra(A, keepIdx) + Inputs + A:DGAlgebra + keepIdx:List + A list of integers giving the indices (into + @ TT "0 .. numgens A.natural - 1" @) of the DG generators to + keep. Duplicates and out-of-range indices raise an error. + InitializeDegreeZeroHomology => Boolean + Whether to compute @ TT "H_0" @ of the result eagerly. + Default @ TT "true" @. + InitializeComplex => Boolean + Whether to build the underlying complex eagerly. Default + @ TT "false" @. + Outputs + B:DGAlgebra + A DG algebra over @ TT "A.ring" @ whose DG generators are the + @ TT "keepIdx" @-indexed generators of @ TT "A" @ in sorted + order, with their differentials restricted accordingly. + Description + Text + The subset @ TT "keepIdx" @ must be d-closed: if the + differential of a kept generator uses a dropped generator, + @ TT "subDGAlgebra" @ raises an error rather than silently + returning a non-DG object. This protects downstream code + from accidentally producing inconsistent structures. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + B = subDGAlgebra(A, {0}) + numgens B.natural + Text + Dropping a generator whose image is referenced in a kept + generator's differential is caught: + Example + R = QQ[x] / ideal(x^2) + A = koszulComplexDGA R + A' = adjoinVariables(A, {x * (gens A.natural)#0}) + try subDGAlgebra(A', {1}) else "error: kept differential uses dropped generator" + Caveat + Duplicate or out-of-range entries in @ TT "keepIdx" @ raise + an error. @ TT "keepIdx" @ is internally sorted, so the + generator order of the output depends only on which indices + appear. + SeeAlso + "Building DG algebras from existing DG algebras" + restrictDifferential + truncateGenerators +/// + +doc /// + Key + restrictDifferential + (restrictDifferential, DGAlgebra, List) + [restrictDifferential, InitializeDegreeZeroHomology] + [restrictDifferential, InitializeComplex] + Headline + Synonym for subDGAlgebra, named to match the restricted-differential vocabulary + Usage + B = restrictDifferential(A, keepIdx) + Inputs + A:DGAlgebra + keepIdx:List + List of integers giving DG generator indices to keep. + InitializeDegreeZeroHomology => Boolean + Default @ TT "true" @. + InitializeComplex => Boolean + Default @ TT "false" @. + Outputs + B:DGAlgebra + Identical to @ TT "subDGAlgebra(A, keepIdx)" @. + Description + Text + Calls directly into @ TO subDGAlgebra @. The two are + interchangeable; @ TT "restrictDifferential" @ is provided so + that calling code emphasizing the restricted-differential + aspect reads more naturally. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + B = restrictDifferential(A, {0}) + numgens B.natural + SeeAlso + "Building DG algebras from existing DG algebras" + subDGAlgebra +/// + +doc /// + Key + truncateGenerators + (truncateGenerators, DGAlgebra, ZZ) + [truncateGenerators, InitializeDegreeZeroHomology] + [truncateGenerators, InitializeComplex] + Headline + Restrict a DG algebra to its generators of hom-degree strictly above a bound + Usage + B = truncateGenerators(A, n) + Inputs + A:DGAlgebra + n:ZZ + A hom-degree bound; generators of hom-degree @ TT "<= n" @ are + dropped. + InitializeDegreeZeroHomology => Boolean + Default @ TT "true" @. + InitializeComplex => Boolean + Default @ TT "false" @. + Outputs + B:DGAlgebra + The sub-DG algebra of @ TT "A" @ generated by the DG + generators whose first-hom-degree component is strictly + greater than @ TT "n" @. + Description + Text + Computes @ TT "keepIdx" @ as + @ TT "select(0 .. #A.Degrees - 1, i -> first A.Degrees#i > n)" @ + and delegates to @ TO subDGAlgebra @. As with + @ TO subDGAlgebra @, the subset must be d-closed; if the + differential of a generator @ TT "T_i" @ of hom-degree + @ TT "> n" @ uses a generator of hom-degree @ TT "<= n" @ that + would be dropped, an error is raised. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + B = truncateGenerators(A, 0) + numgens B.natural + Text + The Koszul algebra has all generators in hom-degree @ TT "1" @ + so truncating below degree @ TT "0" @ is a no-op in this + case. + SeeAlso + "Building DG algebras from existing DG algebras" + subDGAlgebra +/// + +doc /// + Key + killHomologyAtDegree + (killHomologyAtDegree, DGAlgebra, ZZ) + Headline + Adjoin variables killing the homology of a DG algebra at a prescribed degree + Usage + B = killHomologyAtDegree(A, n) + Inputs + A:DGAlgebra + n:ZZ + The hom-degree at which to kill homology. + Outputs + B:DGAlgebra + Either @ TT "A" @ itself (if @ TT "H_n(A) = 0" @) or a new DG + algebra obtained from @ TT "A" @ by adjoining variables in + hom-degree @ TT "n + 1" @ whose differentials are minimal + representatives of @ TT "H_n(A)" @. + Description + Text + Unlike @ TO killCycles @, which searches for the first + nontrivial degree upward from @ TT "StartDegree" @, this + method targets a specific degree. It computes the minimal + generators of @ TT "H_n(A)" @ using the pruning map of + @ TT "prune homology(n, A)" @, lifts them to cycles in + @ TT "A_n" @, and adjoins via @ TO adjoinVariables @. + Example + R = QQ[x, y] + A = koszulComplexDGA R + killHomologyAtDegree(A, 1) === A + Text + Over a regular ring, @ TT "H_1" @ vanishes already, so the + method short-circuits. Over a complete intersection, it + adjoins new generators: + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + B = killHomologyAtDegree(A, 1) + numgens B.natural > numgens A.natural + prune homology(1, B) + SeeAlso + "Building DG algebras from existing DG algebras" + killCycles + adjoinVariables + acyclicClosure +/// + +doc /// + Key + isValidDGAlgebra + (isValidDGAlgebra, DGAlgebra) + Headline + Structural invariant check for a DG algebra + Usage + b = isValidDGAlgebra A + Inputs + A:DGAlgebra + Outputs + b:Boolean + @ TT "true" @ when @ TT "A" @ has all required keys of the + expected types (ring, natural algebra, degree list, + differential list, cache table). + Description + Text + A lightweight structural check, intended to guard + downstream code against hand-assembled or corrupted + @ TT "DGAlgebra" @ hash tables. It does @ EM "not" @ check + the @ TT "d^2 = 0" @ condition; for that, use + @ TO isWellDefinedDifferential @ or the user-facing + @ TO (isWellDefined, DGAlgebra) @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + isValidDGAlgebra A + SeeAlso + "Building DG algebras from existing DG algebras" + isWellDefinedDifferential + (isWellDefined, DGAlgebra) +/// + +doc /// + Key + isWellDefinedDifferential + (isWellDefinedDifferential, DGAlgebra) + (isWellDefinedDifferential, DGModule) + Headline + Semantic check that d^2 = 0 for a DG algebra or DG module + Usage + b = isWellDefinedDifferential A + b = isWellDefinedDifferential M + Inputs + A:DGAlgebra + A DG algebra. + M:DGModule + A DG module. + Outputs + b:Boolean + @ TT "true" @ when the composition + @ TT "d_{n-1} * d_n" @ is zero for every relevant + @ TT "n" @. + Description + Text + The @ EM "semantic" @ half of the well-definedness check: it + verifies that the differential squares to zero on every + generator. The structural half + (@ TO isValidDGAlgebra @, and the corresponding + @ TT "isValidDGModule" @ for modules) is called first as a + pre-condition. + Text + This is the primitive called internally by + @ TO (isWellDefined, DGAlgebra) @ and the user-facing + @ TO (isWellDefined, DGModule) @. It is exported for + low-level use when only the @ TT "d^2 = 0" @ half of + well-definedness needs to be checked. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + isWellDefinedDifferential A + M = freeDGModule(A, {0}) + isWellDefinedDifferential M + SeeAlso + "Building DG algebras from existing DG algebras" + "Well-definedness, acyclicity, and quasi-isomorphism" + isValidDGAlgebra + (isWellDefined, DGAlgebra) + (isWellDefined, DGModule) +/// + +doc /// + Key + "Low-level differential computations and validity checks" + Headline + Primitives behind DG algebra and DG module differentials + Description + Text + The methods documented here implement the raw computations + that higher-level DG algebra and DG module routines rely on. + They split into three groups. + + The first group -- @ TO polyDifferential @ and + @ TO polyDiffMonomial @ -- applies the Leibniz rule directly + to elements of @ TT "A.natural" @ to produce the DG algebra + differential. @ TT "polyDifferential(n, A)" @ returns the + per-degree matrix @ TT "A_n --> A_{n-1}" @ over the base ring + and caches it inside @ TT "A" @; @ TT "polyDifferential(A, f)" @ + applies @ TT "d" @ to a single element by summing over its + terms; @ TT "polyDiffMonomial(A, m)" @ is the monomial kernel + that powers both. + + The second group -- @ TO dgComplex @ -- packages the per-degree + differentials of a DG algebra or DG module as a + @ TO2{Complex, "Complex"} @ of free modules over the base ring. + The result is cached and invalidated by + @ TO invalidateDGAlgebraCache @. + + The third group -- @ TO isValidDGModule @ and + @ TO isDGSubmodule @ -- checks structural invariants. + @ TT "isValidDGModule" @ parallels @ TO isValidDGAlgebra @ and + confirms that the required keys are present with the expected + types; @ TT "isDGSubmodule" @ tests whether a given inclusion + matrix has @ TT "d" @-closed image without performing the + @ TT "d" @-saturation that @ TO dgSubmodule @ would. + + Finally, @ TO2{(getBoundaryPreimage, DGModule, Vector), "getBoundaryPreimage"} @ + lifts boundaries through the module differential, returning a + preimage when one exists and a residue otherwise. + Subnodes + polyDifferential + polyDiffMonomial + dgComplex + isValidDGModule + isDGSubmodule + (getBoundaryPreimage, DGModule, Vector) + (getBoundaryPreimage, DGModule, List) + SeeAlso + "Basic operations on DG Algebras" + "Building DG modules, submodules, and quotients" + "Well-definedness, acyclicity, and quasi-isomorphism" +/// + +doc /// + Key + polyDifferential + (polyDifferential, ZZ, DGAlgebra) + (polyDifferential, DGAlgebra, RingElement) + Headline + Matrix and element-level differentials of a DG algebra + Usage + d = polyDifferential(n, A) + b = polyDifferential(A, f) + Inputs + n:ZZ + A homological degree. + A:DGAlgebra + f:RingElement + An element of @ TT "A.natural" @. + Outputs + d:Matrix + The per-degree map @ TT "A_n --> A_{n-1}" @ over + @ TT "A.ring" @ (first form). + b:RingElement + The image @ TT "d(f)" @ in @ TT "A.natural" @ (second form). + Description + Text + The two-argument form @ TT "polyDifferential(n, A)" @ returns + the matrix of the DG algebra differential in hom-degree + @ TT "n" @, expressed in the monomial basis of @ TT "A_n" @ + and @ TT "A_{n-1}" @. It is cached inside @ TT "A" @ and + reused on subsequent calls; @ TO invalidateDGAlgebraCache @ + clears the cache. Out-of-range degrees are handled + uniformly: degree @ TT "0" @ gives the conventional zero map + @ TT "R^1 --> R^0" @, and degrees past @ TT "maxDegree A" @ + give zero maps of the appropriate shape. + Example + R = QQ[x, y, z] + A = koszulComplexDGA R + d1 = polyDifferential(1, A) + d2 = polyDifferential(2, A) + d3 = polyDifferential(3, A) + assert(d1 * d2 == 0) + assert(d2 * d3 == 0) + Text + The zero-degree map is the conventional @ TT "R^1 --> R^0" @: + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + d0 = polyDifferential(0, A) + assert(source d0 == R^1 and target d0 == R^0) + Text + The element form @ TT "polyDifferential(A, f)" @ applies + @ TT "d" @ to an arbitrary element by summing over its + monomial terms: + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + gs = gens A.natural + polyDifferential(A, gs#0 * gs#1) + SeeAlso + "Low-level differential computations and validity checks" + polyDiffMonomial + (diff, DGAlgebra, RingElement) + invalidateDGAlgebraCache +/// + +doc /// + Key + polyDiffMonomial + (polyDiffMonomial, DGAlgebra, RingElement) + Headline + Leibniz rule on a single monomial of a DG algebra + Usage + b = polyDiffMonomial(A, m) + Inputs + A:DGAlgebra + m:RingElement + A monomial of @ TT "A.natural" @ (or the zero element). + Outputs + b:RingElement + The image @ TT "d(m)" @ in @ TT "A.natural" @. + Description + Text + @ TT "polyDiffMonomial" @ is the monomial kernel of the + DG algebra differential. It splits @ TT "m" @ into its + coefficient and its support powers and then applies the + graded Leibniz rule variable by variable, keeping track of + the sign produced when @ TT "d" @ passes an odd-degree + generator. The routine handles the zero element without + raising an exception, returning @ TT "0" @ in @ TT "A.natural" @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + assert(polyDiffMonomial(A, 0_(A.natural)) == 0_(A.natural)) + gs = gens A.natural + polyDiffMonomial(A, gs#0 * gs#1) + Text + On a two-variable Koszul generator product, the Leibniz rule + gives @ TT "d(T_1 T_2) = d(T_1) T_2 - T_1 d(T_2) = x T_2 - y T_1" @: + Example + r = polyDiffMonomial(A, gs#0 * gs#1) + assert(r == x * gs#1 - y * gs#0) + SeeAlso + "Low-level differential computations and validity checks" + polyDifferential + (diff, DGAlgebra, RingElement) +/// + +doc /// + Key + dgComplex + (dgComplex, DGAlgebra) + (dgComplex, DGModule) + Headline + Package a DG algebra or DG module as a Complex of free base-ring modules + Usage + C = dgComplex A + C = dgComplex M + Inputs + A:DGAlgebra + M:DGModule + Outputs + C:Complex + A @ TO Complex @ of free @ TT "A.ring" @-modules whose + @ TT "i" @-th module is @ TT "A_i" @ (resp.\ @ TT "M_i" @) + and whose differential is @ TT "polyDifferential" @ + (resp.\ @ TO moduleDifferential @). + Description + Text + @ TT "dgComplex" @ assembles the per-degree differentials of + a DG algebra or DG module into a single + @ TO2{Complex, "Complex"} @ object. The result is cached on + the input; calling @ TT "dgComplex" @ again returns exactly + the same complex, and @ TO invalidateDGAlgebraCache @ clears + the cache so that the next call rebuilds the complex. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + C1 = dgComplex A + C2 = dgComplex A + assert(C1 === C2) + invalidateDGAlgebraCache A + assert(not A.cache#?(symbol dgComplex)) + C3 = dgComplex A + assert(instance(C3, Complex)) + Text + When @ TT "maxDegree" @ is infinite -- for example on the + acyclic closure of a non-regular ring -- @ TT "dgComplex" @ + truncates at a canonical finite bound so that a + @ TO Complex @ is always produced. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = acyclicClosure(R, EndDegree => 2) + assert(instance(dgComplex A, Complex)) + Text + The DG module form is parallel: it builds the complex whose + terms are @ TT "M_i" @ and whose differentials are the maps + returned by @ TO moduleDifferential @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + KM = koszulComplexDGM R^1 + D1 = dgComplex KM + D2 = dgComplex KM + assert(D1 === D2) + SeeAlso + "Low-level differential computations and validity checks" + polyDifferential + moduleDifferential + invalidateDGAlgebraCache + toComplex +/// + +doc /// + Key + isValidDGModule + (isValidDGModule, DGModule) + Headline + Structural-invariant check on a DG module + Usage + b = isValidDGModule M + Inputs + M:DGModule + Outputs + b:Boolean + @ TT "true" @ if the stored @ TT "DGModule" @ has all + required keys with the expected types; @ TT "false" @ + otherwise. + Description + Text + @ TT "isValidDGModule" @ parallels @ TO isValidDGAlgebra @: + it confirms that @ TT "M" @ carries the keys + @ TT "dgAlgebra" @, @ TT "ring" @, @ TT "natural" @, + @ TT "Degrees" @, and @ TT "diff" @ with the expected types, + that the referenced @ TT "DGAlgebra" @ is itself structurally + valid, and that the generator-degree list has the same length + as the differential list. It does not verify the semantic + property @ TT "d_M^2 = 0" @; that is the job of + @ TO isWellDefinedDifferential @, and the two together are + what the user-facing @ TO (isWellDefined, DGModule) @ calls. + Example + R = QQ[x, y] / ideal(x^2, y^2) + KM = koszulComplexDGM R^1 + assert(isValidDGModule KM) + SeeAlso + "Low-level differential computations and validity checks" + "Well-definedness, acyclicity, and quasi-isomorphism" + isValidDGAlgebra + isWellDefinedDifferential + (isWellDefined, DGModule) +/// + +doc /// + Key + isDGSubmodule + (isDGSubmodule, DGModule, Matrix) + Headline + Test whether the image of an inclusion matrix is d-closed + Usage + b = isDGSubmodule(M, incMat) + Inputs + M:DGModule + incMat:Matrix + A matrix whose target is @ TT "M.natural" @. + Outputs + b:Boolean + @ TT "true" @ if the column span of @ TT "incMat" @ is + closed under the differential of @ TT "M" @; @ TT "false" @ + otherwise. + Description + Text + @ TT "isDGSubmodule" @ checks whether a matrix of candidate + generators spans a DG submodule: it assembles + @ TT "d_M" @ applied column-wise and then verifies that the + result lies in the column span of @ TT "incMat" @ via a + single @ TT "//" @ solve. Unlike @ TO dgSubmodule @, it + does not saturate: a submodule that is not @ TT "d" @-closed + is reported as such rather than enlarged. The empty matrix + is always @ TT "d" @-closed. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + idM = identityDGModuleMap M + imF = image idM + assert(isDGSubmodule(target idM, (inclusion imF).natural)) + kerF = kernel idM + assert(isDGSubmodule(source idM, (inclusion kerF).natural)) + SeeAlso + "Low-level differential computations and validity checks" + dgSubmodule + (image, DGModuleMap) + (kernel, DGModuleMap) +/// + +doc /// + Key + (getBoundaryPreimage, DGModule, Vector) + Headline + Lift a boundary in a DG module to a preimage under the differential + Usage + (lifted, myLift) = getBoundaryPreimage(M, b) + Inputs + M:DGModule + A free DG module over a @ TO DGAlgebra @. + b:Vector + A homogeneous element of @ TT "M.natural" @. + Outputs + seq:Sequence + A pair @ TT "(lifted, myLift)" @. If @ TT "lifted" @ is + @ TT "true" @, then @ TT "myLift" @ is an element with + @ TT "d_M(myLift) = b" @; otherwise @ TT "myLift" @ is the + residue @ TT "b - d_M(liftAttempt)" @, which records how far + @ TT "b" @ fails to be a boundary. + Description + Text + @ TT "getBoundaryPreimage" @ for DG modules is the module + analogue of @ TO (getBoundaryPreimage, DGAlgebra, RingElement) @: + it attempts to solve @ TT "d_M(x) = b" @ for @ TT "x" @, using + the matrix @ TT "moduleDifferential(homDegree(b) + 1, M)" @ + and a single @ TT "//" @ solve. Over a general ring such a + solve may fail to find a preimage even when one exists, so a + @ TT "true" @ answer is a guarantee but a @ TT "false" @ + answer only says "the particular preimage candidate we tried + left a residue". The returned residue is + @ TT "b - d_M(liftAttempt)" @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + -- d(e_1) = x*e_0, so x*e_0 is a boundary with preimage e_1. + b = x * natGens#0 + (ok, pre) = getBoundaryPreimage(M, b) + assert(ok === true) + assert(diff(M, pre) == b) + Text + On a non-boundary, the first coordinate is @ TT "false" @ + and the second coordinate records the residue: + Example + M2 = freeDGModule(A, {0}) + ng2 = apply(rank M2.natural, i -> (M2.natural)_i) + (ok2, residue) = getBoundaryPreimage(M2, ng2#0) + assert(ok2 === false) + assert(residue != 0) + SeeAlso + "Low-level differential computations and validity checks" + (getBoundaryPreimage, DGModule, List) + getBoundaryPreimage + moduleDifferential + homologyClass +/// + +doc /// + Key + (getBoundaryPreimage, DGModule, List) + Headline + Lift a list of boundaries sharing a hom-degree through the module differential + Usage + (lifted, lifts) = getBoundaryPreimage(M, boundaryList) + Inputs + M:DGModule + A free DG module over a @ TO DGAlgebra @. + boundaryList:List + A list of elements of @ TT "M.natural" @, each either + @ TT "0" @ or homogeneous of a common hom-degree. + Outputs + seq:Sequence + A pair @ TT "(lifted, lifts)" @. If @ TT "lifted" @ is + @ TT "true" @, then @ TT "lifts#i" @ satisfies + @ TT "d_M(lifts#i) = boundaryList#i" @ for every @ TT "i" @; + otherwise @ TT "lifts" @ is the list of per-entry residues. + Description + Text + The list form of @ TO (getBoundaryPreimage, DGModule, Vector) @ + solves multiple preimage problems in a single + @ TT "//" @ call. Zero entries are permitted and contribute + zero columns; all nonzero entries must share a hom-degree. + When every boundary lifts, the returned list preserves the + order of @ TT "boundaryList" @. + Example + R = QQ[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x * natGens#0}) + b = x * natGens#0 + (ok, lifts) = getBoundaryPreimage(M, {b, 0_(M.natural)}) + assert(ok === true) + assert(diff(M, lifts#0) == b) + assert(diff(M, lifts#1) == 0) + SeeAlso + "Low-level differential computations and validity checks" + (getBoundaryPreimage, DGModule, Vector) + getBoundaryPreimage + moduleDifferential +/// + +doc /// + Key + "Accessors and cache management" + Headline + Reading state out of a DG algebra or DG module and maintaining its caches + Description + Text + A @ TO DGAlgebra @ or @ TO DGModule @ stores its defining + data in a small collection of keys -- the base ring, the + graded-commutative algebra, the generator degrees, and the + list of generator differentials -- together with a cache of + derived information (per-degree differential matrices, + homology, the homology algebra, a @ TO Complex @ + presentation). This page collects the two groups of + utilities that manage that state. + + The first group -- @ TO underlyingRing @, + @ TO underlyingAlgebra @, @ TO differential @, and + @ TO generatorDegrees @ -- returns the four defining + ingredients. Because both DG algebras and DG modules carry + the same names, code can remain agnostic about which one it + is handling. + + The second group -- @ TO ensureDGAlgebraCaches @ and + @ TO invalidateDGAlgebraCache @ -- maintains the cache + sub-tables. @ TT "ensureDGAlgebraCaches" @ is idempotent + and guarantees that the standard cache keys are present + with the expected types; @ TO invalidateDGAlgebraCache @ + wipes every cached computed value so that the next call + recomputes from scratch. Routines such as @ TO setDiff @ + that mutate the differential call + @ TO invalidateDGAlgebraCache @ internally so that stale + homology and stale differential matrices are never served. + Subnodes + underlyingRing + underlyingAlgebra + differential + generatorDegrees + ensureDGAlgebraCaches + invalidateDGAlgebraCache + SeeAlso + "Basic operations on DG Algebras" + "Building DG modules, submodules, and quotients" + "Low-level differential computations and validity checks" +/// + +doc /// + Key + underlyingRing + (underlyingRing, DGAlgebra) + (underlyingRing, DGModule) + Headline + The commutative base ring of a DG algebra or DG module + Usage + S = underlyingRing A + S = underlyingRing M + Inputs + A:DGAlgebra + M:DGModule + Outputs + S:Ring + The commutative base ring @ TT "A.ring" @ or @ TT "M.ring" @ + over which the DG structure is defined. + Description + Text + @ TT "underlyingRing" @ returns the base ring. This is the + ring @ TT "R" @ in the DG algebra @ TT "A = R\\langle T_1, T_2, \\ldots \\rangle" @ + or the DG module @ TT "M" @, and it is the ring over which + every matrix returned by @ TO polyDifferential @ or + @ TO moduleDifferential @ is built. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + assert(underlyingRing A === R) + Example + M = koszulComplexDGM R^1 + assert(underlyingRing M === R) + SeeAlso + "Accessors and cache management" + underlyingAlgebra + differential + generatorDegrees +/// + +doc /// + Key + underlyingAlgebra + (underlyingAlgebra, DGAlgebra) + (underlyingAlgebra, DGModule) + Headline + The graded-commutative algebra carrying the DG structure + Usage + N = underlyingAlgebra A + N = underlyingAlgebra M + Inputs + A:DGAlgebra + M:DGModule + Outputs + N:Ring + The graded-commutative ring @ TT "A.natural" @ + (the DG algebra's own ring) or, for a DG module, + the graded free module @ TT "M.natural" @ + (as a @ TO Module @ over @ TT "A.natural" @). + Description + Text + For a @ TO DGAlgebra @, @ TT "underlyingAlgebra" @ returns the + graded-commutative ring @ TT "A.natural" @ -- the ring whose + elements one manipulates when writing polynomials in the DG + generators. For a @ TO DGModule @ it returns the underlying + @ TT "A.natural" @-module @ TT "M.natural" @ on which the + differential acts. In both cases the result is what you + typically @ TT "use" @ before referring to DG generators by + name. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + assert(underlyingAlgebra A === A.natural) + numgens underlyingAlgebra A + Example + M = koszulComplexDGM R^1 + assert(underlyingAlgebra M === M.natural) + SeeAlso + "Accessors and cache management" + underlyingRing + differential + generatorDegrees +/// + +doc /// + Key + differential + (differential, DGAlgebra) + (differential, DGModule) + Headline + The list of generator differentials stored in a DG algebra or DG module + Usage + d = differential A + d = differential M + Inputs + A:DGAlgebra + M:DGModule + Outputs + d:List + The stored list of differentials: for a @ TO DGAlgebra @, + one element of @ TT "A.natural" @ per DG generator; for a + @ TO DGModule @, one element of @ TT "M.natural" @ per + natural generator. + Description + Text + @ TT "differential" @ returns the raw list of generator + differentials that was installed by @ TO setDiff @. The + @ TT "i" @-th entry is the image of the @ TT "i" @-th DG + generator under @ TT "d" @, and every other differential is + computed from it by linearity and the graded Leibniz rule. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + differential A + assert(differential A === A.diff) + Example + M = koszulComplexDGM R^1 + differential M + SeeAlso + "Accessors and cache management" + setDiff + polyDifferential + moduleDifferential +/// + +doc /// + Key + generatorDegrees + (generatorDegrees, DGAlgebra) + (generatorDegrees, DGModule) + Headline + The hom-degrees (and optional internal degrees) of the DG generators + Usage + degs = generatorDegrees A + degs = generatorDegrees M + Inputs + A:DGAlgebra + M:DGModule + Outputs + degs:List + A list of degree vectors, one per DG generator (for a DG + algebra) or one per natural generator (for a DG module). + Description + Text + @ TT "generatorDegrees" @ returns the degree data that was + supplied at construction. Each entry is a list whose first + coordinate is the homological degree and whose remaining + coordinates record the internal (multi-)grading of the base + ring. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + generatorDegrees A + Example + M = freeDGModule(A, {0, 1, 3}) + generatorDegrees M + SeeAlso + "Accessors and cache management" + underlyingRing + underlyingAlgebra + differential +/// + +doc /// + Key + ensureDGAlgebraCaches + (ensureDGAlgebraCaches, DGAlgebra) + (ensureDGAlgebraCaches, DGModule) + Headline + Guarantee that the standard cache sub-tables are present + Usage + A = ensureDGAlgebraCaches A + M = ensureDGAlgebraCaches M + Inputs + A:DGAlgebra + M:DGModule + Outputs + :{DGAlgebra,DGModule} + The same object, with any missing standard cache sub-table + created (the method mutates the input and returns it). + Description + Text + @ TT "ensureDGAlgebraCaches" @ is idempotent: it creates the + cache sub-tables that the rest of the package expects (the + @ TT "homology" @, @ TT "homologyAlgebra" @, and + @ TT "diffs" @ sub-tables for a DG algebra; the @ TT "diffs" @ + sub-table for a DG module), and leaves any that already exist + untouched. Code that reads from the cache can call this + method once up front and thereafter assume the sub-tables are + present with the right types, rather than defensively + checking each access. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + ensureDGAlgebraCaches A + assert(instance(A.cache, CacheTable)) + -- idempotent: calling twice leaves the cache intact + ensureDGAlgebraCaches A + assert(instance(A.cache, CacheTable)) + Example + KM = koszulComplexDGM R^1 + ensureDGAlgebraCaches KM + assert(instance(KM.cache, CacheTable)) + SeeAlso + "Accessors and cache management" + invalidateDGAlgebraCache +/// + +doc /// + Key + invalidateDGAlgebraCache + (invalidateDGAlgebraCache, DGAlgebra) + (invalidateDGAlgebraCache, DGModule) + Headline + Discard cached derived data so that it is recomputed from scratch + Usage + A = invalidateDGAlgebraCache A + M = invalidateDGAlgebraCache M + Inputs + A:DGAlgebra + M:DGModule + Outputs + :{DGAlgebra,DGModule} + The same object with every cached derived value cleared + (the method mutates the input and returns it). + Description + Text + @ TT "invalidateDGAlgebraCache" @ wipes every cache entry + that records a value derived from the differential: per-degree + differential matrices, per-degree homology, the homology + algebra, and any @ TO Complex @ or block-diff summary cached + by @ TO dgComplex @ or @ TO displayBlockDiff @. The cache + sub-tables themselves are recreated empty, so subsequent + lookups recompute the values rather than returning stale + results. Routines that mutate the differential (most + notably @ TO setDiff @) call this function internally. + Example + R = QQ[x, y] / ideal(x^2, y^2) + A = koszulComplexDGA R + C1 = dgComplex A + invalidateDGAlgebraCache A + assert(not A.cache#?(symbol dgComplex)) + C2 = dgComplex A + assert(instance(C2, Complex)) + Example + KM = koszulComplexDGM R^1 + D1 = dgComplex KM + invalidateDGAlgebraCache KM + assert(not KM.cache#?(symbol dgComplex)) + D2 = dgComplex KM + assert(instance(D2, Complex)) + SeeAlso + "Accessors and cache management" + ensureDGAlgebraCaches + setDiff + dgComplex +/// + +doc /// + Key + "DG module primitives and chain-complex export" + Headline + Installing differentials, reading degree data, and exporting to Complex + Description + Text + This page collects the remaining DG module primitives + (installing a differential, asking for the largest + hom-degree, reading off a per-degree basis) together with + the methods that export a DG algebra, DG module, DG module + map, or DG ideal to the ordinary + @ TO2{Complex, "Complex"} @ / @ TO2{ComplexMap, "ComplexMap"} @ + world. + + Installing a differential. @ TO (setDiff, DGModule, List) @ + is the DG module analogue of @ TO (setDiff, DGAlgebra, List) @: + it records the image of each natural generator under + @ TT "d_M" @ and invalidates every cached derived value. + + Reading degree data. @ TO (maxDegree, DGModule) @ and + @ TO (maxDegree, DGQuotientModule) @ report the largest + hom-degree present (or @ TT "infinity" @ if the underlying + DG algebra has an even-degree generator). + @ TO (getBasis, ZZ, DGModule) @ returns an + @ TT "A.ring" @-basis of the hom-degree-@ TT "n" @ piece of + @ TT "M.natural" @. + + Exporting to @ TO Complex @. The @ TO toComplex @ family + packages per-degree differentials as a plain + @ TO Complex @ of free base-ring modules; the + @ TO toComplexMap @ family packages per-degree induced maps + as a @ TO ComplexMap @. Together they let the chain-level + results of the DG machinery be handed off to the rest of + the @ TO2{Complexes, "Complexes"} @ package. + + Block-diagonal display. @ TO displayBlockDiff @ prints the + per-internal-multidegree block decomposition of a DG + algebra differential at a given homological degree, + convenient for inspecting the acyclic closure of a + multigraded quotient. + Subnodes + (setDiff, DGModule, List) + (maxDegree, DGModule) + (maxDegree, DGQuotientModule) + (getBasis, ZZ, DGModule) + (toComplex, DGModule) + (toComplex, DGModule, ZZ) + (toComplex, DGQuotientModule) + (toComplex, DGIdeal) + (toComplexMap, DGModuleMap) + (toComplexMap, DGModuleMap, ZZ) + displayBlockDiff + SeeAlso + "Basic operations on DG Algebras" + "Building DG modules, submodules, and quotients" + "Low-level differential computations and validity checks" +/// + +doc /// + Key + (setDiff, DGModule, List) + Headline + Install the list of generator differentials on a free DG module + Usage + setDiff(M, diffList) + Inputs + M:DGModule + A free DG module (from @ TO freeDGModule @). + diffList:List + One entry per natural generator of @ TT "M" @. Each entry + is a vector of @ TT "M.natural" @ (or @ TT "0" @, or a + one-column matrix that will be folded into a vector). + Outputs + :DGModule + The same @ TT "M" @, mutated to carry the new differentials. + Description + Text + @ TT "setDiff" @ is the DG module analogue of + @ TO (setDiff, DGAlgebra, List) @: it records the image of + the @ TT "i" @-th natural generator under the differential in + the @ TT "i" @-th slot of @ TT "M.diff" @, and clears every + cached derived value so that later calls to + @ TO moduleDifferential @ or @ TO homology @ will recompute + from the newly installed data. Only free DG modules (those + produced by @ TO freeDGModule @) are accepted; the list + length must match the number of natural generators. + Example + R = QQ[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + natGens = apply(rank M.natural, i -> (M.natural)_i) + setDiff(M, {0, x^2 * natGens#0}) + d1 = moduleDifferential(1, M) + d2 = moduleDifferential(2, M) + assert(d1 * d2 == 0) + SeeAlso + "DG module primitives and chain-complex export" + freeDGModule + (setDiff, DGAlgebra, List) + moduleDifferential + invalidateDGAlgebraCache +/// + +doc /// + Key + (maxDegree, DGModule) + Headline + Largest hom-degree present in a DG module + Usage + n = maxDegree M + Inputs + M:DGModule + Outputs + n:{ZZ,InfiniteNumber} + @ TT "maxDegree A + max(first d | d in M.Degrees)" @ when + @ TT "A" @ has finite max hom-degree; @ TT "infinity" @ + otherwise. + Description + Text + @ TT "maxDegree" @ of a DG module combines the hom-degree + bound of the ambient @ TO DGAlgebra @ with the largest + hom-degree shift of a natural generator. When the + underlying DG algebra has an even-degree generator it has + @ TT "infinity" @ as its max degree, and so does every DG + module over it. + Example + R = QQ[x, y] / ideal(x^2, y^2) + KM = koszulComplexDGM R^1 + assert(maxDegree KM == maxDegree KM.dgAlgebra) + Example + A = koszulComplexDGA R + M = freeDGModule(A, {0, 3}) + maxDegree M + SeeAlso + "DG module primitives and chain-complex export" + maxDegree + (maxDegree, DGQuotientModule) + (maxDegree, DGAlgebra) +/// + +doc /// + Key + (maxDegree, DGQuotientModule) + Headline + Largest hom-degree present in a DG quotient module + Usage + n = maxDegree Q + Inputs + Q:DGQuotientModule + Outputs + n:{ZZ,InfiniteNumber} + The @ TT "maxDegree" @ of the ambient DG module + @ TT "Q.ambient" @. + Description + Text + Passing a DG quotient module to @ TO maxDegree @ returns + the max hom-degree of its ambient DG module. Because + taking a quotient cannot introduce elements of higher + hom-degree than are already in the ambient, the ambient's + bound is valid for the quotient. The value is what + @ TO (toComplex, DGQuotientModule) @ and + @ TO (toComplexMap, DGModuleMap) @ use to decide where to + truncate when no @ TO EndDegree @ is supplied. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0, 1}) + z = zeroDGModuleMap(M, M) + Q = cokernel z + assert(maxDegree Q == maxDegree M) + SeeAlso + "DG module primitives and chain-complex export" + maxDegree + (maxDegree, DGModule) + (toComplex, DGQuotientModule) +/// + +doc /// + Key + (getBasis, ZZ, DGModule) + Headline + Basis of the hom-degree-n piece of a DG module + Usage + b = getBasis(n, M) + Inputs + n:ZZ + A homological degree. + M:DGModule + Outputs + b:Matrix + A one-row matrix over @ TT "M.natural" @ whose entries are + the basis elements of @ TT "F_n(M)" @, the hom-degree-@ TT "n" @ + piece of @ TT "M.natural" @ over @ TT "A.ring" @. + Description + Text + @ TT "getBasis(n, M)" @ delegates to + @ TO2{(basis, ZZ, Module), "basis"} @ on the underlying + @ TT "A.natural" @-module @ TT "M.natural" @, restricting to + the variables of @ TT "A.natural" @. The result is the + piece of @ TT "M" @ on which @ TT "moduleDifferential(n, M)" @ + acts as its source-side basis. + Example + R = QQ[x, y] / ideal(x^2, y^2) + KM = koszulComplexDGM R^1 + b = getBasis(1, KM) + assert(instance(b, Matrix)) + assert(numcols b == 2) + SeeAlso + "DG module primitives and chain-complex export" + getBasis + (getBasis, ZZ, DGAlgebra) + moduleDifferential +/// + +doc /// + Key + (toComplex, DGModule) + Headline + Export a DG module to a Complex of free base-ring modules + Usage + C = toComplex M + Inputs + M:DGModule + Outputs + C:Complex + A @ TO Complex @ of free @ TT "A.ring" @-modules whose + @ TT "i" @-th differential is @ TT "moduleDifferential(i+1, M)" @. + Description + Text + @ TT "toComplex" @ packages the per-degree module + differentials as an ordinary @ TO Complex @ so that the + result can flow through the rest of the + @ TO2{Complexes, "Complexes"} @ package. The one-argument + form uses @ TT "maxDegree M" @ as the truncation bound and + errors out if that is @ TT "infinity" @; use + @ TO (toComplex, DGModule, ZZ) @ to supply an explicit bound. + The result is not cached by this method (use + @ TO dgComplex @ for a cached variant). + Example + R = QQ[x, y] / ideal(x^2, y^2) + KM = koszulComplexDGM R^1 + C = toComplex KM + assert(instance(C, Complex)) + assert(C.dd_1 == moduleDifferential(1, KM)) + assert(C.dd_2 == moduleDifferential(2, KM)) + SeeAlso + "DG module primitives and chain-complex export" + toComplex + (toComplex, DGModule, ZZ) + (toComplex, DGAlgebra) + dgComplex + moduleDifferential +/// + +doc /// + Key + (toComplex, DGModule, ZZ) + Headline + Export a DG module to a Complex with an explicit hom-degree bound + Usage + C = toComplex(M, n) + Inputs + M:DGModule + n:ZZ + A hom-degree upper bound. + Outputs + C:Complex + A @ TO Complex @ of free @ TT "A.ring" @-modules, truncated at + hom-degree @ TT "n" @. + Description + Text + The explicit-bound form of @ TO (toComplex, DGModule) @. + Supplying a bound is required when the ambient DG algebra has + infinite hom-degree (as for an @ TO acyclicClosure @), and + convenient when a low bound suffices. + Example + R = QQ[x, y, z] + KM = koszulComplexDGM R^1 + C = toComplex(KM, 2) + assert(instance(C, Complex)) + assert(max C == 2) + SeeAlso + "DG module primitives and chain-complex export" + toComplex + (toComplex, DGModule) + dgComplex + moduleDifferential +/// + +doc /// + Key + (toComplex, DGQuotientModule) + Headline + Export a DG quotient module to a Complex + Usage + C = toComplex Q + Inputs + Q:DGQuotientModule + Outputs + C:Complex + The cokernel of the chain map induced by the inclusion + @ TT "Q.subDGModule.inclusion" @; equivalently, the + per-degree quotient complex of the ambient. + Description + Text + For a DG quotient module @ TT "Q = M / S" @, + @ TT "toComplex Q" @ builds the cokernel at the + @ TO Complex @ level: it exports the ambient DG module + @ TT "M" @ via @ TO (toComplex, DGModule) @, exports the + inclusion of @ TT "S" @ via @ TO (toComplexMap, DGModuleMap) @, + and takes the cokernel of the resulting @ TO ComplexMap @. + If @ TT "Q" @ was previously passed through + @ TO (prune, DGQuotientModule) @ the cached pruned complex + is returned instead. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + M = freeDGModule(A, {0}) + z = zeroDGModuleMap(M, M) + Q = cokernel z + CM = toComplex M + CQ = toComplex Q + assert(rank CM_0 == rank CQ_0) + assert(rank CM_1 == rank CQ_1) + SeeAlso + "DG module primitives and chain-complex export" + (toComplex, DGModule) + (toComplexMap, DGModuleMap) + (prune, DGQuotientModule) +/// + +doc /// + Key + (toComplex, DGIdeal) + Headline + Export a DG ideal to a Complex (not yet implemented) + Usage + C = toComplex I + Inputs + I:DGIdeal + Outputs + C:Complex + Description + Text + @ TT "toComplex" @ on a @ TO DGIdeal @ is not yet + implemented. The natural definition views @ TT "I" @ as a + DG submodule of the regular DG module + @ TT "A.natural" @ over @ TT "A" @, but a canonical wrapper + for the regular-representation view of @ TT "A" @ as a DG + module over itself is not yet in the package, so the method + raises an informative error pointing at the currently + available alternatives. + + Use @ TT "toComplex(ambient I / I)" @ to obtain the + @ TO Complex @ of the quotient DG algebra, or read the + underlying data via @ TT "ideal I" @ and @ TT "gens I" @. + Example + R = ZZ/101[x] + A = koszulComplexDGA R + T = (gens A.natural)#0 + I = dgIdeal(A, {T}) + errored = try (toComplex I; false) else true + assert(errored) + SeeAlso + "DG module primitives and chain-complex export" + "Operations on DG Ideals" + DGIdeal + dgIdeal + (symbol /, DGAlgebra, DGIdeal) +/// + +doc /// + Key + (toComplexMap, DGModuleMap) + Headline + Export a DG module map to a ComplexMap + Usage + cm = toComplexMap f + Inputs + f:DGModuleMap + A DG module map @ TT "M --> N" @. + Outputs + cm:ComplexMap + The induced @ TO ComplexMap @ + @ TT "toComplex M --> toComplex N" @. + Description + Text + @ TT "toComplexMap f" @ assembles the per-degree pieces of + @ TT "f" @ into a @ TO ComplexMap @. When either side has + infinite hom-degree, an @ TO EndDegree @ bound must be + supplied; the option @ TO AssertWellDefined @ (default + @ TT "true" @) causes @ TT "isWellDefined f" @ to be checked + up front. When the target is a DG quotient module only the + canonical projection is supported; the result is the induced + quotient chain map. Use + @ TO (toComplexMap, DGModuleMap, ZZ) @ to extract a single + per-degree component instead. + Example + R = QQ[a, b] / ideal(a^2, b^2) + A = koszulComplexDGA R + phi = dgAlgebraMap(A, A, matrix {gens A.natural}) + cm = toComplexMap phi + assert(instance(cm, ComplexMap)) + SeeAlso + "DG module primitives and chain-complex export" + toComplexMap + (toComplexMap, DGModuleMap, ZZ) + (toComplexMap, DGAlgebraMap) + (toComplex, DGModule) + identityDGModuleMap +/// + +doc /// + Key + (toComplexMap, DGModuleMap, ZZ) + Headline + The per-hom-degree component of a DG module map as a matrix + Usage + m = toComplexMap(f, n) + Inputs + f:DGModuleMap + A DG module map @ TT "M --> N" @. + n:ZZ + A homological degree. + Outputs + m:Matrix + The hom-degree-@ TT "n" @ component + @ TT "F_n(M) --> F_n(N)" @ as an @ TT "A.ring" @-linear + matrix. + Description + Text + Builds the @ TT "A.ring" @-linear matrix that @ TT "f" @ + induces on the hom-degree-@ TT "n" @ pieces of + @ TT "M.natural" @ and @ TT "N.natural" @. This is the + per-degree primitive used by + @ TO (toComplexMap, DGModuleMap) @ when assembling a full + @ TO ComplexMap @. + Example + R = QQ[x, y] / ideal(x^2, y^2) + Mdg = minimalSemifreeResolution(koszulComplexDGA R, R^1 / ideal(x, y), EndDegree => 3) + idM = identityDGModuleMap Mdg + all(0..3, n -> ( + cm := toComplexMap(idM, n); + source cm == target cm and cm == id_(source cm) + )) + SeeAlso + "DG module primitives and chain-complex export" + toComplexMap + (toComplexMap, DGModuleMap) + (toComplex, DGModule) + identityDGModuleMap +/// + +doc /// + Key + killCycles + (killCycles,DGAlgebra) + Headline + Adjoin variables to kill non-bounding cycles in the lowest positive degree + Usage + B = killCycles A + Inputs + A:DGAlgebra + Outputs + B:DGAlgebra + a new DG algebra with extra generators adjoined to make every cycle in the + first positive degree where @ TT "HH_i A" @ is nonzero into a boundary + Description + Text + This is the basic building block of Tate's acyclic closure construction. + Given a DG algebra @ TT "A" @, @ TT "killCycles" @ locates the smallest positive + homological degree @ TT "i" @ in which @ TT "HH_i A" @ is nonzero, picks a + generating set of cycles, and adjoins new DG algebra generators in degree + @ TT "i+1" @ mapping to those cycles. The result is a DG algebra whose + homology agrees with @ TT "A" @ below degree @ TT "i" @ and is killed in + degree @ TT "i" @. Iterating this procedure produces the acyclic closure. + Example + R = ZZ/101[a,b,c,d]/ideal{a^3, b^3, c^3 - d^4} + A = koszulComplexDGA R + A.diff + B = killCycles A + B.diff + Text + The new generator @ TT "T_(2,1)" @ in @ TT "B" @ has differential equal to + a chosen nonzero cycle representative in homological degree 1 of @ TT "A" @. + SeeAlso + DGAlgebra + acyclicClosure + adjoinVariables +/// + +doc /// + Key + adjoinVariables + (adjoinVariables,DGAlgebra,List) + Headline + Extend a DG algebra by a user-chosen list of cycles to be killed + Usage + B = adjoinVariables(A, cycleList) + B = adjoinVariables(A, cycleList, Variable => "U") + Inputs + A:DGAlgebra + cycleList:List + Homogeneous cycles of @ TT "A" @, i.e.\ elements @ TT "z" @ in + @ TT "A.natural" @ with @ TT "(A.diff)(z) = 0" @ (no check is + performed). Each cycle must have the same multi-degree shape as + @ TT "A.Degrees" @. + Variable => String + Base name for the newly adjoined generators. Defaults to the base + symbol already used by @ TT "A" @, or @ TT "\"T\"" @ if @ TT "A" @ + has no generators. + Outputs + B:DGAlgebra + A semifree extension of @ TT "A" @ with one new generator per entry + of @ TT "cycleList" @. The new generator for a cycle @ TT "z" @ of + hom-degree @ TT "n" @ has hom-degree @ TT "n + 1" @ and differential + @ TT "z" @. + Description + Text + Whereas @ TO killCycles @ chooses representatives of a basis of the + first nonzero homology of @ TT "A" @ automatically, + @ TT "adjoinVariables" @ lets the user specify exactly which cycles + become boundaries. This is the primitive used by + @ TO acyclicClosure @, @ TO killCycles @, and @ TO minimalModel @, + but can also be called directly to build DG algebra resolutions by + hand. + Text + @ BOLD "Variable convention." @ The new generators extend the + existing doubly-indexed naming scheme @ TT "base_(i, j)" @: for + each adjoined cycle of hom-degree @ TT "n" @, a new generator named + @ TT "base_(n+1, k)" @ is created, where @ TT "k" @ continues the + 1-indexed counter already in use at hom-degree @ TT "n + 1" @ in + @ TT "A" @. Existing generator names are preserved verbatim. + Passing @ TT "Variable => \"U\"" @ switches the base symbol for + @ EM "the new generators only" @: + Example + R = ZZ/101[a, b, c] / ideal(a^3, b^3, c^3) + A = koszulComplexDGA R + z = a^2 * (gens A.natural)_0 + polyDifferential(A, z) == 0 + B = adjoinVariables(A, {z}) + gens B.natural + B' = adjoinVariables(A, {z}, Variable => "U") + gens B'.natural + Text + Killing a single cycle lowers the size of the corresponding + homology: + Example + apply(3, i -> numgens prune homology(i, A)) + apply(3, i -> numgens prune homology(i, B)) + Text + Over a non-c.i.\ example with a non-regular relation, one can + inspect the first homology, pick a representative, and kill just + that class (leaving other @ TT "H_1" @ classes intact): + Example + S = ZZ/101[a, b, c, d] / ideal(a^3, b^3, c^3 - d^4) + AS = koszulComplexDGA S + prune homology(1, AS) + BS = adjoinVariables(AS, {a^2 * (gens AS.natural)_0}) + prune homology(1, BS) + Caveat + The package does @ EM "not" @ verify that the supplied elements are + actually cycles; passing a non-cycle produces a malformed DG algebra. + Use @ TT "polyDifferential(A, z) == 0" @ to check before calling + (the shorthand @ TT "(A.diff)(z)" @ applies @ TT "A.diff" @ as a + ring map, which only agrees with the differential on monomials that + contain at most one algebra generator and hence is @ EM "not" @ a + valid cycle check for products). + SeeAlso + DGAlgebra + killCycles + acyclicClosure + minimalModel + setDiff +/// + +doc /// + Key + homologyAlgebra + (homologyAlgebra,DGAlgebra) + Headline + Compute the homology algebra of a DGAlgebra. + Usage + HA = homologyAlgebra(A) + Inputs + A:DGAlgebra + Outputs + HA:Ring + Description + Example + R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} + A = koszulComplexDGA(R) + apply(maxDegree A + 1, i -> numgens prune homology(i,A)) + HA = homologyAlgebra(A) + Text + Note that HA is a graded commutative polynomial ring (i.e. an exterior algebra) since R is a complete intersection. + Example + R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4,a^3*b^3*c^3*d^3} + A = koszulComplexDGA(R) + apply(maxDegree A + 1, i -> numgens prune homology(i,A)) + HA = homologyAlgebra(A) + numgens HA + HA.cache.cycles + Example + Q = ZZ/101[x,y,z] + I = ideal{y^3,z*x^2,y*(z^2+y*x),z^3+2*x*y*z,x*(z^2+y*x),z*y^2,x^3,z*(z^2+2*x*y)} + R = Q/I + A = koszulComplexDGA(R) + apply(maxDegree A + 1, i -> numgens prune homology(i,A)) + HA = homologyAlgebra(A) + Text + One can check that HA has Poincare duality since R is Gorenstein. + Text + If your DGAlgebra has generators in even degrees, then one must specify the options GenDegreeLimit and RelDegreeLimit. + Example + R = ZZ/101[a,b,c,d] + S = R/ideal{a^4,b^4,c^4,d^4} + A = acyclicClosure(R,EndDegree=>3) + B = A ** S + HB = homologyAlgebra(B,GenDegreeLimit=>7,RelDegreeLimit=>14) +/// + +doc /// + Key + (homology,DGAlgebra) + Headline + The homology algebra of a DG algebra + Usage + HA = homology A + HA = HH A + Inputs + A:DGAlgebra + Outputs + HA:Ring + the homology algebra @ TT "HH_*(A)" @, as a graded-commutative ring + Description + Text + Returns the homology algebra of @ TT "A" @, caching the result inside @ TT "A" @ + so subsequent calls are fast. This is a convenience wrapper around + @ TO homologyAlgebra @ that supports the @ TT "HH" @ shorthand. + Example + R = ZZ/101[a,b,c] / ideal(a^3, b^3, c^3, a^2*b^2*c^2) + A = koszulComplexDGA R + HA = HH A + numgens HA + apply(maxDegree A + 1, i -> numgens prune homology(i, A)) + Text + For DG algebras with even-degree generators (such as acyclic closures of non-regular + rings), call @ TO homologyAlgebra @ directly and supply + @ TO GenDegreeLimit @ and @ TO RelDegreeLimit @ explicitly. + SeeAlso + homologyAlgebra + zerothHomology + (homology, ZZ, DGAlgebra) +/// + +doc /// + Key + torAlgebra + (torAlgebra,Ring) + Headline + Computes the Tor algebra of a ring + Usage + torR = torAlgebra(R) + Inputs + R:Ring + Outputs + torR:Ring + Description + Example + R = ZZ/101[a,b,c,d] + TorR = torAlgebra(R) + S = R/ideal{a^3,b^3,c^3,d^5} + TorS = torAlgebra(S, GenDegreeLimit => 3) + Text + The above example calculates the Tor algebra of R and S up to degree 3, by default. One can also specify the maximum degree + to compute generators of the Tor algebra by specifying the GenDegreeLimit option. + Example + R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3,a^2*b^2*c^3*d^2} + TorR = torAlgebra(R,GenDegreeLimit=>5) +/// + +doc /// + Key + (torAlgebra,Ring,Ring) + Headline + Computes Tor_R(S,k) up to a specified generating and relating degree. + Usage + TorRS = torAlgebra(R,S,GenDegreeLimit=>m,RelDegreeLimit=>n) + Inputs + R:Ring + S:Ring + Outputs + TorRS:Ring + Description + Example + R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} + M = coker matrix {{a^3*b^3*c^3*d^3}}; + S = R/ideal{a^3*b^3*c^3*d^3} + HB = torAlgebra(R,S,GenDegreeLimit=>4,RelDegreeLimit=>8) + numgens HB + apply(5,i -> #(flatten entries getBasis(i,HB))) + Mres = freeResolution(M, LengthLimit=>8) + Text + Note that in this example, $Tor_*^R(S,k)$ has trivial multiplication, since the + map from R to S is a Golod homomorphism by a theorem of Levin and Avramov. +/// + +doc /// + Key + maxDegree + (maxDegree,DGAlgebra) + Headline + Computes the maximum homological degree of a DGAlgebra + Usage + mDegree = maxDegree(A) + Inputs + A:DGAlgebra + Outputs + mDegree:ZZ + The maximum degree of the DGAlgebra A (this can be infinite). + Description + Text + Note that if the DGAlgebra A has any generators of even degree, then maxDegree returns infinity. + Example + R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3} + A = koszulComplexDGA(R) + B = acyclicClosure(A,EndDegree=>3) + maxDegree(A) + maxDegree(B) +/// + +doc /// + Key + isHomologyAlgebraTrivial + (isHomologyAlgebraTrivial,DGAlgebra) + Headline + Determines if the homology algebra of a DGAlgebra is trivial + Usage + isTriv = isHomologyAlgebraTrivial(A) + Inputs + A:DGAlgebra + Outputs + isTriv:Boolean + Description + Text + This function computes the homology algebra of the DGAlgebra A and determines if the multiplication on H(A) is trivial. + Example + R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} + S = R/ideal{a^3*b^3*c^3*d^3} + A = acyclicClosure(R,EndDegree=>3) + B = A ** S + isHomologyAlgebraTrivial(B,GenDegreeLimit=>6) + Text + The command returns true since R --> S is Golod. Notice we also used the option GenDegreeLimit here. + Example + R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} + A = koszulComplexDGA(R) + isHomologyAlgebraTrivial(A) + Text + The command returns false, since R is Gorenstein, and so HA has Poincare Duality, hence the multiplication + is far from trivial. +/// + +doc /// + Key + isAcyclic + (isAcyclic,DGAlgebra) + Headline + Determine whether a DG algebra has no higher homology + Usage + isAcyc = isAcyclic A + Inputs + A:DGAlgebra + Outputs + isAcyc:Boolean + @ TT "true" @ if @ TT "HH_i A = 0" @ for all @ TT "i > 0" @, and @ TT "false" @ otherwise + Description + Text + A DG algebra is acyclic (in the convention used by this package) if it has + zero homology in all positive degrees, so that @ TT "A -> HH_0 A" @ is a + quasi-isomorphism. @ TT "isAcyclic" @ checks homology degree by degree up + through @ TT "maxDegree A" @; for unbounded DG algebras an @ TO EndDegree @ + option bounds the search. + Text + The Koszul complex of a regular sequence is acyclic: + Example + Q = ZZ/101[a,b,c,d] + I = ideal(a^4, b^4, c^4, d^4) + isAcyclic(koszulComplexDGA I) + Text + But the Koszul complex of a ring with nontrivial relations in homology is not: + Example + R = ZZ/101[a,b,c,d] / ideal(a^4 + b^4 + c^4 + d^4) + isAcyclic(koszulComplexDGA R) + Text + An acyclic closure built to sufficient degree is acyclic in the checked range: + Example + R = ZZ/101[a,b,c,d] / ideal(a^3, b^3, c^3, d^3) + A = acyclicClosure(R, EndDegree => 3) + isAcyclic(A, EndDegree => 3) + SeeAlso + DGAlgebra + (homology, ZZ, DGAlgebra) + acyclicClosure +/// + +doc /// + Key + isGolod + (isGolod,Ring) + Headline + Determines if a ring is Golod + Usage + isGol = isGolod(R) + Inputs + R:Ring + Outputs + isGol:Boolean + Description + Text + This function determines if the Koszul complex of a ring R admits a trivial Massey operation. If one exists, then R is Golod. + Example + R = ZZ/101[a,b,c,d]/ideal{a^4+b^4+c^4+d^4} + isGolod(R) + Text + Hypersurfaces are Golod, but + Example + R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} + isGolod(R) + Text + complete intersections of higher codimension are not. Here is another example: + Example + Q = ZZ/101[a,b,c,d] + R = Q/(ideal vars Q)^2 + isGolod(R) + Text + The above is a (CM) ring minimal of minimal multiplicity, hence Golod. The next example was found + by Lukas Katthan, and appears in his arXiv paper 1511.04883. It is the first known example + of an algebra that is not Golod, but whose Koszul complex has a trivial homology product. + Example + Q = ZZ/101[x_1,x_2,y_1,y_2,z,w] + I = ideal {x_1*x_2^2,z^2*w,y_1*y_2^2,x_2^2*z*w,y_2^2*z^2,x_1*x_2*y_1*y_2,x_2^2*y_2^2*z,x_1*y_1*z} + R = Q/I + isHomologyAlgebraTrivial koszulComplexDGA R + isGolod R + Text + Note that since the Koszul complex is zero in homological degree beyond the embedding dimension, there are only finitely + many Massey products that one needs to check to verify that a ring is Golod. +/// + +doc /// + Key + isGolodHomomorphism + (isGolodHomomorphism,QuotientRing) + Headline + Determines if the canonical map from the ambient ring is Golod + Usage + isGol = isGolodHomomorphism(R) + Inputs + R:QuotientRing + Outputs + isGol:Boolean + Description + Text + This function determines if the canonical map from ambient R --> R is Golod. It does this by computing an acyclic closure of + ambient R (which is a @ TO DGAlgebra @), then tensors this with R, and determines if this DG Algebra has a trivial Massey operation + up to a certain homological degree provided by the option GenDegreeLimit. + Example + R = ZZ/101[a,b,c,d]/ideal{a^4+b^4+c^4+d^4} + isGolodHomomorphism(R,GenDegreeLimit=>5) + Text + If R is a Golod ring, then ambient R $\rightarrow$ R is a Golod homomorphism. + Example + Q = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} + R = Q/ideal (a^3*b^3*c^3*d^3) + isGolodHomomorphism(R,GenDegreeLimit=>5,TMOLimit=>3) + Text + The map from Q to R is Golod by a result of Avramov and Levin; we can only find the trivial Massey operations out to a given degree. +/// + +doc /// + Key + getGenerators + (getGenerators,DGAlgebra) + Headline + Cycles whose classes generate the homology algebra of a DG algebra + Usage + cycleList = getGenerators A + Inputs + A:DGAlgebra + Outputs + cycleList:List + a list of cycles in @ TT "A.natural" @ whose homology classes generate + @ TT "HH_*(A)" @ as a graded-commutative algebra over @ TT "A.ring" @ + Description + Text + Walks up the homological degrees of @ TT "A" @, computing a basis of each + homology module and selecting minimal new generators not already reachable + by products of lower-degree cycles. The returned list is exactly the + generating set used internally by @ TO homologyAlgebra @ to build its + presentation. + Text + This version of the function assumes all algebra generators of @ TT "A" @ + lie in odd homological degree. When @ TT "A" @ has generators in even + degree, a finite generating set need not exist, and the search depth must + be bounded with @ TT "EndDegree" @ or @ TO GenDegreeLimit @. + Example + R = ZZ/101[a,b,c] / ideal(a^3, b^3, c^3, a^2*b^2*c^2) + A = koszulComplexDGA R + netList getGenerators A + Text + The generators above are the cycles used to present @ TT "homologyAlgebra A" @; + their classes span @ TT "HH_i A" @ in each positive degree. + Example + apply(maxDegree A + 1, i -> numgens prune homology(i, A)) + SeeAlso + DGAlgebra + homologyAlgebra + (homology, ZZ, DGAlgebra) + homologyClass +/// + +doc /// + Key + deviations + (deviations,Ring) + (deviations,Complex) + (deviations,RingElement,List) + Headline + Computes the deviations of the input ring, complex, or power series. + Usage + devTally = deviations(R) + Inputs + R:Ring + Outputs + devTally:Tally + Description + Text + This command computes the deviations of a @ TO Ring @, a @ TO Complex @, or a power series in the form of a @ TO RingElement @. + The deviations are the same as the degrees of the generators of the acyclic closure of R, or the degrees of the generators of the + Tor algebra of R. This function takes an option called Limit (default value 3) that specifies the largest deviation to compute. + Example + R = ZZ/101[a,b,c,d]/ideal {a^3,b^3,c^3,d^3} + deviations(R) + deviations(R,DegreeLimit=>4) + S = R/ideal{a^2*b^2*c^2*d^2} + deviations(S,DegreeLimit=>4) + T = ZZ/101[a,b]/ideal {a^2-b^3} + deviations(T,DegreeLimit=>4) + Text + Note that the deviations of T are not graded, since T is not graded. When calling deviations on a Complex, the + zeroth free module must be cyclic, and this is checked. The same goes for the case + of a RingElement. + Example + R = ZZ/101[a,b,c,d]/ideal {a^3,b^3,c^3,d^3} + A = degreesRing R + kRes = freeResolution(coker vars R, LengthLimit => 4) + pSeries = poincareN kRes + devA = deviations(R,DegreeLimit=>5) + devB = deviations(kRes,DegreeLimit=>5) + devC = deviations(pSeries,degrees R, DegreeLimit=>5) + devA === devB and devB === devC +/// + +doc /// + Key + deviationsToPoincare + (deviationsToPoincare,HashTable) + [deviationsToPoincare,DegreeLimit] + Headline + Computes the power series corresponding to a set of deviations. + Usage + pSeries = deviationsToPoincare(devHash) + Inputs + devHash:HashTable + HashTable of the same form as the output from @ TO deviations @ + Outputs + pSeries:RingElement + Description + Text + This command takes a HashTable of the same form output from @ TO deviations @ and produces the Poincare series corresponding to it. + The (key,value) pairs must be of the form homologicalDegree=>number or (homologicalDegree,internalDegree)=>number. + Because + Example + R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} + RDevs = deviations(R,DegreeLimit=>6) + devPSeries = deviationsToPoincare(RDevs,DegreeLimit=>6) + pSeries = poincareN (freeResolution(coker vars R, LengthLimit=>6)) + substitute(devPSeries,ring pSeries) == pSeries +/// + +doc /// + Key + findTrivialMasseyOperation + findNaryTrivialMasseyOperation + (findTrivialMasseyOperation,DGAlgebra) + (findNaryTrivialMasseyOperation,DGAlgebra,List,HashTable,ZZ) + Headline + Finds a trivial Massey operation on a set of generators of H(A) + Usage + tmo = findTrivialMasseyOperation(A) + Inputs + A:DGAlgebra + Outputs + seq:Sequence + A sequence seq whose first entry reports whether a trivial Massey operation has been found, and the second + entry is a hash table with keys given by monomials in a generating set of the positive degree homology of + A and values the element that bounds the Massey product corresponding to that monomial. + Description + Text + This function the element that bounds all potentially nonzero Massey products (before taking homology class). + The maximum degree of a generating cycle is specified in the option GenDegreeLimit, if needed. + Text + Golod rings are defined by being those rings whose Koszul complex K^R has a trivial Massey operation. + Also, the existence of a trivial Massey operation on a DG algebra A forces the multiplication on H(A) + to be trivial. An example of a ring R such that H(K^R) has trivial multiplication, yet K^R does not admit + a trivial Massey operation is unknown. Such an example cannot be monomially defined, by a result of + Jollenbeck and Berglund. + Text + This is an example of a Golod ring. It is Golod since it is the Stanley-Reisner ideal of a flag complex + whose 1-skeleton is chordal [Jollenbeck-Berglund]. + Example + Q = ZZ/101[x_1..x_6] + I = ideal (x_3*x_5,x_4*x_5,x_1*x_6,x_3*x_6,x_4*x_6) + R = Q/I + A = koszulComplexDGA(R) + isHomologyAlgebraTrivial(A,GenDegreeLimit=>3) + cycleList = getGenerators(A) + (hasTMO, tmoSoFar) = findTrivialMasseyOperation(A) + assert(hasTMO) + Text + Below is an example of a Teter ring (Artinian Gorenstein ring modulo its socle), and the computation in Avramov and Levin's + paper shows that H(A) does not have trivial multiplication, hence no trivial Massey operation can exist. + Example + Q = ZZ/101[x,y,z] + I = ideal (x^3,y^3,z^3,x^2*y^2*z^2) + R = Q/I + A = koszulComplexDGA(R) + isHomologyAlgebraTrivial(A) + cycleList = getGenerators(A) + assert(not first findTrivialMasseyOperation(A)) + Text + The related function @ TO findNaryTrivialMasseyOperation @ find only the nth order trivial Massey operations. +/// + +doc /// + Key + masseyTripleProduct + (masseyTripleProduct,DGAlgebra,RingElement,RingElement,RingElement) + Headline + Computes the Massey triple product of a set of cycles or homology classes + Usage + h = masseyTripleProduct(A,h1,h2,h3) + Inputs + A:DGAlgebra + h1:RingElement + h2:RingElement + h3:RingElement + Outputs + h:RingElement + The return value is either the homology class of the Massey triple product defined + by the inputs or a cycle representing the homology class. + Description + Text + These functions compute the Massey triple product of either three homology classes + or three cycles that represent nonzero homology classes for which the Massey triple product + is defined. + Text + For an example, we return to an example due to Lukas Katthan which was discussed in @ TO isGolod @. + First, we define the algebra: + Example + Q = QQ[x_1,x_2,y_1,y_2,z] + I = ideal (x_1*x_2^2,y_1*y_2^2,z^3,x_1*x_2*y_1*y_2,y_2^2*z^2,x_2^2*z^2,x_1*y_1*z,x_2^2*y_2^2*z) + R = Q/I + KR = koszulComplexDGA R + Text + The following are cycles: + Example + z1 = z^2*T_(1,5) + z2 = y_2^2*T_(1,3) + z3 = x_2^2*T_(1,1) + Text + and z1*z2, z2*z3 vanish in homology: + Example + (lifted12,lift12) = getBoundaryPreimage(KR,z1*z2) + (lifted23,lift23) = getBoundaryPreimage(KR,z2*z3) + Text + Note that the first return value of @ TO getBoundaryPreimage @ indicates that the inputs + are indeed boundaries, and the second value is the lift of the boundary along the differential. + Text + Given cycles z1,z2,z3 such that z1*z2 and z2*z3 are boundaries, + the Massey triple product of the homology classes represented by z1,z2 and z3 + is the homology class of lift12*z3 + z1*lift23. To see this, we compute and check: + Example + z123 = masseyTripleProduct(KR,z1,z2,z3) + z123 == lift12*z3 + z1*lift23 + Text + One may also compute Massey triple products directly on elements of the homology + algebra itself, as is seen with the command masseyTripleProduct: + Example + H = HH(KR) + h1 = homologyClass(KR,z1) + h2 = homologyClass(KR,z2) + h3 = homologyClass(KR,z3) + h123 = masseyTripleProduct(KR,h1,h2,h3) + h123 == homologyClass(KR,z123) +/// + +doc /// + Key + (masseyTripleProduct,DGAlgebra,ZZ,ZZ,ZZ) + Headline + Computes the matrix representing all triple Massey operations. + Usage + mat = masseyTripleProduct(A,l,m,n) + Inputs + A:DGAlgebra + l:ZZ + m:ZZ + n:ZZ + Outputs + mat:Matrix + Description + Text + Given a triple of homology classes h1,h2,h3, such that h1h2 = h2h3 = 0, + the Massey triple product of h1,h2 and h3 may be defined as in + @ TO masseyTripleProduct @. This command computes a basis of the homology + algebra of A in degrees l,m and n respectively, and expresses the triple + Massey operation of each triple, provided it is defined. If a triple product + is not defined (i.e. if either h1h2 or h2h3 is not zero) then the triple + product is reported as zero in the matrix. + Text + The following example appears in "On the Hopf algebra of a Local Ring" by Avramov + as an example of a nonvanishing Massey operation which an algebra generator: + Example + Q = QQ[t_1,t_2,t_3,t_4] + I = ideal (t_1^3,t_2^3,t_3^3-t_1*t_2^2,t_1^2*t_3^2,t_1*t_2*t_3^2,t_2^2*t_4,t_4^2) + R = Q/I + KR = koszulComplexDGA R + H = HH(KR) + masseys = masseyTripleProduct(KR,1,1,1); + rank masseys + Text + As you can see, this command is useful to determine the number of linearly independent + elements that arise as triple Massey products. + Text + For example, the following Massey triple product is nonvanishing and is an + algebra generator: + Example + masseyTripleProduct(KR,X_2,X_4,X_1) +/// + +doc /// + Key + expandGeomSeries + (expandGeomSeries,List,ZZ) + (expandGeomSeries,RingElement,ZZ) + Headline + Expand a geometric series to a specified degree. + Usage + pSeries = expandGeomSeries(f,n) + Inputs + f:RingElement + Ratio of the geometric series to be expanded. + n:ZZ + Degree which to expand the geometric series. + Outputs + pSeries:RingElement + Power series representation of the geometric series. + Description + Text + If the user supplies a list instead of a RingElement as the first argument, the return + value is the product of all the each of the geometric series expanded to degree n obtained + by calling expandGeomSeries on each element of the list. + Example + A = ZZ[S,T_0,T_1] + f = expandGeomSeries(S^2*T_0^8,10) + g = expandGeomSeries(S^4*T_1^15,10) + h = expandGeomSeries({S^2*T_0^8,S^4*T_1^15},10) + B = A/(first gens A)^11 + substitute(f*g,B) == h +/// + +doc /// + Key + torMap + (torMap,RingMap) + [torMap,GenDegreeLimit] + Headline + Compute the map of Tor algebras associated to a RingMap. + Usage + torPhi = torMap(phi) + Inputs + phi:RingMap + Outputs + torPhi:RingMap + Description + Text + The functor Tor_R(M,N) is also functorial in the ring argument. Therefore, a ring map phi from A to B induces an algebra map + from the Tor algebra of A to the Tor algebra of B. + Example + R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3,a^2*b^2*c^2} + S = R/ideal{a*b^2*c^2,a^2*b*c^2,a^2*b^2*c} + f = map(S,R) + fTor = torMap(f,GenDegreeLimit=>3) + matrix fTor + Text + In the following example, the map on Tor is surjective, which means that the ring homomorphism is large (Dress-Kramer). + Example + R = ZZ/101[a,b,c,d]/ideal{a^3,b^3,c^3,d^3,a*c,a*d,b*c,b*d} + S = ZZ/101[a,b]/ideal{a^3,b^3} + f = map(S,R,matrix{{a,b,0,0}}) + fTor = torMap(f,GenDegreeLimit=>4) + matrix fTor +/// + +doc /// + Key + (diff,DGAlgebra,RingElement) + Headline + Apply the differential of a DGAlgebra to a ring element + Usage + b = diff(A, a) + Inputs + A:DGAlgebra + a:RingElement + an element of @ TT "A.natural" @ + Outputs + b:RingElement + the image @ TT "d_A(a)" @ + Description + Text + Returns the image of @ TT "a" @ under the differential of @ TT "A" @. + The element @ TT "a" @ must be a ring element of the underlying graded-commutative + algebra @ TT "A.natural" @; products, sums, and polynomial expressions in the + DG generators are all valid inputs. The differential is applied by the Leibniz rule, + so homogeneous components are processed independently. + Example + R = ZZ/101[x,y,z] + A = koszulComplexDGA R + diff(A, T_(1,1) * T_(1,2)) + diff(A, diff(A, T_(1,1) * T_(1,2) * T_(1,3))) + Text + When @ TT "a" @ is a cycle, the result is zero: + Example + z = x*T_(1,2) - y*T_(1,1) + diff(A, z) + Text + To solve the equation @ TT "diff(A, b) == z" @ for a boundary @ TT "z" @, + use @ TO getBoundaryPreimage @. + SeeAlso + DGAlgebra + getBoundaryPreimage + homologyClass +/// + +doc /// + Key + getBoundaryPreimage + (getBoundaryPreimage,DGAlgebra,RingElement) + (getBoundaryPreimage,DGAlgebra,List) + Headline + Attempt to find a preimage of a boundary under the differential of a DGAlgebra. + Usage + (lifted,myLift) = getBoundaryPreimage(A,z) + Inputs + A:DGAlgebra + z:RingElement + Outputs + seq:Sequence + Description + Text + The first element in the return value is a boolean value indicating whether the + lift was possible. If true, the second coordinate of the return value is the lift. + If false, then the second coordinate of the return value is the reduction of the + input modulo the image. + Example + Q = QQ[x_1,x_2,y_1,y_2,z] + I = ideal (x_1*x_2^2,y_1*y_2^2,z^3,x_1*x_2*y_1*y_2,y_2^2*z^2,x_2^2*z^2,x_1*y_1*z,x_2^2*y_2^2*z) + R = Q/I + KR = koszulComplexDGA R + Text + The following are cycles: + Example + z1 = z^2*T_(1,5) + z2 = y_2^2*T_(1,3) + z3 = x_2^2*T_(1,1) + {diff(KR,z1),diff(KR,z1),diff(KR,z1)} + Text + and z1*z2, z2*z3 vanish in homology: + Example + (lifted12,lift12) = getBoundaryPreimage(KR,z1*z2) + (lifted23,lift23) = getBoundaryPreimage(KR,z2*z3) + Text + We can check that the differential of the lift is the supposed boundary: + Example + diff(KR,lift23) == z2*z3 +/// + +doc /// + Key + homologyClass + (homologyClass,DGAlgebra,RingElement) + Headline + Computes the element of the homology algebra corresponding to a cycle in a DGAlgebra. + Usage + h = homologyClass(A,z) + Inputs + A:DGAlgebra + z:RingElement + Outputs + h:RingElement + Description + Text + This function computes the element in the homology algebra of a cycle in a @ TO DGAlgebra @. + In order to do this, the @ TO homologyAlgebra @ is retrieved (or computed, if it hasn't been + already). + Example + Q = QQ[x,y,z] + I = ideal (x^3,y^3,z^3) + R = Q/I + KR = koszulComplexDGA R + z1 = x^2*T_(1,1) + z2 = y^2*T_(1,2) + H = HH(KR) + homologyClass(KR,z1*z2) +/// + +doc /// + Key + zerothHomology + (zerothHomology,DGAlgebra) + Headline + The zeroth homology of a DG algebra as a ring + Usage + HA0 = zerothHomology A + Inputs + A:DGAlgebra + Outputs + HA0:Ring + @ TT "HH_0(A)" @, presented as a quotient of @ TT "A.ring" @ + Description + Text + Returns @ TT "HH_0(A)" @ as a ring, computed by taking @ TT "A.ring" @ modulo the image + of the degree-one part of the differential. For a Koszul complex + @ TT "KR = koszulComplexDGA I" @ over @ TT "R" @, this recovers the quotient ring @ TT "R/I" @. + Example + Q = ZZ/101[x,y,z] + I = ideal(x^2, y^2, z^2, x*y*z) + KQ = koszulComplexDGA I + H0 = zerothHomology KQ + describe H0 + Text + When @ TT "A" @ is an acyclic closure, @ TT "zerothHomology A" @ returns the quotient + ring on which the DG algebra resolves the residue field. + Example + R = Q / I + A = acyclicClosure(R, EndDegree => 2) + zerothHomology A + SeeAlso + (homology, DGAlgebra) + homologyAlgebra +/// + +doc /// + Key + getDegNModule + (getDegNModule,ZZ,Ring,Ring) + Headline + Extract the degree-n strand of a graded algebra as a module over its degree-zero piece + Usage + MN = getDegNModule(N, R, A) + Inputs + N:ZZ + the homological degree to extract + R:Ring + the degree-zero piece of @ TT "A" @, i.e.\ @ TT "A_0" @ + A:Ring + a graded @ TT "R" @-algebra with @ TT "A_0 = R" @ + Outputs + MN:Module + the degree-@ TT "N" @ component of @ TT "A" @, presented as an @ TT "R" @-module + Description + Text + Given a graded algebra @ TT "A" @ over @ TT "R" @ with @ TT "A_0 = R" @, the strand + @ TT "A_N" @ is an @ TT "R" @-module. This function returns that strand with a + minimal presentation by monomials of internal degree @ TT "N" @ modulo the relations + of @ TT "A" @. It is most often applied to a homology algebra @ TT "HA = HH A" @ + together with its degree-zero subring @ TT "HA_0 = zerothHomology A" @. + Example + Q = ZZ/101[x,y,z] + I = ideal(x^3, y^3, z^3) + R = Q / I + KR = koszulComplexDGA R + HA = HH KR; + H0 = zerothHomology KR + M1 = getDegNModule(1, H0, HA) + M2 = getDegNModule(2, H0, HA) + Text + The ranks of these strands are the Koszul Betti numbers of @ TT "R" @: + Example + apply(0..3, n -> numgens getDegNModule(n, H0, HA)) + SeeAlso + zerothHomology + homologyAlgebra + homologyModule +/// + +doc /// + Key + DGAlgebraMap + Headline + The class of DG algebra maps + Description + Text + A @ TT "DGAlgebraMap" @ represents a morphism of @ TO2{DGAlgebra, "DG algebras"} @. + It carries four pieces of data: a @ TT "source" @ and @ TT "target" @ (each a + @ TO DGAlgebra @), an underlying ring map @ TT "f.natural" @ on the graded-commutative + algebras, and a degree-zero ring map @ TT "f.ringMap" @ between the underlying rings. + A DG algebra map is a ring map that commutes with the differentials, so that + @ TT "d_B \\circ f = f \\circ d_A" @ where @ TT "d_A" @ and @ TT "d_B" @ are the + differentials of the source and target. + Text + There are two principal ways to construct a DG algebra map: + @ TO dgAlgebraMap @ builds one from a matrix specifying where the DG generators go, + and @ TO liftToDGMap @ lifts a ring map on the degree-zero part to a morphism of + acyclic closures or Koszul complexes. + Example + R = ZZ/101[a,b,c]/ideal{a^3, b^3, c^3} + S = R / ideal{a^2*b^2*c^2} + A = acyclicClosure(R, EndDegree => 3) + B = acyclicClosure(S, EndDegree => 3) + phi = liftToDGMap(B, A, map(S, R)) + source phi === A + target phi === B + Text + Once constructed, a DG algebra map can be checked with @ TO (isWellDefined, DGAlgebraMap) @, + converted to a @ TO ComplexMap @ via @ TO toComplexMap @, or pushed through homology + with @ TO (homology, DGAlgebraMap) @. + SeeAlso + dgAlgebraMap + liftToDGMap + (isWellDefined, DGAlgebraMap) + toComplexMap + (homology, DGAlgebraMap) + ringMap +/// + +doc /// + Key + ringMap + Headline + The degree-zero ring map underlying a DGAlgebraMap + Usage + phi.ringMap + Description + Text + Every @ TO DGAlgebraMap @ @ TT "phi" @ from @ TT "A" @ to @ TT "B" @ includes a key + @ TT "ringMap" @: a @ TO RingMap @ from @ TT "A.ring" @ to @ TT "B.ring" @ giving the + restriction of @ TT "phi" @ to degree zero. When the two DG algebras live over the + same ring, @ TT "phi.ringMap" @ is the identity; in general it is the degree-zero + ring map used to lift @ TT "phi" @. + Example + R = ZZ/101[a,b,c]/ideal{a^3, b^3, c^3} + S = R / ideal{a^2*b^2*c^2} + A = acyclicClosure(R, EndDegree => 2) + B = acyclicClosure(S, EndDegree => 2) + phi = liftToDGMap(B, A, map(S, R)) + phi.ringMap + Text + The @ TT "ringMap" @ is the bridge used internally by @ TO toComplexMap @ to + pushforward complexes between different rings. + SeeAlso + DGAlgebraMap + liftToDGMap + toComplexMap +/// + +doc /// + Key + dgAlgebraMap + (isWellDefined,DGAlgebraMap) + (dgAlgebraMap,DGAlgebra,DGAlgebra,Matrix) + Headline + Define a DG algebra map between DG algebras. + Usage + phi = dgAlgebraMap(B,A,M) + Inputs + A:DGAlgebra + Source + B:DGAlgebra + Target + M:Matrix + A matrix representing where the generators of A should be mapped to (akin to ringMap) + Outputs + phi:DGAlgebraMap + Description + Example + R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} + K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") + K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") + g = dgAlgebraMap(K1,K2,matrix{{Y_(1,2),Y_(1,3)}}) + isWellDefined g + Text + The function does not check that the DG algebra map is well defined, however. + Example + f = dgAlgebraMap(K2,K1,matrix{{0,T_(1,1),T_(1,2)}}) + isWellDefined f +/// + +doc /// + Key + toComplexMap + (toComplexMap,DGAlgebraMap) + (toComplexMap,DGAlgebraMap,ZZ) + [toComplexMap,AssertWellDefined] + [toComplexMap,EndDegree] + Headline + Construct the ComplexMap associated to a DGAlgebraMap + Usage + psi = toComplexMap phi + Inputs + phi:DGAlgebraMap + Outputs + psi:ComplexMap + Description + Example + R = ZZ/101[a,b,c]/ideal{a^3+b^3+c^3,a*b*c} + K1 = koszulComplexDGA(ideal vars R,Variable=>"Y") + K2 = koszulComplexDGA(ideal {b,c},Variable=>"T") + g = dgAlgebraMap(K1,K2,matrix{{Y_(1,2),Y_(1,3)}}) + g' = toComplexMap g + Text + The option @ TO EndDegree @ must be specified if the source of phi has any algebra generators of even degree. The option @ TO AssertWellDefined @ + is used if one wishes to assert that the result of this computation is indeed a chain map. One can construct just the nth map in the + chain map by providing the second @ TO ZZ @ parameter. + Text + This function also works when working over different rings, such as the case when the @ TO DGAlgebraMap @ is produced via + @ TO liftToDGMap @ and in the next example. In this case, the target module is produced via @ TO pushForward @. + Example + R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} + S = R/ideal{a^2*b^2*c^2} + f = map(S,R) + A = acyclicClosure(R,EndDegree=>3) + B = acyclicClosure(S,EndDegree=>3) + phi = liftToDGMap(B,A,f) + toComplexMap(phi,EndDegree=>3) +/// + +doc /// + Key + liftToDGMap + (liftToDGMap,DGAlgebra,DGAlgebra,RingMap) + [liftToDGMap,EndDegree] + Headline + Lift a ring homomorphism in degree zero to a DG algebra morphism + Usage + phiTilde = liftToDGMap(B,A,phi) + Inputs + B:DGAlgebra + Target + A:DGAlgebra + Source + phi:RingMap + Map from A in degree zero to B in degree zero + Outputs + phiTilde:DGAlgebraMap + DGAlgebraMap lifting phi to a map of DGAlgebras. + Description + Text + In order for phiTilde to be defined, phi of the image of the differential of A in degree 1 must lie in the image of the + differential of B in degree 1. At present, this condition is not checked. + Example + R = ZZ/101[a,b,c]/ideal{a^3,b^3,c^3} + S = R/ideal{a^2*b^2*c^2} + f = map(S,R) + A = acyclicClosure(R,EndDegree=>3) + B = acyclicClosure(S,EndDegree=>3) + phi = liftToDGMap(B,A,f) + toComplexMap(phi,EndDegree=>3) +/// + +doc /// + Key + (homology,DGAlgebraMap) + Headline + The induced ring map on homology algebras + Usage + homologyPhi = homology phi + homologyPhi = HH phi + Inputs + phi:DGAlgebraMap + Outputs + homologyPhi:RingMap + the induced map @ TT "HH(source phi) -> HH(target phi)" @ + Description + Text + A @ TO DGAlgebraMap @ @ TT "phi: A -> B" @ is a chain map, so it descends to a ring + map on homology algebras. This function computes that induced map, returning a + @ TO RingMap @ from @ TO homologyAlgebra @ of the source to @ TO homologyAlgebra @ + of the target. The homology algebras are computed via @ TO homologyAlgebra @ if + they have not been cached. + Example + R = ZZ/101[a,b,c]/ideal{a^3 + b^3 + c^3, a*b*c} + K1 = koszulComplexDGA(ideal vars R, Variable => "Y") + K2 = koszulComplexDGA(ideal{b, c}, Variable => "T") + g = dgAlgebraMap(K1, K2, matrix{{Y_(1,2), Y_(1,3)}}) + HHg = HH g + source HHg === homologyAlgebra K1 + target HHg === homologyAlgebra K2 + SeeAlso + DGAlgebraMap + homologyAlgebra + toComplexMap +/// + +-- moved this out of the documentation since it is not complete +-- (homology,DGAlgebraMap,ZZ) + +-- Text +-- One can also supply the second argument (a ZZ) in order to obtain the map on homology in a specified degree. +-- (This is currently not available). + +doc /// + Key + dgAlgebraMultMap + (dgAlgebraMultMap,DGAlgebra,RingElement) + Headline + Returns the chain map corresponding to multiplication by a cycle. + Usage + phi = dgAlgebraMultMap(A,z) + Inputs + A:DGAlgebra + z:RingElement + Outputs + phi:ComplexMap + Description + Text + If A is a DGAlgebra, and z is a cycle of A, then left multiplication of A by z gives + a chain map from A to A. This command converts A to a complex using @ TO toComplex @, + and constructs a @ TO ComplexMap @ that represents left multiplication by z. + This command is used to determine the module structure that is computed in + @ TO homologyModule @. + Example + R = QQ[x,y,z]/ideal{x^3,y^3,z^3} + KR = koszulComplexDGA R + z1 = x^2*T_(1,1) + phi = dgAlgebraMultMap(KR,z1) + Text + As you can see, the degree of phi is the homological degree of z: + Example + degree phi == first degree z + Text + Care is also taken to ensure the resulting map is homogeneous if R and z are: + Example + isHomogeneous phi + Text + One may then view the action of multiplication by the homology class of z upon + taking the induced map in homology: + Example + Hphi = prune HH(phi); (Hphi_0,Hphi_1,Hphi_2) +/// + +doc /// + Key + homologyModule + (homologyModule,DGAlgebra,Module) + Headline + Compute the homology of a DGModule as a module over a DGAlgebra. + Usage + HM = homologyModule(A,M) + Inputs + A:DGAlgebra + M:Module + Outputs + HM:Module + Description + Text + Given a DGAlgebra A over a ring R, and an R-module M, A ** M carries the structure + of a left DG module over A. It follows that H(A ** M) is a module over H(A). + Although DGModules have yet to be implemented as objects in Macaulay2 in their own right, + the current infrastructure (with a little extra work) allows us to determine the module structure + of this type of DG module as a module over the homology algebra of A. + Text + Currently, this code will only work on DGAlgebras that are finite over their ring + of definition, such as Koszul complexes. (Truncations of) module structures in case + of non-finite DGAlgebras may be made available in a future update. + Text + For an example, we will compute the module structure of the Koszul homology of + the canonical module over the Koszul homology algebra. + Example + Q = QQ[x,y,z,w] + I = ideal (w^2, y*w+z*w, x*w, y*z+z^2, y^2+z*w, x*y+x*z, x^2+z*w) + R = Q/I + KR = koszulComplexDGA R + cxKR = toComplex KR + HKR = HH(KR) + Text + The following is the graded canonical module of R: + Example + degList = first entries vars Q / degree / first + M = Ext^4(Q^1/I,Q^{-(sum degList)}) ** R + Text + We obtain the Koszul homology module using the following command: + Example + HKM = homologyModule(KR,M); + Text + One may notice the duality of HKR and HKM by considering their Hilbert series: + Example + hsHKR = value numerator reduceHilbert hilbertSeries HKR + hsHKM = value numerator reduceHilbert hilbertSeries HKM + AA = ring hsHKR + e = numgens Q + hsHKR == T_0^e*T_1^e*sub(hsHKM, {T_0 => T_0^(-1), T_1 => T_1^(-1)}) +/// + +doc /// + Key + (source,DGAlgebraMap) + Headline + The source of a DG algebra map + Usage + A = source phi + Inputs + phi:DGAlgebraMap + Outputs + A:DGAlgebra + the source DG algebra of @ TT "phi" @ + Description + Text + Returns the @ TO DGAlgebra @ from which @ TT "phi" @ is defined. + Example + R = ZZ/101[a,b,c]/ideal{a^3, b^3, c^3} + S = R / ideal{a^2*b^2*c^2} + A = acyclicClosure(R, EndDegree => 2) + B = acyclicClosure(S, EndDegree => 2) + phi = liftToDGMap(B, A, map(S, R)) + source phi === A + SeeAlso + DGAlgebraMap + (target, DGAlgebraMap) + liftToDGMap +/// + +doc /// + Key + (target,DGAlgebraMap) + Headline + The target of a DG algebra map + Usage + B = target phi + Inputs + phi:DGAlgebraMap + Outputs + B:DGAlgebra + the target DG algebra of @ TT "phi" @ + Description + Text + Returns the @ TO DGAlgebra @ into which @ TT "phi" @ maps. + Example + R = ZZ/101[a,b,c]/ideal{a^3, b^3, c^3} + S = R / ideal{a^2*b^2*c^2} + A = acyclicClosure(R, EndDegree => 2) + B = acyclicClosure(S, EndDegree => 2) + phi = liftToDGMap(B, A, map(S, R)) + target phi === B + SeeAlso + DGAlgebraMap + (source, DGAlgebraMap) + liftToDGMap +/// + +doc /// + Key + AssertWellDefined + Headline + Option to verify that a constructed DG algebra map respects differentials + Usage + liftToDGMap(..., AssertWellDefined => true) + toComplexMap(..., AssertWellDefined => true) + Description + Text + When set to @ TT "true" @, the constructor checks that the output + @ TO DGAlgebraMap @ or the resulting @ TO ComplexMap @ actually commutes with the + differentials up to the working degree. This is a @ TT "false" @ by default + because the check can be expensive for large acyclic closures; set it to + @ TT "true" @ when debugging a DG map that behaves unexpectedly. + SeeAlso + liftToDGMap + toComplexMap + dgAlgebraMap +/// + +doc /// + Key + StartDegree + Headline + Option to specify the first homological degree processed by an iterative DG construction + Usage + acyclicClosure(..., StartDegree => n) + killCycles(..., StartDegree => n) + getGenerators(..., StartDegree => n) + Description + Text + Several routines in this package build DG algebras one homological degree at a + time, starting from degree @ TT "StartDegree" @ and incrementing up to an + appropriate endpoint. Raising @ TT "StartDegree" @ can be useful when a partial + acyclic closure has already been cached and you wish to continue the construction + from where it left off. The default value is @ TT "1" @. + SeeAlso + EndDegree + acyclicClosure + killCycles + getGenerators +/// + +doc /// + Key + EndDegree + Headline + Option to specify the last homological degree processed by an iterative DG construction + Usage + acyclicClosure(..., EndDegree => n) + killCycles(..., EndDegree => n) + isAcyclic(..., EndDegree => n) + toComplexMap(..., EndDegree => n) + Description + Text + Bounds how far an iterative DG algebra computation should proceed. In @ TO acyclicClosure @ + this limits the homological degree of new generators adjoined; in @ TO isAcyclic @ + it is the highest degree in which homology is checked; in @ TO toComplexMap @ it + governs how many homological degrees of the resulting chain map are computed. + Text + The default value varies by function and is documented on each specific bracketed + option node. This option is required whenever a DG algebra has generators in + even homological degree, since the algebra is otherwise unbounded. + SeeAlso + StartDegree + acyclicClosure + killCycles + isAcyclic + toComplexMap +/// + +doc /// + Key + GenDegreeLimit + Headline + Option to bound the homological degree searched for algebra generators + Usage + homologyAlgebra(..., GenDegreeLimit => n) + torAlgebra(..., GenDegreeLimit => n) + findTrivialMasseyOperation(..., GenDegreeLimit => n) + isHomologyAlgebraTrivial(..., GenDegreeLimit => n) + isGolodHomomorphism(..., GenDegreeLimit => n) + Description + Text + Routines that assemble a homology algebra — such as @ TO homologyAlgebra @ and + @ TO torAlgebra @ — must, in general, walk up all homological degrees to find + algebra generators. When the DG algebra has generators in even degree, that + process is infinite; @ TT "GenDegreeLimit" @ caps the search. The answer is a + truncation of the full homology algebra below the specified degree, and is + exact in the bounded case. + SeeAlso + RelDegreeLimit + homologyAlgebra + torAlgebra + findTrivialMasseyOperation + isHomologyAlgebraTrivial + isGolodHomomorphism +/// + +doc /// + Key + RelDegreeLimit + Headline + Option to bound the homological degree searched for algebra relations + Usage + homologyAlgebra(..., RelDegreeLimit => n) + torAlgebra(..., RelDegreeLimit => n) + Description + Text + Once generators have been chosen (see @ TO GenDegreeLimit @), a homology algebra + computation must determine relations among them. @ TT "RelDegreeLimit" @ bounds + the homological degree up to which those relations are searched. Typical practice + is to set @ TT "RelDegreeLimit" @ to roughly twice @ TT "GenDegreeLimit" @, since + relations can be degree-sums of generator pairs. + SeeAlso + GenDegreeLimit + homologyAlgebra + torAlgebra +/// + +doc /// + Key + TMOLimit + Headline + Option to bound the arity of Massey operations searched for by findTrivialMasseyOperation + Usage + findTrivialMasseyOperation(..., TMOLimit => n) + isGolodHomomorphism(..., TMOLimit => n) + Description + Text + A trivial Massey operation on a DG algebra is a system of null-homotopies that + simultaneously trivialize every Massey product. @ TT "TMOLimit" @ caps the arity + of Massey products considered: only @ TT "k" @-fold products with @ TT "k <= n" @ + are tested for bounding. This makes the search finite even when the full system + would be infinite. In practice, @ TT "TMOLimit" @ values of @ TT "3" @ or @ TT "4" @ + suffice for small examples. + SeeAlso + findTrivialMasseyOperation + isGolodHomomorphism + isGolod +/// + +doc /// + Key + [isAcyclic,EndDegree] + Headline + Option to specify the degree to finish checking acyclicity + Usage + isAcyclic(...,EndDegree=>n) +/// + +doc /// + Key + [acyclicClosure,StartDegree] + Headline + Option to specify the degree to start computing the acyclic closure. + Usage + acyclicClosure(...,StartDegree=>n) + Description + Text + The default value is 1. +/// + +doc /// + Key + [acyclicClosure,EndDegree] + Headline + Option to specify the degree to stop computing the acyclic closure + Usage + A = acyclicClosure(B,EndDegree=>n) + Inputs + B: DGAlgebra + n: ZZ + Description + Text + All generators of the acyclic closure will be found up to homological degree n. + Note that B can be an ordinary ring, a factor ring of a polynomial ring, treated as + a DGAlgebra generated in homological degree 0, as in the following example: + + The default value of EndDegree is 3. + Example + R = ZZ/101[x,y,z,w]/(ideal"x3,y3,z3,x2yz") + acyclicClosure R + acyclicClosure(R,EndDegree => 3) +/// + +doc /// + Key + [getGenerators,StartDegree] + Headline + First homological degree in which to search for new algebra generators + Usage + getGenerators(..., StartDegree => n) + Description + Text + Degrees below @ TT "n" @ are skipped when walking @ TO getGenerators @ up through + the DG algebra. Raising @ TT "StartDegree" @ is useful when earlier degrees have + been inspected separately. See @ TO StartDegree @. + SeeAlso + getGenerators + StartDegree + [getGenerators,DegreeLimit] +/// + +doc /// + Key + [getGenerators,DegreeLimit] + Headline + Last homological degree in which to search for algebra generators + Usage + getGenerators(..., DegreeLimit => n) + Description + Text + Caps the homological degree of candidates considered by @ TO getGenerators @. + When the underlying DG algebra has generators in even degree, this option + is required to make the search finite. + SeeAlso + getGenerators + [getGenerators,StartDegree] +/// + +doc /// + Key + [killCycles,StartDegree] + Headline + First homological degree from which killCycles begins searching for cycles + Usage + killCycles(..., StartDegree => n) + Description + Text + @ TO killCycles @ searches upward from @ TT "StartDegree" @ for the first + degree containing a non-bounding cycle, then adjoins generators to kill + that cycle. See @ TO StartDegree @. + SeeAlso + killCycles + StartDegree + [killCycles,EndDegree] +/// + +doc /// + Key + [killCycles,EndDegree] + Headline + Last homological degree processed by killCycles + Usage + killCycles(..., EndDegree => n) + Description + Text + Bounds the search depth of @ TO killCycles @. By default @ TT "EndDegree => -1" @ + tells @ TO killCycles @ to stop after processing the first degree with nontrivial + homology. See @ TO EndDegree @. + SeeAlso + killCycles + EndDegree + [killCycles,StartDegree] +/// + +doc /// + Key + [killCycles,Variable] + Headline + Option to name the DG algebra generators adjoined by killCycles + Usage + killCycles(..., Variable => "T") + Description + Text + @ TO killCycles @ adjoins new DG generators that become boundaries of previously + non-bounding cycles. The @ TT "Variable" @ option specifies the base name used + for these new generators, so that subsequent calls produce readable and + non-colliding names. + SeeAlso + killCycles + acyclicClosure +/// + +doc /// + Key + [adjoinVariables,Variable] + Headline + Option to name the DG algebra generators adjoined by adjoinVariables + Usage + adjoinVariables(..., Variable => "T") + Description + Text + @ TO adjoinVariables @ adjoins a new DG generator for each cycle in its input list. + The @ TT "Variable" @ option specifies the base name used for these new generators. + SeeAlso + adjoinVariables + killCycles +/// + +doc /// + Key + [acyclicClosure,Variable] + Headline + Option to name the DG algebra generators adjoined during acyclic closure + Usage + acyclicClosure(..., Variable => "T") + Description + Text + As @ TO acyclicClosure @ iteratively @ TO2{killCycles, "kills cycles"} @, it adjoins + new DG algebra generators indexed by @ TT "T_(i, j)" @ (or the given base name). + Use the @ TT "Variable" @ option to control that base name. + SeeAlso + acyclicClosure + killCycles + Variable +/// + +doc /// + Key + [getBasis,Limit] + Headline + Maximum number of basis elements to return from getBasis + Usage + getBasis(..., Limit => n) + Description + Text + Caps the size of the returned basis, which is useful when only the first few + elements are needed (for example, when enumerating cycles degree by degree). + SeeAlso + getBasis +/// + +doc /// + Key + [homologyAlgebra,GenDegreeLimit] + Headline + Maximum homological degree searched for homology-algebra generators + Usage + homologyAlgebra(..., GenDegreeLimit => n) + Description + Text + Required when the DG algebra has generators in even degree; see @ TO GenDegreeLimit @. + SeeAlso + homologyAlgebra + GenDegreeLimit + [homologyAlgebra,RelDegreeLimit] +/// + +doc /// + Key + [homologyAlgebra,RelDegreeLimit] + Headline + Maximum homological degree searched for homology-algebra relations + Usage + homologyAlgebra(..., RelDegreeLimit => n) + Description + Text + Bounds the degree up to which relations are computed; see @ TO RelDegreeLimit @. + SeeAlso + homologyAlgebra + RelDegreeLimit + [homologyAlgebra,GenDegreeLimit] +/// + +doc /// + Key + [torAlgebra,GenDegreeLimit] + Headline + Maximum homological degree searched for Tor-algebra generators + Usage + torAlgebra(..., GenDegreeLimit => n) + Description + Text + See @ TO GenDegreeLimit @. + SeeAlso + torAlgebra + GenDegreeLimit + [torAlgebra,RelDegreeLimit] +/// + +doc /// + Key + [torAlgebra,RelDegreeLimit] + Headline + Maximum homological degree searched for Tor-algebra relations + Usage + torAlgebra(..., RelDegreeLimit => n) + Description + Text + See @ TO RelDegreeLimit @. + SeeAlso + torAlgebra + RelDegreeLimit + [torAlgebra,GenDegreeLimit] +/// + +doc /// + Key + [findTrivialMasseyOperation,GenDegreeLimit] + Headline + Maximum homological degree of generators used in the trivial Massey operation search + Usage + findTrivialMasseyOperation(..., GenDegreeLimit => n) + Description + Text + See @ TO GenDegreeLimit @. + SeeAlso + findTrivialMasseyOperation + GenDegreeLimit + TMOLimit +/// + +doc /// + Key + [findTrivialMasseyOperation,TMOLimit] + Headline + Maximum arity of Massey products searched for bounding + Usage + findTrivialMasseyOperation(..., TMOLimit => n) + Description + Text + See @ TO TMOLimit @. + SeeAlso + findTrivialMasseyOperation + TMOLimit +/// + +doc /// + Key + [isHomologyAlgebraTrivial,GenDegreeLimit] + Headline + Maximum homological degree of generators used by isHomologyAlgebraTrivial + Usage + isHomologyAlgebraTrivial(..., GenDegreeLimit => n) + Description + Text + See @ TO GenDegreeLimit @. + SeeAlso + isHomologyAlgebraTrivial + GenDegreeLimit +/// + +doc /// + Key + [isGolodHomomorphism,GenDegreeLimit] + Headline + Maximum homological degree of generators used by isGolodHomomorphism + Usage + isGolodHomomorphism(..., GenDegreeLimit => n) + Description + Text + See @ TO GenDegreeLimit @. + SeeAlso + isGolodHomomorphism + GenDegreeLimit + TMOLimit +/// + +doc /// + Key + [deviations,DegreeLimit] + Headline + Maximum homological degree to which deviations are computed + Usage + deviations(..., DegreeLimit => n) + Description + Text + Bounds the homological degree of generators reported by @ TO deviations @. + The default value is @ TT "3" @. + SeeAlso + deviations + deviationsToPoincare +/// + +doc /// + Key + [isGolodHomomorphism,TMOLimit] + Headline + Option to cap the arity of the trivial Massey operation used in isGolodHomomorphism + Usage + isGolodHomomorphism(...,TMOLimit=>n) + Description + Text + Passes through to @ TO findTrivialMasseyOperation @: only products of + up to @ TT "n" @ cycle representatives are considered when searching + for a trivial Massey operation on the Koszul homology. See + @ TO TMOLimit @. + SeeAlso + isGolodHomomorphism + TMOLimit + findTrivialMasseyOperation +/// + +doc /// + Key + Verbosity + Headline + Option controlling the verbosity level of DGAlgebras computations + Usage + homologyAlgebra(..., Verbosity => n) + getGenerators(..., Verbosity => n) + Description + Text + Several iterative routines in this package accept a @ TT "Verbosity" @ + option whose value is a nonnegative integer. Higher values print more + diagnostic output about the steps being performed (cycle searches, relation + searches, degree-by-degree progress, etc.). The default value is @ TT "0" @, + which suppresses all progress output. + SeeAlso + homologyAlgebra + getGenerators +/// + +doc /// + Key + [homologyAlgebra,Verbosity] + Headline + Option to control progress output from homologyAlgebra + Usage + homologyAlgebra(...,Verbosity=>n) + Description + Text + Larger values of @ TT "n" @ print progress messages about the + generator and relation searches carried out by + @ TO homologyAlgebra @. The default is @ TT "0" @ (no output). + SeeAlso + homologyAlgebra +/// + +doc /// + Key + [getGenerators,Verbosity] + Headline + Option to control progress output from getGenerators + Usage + getGenerators(...,Verbosity=>n) + Description + Text + Larger values of @ TT "n" @ print progress messages about the + cycle enumeration and independence checks in @ TO getGenerators @. + The default is @ TT "0" @ (no output). + SeeAlso + getGenerators +/// + +doc /// + Key + (net,DGAlgebra) + Headline + Short-form display of a DG algebra + Usage + net A + Inputs + A:DGAlgebra + Outputs + :Net + a one-line summary of @ TT "A" @ + Description + Text + Produces the text Macaulay2 prints when a @ TO DGAlgebra @ appears at the top + level. The display names the underlying ring, the generators of @ TT "A.natural" @, + and any differentials known so far, giving a compact identifier rather than a + full data dump. Use @ TT "peek A" @ or @ TT "displayBlockDiff" @ for a detailed + view of the internal state. + Example + R = ZZ/101[x,y,z] / ideal(x^3, y^3, z^3) + A = koszulComplexDGA R + net A + A + SeeAlso + DGAlgebra + displayBlockDiff +/// + +doc /// + Key + (net,DGAlgebraMap) + Headline + Short-form display of a DG algebra map + Usage + net phi + Inputs + phi:DGAlgebraMap + Outputs + :Net + a short summary of @ TT "phi" @ + Description + Text + Produces the text Macaulay2 prints when a @ TO DGAlgebraMap @ is displayed at + the top level. The output lists the action of @ TT "phi" @ on the generators + of the underlying algebra, using the information stored in @ TT "phi.natural" @. + Example + R = ZZ/101[a,b,c]/ideal{a^3 + b^3 + c^3, a*b*c} + K1 = koszulComplexDGA(ideal vars R, Variable => "Y") + K2 = koszulComplexDGA(ideal{b, c}, Variable => "T") + g = dgAlgebraMap(K1, K2, matrix{{Y_(1,2), Y_(1,3)}}) + net g + g + SeeAlso + DGAlgebraMap + dgAlgebraMap +/// + +doc/// + Key + displayBlockDiff + (displayBlockDiff, DGAlgebra, ZZ) + (displayBlockDiff, DGAlgebra, Array, Array) + (displayBlockDiff, DGAlgebra, List, List) + (displayBlockDiff, DGAlgebra, VisibleList) + Headline + Shows natural decomposition of a map in the Tate resolution + Usage + displayBlockDiff (A, n) + displayBlockDiff (A, LA, LA) + displayBlockDiff (A, L, L) + displayBlockDiff (A, V) + Inputs + A:DGAlgebra + of the sort produced by @TO acyclicClosure@ or @TO killCycles@ + n:ZZ + LA:Array + of the form [L] where L is a list representing a multi-index in the complex + L:List + of indices as above + V:VisibleList + a list, array or sequence representing a (single) multi-index in the complex defined by A + Description + Text + For example, consider the first five steps in the resolution of the residue field + in the following example: + Example + R = QQ[x,y,z]/(ideal(x^3,y^3,z^3,x*y*z)) + G = betti freeResolution (coker vars R, LengthLimit => 5) + Text + It is a free graded-commutative divided power algebra on generator of each degree starting with 1. + We can compute the beginning of the complex corresponding to the first 3 factors with as + Example + A = acyclicClosure(R,EndDegree => 2) + F = toComplex(A, 5); + betti F + betti F + Text + Since we gave @TO acyclicClosure @ the EndDegree 2, the complex produced is exact up to + step 2; that is betti F and betti G agree up to the column 3. There are 3 chunks of generators + in A, of homological degrees 1,2,3. + + From the betti table of F + we see the three generators of the exterior algebra K as the linear part of the resolution. + The second strand, 4,12,12,4 is the tensor product of K with the 4 generators of A that have + homological degree 2 and correspond to the 4 generators of I; thus they have internal degree 3. + We also see that A has three generators in homological degree 3 and internal degree 5. + K multiplied by these generators accounts for 3,9,9 of the 3rd strand, while the symmetric + square of the 4 generators of homological degree 2, account for 0,10,0 and the product of + these with the generators of degree 1 account for the remaining 0,0,30. Finally the 12 + in the 4th row represents the product of the 4 generators of A in homological degree with + the 3 in degree 3. + + The native display of the differentials of this complex does not distinguish these pieces, + but displayBlockDiff allows one to look at them in various ways: + Example + F.dd_3 + displayBlockDiff(A,3) + Text + Here the triples of numbers represent the number of factors from the generators of each of the three chunks of + variables: the first index gives the number from the Koszul complex, the second from the 4 variable in homological degree + 2 and the third the number from the 3 variables of homological degree 3. Thus the sum of the + three indices is the homological degree. The sources of the blocks are listed on the top row, the targets + are given by the columns. + We can see the lists of indices of the source and target with + Example + indices source blockDiff(A,5) + indices target blockDiff(A,5) + Text + We can extract one or several blocks from F.dd_3 as follows: + Example + R = QQ[x,y,z]/(ideal(x^3,y^3,z^3,x*y*z)) + A = acyclicClosure(R,EndDegree => 2) + Text + We can specify a lists of source and target degrees, either as lists representing an + index for th source and an index for the target: + Example + displayBlockDiff(A, {0,2,3}, {0,4,0}) + Text + or as a pair of arrays of such lists + Example + displayBlockDiff(A, [{0,2,3}], [{1,0,3},{0,4,0}]) + Text + or look at all the blocks with a given target summand with: + Example + displayBlockDiff(A, (1,0,3) ) + SeeAlso + blockDiff + acyclicClosure +/// + +doc /// + Key + blockDiff + (blockDiff, DGAlgebra, ZZ) + Headline + prepares a map for display + Usage + X = blockDiff(A,n) + Inputs + A: DGAlgebra + n: ZZ + Outputs + X:Matrix + map between labeled direct sums + Description + Text + Use @TO displayBlockDiff@ to display the blocks in various ways. + SeeAlso + displayBlockDiff +/// diff --git a/M2/Macaulay2/packages/DGAlgebras/tests.m2 b/M2/Macaulay2/packages/DGAlgebras/tests.m2 new file mode 100644 index 00000000000..05dee7f943e --- /dev/null +++ b/M2/Macaulay2/packages/DGAlgebras/tests.m2 @@ -0,0 +1,1193 @@ +TEST /// +R = QQ[x,y,z]/(ideal(x^3,y^3,z^3,x*y*z)) +A = acyclicClosure(R,EndDegree => 2) +X = blockDiff(A,3) +Y = blockDiff(A,2) +--reconstruct a composite block: +assert(0 == Y_[{2,0,0}]^[{1,0,0}] *X^[{2,0,0}]_[{1,2,0}] + Y_[{0,2,0}]^[{1,0,0}] *X^[{0,2,0}]_[{1,2,0}] ) + +/// +TEST /// +-- test 0 : isHomogeneous, toComplex, maxDegree +R = ZZ/101[x,y,z] +A1 = freeDGAlgebra(R,{{1},{1},{1},{3}}) +setDiff(A1,{x,y,z,x*T_(1,2)*T_(1,3)-y*T_(1,1)*T_(1,3)+z*T_(1,1)*T_(1,2)}) +assert(not A1.isHomogeneous) +A1dd = toComplex(A1) +A1dd.dd + +A2 = freeDGAlgebra(R,{{1,1},{1,1},{1,1},{3,3}}) +setDiff(A2,{x,y,z,x*T_(1,2)*T_(1,3)-y*T_(1,1)*T_(1,3)+z*T_(1,1)*T_(1,2)}) +assert(A2.isHomogeneous) +A2dd = toComplex(A2) +A2dd.dd + +B1 = koszulComplexDGA(R) +assert(B1.isHomogeneous) +B1dd = toComplex(B1) +B1dd.dd + +R = ZZ/101[x,y,z] +R2 = R/ideal {x^2-z^3} +B2 = koszulComplexDGA(R2) +assert(not B2.isHomogeneous) +B2dd = toComplex(B2) +B2dd.dd + +R = QQ[x,y,z] +B = koszulComplexDGA(R) +toComplex(B) +degrees B.natural +A = freeDGAlgebra(R,{{1},{1},{1},{3}}) +setDiff(A,{x,y,z,x*T_(1,2)*T_(1,3)-y*T_(1,1)*T_(1,3)+z*T_(1,1)*T_(1,2)}) +Add = toComplex(A) +assert(apply(maxDegree(A)+1, i -> prune HH_i(Add)) == {coker vars R,0,0,coker vars R,0,0,0}) +/// + +TEST /// +-- test 1 : differential tests +R = ZZ/101[x,y,z, Degrees => {2,2,3}] +kRes = freeResolution coker vars R +kRes.dd_3 +A = koszulComplexDGA(R) +d3 = A.dd_3 +d2 = A.dd_2 +d1 = A.dd_1 +assert(source d1 == target d2) +assert(source d2 == target d3) +assert(d1*d2 == 0) +assert(d2*d3 == 0) +S1 = R/ideal (x^3-z^2) +B1 = koszulComplexDGA(S1) +d3 = B1.dd_3 +d2 = B1.dd_2 +d1 = B1.dd_1 +assert(source d1 == target d2) +assert(source d2 == target d3) +assert(d1*d2 == 0) +assert(d2*d3 == 0) +use R +S2 = R/ideal (x^4-z^2) +B2 = koszulComplexDGA(S2) +d3 = B2.dd_3 +d2 = B2.dd_2 +d1 = B2.dd_1 +assert(source d1 == target d2) +assert(source d2 == target d3) +assert(d1*d2 == 0) +assert(d2*d3 == 0) +/// + +TEST /// +--- test 2 : homology, homologyAlgebra, HH_ZZ, HH +R = ZZ/32003[a,b,x,y]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} +koszulR = koszul vars R +time apply(5,i -> numgens prune HH_i(koszulR)) +A = koszulComplexDGA(R) +HH_2(A) +HH(A) +hh2 = prune HH_2(koszulR) +hh2' = prune HH_2(A) +assert(hh2 == hh2') +/// + +TEST /// +-- test 3 : torAlgebra, deviations +R1 = QQ[x,y,z]/ideal{x^3,y^4,z^5} +TorR1 = torAlgebra(R1,GenDegreeLimit=>4) +devR1 = deviations(R1,DegreeLimit=>4) +use R1 +M = coker matrix {{x^2*y^3*z^4}} +Mres = freeResolution(M, LengthLimit => 7) +R2 = QQ[x,y,z]/ideal{x^3,y^4,z^5,x^2*y^3*z^4} +time TorR1R2 = torAlgebra(R1,R2,GenDegreeLimit=>5,RelDegreeLimit=>10) +-- the multiplication is trivial, since the map R3 --> R4 is Golod +numgens TorR1R2 +numgens ideal TorR1R2 +apply(21, i -> #(flatten entries getBasis(i,TorR1R2))) +assert(sum oo - 1 == numgens TorR1R2) +/// + +TEST /// +-- test 4 : findEasyRelations +debug DGAlgebras +R1 = ZZ/32003[a,b,x,y]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} +R2 = ZZ/32003[a,b,x,y,Degrees=>{1,1,2,2}]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} +A1 = koszulComplexDGA(R1) +A2 = koszulComplexDGA(R2) +cycleList1 = getGenerators(A1,DegreeLimit=>4) +cycleList2 = getGenerators(A2,DegreeLimit=>4) +HAEasy1 = findEasyRelations(A1,cycleList1) +HAEasy2 = findEasyRelations(A2,cycleList2) +tally ((flatten entries basis HAEasy1) / degree) +pairs (tally ((flatten entries basis HAEasy1) / degree)) +myList1 = {({4,8},1),({3,4},1),({3,5},6),({3,6},6),({3,7},4),({2,3},4),({2,4},11),({2,5},8),({2,6},4),({1,2},4),({1,3},4),({1,4},1),({0,0},1)} +myList2 = {({0},1),({1},9),({2},27),({3},17),({4},1)} +tally ((flatten entries basis HAEasy1) / degree) +tally myList1 +assert(pairs tally((flatten entries basis HAEasy1) / degree) == myList1) +assert(pairs tally((flatten entries basis HAEasy2) / degree) == myList2) +/// + +TEST /// +-- test 5 : homology of a DGA whose H_0 is not a field +R = ZZ/32003[a,b] +I = ideal{a^6,b^6} +A = koszulComplexDGA(I) +HA = HH A +describe HA +use R +J = I + ideal {a^4*b^5,a^5*b^4} +B = koszulComplexDGA(J) +getGenerators(B) +apply(5, i -> numgens prune homology(i,B)) +apply(5, i -> prune homology(i,B)) +HB = HH B +HB2 = zerothHomology B +HB.cache.cycles +ideal HB +-- looks right... +getDegNModule(0,HB2,HB) +getDegNModule(1,HB2,HB) +getDegNModule(2,HB2,HB) +getDegNModule(3,HB2,HB) +getDegNModule(4,HB2,HB) + +R = ZZ/32003[a,b,c] +I = (ideal vars R)^2 +A = koszulComplexDGA(I) +apply(10, i -> prune homology(i,A)) +time HA = HH A +HA2 = zerothHomology A +tally ((ideal HA)_* / degree / first) +select ((ideal HA)_*, f -> first degree f == 2) +-- looks right... +getDegNModule(0,HA2,HA) +getDegNModule(1,HA2,HA) +getDegNModule(2,HA2,HA) +getDegNModule(3,HA2,HA) +-- need to add asserts +/// + +TEST /// +-- test 6 : homologyAlgebra +R = ZZ/32003[a,b,x,y]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} +koszulR = koszul vars R +time apply(5,i -> numgens prune HH_i(koszulR)) +A = koszulComplexDGA(R) +time apply(5,i -> numgens prune homology(i,A)) +-- ~2.15 seconds on mbp, with graded differentials +time HA = HH A +assert(numgens HA == 34) +assert(numgens ideal HA == 576) +assert(#(first degrees HA) == 2) + +-- same example, but not graded because of the degree change. The homologyAlgebra function +-- will then only return a graded algebra +R2 = ZZ/32003[a,b,x,y,Degrees=>{1,1,2,2}]/ideal{a^3,b^3,x^3,y^3,a*x,a*y,b*x,b*y,a^2*b^2-x^2*y^2} +koszulR2 = koszul vars R2 +time apply(5,i -> numgens prune HH_i(koszulR2)) +A2 = koszulComplexDGA(R2) +time apply(5,i -> numgens prune homology(i,A2)) +-- ~2.85 seconds on mbp, with ungraded differentials +time HA2 = homologyAlgebra A2 +assert(numgens HA2 == 34) +assert(numgens ideal HA2 == 576) +-- should only be singly graded +assert(#(first degrees HA2) == 1) +/// + +TEST /// +-- test 7 : acyclicClosure, isHomologyAlgebraTrivial, isGolod, isGolodHomomorphism +R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} +M = coker matrix {{a^3*b^3*c^3*d^3}}; +S = R/ideal{a^3*b^3*c^3*d^3} +time A = acyclicClosure(R,EndDegree=>6) +B = A ** S +assert(isHomologyAlgebraTrivial(B,GenDegreeLimit=>6)) +assert(isGolodHomomorphism(S,GenDegreeLimit=>6,TMOLimit=>3)) +-- returns true since R --> S is Golod +R = ZZ/101[a,b,c,d]/ideal{a^4,b^4,c^4,d^4} +A = koszulComplexDGA(R) +assert(not isHomologyAlgebraTrivial(A)) +assert(not isGolod R) +-- false, since R is Gorenstein, and so HA has Poincare Duality +/// + +TEST /// +-- test 8 : DGAlgebra ** DGAlgebra - need to add in an assert +R = ZZ/101[a,b,c,d] +I = ideal(a,b) +J = ideal(c,d) +A = koszulComplexDGA(I) +B = koszulComplexDGA(J) +Cdd = toComplex(A ** B) +Cdd.dd +/// + +TEST /// +-- test 9 : isHomologyAlgebraTrivial, getGenerators, findTrivialMasseyOperation +Q = ZZ/101[x_1..x_6] +I = ideal (x_3*x_5,x_4*x_5,x_1*x_6,x_3*x_6,x_4*x_6) +R = Q/I +A = koszulComplexDGA(R) +isHomologyAlgebraTrivial(A,GenDegreeLimit=>3) +cycleList = getGenerators(A) +assert(first findTrivialMasseyOperation(A)) + +-- this is a Teter ring, and the computation in Avramov and Levin's paper shows +-- H(A) does not have trivial multiplication. +Q = ZZ/101[x,y,z] +I = ideal (x^3,y^3,z^3,x^2*y^2*z^2) +R = Q/I +A = koszulComplexDGA(R) +assert(not isHomologyAlgebraTrivial(A,GenDegreeLimit=>3)) +cycleList = getGenerators(A) +prodList = apply(subsets(cycleList,2), l -> (first degree l#0 + first degree l#1,l#0*l#1)); +assert(not first findTrivialMasseyOperation(A)) +/// + +TEST /// +-- test 10 : isAcyclic +R = ZZ/101[a,b,c,d] +A = koszulComplexDGA(R) +B = koszulComplexDGA({a^4,b^4,c^4,d^4}) +C = koszulComplexDGA((ideal vars R)^2) +assert(isAcyclic A) +assert(isAcyclic B) +assert(not isAcyclic C) +/// + +TEST /// +-- test 11 : isGolod and isHomologyAlgebraTrivial example +-- Interesting case due to Katthan. +Q = ZZ/101[x_1,x_2,y_1,y_2,z,w] +I = ideal {x_1*x_2^2,z^2*w,y_1*y_2^2,x_2^2*z*w,y_2^2*z^2,x_1*x_2*y_1*y_2,x_2^2*y_2^2*z,x_1*y_1*z} +R = Q/I +assert(isHomologyAlgebraTrivial koszulComplexDGA R == true) +assert(isGolod R == false) +/// + +TEST /// +--- test 12: isGolod and isHomologyAlgebraTrivial example again +--- This example is due to Roos +S = QQ[x,y,z,u] +I = ideal(u^3, x*y^2, (x+y)*z^2, x^2*u+z*u^2, y^2*u+x*z*u, y^2*z+y*z^2) + -- you can see that the mult on the koszul homology will be trivial +betti (A = freeResolution I) +R = S/I +assert(isHomologyAlgebraTrivial koszulComplexDGA R == true) +assert(isGolod R == false) +/// + +TEST /// +-- test 13: freeDGAlgebra variable-naming convention and Variable option +R = QQ[x, y, z] +A = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}) +-- Default base symbol T, doubly-indexed by (hom-degree, counter). +assert(toString gens A.natural == "{T_(1,1), T_(1,2), T_(1,3), T_(3,1)}") +B = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}, Variable => "U") +assert(toString gens B.natural == "{U_(1,1), U_(1,2), U_(1,3), U_(3,1)}") +-- List form: each symbol supplied verbatim. +C = freeDGAlgebra(R, {{1}, {1}, {1}}, Variable => {getSymbol "P", getSymbol "Q", getSymbol "RR"}) +assert(toString gens C.natural == "{P, Q, RR}") +assert(numgens C.natural == 3) +-- Multigrade vs single-grade homogeneity. +D1 = freeDGAlgebra(R, {{1, 1}, {1, 1}, {1, 1}, {3, 3}}) +setDiff(D1, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}) +assert(isHomogeneous D1) +D2 = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}) +setDiff(D2, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}) +assert(not isHomogeneous D2) +/// + +TEST /// +-- test 14: koszulComplexDGA naming convention and Variable option +R = QQ[a, b, c] +A = koszulComplexDGA R +assert(toString gens A.natural == "{T_(1,1), T_(1,2), T_(1,3)}") +AS = koszulComplexDGA(R, Variable => "S") +assert(toString gens AS.natural == "{S_(1,1), S_(1,2), S_(1,3)}") +-- Koszul complex on a regular sequence has acyclic higher homology. +assert(isAcyclic A) +-- Differentials on Koszul gens match the ring vars. +natGens = gens A.natural +assert((A.diff) natGens_0 == sub(a, A.natural)) +assert((A.diff) natGens_1 == sub(b, A.natural)) +assert((A.diff) natGens_2 == sub(c, A.natural)) +-- (koszulComplexDGA, List) form: regular sequence in polynomial ring. +S = QQ[x, y, z] +B = koszulComplexDGA({x^2, y*z}) +assert(numgens B.natural == 2) +-- (x^2, yz) is regular in QQ[x,y,z], so Koszul homology vanishes in deg > 0. +assert(prune HH_1 toComplex B == 0) +assert(prune HH_2 toComplex B == 0) +/// + +TEST /// +-- test 15: acyclicClosure Tate construction and variable convention +-- C.i. case: exactly three hom-degree-2 gens kill the three Koszul relation cycles. +R = ZZ/101[a, b, c] / ideal(a^3, b^3, c^3) +A = koszulComplexDGA R +B = acyclicClosure(A, EndDegree => 2) +assert(numgens B.natural == 6) +-- The Tate theorem: for a c.i., no further gens appear past hom-degree 2. +B2 = acyclicClosure(A, EndDegree => 4) +assert(numgens B2.natural == numgens B.natural) +-- Differentials on the newly adjoined generators are a_i^2 * T_(1,i). +(aa, bb, cc) = (sub(a, B.natural), sub(b, B.natural), sub(c, B.natural)) +natGens = gens B.natural +-- First three gens are T_(1,*) with diff a,b,c. +assert((B.diff) natGens_0 == aa) +assert((B.diff) natGens_1 == bb) +assert((B.diff) natGens_2 == cc) +-- Next three gens are T_(2,*) with diff a_i^2 * T_(1,i). +assert((B.diff) natGens_3 == aa^2 * natGens_0) +assert((B.diff) natGens_4 == bb^2 * natGens_1) +assert((B.diff) natGens_5 == cc^2 * natGens_2) +-- Variable => ... renames only the new generators. +D = acyclicClosure(A, EndDegree => 2, Variable => "U") +assert(toString take(gens D.natural, 3) == "{T_(1,1), T_(1,2), T_(1,3)}") +assert(toString drop(gens D.natural, 3) == "{U_(2,1), U_(2,2), U_(2,3)}") +/// + +TEST /// +-- test 16: acyclicClosure deviation sequences +-- Non-c.i. has higher deviations; c.i. does not. +S = QQ[x, y, z] / ideal(x^2, y^2, z^2) +CS = acyclicClosure(S, EndDegree => 4) +homdegCounts = tally apply(CS.Degrees, d -> first d) +assert(homdegCounts#1 == 3) -- three Koszul exterior gens +assert(homdegCounts#2 == 3) -- three Tate polynomial gens +assert(not homdegCounts#?3) -- no hom-deg 3 gens (c.i.) +assert(not homdegCounts#?4) -- no hom-deg 4 gens (c.i.) +T = QQ[x, y] / ideal(x^2, x*y, y^2) +CT = acyclicClosure(T, EndDegree => 3) +homdegCountsT = tally apply(CT.Degrees, d -> first d) +assert(homdegCountsT#1 == 2) -- two Koszul exterior gens +assert(homdegCountsT#2 == 3) -- three relation-killers +assert(homdegCountsT#?3) -- non-c.i. has hom-deg 3 deviation +/// + +TEST /// +-- test 17: adjoinVariables preserves existing names, cycleCheck via polyDifferential +R = ZZ/101[a, b, c] / ideal(a^3, b^3, c^3) +A = koszulComplexDGA R +z = a^2 * (gens A.natural)_0 +assert(polyDifferential(A, z) == 0) +B = adjoinVariables(A, {z}) +assert(numgens B.natural == numgens A.natural + 1) +-- First three gens of B preserve A's names; new gen continues the counter. +assert(toString take(gens B.natural, 3) == "{T_(1,1), T_(1,2), T_(1,3)}") +-- Variable => ... renames only the new generator. +C = adjoinVariables(A, {z}, Variable => "U") +assert(toString last gens C.natural == "U_(2,1)") +-- The added generator's differential is exactly the cycle z. +assert((B.diff) last gens B.natural == sub(z, B.natural)) +/// + +TEST /// +-- test 18: setDiff ordering, validity via isWellDefined +R = ZZ/101[x, y, z] +A = freeDGAlgebra(R, {{1}, {1}, {1}, {3}}) +setDiff(A, {x, y, z, x*T_(1,2)*T_(1,3) - y*T_(1,1)*T_(1,3) + z*T_(1,1)*T_(1,2)}) +-- Differentials are in gen-order matching A.Degrees. +diffs = take(flatten entries matrix A.diff, numgens A.natural) +assert(diffs#0 == x) +assert(diffs#1 == y) +assert(diffs#2 == z) +assert(isWellDefined A) +/// + +TEST /// +-- test 19: freeDGModule construction, setDiff, and matrix factorization +-- 2-periodic min. free resolution of k = R/x over R = QQ[x]/(x^2). +R = QQ[x] / ideal(x^2) +A = koszulComplexDGA R +F = freeDGModule(A, toList(0..4)) +natGens = apply(rank F.natural, i -> (F.natural)_i) +setDiff(F, {0, x * natGens#0, x * natGens#1, x * natGens#2, x * natGens#3}) +-- Every consecutive pair of differentials composes to zero. +d1 = moduleDifferential(1, F) +d2 = moduleDifferential(2, F) +d3 = moduleDifferential(3, F) +d4 = moduleDifferential(4, F) +assert(d1 * d2 == 0) +assert(d2 * d3 == 0) +assert(d3 * d4 == 0) +assert(isWellDefined F) +/// + +TEST /// +-- test 20: koszulComplexDGM over regular ring vs complete intersection +-- Regular ring: higher Koszul-module homology vanishes. +S1 = QQ[x, y, z] +KM1 = koszulComplexDGM S1^1 +C1 = toComplex KM1 +assert(prune HH_1 C1 == 0) +assert(prune HH_2 C1 == 0) +assert(prune HH_3 C1 == 0) +-- Complete intersection: higher Koszul-module homology is nonzero. +S2 = QQ[x, y] / ideal(x^2, y^2) +KM2 = koszulComplexDGM S2^1 +C2 = toComplex KM2 +assert(prune HH_1 C2 != 0) +assert(prune HH_2 C2 != 0) +/// + +TEST /// +-- test 21: dgModuleMap mult-by-y on semifree resolution of residue field +-- Over R = k[x,y]/(x^2,y^2), multiplying by y annihilates the residue field, +-- so the induced chain map has zero action on all homology. +R = QQ[x, y] / ideal(x^2, y^2) +A = koszulComplexDGA R +Mdg = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 3) +natGens = apply(rank Mdg.natural, i -> (Mdg.natural)_i) +fy = dgModuleMap(Mdg, Mdg, apply(natGens, g -> y * g)) +assert(isWellDefined fy) +cmy = toComplexMap fy +assert(all(0..3, n -> prune HH_n cmy == 0)) +-- Matrix form also works for the identity. +idmat = id_(Mdg.natural) +g = dgModuleMap(Mdg, Mdg, idmat) +assert(isWellDefined g) +assert(g == identityDGModuleMap Mdg) +/// + +TEST /// +-- test 22: identityDGModuleMap and zeroDGModuleMap composition laws +R = ZZ/101[x, y] / ideal(x^2, y^2) +A = koszulComplexDGA R +KM = koszulComplexDGM R^1 +-- id_KM is the identity DGModuleMap; also compares equal to 1. +assert(id_KM == identityDGModuleMap KM) +assert(id_KM == 1) +-- Composition with mult-by-y on a semifree resolution. +Mdg = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 2) +natGens = apply(rank Mdg.natural, i -> (Mdg.natural)_i) +fy = dgModuleMap(Mdg, Mdg, apply(natGens, v -> y * v)) +idMdg = identityDGModuleMap Mdg +assert(idMdg * fy == fy) +assert(fy * idMdg == fy) +assert(idMdg * idMdg == idMdg) +-- Zero map: neutral for +, absorbing for *. +zM = zeroDGModuleMap(Mdg, Mdg) +assert(idMdg + zM == idMdg) +assert(zM * idMdg == zM) +assert(idMdg * zM == zM) +-- Cokernel of zero recovers the module (rank equality). +Q = cokernel zM +assert(rank Q.natural == rank Mdg.natural) +K = kernel zM +assert(instance(K, DGSubmodule)) +/// + +TEST /// +-- test 23: dgSubmodule d-closure on a minimal semifree resolution +R = QQ[x, y] / ideal(x^2, y^2) +A = koszulComplexDGA R +Mdg = minimalSemifreeResolution(A, R^1 / ideal(x, y), EndDegree => 3) +-- Seeding with hom-degree-2 gens; d-closure pulls in hom-degree-4 gens. +pos2 = positions(Mdg.Degrees, d -> d#0 == 2) +seedHi = (id_(Mdg.natural))_pos2 +Shi = dgSubmodule(Mdg, seedHi) +assert(isWellDefined Shi) +assert(maxDegree Shi == 4) +-- Higher-degree gens are pulled in by d-closure (dim > seed dim). +assert(rank (inclusion Shi).source > numcols seedHi) +/// + +TEST /// +-- test 24: dgIdeal d-saturation of a non-cycle seed +R = QQ[x, y] +A = koszulComplexDGA R +T1 = (A.natural)_0 +T2 = (A.natural)_1 +-- T_1 * T_2 is not a cycle: d(T_1*T_2) = -y*T_1 + x*T_2. +dz = polyDifferential(A, T1 * T2) +assert(dz != 0) +J = dgIdeal(A, {T1 * T2}) +-- d-saturation should include both T_1 * T_2 and its differential. +mg = mingens J.natural +assert(numcols mg == 2) +-- Unit and zero edge-cases. +Z = dgIdeal(A, {}) +assert(isZero Z) +U = dgIdeal(A, {1_(A.natural), x_(A.natural)}) +assert(U.natural == ideal 1_(A.natural)) +/// + +TEST /// +-- test 25: DGSubmodule / DGQuotientModule types and accessors +R = ZZ/101[x] +A = koszulComplexDGA R +M = freeDGModule(A, {0}) +S = dgSubmodule(M, id_(M.natural)) +assert(ambient S === M) +assert(source inclusion S === S) +assert(target inclusion S === M) +Z = dgSubmodule(M, {}) +Q = M / Z +assert(instance(Q, DGQuotientModule)) +assert(ambient Q === M) +assert(subDGModule Q === Z) +assert(source projection Q === M) +assert(target projection Q === Q) +-- freeDGModule with multigraded ring +Rg = ZZ/101[x, y, Degrees => {{1, 0}, {0, 1}}] +Ag = koszulComplexDGA Rg +Mg = freeDGModule(Ag, {{0, 0, 0}, {1, 0, 0}}) +assert(#degrees Mg == 2) +-- dgQuotientModule explicit constructor +Qexp = dgQuotientModule(M, Z) +assert(instance(Qexp, DGQuotientModule)) +assert(ambient Qexp === M) +assert(subDGModule Qexp === Z) +-- full-sub quotient is zero +Sfull = dgSubmodule(M, id_(M.natural)) +assert(isZero (M / Sfull)) +-- ambient DGIdeal +I = dgIdeal(A, {x_(A.natural)}) +assert(ambient I === A) +/// + +TEST /// +-- test 26: module-like API on DG modules (numgens/rank/degrees/isHomogeneous/isFreeDGModule/isZero) +R = ZZ/101[x, y] +A = koszulComplexDGA R +Anat = A.natural +M = freeDGModule(A, {0, 1, 2}) +assert(numgens M == 3) +assert(rank M == 3) +assert(degrees M == {{0,0},{1,0},{2,0}}) +assert(isHomogeneous M) +assert(isFreeDGModule M) +assert(not isZero M) +-- Submodule accessors +M1 = freeDGModule(A, {0}) +S = dgSubmodule(M1, matrix {{x_Anat, y_Anat}}) +assert(numgens S == 2) +assert(isHomogeneous S) +Q = M1 / S +assert(numgens Q == numgens M1) +assert(isHomogeneous Q) +assert(super S === M1) +assert(super Q === M1) +assert(cover Q === M1) +assert(numcols relations Q == 2) +-- Zero module +M0 = freeDGModule(A, {}) +assert(isZero M0) +assert(numgens M0 == 0) +Zsub = dgSubmodule(M1, map(M1.natural, Anat^0, 0)) +assert(isZero Zsub) +Full = dgSubmodule(M1, id_(M1.natural)) +assert(isZero (M1 / Full)) +-- Free-after-tensor +MNK = (M ** R^2) ** R^2 +assert(isFreeDGModule MNK) +/// + +TEST /// +-- test 27: DGSubmodule operations (+, intersect, ==, isSubset) +R = ZZ/101[x, y] +A = koszulComplexDGA R +Anat = A.natural +M = freeDGModule(A, {0}) +S = dgSubmodule(M, matrix {{x_Anat}}) +T = dgSubmodule(M, matrix {{y_Anat}}) +ST = S + T +assert(isWellDefined ST) +assert(isSubset(S, ST) and isSubset(T, ST)) +assert((S + S) == S) +-- intersect +T2 = dgSubmodule(M, matrix {{x_Anat * y_Anat}}) +cap = intersect(S, T2) +assert(isWellDefined cap) +assert(isSubset(cap, S) and isSubset(cap, T2)) +Zsub = dgSubmodule(M, map(M.natural, Anat^0, 0)) +assert(isZero intersect(S, Zsub)) +-- equality/inclusion +U = dgSubmodule(M, matrix {{x_Anat, y_Anat}}) +assert(isSubset(S, U)) +assert(not isSubset(U, S)) +assert(S == S) +-- Different ambient => not equal +M' = freeDGModule(A, {0}) +S' = dgSubmodule(M', matrix {{x_Anat}}) +assert(not (S == S')) +/// + +TEST /// +-- test 28: image, kernel, cokernel of DGModuleMap +R = ZZ/101[x] +A = koszulComplexDGA R +M = freeDGModule(A, {0, 1}) +natGens = apply(rank M.natural, i -> (M.natural)_i) +setDiff(M, {0, x * natGens#0}) +idM = identityDGModuleMap M +zM = zeroDGModuleMap(M, M) +-- image +imId = image idM +imZ = image zM +assert(isWellDefined imId) +assert(isWellDefined imZ) +assert(numcols (inclusion imId).natural == rank M.natural) +assert(numcols (inclusion imZ).natural == 0) +-- image of inclusion recovers the submodule +Sfull = dgSubmodule(M, id_(M.natural)) +assert((image (inclusion Sfull)) == Sfull) +-- kernel +kerId = kernel idM +kerZ = kernel zM +assert(isWellDefined kerId) +assert(isWellDefined kerZ) +assert(numcols (inclusion kerId).natural == 0) +assert(numcols (inclusion kerZ).natural == rank M.natural) +assert(isZero kernel (inclusion Sfull)) +-- cokernel +assert(isZero cokernel idM) +Q = cokernel zM +assert(ambient Q === M) +assert(subDGModule Q == image zM) +assert(isZero cokernel (inclusion Sfull)) +/// + +TEST /// +-- test 29: homology of DG modules and DG module maps +R = QQ[x, y]/ideal(x^2, y^2) +A = koszulComplexDGA R +k = R^1 / ideal(x, y) +M = minimalSemifreeResolution(A, k, EndDegree => 2) +H0 = prune homology(0, M) +assert(numgens H0 == 1) +idM = identityDGModuleMap M +h0 = homology(idM, 0) +assert(h0 == id_(source h0)) +-- Regular ring: higher Koszul-module homology vanishes. +RR1 = QQ[x, y, z] +KM = koszulComplexDGM RR1^1 +assert(numgens prune homology(0, KM) == 1) +assert(prune homology(1, KM) == 0) +assert(prune homology(2, KM) == 0) +-- homology functor on DG module +HM = homology M +assert(ring HM === HH A) +HM' = homologyModule M +assert(HM == HM') +-- homology of mult-by-x is zero on resolution of residue field +natGens = apply(rank M.natural, i -> (M.natural)_i) +xMap = dgModuleMap(M, M, apply(natGens, g -> x * g)) +h0x = homology(xMap, 0) +assert(h0x == map(target h0x, source h0x, 0)) +-- zero map induces zero homology +zM = zeroDGModuleMap(M, M) +h0z = homology(zM, 0) +assert(h0z == map(target h0z, source h0z, 0)) +/// + +TEST /// +-- test 30: homology of DGQuotientModule and homologyClass +R = ZZ/101[x] +A = koszulComplexDGA R +M = freeDGModule(A, {0, 1}) +natGens = apply(rank M.natural, i -> (M.natural)_i) +setDiff(M, {0, x * natGens#0}) +S = dgSubmodule(M, (id_(M.natural))_{1}) +Q = M / S +h0 = prune homology(0, Q) +h1 = prune homology(1, Q) +h2 = prune homology(2, Q) +assert(numgens h0 == 1) +assert(numgens h1 == 1) +assert(h2 == 0) +HQ = homology Q +assert(ring HQ === HH A) +HQ' = homologyModule Q +assert(HQ == HQ') +-- homologyClass: cycle is nonzero, boundary is zero +z = natGens#0 +H = prune homology(0, M) +hc = homologyClass(M, z) +assert(hc != 0_H) +b = x * natGens#0 +hb = homologyClass(M, b) +assert(hb == 0_(prune homology(0, M))) +/// + +TEST /// +-- test 31: prune / minimalPresentation +R = ZZ/101[x, y] +A = koszulComplexDGA R +Anat = A.natural +M = freeDGModule(A, {0}) +-- Redundant seed pruned to one generator. +S = dgSubmodule(M, matrix {{1_Anat, x_Anat, y_Anat}}) +Sp = prune S +assert(numcols (inclusion S).natural == 3) +assert(numcols (inclusion Sp).natural == 1) +assert(image (inclusion S).natural == image (inclusion Sp).natural) +assert(isWellDefined Sp) +Sp' = minimalPresentation S +assert(numcols (inclusion Sp').natural == 1) +-- prune on quotient +Q = M / S +Qp = prune Q +assert(image (inclusion Qp.subDGModule).natural == image (inclusion S).natural) +Qp' = minimalPresentation Q +assert(image (inclusion Qp'.subDGModule).natural == image (inclusion S).natural) +-- prune on DGModule / DGAlgebra / maps is identity +Mfree = freeDGModule(A, {0, 1}) +assert((prune Mfree) === Mfree) +assert((minimalPresentation Mfree) === Mfree) +assert((prune A) === A) +assert((minimalPresentation A) === A) +phi = identityDGAlgebraMap A +assert((prune phi) === phi) +assert((minimalPresentation phi) === phi) +idMp = identityDGModuleMap Mfree +assert((prune idMp) === idMp) +assert((minimalPresentation idMp) === idMp) +/// + +TEST /// +-- test 32: predicates (isWellDefined, isAcyclic, isQuasiIsomorphism, annihilator) +R = QQ[x, y, z] +A = koszulComplexDGA R +assert(isWellDefined A) +assert(isAcyclic A) +-- Acyclic on a DG module (Koszul on regular ring) +KM = koszulComplexDGM R^1 +assert(isAcyclic KM) +-- Non-acyclic on c.i. +Rci = QQ[x, y]/ideal(x^2, y^2) +KMci = koszulComplexDGM Rci^1 +assert(not isAcyclic(KMci, EndDegree => 3)) +-- Identity DGAlgebraMap is quasi-iso +phi = identityDGAlgebraMap A +assert(isWellDefined phi) +assert(isQuasiIsomorphism phi) +-- Submodule/quotient well-definedness +Anat = A.natural +Mfree = freeDGModule(A, {0}) +Sub = dgSubmodule(Mfree, matrix {{1_Anat}}) +Quo = Mfree / Sub +assert(isWellDefined Sub) +assert(isWellDefined Quo) +-- isWellDefined on DGModuleMap +Aci = koszulComplexDGA Rci +Mdg = minimalSemifreeResolution(Aci, Rci^1 / ideal(x, y), EndDegree => 2) +idM = identityDGModuleMap Mdg +zM = zeroDGModuleMap(Mdg, Mdg) +assert(isWellDefined idM) +assert(isWellDefined zM) +-- annihilator of submodule +A2 = koszulComplexDGA (ZZ/101[x, y]) +A2nat = A2.natural +M2 = freeDGModule(A2, {0}) +Sx = dgSubmodule(M2, matrix {{x_A2nat}}) +Ian = annihilator Sx +assert(isWellDefined Ian) +S0 = dgSubmodule(M2, map(M2.natural, A2nat^0, 0)) +assert(annihilator S0 == dgIdeal(A2, {1_A2nat})) +-- annihilator of quotient +Q2 = M2 / Sx +IQ = annihilator Q2 +assert(isWellDefined IQ) +assert(isSubset(dgIdeal(A2, {x_A2nat}), IQ)) +-- quotient acyclicity edge case +R0 = ZZ/101[x] +A0 = koszulComplexDGA R0 +M0 = freeDGModule(A0, {0, 1}) +natGens0 = apply(rank M0.natural, i -> (M0.natural)_i) +setDiff(M0, {0, x * natGens0#0}) +Sfull0 = dgSubmodule(M0, id_(M0.natural)) +assert(isAcyclic (M0 / Sfull0)) +/// + +TEST /// +-- test 33: koszulComplexDGM, adjoinGenerators, killCycles +R = QQ[x, y] +A = koszulComplexDGA R +KM = koszulComplexDGM(A, R^1 / ideal(x)) +assert(KM.dgAlgebra === A) +-- adjoinGenerators extends degree list +R2 = QQ[x] +A2 = koszulComplexDGA R2 +M = freeDGModule(A2, {0}) +natGens = apply(rank M.natural, i -> (M.natural)_i) +Mnew = adjoinGenerators(M, {x * natGens#0}) +assert(#Mnew.Degrees == 2) +assert(first Mnew.Degrees#1 == 1) +-- killCycles on a DG module +M2k = adjoinGenerators(M, {x * natGens#0}) +assert(prune homology(0, M2k) != 0) +M3 = killCycles(M2k, StartDegree => 1, EndDegree => 2) +assert(prune homology(1, M3) == 0) +/// + +TEST /// +-- test 34: semifreeResolution / minimalSemifreeResolution / isMinimalSemifreeResolution +R = QQ[x] +A = koszulComplexDGA R +M = R^1 / ideal(x) +Mdg = semifreeResolution(A, M, EndDegree => 3) +assert(prune homology(0, Mdg) != 0) +assert(all(1..3, n -> prune homology(n, Mdg) == 0)) +-- 2-arg form w/ ambient ring +Mdg' = semifreeResolution(M, EndDegree => 2) +assert(Mdg'.ring === R) +assert(Mdg'.dgAlgebra.ring === R) +-- Minimal resolution over c.i. +Rci = QQ[x, y] / ideal(x^2, y^2) +kci = Rci^1 / ideal(x, y) +Aci = koszulComplexDGA Rci +Mmin = minimalSemifreeResolution(Aci, kci, EndDegree => 3) +assert(isMinimalSemifreeResolution Mmin) +assert(all(1..3, n -> prune homology(n, Mmin) == 0)) +-- Ranks 1,2,3,4 at degrees 0,1,2,3 +assert(toList apply(0..3, n -> numcols moduleDifferential(n, Mmin)) == {1, 2, 3, 4}) +-- 2-arg minimal +Mm2 = minimalSemifreeResolution(M, EndDegree => 2) +assert(Mm2.ring === R) +assert(isMinimalSemifreeResolution Mm2) +-- isMinimalSemifreeResolution negative +Mbad = freeDGModule(A, {0, 1}) +natGens = apply(rank Mbad.natural, i -> (Mbad.natural)_i) +setDiff(Mbad, {0, natGens#0}) -- diff(e_1) = e_0 alone, not minimal +assert(not isMinimalSemifreeResolution Mbad) +/// + +TEST /// +-- test 35: polyDifferential, polyDiffMonomial, dgComplex, isValidDGModule, isDGSubmodule +R = QQ[x, y, z] +A = koszulComplexDGA R +-- d^2 = 0 on Koszul resolution of regular ring +d1 = polyDifferential(1, A) +d2 = polyDifferential(2, A) +d3 = polyDifferential(3, A) +assert(d1 * d2 == 0) +assert(d2 * d3 == 0) +-- degree 0: R^1 -> R^0 +Rci = QQ[x, y] / ideal(x^2, y^2) +Aci = koszulComplexDGA Rci +d0 = polyDifferential(0, Aci) +assert(source d0 == Rci^1 and target d0 == Rci^0) +-- element form and Leibniz +gsci = gens Aci.natural +rpd = polyDiffMonomial(Aci, 0_(Aci.natural)) +assert(rpd == 0_(Aci.natural)) +r2 = polyDiffMonomial(Aci, gsci#0 * gsci#1) +assert(r2 == x * gsci#1 - y * gsci#0) +-- dgComplex caching +C1 = dgComplex Aci +C2 = dgComplex Aci +assert(C1 === C2) +invalidateDGAlgebraCache Aci +assert(not Aci.cache#?(symbol dgComplex)) +C3 = dgComplex Aci +assert(instance(C3, Complex)) +-- dgComplex over infinite DGAlgebra (acyclicClosure) +Acc = acyclicClosure(Rci, EndDegree => 2) +assert(instance(dgComplex Acc, Complex)) +-- dgComplex on DGModule (caching) +KMci = koszulComplexDGM Rci^1 +D1 = dgComplex KMci +D2 = dgComplex KMci +assert(D1 === D2) +assert(isValidDGModule KMci) +-- isDGSubmodule: image and kernel of identityDGModuleMap are d-stable +R0 = ZZ/101[x] +A0 = koszulComplexDGA R0 +Mdg = freeDGModule(A0, {0, 1}) +natGens0 = apply(rank Mdg.natural, i -> (Mdg.natural)_i) +setDiff(Mdg, {0, x * natGens0#0}) +idM = identityDGModuleMap Mdg +imF = image idM +assert(isDGSubmodule(target idM, (inclusion imF).natural)) +kerF = kernel idM +assert(isDGSubmodule(source idM, (inclusion kerF).natural)) +/// + +TEST /// +-- test 36: getBoundaryPreimage +R = QQ[x, y] / ideal(x^2, y^2) +A = koszulComplexDGA R +M = freeDGModule(A, {0, 1}) +natGens = apply(rank M.natural, i -> (M.natural)_i) +setDiff(M, {0, x * natGens#0}) +b = x * natGens#0 +(ok, pre) = getBoundaryPreimage(M, b) +assert(ok === true) +assert(diff(M, pre) == b) +-- non-boundary case +M2 = freeDGModule(A, {0}) +ng2 = apply(rank M2.natural, i -> (M2.natural)_i) +(ok2, residue) = getBoundaryPreimage(M2, ng2#0) +assert(ok2 === false) +assert(residue != 0) +-- list form: zero + boundary +R3 = QQ[x] +A3 = koszulComplexDGA R3 +M3 = freeDGModule(A3, {0, 1}) +natGens3 = apply(rank M3.natural, i -> (M3.natural)_i) +setDiff(M3, {0, x * natGens3#0}) +b3 = x * natGens3#0 +(okL, lifts) = getBoundaryPreimage(M3, {b3, 0_(M3.natural)}) +assert(okL === true) +assert(diff(M3, lifts#0) == b3) +assert(diff(M3, lifts#1) == 0) +/// + +TEST /// +-- test 37: moduleDifferential, diff(DGModule/DGQuotientModule, Vector), displays +R = QQ[x, y] / ideal(x^2, y^2) +k = R^1 / ideal(x, y) +A = koszulComplexDGA R +Mdg = minimalSemifreeResolution(A, k, EndDegree => 2) +d1 = moduleDifferential(1, Mdg) +d2 = moduleDifferential(2, Mdg) +assert(d1 * d2 == 0) +-- (diff, DGModule, Vector) +R2 = QQ[x] +A2 = koszulComplexDGA R2 +M = freeDGModule(A2, {0, 1}) +natGens = apply(rank M.natural, i -> (M.natural)_i) +setDiff(M, {0, x * natGens#0}) +assert(diff(M, natGens#1) == x * natGens#0) +assert(diff(M, natGens#0) == 0_(M.natural)) +-- (diff, DGQuotientModule, Vector) +R3 = ZZ/101[x] +A3 = koszulComplexDGA R3 +M3 = freeDGModule(A3, {0, 1}) +natGens3 = apply(rank M3.natural, i -> (M3.natural)_i) +setDiff(M3, {0, x * natGens3#0}) +S = dgSubmodule(M3, (id_(M3.natural))_{1}) +Q = M3 / S +assert(instance(projection Q, DGModuleMap)) +assert(diff(Q, (Q.natural)_0) == 0_(Q.natural)) +-- generatorTable, dgModuleSummary, displayModuleBlockDiff return a Net +assert(instance(generatorTable M, Net)) +assert(instance(dgModuleSummary(Mdg, 3), Net)) +-- moduleBlockDiff columns +Mdg3 = minimalSemifreeResolution(A, k, EndDegree => 3) +b2 = moduleBlockDiff(Mdg3, 2) +assert(matrix b2 == moduleDifferential(2, Mdg3)) +srcIdx = indices source b2 +assert(all(srcIdx, l -> #l == 2 and instance(l#0, ZZ) and instance(l#1, List))) +assert(instance(displayModuleBlockDiff(Mdg, 2), Net)) +/// + +TEST /// +-- test 38: manipulation (baseChange / subDGAlgebra / restrictDifferential / truncateGenerators / killHomologyAtDegree) +R = QQ[x, y] +S = R / ideal(x^2, y^2) +A = koszulComplexDGA R +B = baseChange(A, S) +assert(underlyingRing B === S) +assert(isWellDefinedDifferential B) +-- RingMap form +phi = map(R, R, {x, 0}) +Bphi = baseChange(A, phi) +assert(underlyingRing Bphi === R) +-- subDGAlgebra / restrictDifferential / truncateGenerators +Ac = koszulComplexDGA (QQ[x, y] / ideal(x^2, y^2)) +Bsub = subDGAlgebra(Ac, {0}) +assert(numgens Bsub.natural == 1) +Brd = restrictDifferential(Ac, {0}) +assert(numgens Brd.natural == 1) +Btr = truncateGenerators(Ac, 0) +assert(numgens Btr.natural == numgens Ac.natural) +-- subDGAlgebra must receive a d-closed sub-list +Rx = QQ[x] / ideal(x^2) +Ax = koszulComplexDGA Rx +Ax' = adjoinVariables(Ax, {x * (gens Ax.natural)#0}) +errored = try (subDGAlgebra(Ax', {1}); false) else true +assert(errored) +-- killHomologyAtDegree +Areg = koszulComplexDGA (QQ[x, y]) +assert(killHomologyAtDegree(Areg, 1) === Areg) -- no homology in deg 1 +Bkill = killHomologyAtDegree(Ac, 1) +assert(numgens Bkill.natural > numgens Ac.natural) +assert(prune homology(1, Bkill) == 0) +-- isValidDGAlgebra / isWellDefinedDifferential +assert(isValidDGAlgebra Ac) +assert(isWellDefinedDifferential Ac) +Mfree = freeDGModule(Ac, {0}) +assert(isWellDefinedDifferential Mfree) +/// + +TEST /// +-- test 39: accessors (underlyingRing, underlyingAlgebra, differential, generatorDegrees, caching) +R = QQ[x, y] / ideal(x^2, y^2) +A = koszulComplexDGA R +assert(underlyingRing A === R) +M = koszulComplexDGM R^1 +assert(underlyingRing M === R) +assert(underlyingAlgebra A === A.natural) +assert(underlyingAlgebra M === M.natural) +assert(differential A === A.diff) +-- generatorDegrees returns whatever the DGA records +assert(generatorDegrees A == A.Degrees) +Mfree = freeDGModule(A, {0, 1, 3}) +assert(generatorDegrees Mfree == Mfree.Degrees) +-- Caches +ensureDGAlgebraCaches A +assert(instance(A.cache, CacheTable)) +ensureDGAlgebraCaches A +assert(instance(A.cache, CacheTable)) +ensureDGAlgebraCaches M +assert(instance(M.cache, CacheTable)) +-- invalidateDGAlgebraCache resets cache slots +C1 = dgComplex A +invalidateDGAlgebraCache A +assert(not A.cache#?(symbol dgComplex)) +C2 = dgComplex A +assert(instance(C2, Complex)) +D1 = dgComplex M +invalidateDGAlgebraCache M +assert(not M.cache#?(symbol dgComplex)) +D2 = dgComplex M +assert(instance(D2, Complex)) +/// + +TEST /// +-- test 40: extensive DGIdeal operations (+, *, ^, intersect, :, %, quotient, predicates) +R = ZZ/101[x, y] +A = koszulComplexDGA R +Anat = A.natural +I = dgIdeal(A, {x_Anat}) +J = dgIdeal(A, {y_Anat}) +assert isWellDefined I +assert isDGIdeal(A, I.natural) +-- +, *, ^, intersect, : +assert isWellDefined(I + J) +assert(I + J == J + I) +Zp = dgIdeal(A, {}) +assert(Zp + I == I) +assert isWellDefined(I * J) +assert(I * J == J * I) +Uone = dgIdeal(A, {1_Anat}) +assert(Uone * I == I) +I5 = dgIdeal(A, {x_Anat, y_Anat}) +assert isWellDefined(I5^2) +assert isWellDefined(I5^3) +assert(I5^2 * I5 == I5^3) +I5zero = I5^0 +assert(I5zero.natural == ideal 1_Anat) +assert isWellDefined(Zp^0) +assert isWellDefined(Zp^5) +assert isWellDefined intersect(I, J) +ZI = intersect(Zp, I) +assert(numgens ZI.natural == 0) +Ic = dgIdeal(A, {x_Anat * y_Anat}) +Jc = dgIdeal(A, {y_Anat}) +Kc = Ic : Jc +assert isWellDefined Kc +-- isSubset, ==, % +Ia = dgIdeal(A, {x_Anat}) +Ja = dgIdeal(A, {x_Anat, y_Anat}) +assert isSubset(Ia, Ja) +assert(Ia != Ja) +assert(Ia == Ia) +assert((x_Anat^2 % I) == 0) +assert((y_Anat^2 % I) == y_Anat^2) +-- Accessors +Im = dgIdeal(A, {x_Anat, x_Anat * y_Anat}) +ignore = mingens Im +ignore = numgens Im +ignore = module Im +assert(numgens Im == numgens Im.natural) +assert(ring Im === R) +assert(ambient Im === A) +-- prune +Ipr = dgIdeal(A, {x_Anat, x_Anat * y_Anat, x_Anat^2}) +Jpr = prune Ipr +assert isWellDefined Jpr +assert(Jpr.natural == Ipr.natural) +-- quotient A / I +Rq = ZZ/101[x] +Aq = koszulComplexDGA Rq +Tq = (Aq.natural)_0 +Iq = dgIdeal(Aq, {Tq}) +Bq = Aq / Iq +assert(class Bq === DGAlgebra) +assert isWellDefined Bq +-- predicates +Ih = dgIdeal(A, {x_Anat, y_Anat}) +assert isHomogeneous Ih +assert not isZero Ih +Zh = dgIdeal(A, {}) +assert isZero Zh +-- isDGIdeal positive/negative +assert isDGIdeal(A, ideal(x_Anat, y_Anat)) +Tsym = Anat_0 -- T_{1,1}, with d(T) = x +assert not isDGIdeal(A, ideal(Tsym)) +JT = dgIdeal(A, {Tsym}) +assert isDGIdeal(A, JT.natural) +-- zero-seed isZero +Z' = dgIdeal(A, {0_Anat, 0_Anat}) +assert isZero Z' +/// + +TEST /// +-- test 41: export layer (setDiff DGModule, maxDegree, getBasis, toComplex, toComplexMap) +R = QQ[x] +A = koszulComplexDGA R +M = freeDGModule(A, {0, 1}) +natGens = apply(rank M.natural, i -> (M.natural)_i) +setDiff(M, {0, x^2 * natGens#0}) +d1 = moduleDifferential(1, M) +d2 = moduleDifferential(2, M) +assert(d1 * d2 == 0) +-- maxDegree on DGModule matches its ambient DGAlgebra +Rci = QQ[x, y] / ideal(x^2, y^2) +KM = koszulComplexDGM Rci^1 +assert(maxDegree KM == maxDegree KM.dgAlgebra) +-- maxDegree on DGQuotientModule matches ambient module +R3 = ZZ/101[x] +A3 = koszulComplexDGA R3 +M3 = freeDGModule(A3, {0, 1}) +zM3 = zeroDGModuleMap(M3, M3) +Q3 = cokernel zM3 +assert(maxDegree Q3 == maxDegree M3) +-- getBasis on Koszul DG module +b = getBasis(1, KM) +assert(instance(b, Matrix)) +assert(numcols b == 2) +-- toComplex on DGModule +C = toComplex KM +assert(instance(C, Complex)) +assert(C.dd_1 == moduleDifferential(1, KM)) +assert(C.dd_2 == moduleDifferential(2, KM)) +-- toComplex on DGModule with degree cap +KMr = koszulComplexDGM (QQ[x, y, z])^1 +Cr = toComplex(KMr, 2) +assert(instance(Cr, Complex)) +assert(max Cr == 2) +-- toComplex on DGQuotientModule +CM = toComplex M3 +CQ = toComplex Q3 +assert(rank CM_0 == rank CQ_0) +assert(rank CM_1 == rank CQ_1) +-- toComplex on DGIdeal: errors (intended) +Aid = koszulComplexDGA (ZZ/101[x]) +Tid = (gens Aid.natural)#0 +Iid = dgIdeal(Aid, {Tid}) +errored = try (toComplex Iid; false) else true +assert(errored) +-- toComplexMap on DGAlgebraMap +Rcu = QQ[u, v] / ideal(u^2, v^2) +Acu = koszulComplexDGA Rcu +phi = dgAlgebraMap(Acu, Acu, matrix {gens Acu.natural}) +cm = toComplexMap phi +assert(instance(cm, ComplexMap)) +-- toComplexMap on DGModuleMap identity at each degree +Rc2 = QQ[x, y] / ideal(x^2, y^2) +Mdg = minimalSemifreeResolution(koszulComplexDGA Rc2, Rc2^1 / ideal(x, y), EndDegree => 3) +idMdg = identityDGModuleMap Mdg +assert(all(0..3, n -> ( + cmn := toComplexMap(idMdg, n); + source cmn == target cmn and cmn == id_(source cmn) + ))) +/// diff --git a/M2/Macaulay2/packages/Depth.m2 b/M2/Macaulay2/packages/Depth.m2 index dea710053a3..2b090c28a2d 100644 --- a/M2/Macaulay2/packages/Depth.m2 +++ b/M2/Macaulay2/packages/Depth.m2 @@ -25,7 +25,7 @@ newPackage( } }, Headline => "aids in computations related to depth", - PackageImports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, Keywords => {"Commutative Algebra"}, DebuggingMode => false ) @@ -105,7 +105,7 @@ assert( depth(ideal(1_A),A) === infinity ) /// TEST/// -S = ZZ/101[x_1..x_(9)]; +S = ZZ/101[x_1..x_4]; J = ideal vars S; T = S/J^5; I = ideal vars T; @@ -548,7 +548,7 @@ doc /// maximal ideal on the module: Example S = ZZ/101[a,b,c,d] - K = koszul vars S + K = koszulComplex vars S apply(numgens S, i-> depth coker K.dd_(i+1)) Text and here is one computing systems of parameters. The "Density" (a number between @@ -873,7 +873,7 @@ TEST/// /// TEST/// S = ZZ/101[a,b,c,d] - K = koszul vars S + K = koszulComplex vars S apply(numgens S, i-> depth coker K.dd_(i+1)) I = ideal"ab,bc,cd,da" diff --git a/M2/Macaulay2/packages/DeterminantalRepresentations.m2 b/M2/Macaulay2/packages/DeterminantalRepresentations.m2 index ea7debf5d47..b06837a85ba 100644 --- a/M2/Macaulay2/packages/DeterminantalRepresentations.m2 +++ b/M2/Macaulay2/packages/DeterminantalRepresentations.m2 @@ -121,7 +121,7 @@ cubicBivariateDetRep RingElement := List => opts -> f -> ( L := flatten apply(select(clean(eps, L0), isDoublyStochastic), M -> orthogonalFromOrthostochastic(M, opts)); if k === QQ then ( numDigits := ceiling(-log_10(eps)); - (D1, D2) = (D1/round_numDigits, D2/round_numDigits); + (D1, D2) = (D1/round_numDigits/(x->x^QQ), D2/round_numDigits/(x->x^QQ)); L = L/roundMatrix_numDigits; ); (D1, D2) = (D1, D2)/diagonalMatrix_k; diff --git a/M2/Macaulay2/packages/EdgeIdeals.m2 b/M2/Macaulay2/packages/EdgeIdeals.m2 index 0b80a2e76d3..93445cd5290 100644 --- a/M2/Macaulay2/packages/EdgeIdeals.m2 +++ b/M2/Macaulay2/packages/EdgeIdeals.m2 @@ -1038,7 +1038,7 @@ randomUniformHyperGraph (PolynomialRing,ZZ,ZZ) := (R,card,num) -> ( if card <= 0 then error "cardinalities of hypergraphs must be positive integers"; if num < 0 then error "number of edges must be nonnegative"; if num > binomial(numgens R,card) then error "can't make that many edges"; - edges := take(random subsets(gens R,card),num); + edges := shuffle(subsets(gens R,card),num); hyperGraph(R,edges) ) @@ -1049,7 +1049,7 @@ randomUniformHyperGraph (PolynomialRing,ZZ,ZZ) := (R,card,num) -> ( ----------------------------------------------------------- recursiveRandomHyperGraph = (V,L,D,BranchLimit,TerminateTime) -> ( if #D === 0 then return L; - V = random V; + V = shuffle V; W := set take(V, D#0); if any(L, l -> all(W, w-> member(w#0,l))) then return null; if any(L, l -> all(l, w-> member(w#0,W))) then return null; diff --git a/M2/Macaulay2/packages/EliminationTemplates.m2 b/M2/Macaulay2/packages/EliminationTemplates.m2 new file mode 100644 index 00000000000..01982562965 --- /dev/null +++ b/M2/Macaulay2/packages/EliminationTemplates.m2 @@ -0,0 +1,2119 @@ +-- -*- coding: utf-8 -*- +newPackage( + "EliminationTemplates", + Version => "1", + Date => "April 30, 2026", + Authors => { + {Name => "Manav Batavia", + Email => "manavbatavia@gmail.com", + HomePage => ""}, + {Name => "Cheng Chen", + Email => "chengchen@math.wisc.edu", + HomePage => ""}, + {Name => "Wanchun / Rosie Shen", + Email => "wshen@math.harvard.edu", + HomePage => ""}, + {Name => "Anna Natalie Chlopecki", + Email => "achlopec@purdue.edu", + HomePage => ""}, + {Name => "Tim Duff", + Email => "tduff@missouri.edu", + HomePage => "https://timduff35.github.io/timduff35/"}, + {Name => "Will Huang", + Email => "williamhuang5120@gmail.com", + HomePage => ""}, + {Name => "Aolong Li", + Email => "lial0921.miu@gmail.com", + HomePage => ""}}, + Headline => "Elimination Templates", + PackageImports => {"EigenSolver", "NumericalAlgebraicGeometry"}, + Keywords => {"Applied Algebraic Geometry", "Numerical Algebraic Geometry"}, + HomePage => "", + DebuggingMode => false, + AuxiliaryFiles => true +) + +-- Martyushev's matrixHi + greedy parameter commit (CVPR 2022 §3–§4). The +-- Martyushev file provides the paper's algorithmic core (buildGapPolys, +-- buildHSymbolic, adjustParams) and a basis-search cluster for future +-- non-standard basis exploration (buildGreedyTemplateWithBasis, searchBases, +-- randomStandardBases, randomNonstandardBases, parseMartyushevBases, +-- isValidBasis, buildTemplateFromH). Helpers specific to the main pipeline +-- (reshapeGreedyToH0, extractActionFromTemplate, +-- recoverSolutionsFromEigenvectors) live in this file, directly above +-- getH0 / getActionMatrix / templateSolve. +load "./EliminationTemplates/Martyushev.m2" + +export { + "getTemplate", + "getTemplateMatrix", + "getActionMatrix", + "templateSolve", + "EliminationTemplate", + "eliminationTemplate", + "actionVariable", + "copyTemplate", + -- Option on pipeline methods. `AdjustParams => false` runs the Greedy + -- strategy up to the `buildHSymbolic` particular solution (α := 0), + -- skipping the greedy parameter-commit step. Default `true`. + "AdjustParams" +} + + + +EliminationTemplate = new Type of HashTable +ShiftSet = new Type of List +MonomialPartition = new Type of List + +-- True when `a` is a monomial (single term with unit coefficient), so `a * b_i` +-- is a single monomial for every basis monomial `b_i`. The monomial-action short-circuit uses this +-- to skip the graph-ring extension for Default / Larsson / Greedy, saving |B| +-- rows and matching the paper template structure. +isMonomialAction = a -> a == leadMonomial a + +eliminationTemplate = method(Options => {}) +eliminationTemplate (RingElement, Ideal) := o -> (aVar, J) -> ( + R := ring J; + new EliminationTemplate from { + "actionVariable" => aVar, + ideal => J, + cache => new CacheTable from {} + } +) + +net EliminationTemplate := E -> ( + str := " action variable: " | toString(actionVariable E); + if E.cache#?"templateMatrix" then str = "Template matrix:\n" | net(E.cache#"templateMatrix") | str; + if E.cache#?"actionMatrix" then str = "Action matrix:\n" | net(E.cache#"actionMatrix") | str; + str +) + +actionVariable = method() +actionVariable EliminationTemplate := E -> E#"actionVariable" +ideal EliminationTemplate := E -> E#ideal +basis EliminationTemplate := o -> E -> E.cache#basis + +-- ============================================================================ +-- reshapeGreedyToH0: reshape Greedy's H (nG x nF, post-adjustParams) into the +-- mainline H0 shape (nF x nB) used by getTemplateHelper / getTemplate. +-- +-- Mainline contract: H0[j, k] encodes the shift data for basis monomial b_k +-- via generator F_j; columns k with `a * b_k` already in B are zero. +-- +-- Greedy produces H in a different natural shape: H[i, j] encodes the +-- cofactor of generator F_j for gap polynomial G_i, where G_i corresponds to +-- the i-th residual a * b_{sigma(i)}. The reshape is a permutation + +-- zero-pad: H0[j, sigma(i)] = H[i, j], all other H0 columns zero. +-- ============================================================================ +reshapeGreedyToH0 = method() +reshapeGreedyToH0(Matrix, RingElement, List, ZZ) := (Hfinal, aVar, Blist, nF) -> ( + R := ring first Blist; + a := sub(aVar, R); + aBlist := apply(Blist, b -> a * b); + Bset := set Blist; + sigmaList := positions(aBlist, m -> not Bset#?m); + nB := #Blist; + nG := numRows Hfinal; + assert(nG == #sigmaList); + H0 := mutableMatrix(R, nF, nB); + for i from 0 to nG - 1 do ( + k := sigmaList#i; + for j from 0 to nF - 1 do H0_(j, k) = sub(Hfinal_(i, j), R); + ); + matrix H0 +); + +-- ============================================================================ +-- extractActionFromTemplate: read the action matrix off the [E | R | B] +-- template by RREF + pivot extraction. +-- +-- Inputs: +-- M — template matrix over the base field, with column blocks +-- [excess | residual | basis]. +-- resMonsList — residual monomials (a*b_i not in B), in the same column +-- order as the residual block of M. +-- Blist — quotient basis monomials, in R, in the same column order +-- as the basis block of M. +-- aVar — action variable. +-- +-- Output: |B| x |B| matrix Ma over coefficientRing(R) such that, modulo I, +-- a * b_i = sum_k Ma_(k,i) * b_k. +-- +-- For each b_i in B compute a*b_i. If a*b_i is in B, the i-th column of Ma +-- is the standard basis vector e_k. Otherwise a*b_i is some residual r_j; +-- find the RREF pivot row for the j-th residual column and read off the +-- B-block of that row (negated, since the RREF row says 1*r_j + sum (...) b_k +-- + ... = 0, so r_j = -sum (...) b_k modulo I). +-- ============================================================================ +extractActionFromTemplate = method() +extractActionFromTemplate(Matrix, List, List, RingElement) := (M, resMonsList, Blist, aVar) -> ( + R := ring first Blist; + FF := coefficientRing R; + a := sub(aVar, R); + nR := #resMonsList; + nB := #Blist; + nE := numColumns M - nR - nB; + Rref := reducedRowEchelonForm M; + matrix(FF, apply(nB, i -> ( + bi := Blist#i; + abi := a * bi; + kInB := position(Blist, b -> b == abi); + if kInB =!= null then ( + apply(nB, k -> if k == kInB then 1_FF else 0_FF) + ) else ( + jInR := position(resMonsList, r -> r == abi); + if jInR === null then + error("extractActionFromTemplate: a*b_i not in R or B: " | toString abi); + colR := nE + jInR; + pivRow := null; + for r from 0 to numRows Rref - 1 do ( + leading := position(0..numColumns Rref - 1, c -> Rref_(r,c) != 0_FF); + if leading === colR then (pivRow = r; break); + ); + if pivRow === null then + error("extractActionFromTemplate: no pivot for residual col " | toString colR); + apply(nB, k -> -Rref_(pivRow, nE + nR + k)) + ) + ))) +) + +-- ============================================================================ +-- recoverSolutionsFromEigenvectors: given an action matrix Ma (entries in +-- coefficientRing R) and the basis monomials Blist (in R), eigendecompose +-- over CC and return the list of complex solutions, one per eigenvector. +-- +-- Each eigenvector v is a vector of length |B|. Modulo I, v_k corresponds to +-- the value of b_k at one solution point (up to scale). We rescale so the +-- slot for the monomial 1 equals 1, then read each variable's coordinate +-- from its position in Blist. +-- +-- Output: a List of solutions; each solution is a List of CC values, one +-- per variable of R in declaration order. Variables not present in Blist +-- (e.g. eliminated in the quotient) get 0_CC; callers who need them should +-- use the RREF-based recoverSolutions fallback. +-- ============================================================================ +recoverSolutionsFromEigenvectors = method() +recoverSolutionsFromEigenvectors(Matrix, List, Ring) := (Ma, Blist, R) -> ( + TC := sub(Ma, CC); + (evals, P) := eigenvectors TC; + oneIdx := position(Blist, b -> b == 1_R); + if oneIdx === null then + error("recoverSolutionsFromEigenvectors: 1 not found in basis Blist"); + varPos := apply(numgens R, i -> position(Blist, b -> b == (gens R)#i)); + sols := for k from 0 to numColumns P - 1 list ( + v := P_{k}; + sc := v_(oneIdx, 0); + if abs sc < 1e-12 then continue; + v = (1/sc) * v; + coords := apply(varPos, p -> if p =!= null then v_(p,0) else 0_CC); + coords + ); + sols +) + +getH0 = method(Options => {MonomialOrder => null, Strategy => null, AdjustParams => true}) +getH0 (RingElement, Ideal) := o -> (a, J) -> ( + R := ring J; + B := basis(R/J); + getH0(a, B, J, o) +) +getH0 (RingElement, Matrix, Ideal) := o -> (a, B, J) -> ( + R := ring J; + a = sub(a, R); + B = sub(B, R); + FF := coefficientRing R; + + -- Greedy (Martyushev CVPR 2022) computes H via gap polynomials + α-commit, + -- then lifts to the mainline H0 shape (nF x nB). For QQ input with + -- AdjustParams => true, pin the whole computation (including adjustParams' + -- RREF on α's) to ZZ/p to avoid rational coefficient blowup; the result + -- is lifted back to R via per-coefficient ZZ→QQ. Shift monomial support + -- — all the downstream (getTemplateHelper, getTemplate) consumes — is + -- preserved by the lift, which is what matters for template construction. + -- Downstream reads only monomials(H0^{i}) per row, ignoring coefficients. + if (o.Strategy === "Greedy") then ( + pinToModP := (FF === QQ) and o.AdjustParams; + Rw := if pinToModP then ( + (ZZ/32749)[gens R, MonomialOrder => (options R).MonomialOrder] + ) else R; + aw := if pinToModP then sub(a, Rw) else a; + Jw := if pinToModP then sub(J, Rw) else J; + Bw := if pinToModP then sub(B, Rw) else B; + BlistGr := flatten entries Bw; + (gp, resMons, BlistChk) := buildGapPolys(aw, matrix{BlistGr}, Jw); + nFGr := numgens Jw; + nBGr := #BlistGr; + if #gp == 0 then return map(R^nFGr, R^nBGr, 0); + FlistGr := flatten entries gens Jw; + RB := (toList resMons) | BlistGr; + (Hsym, Rext, alphaVars, perRow) := buildHSymbolic(FlistGr, gp); + Hfinal := if o.AdjustParams then ( + adjustParams(FlistGr, Hsym, Rext, (alphaVars, RB)) + ) else if #alphaVars > 0 then ( + -- Particular α:=0 solution, projected back to Rw. + finalSubst := map(Rw, Rext, + apply(numgens Rw, i -> Rw_i) | apply(#alphaVars, k -> 0_Rw)); + matrix apply(numRows Hsym, i -> + apply(numColumns Hsym, j -> finalSubst(Hsym_(i,j)))) + ) else matrix Hsym; + H0w := reshapeGreedyToH0(Hfinal, aw, BlistGr, nFGr); + if not pinToModP then return H0w; + -- Lift H0w from Rw (ZZ/p coefficients) back to R (QQ coefficients). + -- Coefficients map via ZZ/p → ZZ (representative in [0, p)) → QQ. + -- Monomials map via variable-to-variable. + liftEntry := p -> ( + if p == 0_Rw then 0_R + else ( + (mons, coeffs) := coefficients p; + monsR := sub(mons, R); + coeffsR := matrix apply(numRows coeffs, i -> apply(numColumns coeffs, j -> + sub(lift(coeffs_(i, j), ZZ), QQ))); + first first entries (monsR * coeffsR) + ) + ); + return matrix apply(numRows H0w, i -> apply(numColumns H0w, j -> liftEntry (H0w_(i,j)))); + ); + + MO := if not instance(o.MonomialOrder, Nothing) then o.MonomialOrder else (options R).MonomialOrder; + S := newRing(R, MonomialOrder => MO); + F := sub(J, S); + G := gb(F, ChangeMatrix => true); + aS := sub(a, S); + BS := sub(B, S); + P := last coefficients(BS%F); -- change of basis matrix + V := aS * BS - lift(aS * sub(BS * (inverse P), S/F), S) * P; + HVG := V // gens G; + HGF := getChangeMatrix G; + assert(gens G * HVG - V == 0); + assert(gens F * HGF - gens G == 0); + H0 := HGF * HVG; + H0 = sub(H0, ring J); + + if (o.Strategy === null) then ( + H0 + ) + else if (o.Strategy === "Larsson") then ( + -- Larsson CVPR 2017: reduce H0 mod the first syzygy module of F. + H0res := H0 % image(syz(gens(J))); + sub(H0res, ring J) + ) + else (error "Strategy not supported. getH0 accepts null (Default) / \"Larsson\" / \"Greedy\". For the particular-α:=0 mode (formerly Strategy => \"MatrixHi\") pass Strategy => \"Greedy\", AdjustParams => false.") +) + +shiftPolynomials = (shifts, J) -> ( + assert(length shifts == numgens J); + apply(shifts, J_*, (m, f) -> f * sub(m, ring J)) +) + +getTemplateHelper = (a, B, J, o) -> ( + H0 := getH0(a, B, J, o); + shifts := new ShiftSet from apply(numgens J, i -> monomials(H0^{i})); + allMons := union(set \ flatten \ entries \ monomials \ shiftPolynomials(shifts, J)); + monsB := set flatten entries(lift(B, ring J)); + monsR := set flatten entries(a * lift(B, ring J)) - monsB; + monsE := allMons - union(monsR, monsB); + (shifts, new MonomialPartition from rsort \ toList \ {monsE, monsR, monsB}) +) + +getTemplate = method(Options => {MonomialOrder => null, Strategy => null, AdjustParams => true}) +getTemplate(EliminationTemplate) := o -> E -> ( + if E.cache#?"monomialPartition" and (o.Strategy === null or (E.cache#?"lastPartitionStrategy" and E.cache#"lastPartitionStrategy" === o.Strategy)) then ( + (E.cache#"shifts", E.cache#"monomialPartition") + ) else ( + J := ideal E; + R := ring J; + a := sub(actionVariable E, R); + + -- Monomial action goes through [excess | residual | basis] + -- with extractActionFromTemplate. No graph-ring lift, no (s - a) * b_k + -- shifts, template matches Martyushev paper sizes. + -- + -- Increment 3: polynomial action lifts to R_s = R[s] with the extra + -- generator s - a, so that s IS a ring variable on R_s/Is (monomial + -- action on Is in R_s). The same [excess | residual | basis] pipeline + -- then handles both cases uniformly. + local shOrig; + local mpHelper; + local mpHelperOrig; + local B; + if isMonomialAction a then ( + B = lift(basis(R/J), R); + (shOrig, mpHelper) = getTemplateHelper(a, B, J, o); + + BlistMono := flatten entries B; + BsetMono := set BlistMono; + resMonsMono := select(apply(BlistMono, b -> a * b), m -> not BsetMono#?m); + allMonsMono := union(set \ flatten \ entries \ monomials \ shiftPolynomials(shOrig, J)); + monsBMono := BsetMono; + monsRMono := set resMonsMono; + monsEMono := allMonsMono - monsBMono - monsRMono; + mpMono := new MonomialPartition from rsort \ toList \ {monsEMono, monsRMono, monsBMono}; + + E.cache#basis = B; + E.cache#"shifts" = shOrig; + E.cache#"monomialPartition" = mpMono; + E.cache#"lastPartitionStrategy" = o.Strategy; + E.cache#"isMonomialAction" = true; + -- basisList / residualMonomials MUST match the template's B- and R-column + -- ordering (mpMono#2 and mpMono#1 respectively), otherwise + -- extractActionFromTemplate reads the wrong columns. + E.cache#"basisList" = mpMono#2; + E.cache#"residualMonomials" = mpMono#1; + return (shOrig, mpMono); + ); + + -- Polynomial action: lift to R_s = R[s] / , then drive the same + -- [excess | residual | basis] pipeline on (s, Is) in R_s. After the + -- lift s is a ring variable, so isMonomialAction(s) is true and the + -- Greedy pipeline (buildGapPolys etc.) works on R_s even when the + -- original action was polynomial on R. + -- + -- Follow origin's shift construction: compute shifts from getH0 on the + -- ORIGINAL (R, J) and lift them into R_s, then append |B| shifts for + -- the (s - a) generator (one per basis monomial). Building shifts on + -- the LIFTED ideal directly yields a much larger template whose RREF + -- is numerically fragile during solution recovery. + B = lift(basis(R/J), R); + (shOrig, mpHelperOrig) = getTemplateHelper(a, B, J, o); + + K := coefficientRing R; + ringVars := flatten entries vars R; + MO := if not instance(o.MonomialOrder, Nothing) then o.MonomialOrder else (options R).MonomialOrder; + Rs := K[prepend("s", ringVars), MonomialOrder => {Eliminate 1, MO}]; + + toRs := map(Rs, R, apply(numgens R, i -> Rs_(i+1))); + aS := toRs(a); + actVarS := Rs_0; + Is := ideal(toRs(gens J) | matrix{{actVarS - aS}}); + Bs := toRs(B); + sortedBs := rsort flatten entries Bs; + + shLifted := new ShiftSet from ( + apply(shOrig, sh -> toRs(sh)) | {matrix {sortedBs}} + ); + + allMonsLifted := union(set \ flatten \ entries \ monomials \ shiftPolynomials(shLifted, Is)); + monsBLifted := set sortedBs; + monsRLifted := set apply(sortedBs, b -> actVarS * b); + monsELifted := allMonsLifted - monsBLifted - monsRLifted; + mpLifted := new MonomialPartition from rsort \ toList \ {monsELifted, monsRLifted, monsBLifted}; + + E.cache#basis = Bs; + E.cache#"graphIdeal" = Is; + E.cache#"shifts" = shLifted; + E.cache#"monomialPartition" = mpLifted; + E.cache#"lastPartitionStrategy" = o.Strategy; + E.cache#"isMonomialAction" = true; + E.cache#"liftedToRs" = true; + E.cache#"liftedActionVar" = actVarS; + E.cache#"basisList" = mpLifted#2; + E.cache#"residualMonomials" = mpLifted#1; + (shLifted, mpLifted) + ) +) + +copyTemplate = method(Options => {}) +copyTemplate(EliminationTemplate, Ideal) := o -> (E, J) -> ( + Rnew := ring J; + FFnew := coefficientRing Rnew; + aNew := sub(actionVariable E, Rnew); + Enew := eliminationTemplate(aNew, J); + + -- Monomial-direct (non-lifted): basis / shifts live in R, + -- transplant via sub(..., Rnew). Increment 3 lifted templates have + -- isMonomialAction = true too but their cache is in R_s, so they route + -- through the graph-ideal branch below (which handles R_s transplant). + local mpNew; + if E.cache#?"isMonomialAction" and E.cache#"isMonomialAction" + and not (E.cache#?"liftedToRs" and E.cache#"liftedToRs") then ( + Enew.cache#"isMonomialAction" = true; + if E.cache#?basis then Enew.cache#basis = sub(E.cache#basis, Rnew); + if E.cache#?"shifts" then Enew.cache#"shifts" = new ShiftSet from apply(E.cache#"shifts", sh -> sub(sh, Rnew)); + if E.cache#?"monomialPartition" then Enew.cache#"monomialPartition" = + new MonomialPartition from apply(E.cache#"monomialPartition", + mp -> apply(mp, m -> sub(m, Rnew))); + if E.cache#?"basisList" then Enew.cache#"basisList" = + apply(E.cache#"basisList", b -> sub(b, Rnew)); + if E.cache#?"residualMonomials" then Enew.cache#"residualMonomials" = + apply(E.cache#"residualMonomials", m -> sub(m, Rnew)); + if E.cache#?"lastPartitionStrategy" then Enew.cache#"lastPartitionStrategy" = E.cache#"lastPartitionStrategy"; + if E.cache#?"lastMatrixStrategy" then Enew.cache#"lastMatrixStrategy" = E.cache#"lastMatrixStrategy"; + if E.cache#?"lastActionStrategy" then Enew.cache#"lastActionStrategy" = E.cache#"lastActionStrategy"; + if E.cache#?"shifts" and E.cache#?"monomialPartition" then ( + shiftsNew := Enew.cache#"shifts"; + mpNew = Enew.cache#"monomialPartition"; + allMonsMatNew := mpNew#0 | mpNew#1 | mpNew#2; + Enew.cache#"templateMatrix" = sub( + transpose fold( + apply(shiftPolynomials(shiftsNew, J), + m -> last coefficients(m, Monomials => allMonsMatNew)), + (u, v) -> u | v), + coefficientRing Rnew); + ); + return Enew; + ); + + if E.cache#?"graphIdeal" then ( + Rs := ring E.cache#"graphIdeal"; + Rsnew := FFnew[gens ring E.cache#"graphIdeal", MonomialOrder => (options Rs).MonomialOrder]; + + if E.cache#?basis then Enew.cache#basis = sub(if E.cache#?basis then E.cache#basis else basis E, Rsnew); + if E.cache#?"shifts" then Enew.cache#"shifts" = apply(E.cache#"shifts", sh -> sub(sh, Rsnew)); + if E.cache#?"monomialPartition" then Enew.cache#"monomialPartition" = apply(E.cache#"monomialPartition", mp -> apply(mp, m -> sub(m, Rsnew))); + if E.cache#?"lastPartitionStrategy" then Enew.cache#"lastPartitionStrategy" = E.cache#"lastPartitionStrategy"; + if E.cache#?"lastMatrixStrategy" then Enew.cache#"lastMatrixStrategy" = E.cache#"lastMatrixStrategy"; + if E.cache#?"lastActionStrategy" then Enew.cache#"lastActionStrategy" = E.cache#"lastActionStrategy"; + + -- Increment 3: propagate lifted-polynomial-action flags + basisList / residualMonomials markers. + if E.cache#?"isMonomialAction" then Enew.cache#"isMonomialAction" = E.cache#"isMonomialAction"; + if E.cache#?"liftedToRs" then Enew.cache#"liftedToRs" = E.cache#"liftedToRs"; + if E.cache#?"liftedActionVar" then Enew.cache#"liftedActionVar" = sub(E.cache#"liftedActionVar", Rsnew); + + toRsnew := map(Rsnew, Rnew, apply(numgens Rnew, i -> Rsnew_(i+1))); + JsGens := toRsnew(gens J); + aS := toRsnew(aNew); + actVar := Rsnew_0; + + Enew.cache#"graphIdeal" = ideal(JsGens | matrix{{actVar - aS}}); + + if Enew.cache#?"monomialPartition" then ( + mpNewCopy := Enew.cache#"monomialPartition"; + Enew.cache#"basisList" = mpNewCopy#2; + Enew.cache#"residualMonomials" = mpNewCopy#1; + ); + + if E.cache#?"templateMatrix" then ( + isLifted := E.cache#?"liftedToRs" and E.cache#"liftedToRs"; + shNew := Enew.cache#"shifts"; + mpNew = Enew.cache#"monomialPartition"; + IsNew := Enew.cache#"graphIdeal"; + Enew.cache#"templateMatrix" = if isLifted then ( + -- Increment 3 [E|R|B] layout: include residual block. + allMonsMatL := mpNew#0 | mpNew#1 | mpNew#2; + sub( + transpose fold( + apply(shiftPolynomials(shNew, IsNew), + m -> last coefficients(m, Monomials => allMonsMatL)), + (u, v) -> u | v), + coefficientRing Rsnew) + ) else ( + -- Origin's [E|B] layout (unreachable in current code path, kept + -- as the fallback target for a future LU-on-RREF-error guard). + getTemplateMatrix(shNew, mpNew, IsNew) + ); + ); + ); + Enew +) + +getTemplateMatrix = method(Options => {MonomialOrder => null, Strategy => null, AdjustParams => true}) +getTemplateMatrix(RingElement, Matrix, Ideal) := o -> (a, B, J) -> ( + getTemplateMatrix(eliminationTemplate(a, J), o) +) +getTemplateMatrix(ShiftSet, MonomialPartition, Ideal) := o -> (shifts, monomialPartition, J) -> ( + allMons := monomialPartition#0 | monomialPartition#2; + sub(transpose fold(apply(shiftPolynomials(shifts, J), m -> last coefficients(m, Monomials => allMons)), (a,b) -> a|b), coefficientRing ring J) +) +getTemplateMatrix(EliminationTemplate) := o -> E -> ( + -- Unified path post-Increment-3: every strategy flows through + -- getTemplate -> [E | R | B] fold. Strategy choice only affects the H0 + -- computation inside getH0 (Default: GB; Larsson: H0 mod syz F; Greedy: + -- buildGapPolys + buildHSymbolic + adjustParams or alpha:=0). The + -- column layout is always [E | R | B] for both monomial-direct and + -- polynomial-lifted templates. + if E.cache#?"templateMatrix" and (o.Strategy === null or (E.cache#?"lastMatrixStrategy" and E.cache#"lastMatrixStrategy" === o.Strategy)) then ( + E.cache#"templateMatrix" + ) else ( + (shifts, monomialPartition) := getTemplate(E, o); + -- The shifts / partition were built against graphIdeal = J or + -- J_s = J + (lifted case); use that ideal so generator + -- counts match shiftPolynomials' assertion. + idealForFold := if E.cache#?"graphIdeal" then E.cache#"graphIdeal" else ideal E; + allMonsMat := monomialPartition#0 | monomialPartition#1 | monomialPartition#2; + ret := sub( + transpose fold( + apply(shiftPolynomials(shifts, idealForFold), + m -> last coefficients(m, Monomials => allMonsMat)), + (u, v) -> u | v), + coefficientRing ring idealForFold); + E.cache#"templateMatrix" = ret; + E.cache#"lastMatrixStrategy" = o.Strategy; + ret + ) +) + +getActionMatrix = method(Options => {MonomialOrder => null, Strategy => null, AdjustParams => true}) +getActionMatrix(RingElement, MonomialPartition, Matrix) := o -> (actVar, mp, M) -> ( + numE := length mp#0; + numB := length mp#2; + FF := ring M; + m := numrows M; + n := numcols M; + numTop := m - numB; + MtopE := M_{0 .. numE-1}^{0..numTop-1}; + MtopB := M_{numE .. n-1}^{0..numTop-1}; + X := solve(MtopE, MtopB, ClosestFit => if instance(FF, InexactFieldFamily) or instance(FF, InexactField) then true else false); + MbotE := M_{0 .. numE-1}^{numTop .. m-1}; + MbotB := M_{numE .. n-1}^{numTop .. m-1}; + MbotE * X - MbotB +) +getActionMatrix(EliminationTemplate) := o -> E -> ( + -- Unified action extraction post-Increment-3: RREF + pivot on the + -- [E | R | B] template for every strategy and every action type. The + -- action variable is the user's `a` for monomial-direct templates, or + -- the cached `s` in R_s for polynomial-action templates (lifted to R_s + -- = R[s] / ). Fallback to origin's LU split-solve on [E | B] if + -- RREF raises a catchable error (QQ coefficient blowup on adversarial + -- input). + if E.cache#?"actionMatrix" and (o.Strategy === null or (E.cache#?"lastActionStrategy" and E.cache#"lastActionStrategy" === o.Strategy)) then ( + E.cache#"actionMatrix" + ) else ( + getTemplate(E, o); -- populates partition / basisList / residualMonomials + templateMatrix := getTemplateMatrix(E, o); + + actVarForExtract := if E.cache#?"liftedToRs" and E.cache#"liftedToRs" + then E.cache#"liftedActionVar" + else actionVariable E; + ret := try ( + extractActionFromTemplate(templateMatrix, E.cache#"residualMonomials", + E.cache#"basisList", actVarForExtract) + ) else ( + --stderr << "-- [getActionMatrix] RREF failed on [E|R|B]; " +-- << "falling back to LU split-solve on [E|B]" << endl; + mp1 := E.cache#"monomialPartition"; + shiftsNow := E.cache#"shifts"; + idealNow := if E.cache#?"graphIdeal" then E.cache#"graphIdeal" else ideal E; + mpFallback := new MonomialPartition from {mp1#0 | mp1#1, {}, mp1#2}; + matFallback := getTemplateMatrix(shiftsNow, mpFallback, idealNow, o); + getActionMatrix(actVarForExtract, mpFallback, matFallback, o) + ); + E.cache#"actionMatrix" = ret; + E.cache#"lastActionStrategy" = o.Strategy; + ret + ) +) + +-* + +*- +getEigenMatrix = method(Options => {MonomialOrder => null, Strategy => null, AdjustParams => true}) +getEigenMatrix(EliminationTemplate) := o -> (E) -> ( + Ma := getActionMatrix(E, o); + -- Monomial-direct templates let downstream recoverSolutionsFromEigenvectors do + -- its own normalization by the "1" slot, so return raw eigenvectors + -- against the cached basis list. Polynomial-lifted templates (and any + -- path that falls through to recoverSolutions) need eigenvectors + -- pre-normalized so monomial values can be read directly. + if (E.cache#?"isMonomialAction" and E.cache#"isMonomialAction") + and not (E.cache#?"liftedToRs" and E.cache#"liftedToRs") then ( + (svals, P) := eigenvectors sub(Ma, CC); + (matrix{E.cache#"basisList"}, P) + ) else ( + (svals2, P2) := eigenvectors sub(Ma, CC); + cleanEvecs := clean_(1e-10) (P2 * inverse diagonalMatrix(P2^{numColumns P2 - 1})); + (transpose rsort basis E, cleanEvecs) + ) +) +getEigenMatrix(Ideal) := o -> (I) -> getEigenMatrix(random(1, ring I), I, o) +getEigenMatrix(RingElement, Ideal) := o -> (a, J) -> ( + E := eliminationTemplate(a, J); + getEigenMatrix(E, o) +) + +templateSolve = method(Options => {MonomialOrder => null, Strategy => null, AdjustParams => true}) +templateSolve(EliminationTemplate) := o -> (E) -> ( + -- Unified solve post-Increment-3. Compute the action matrix Ma (RREF + -- + pivot on [E | R | B]), eigendecompose over CC. If every ring + -- variable appears in the basis, read coordinates directly from + -- basis-indexed eigenvector slots (recoverSolutionsFromEigenvectors). Otherwise + -- fall back to recoverSolutions, which uses RREF on the template to + -- express excess / residual monomials in terms of B. + Ma := getActionMatrix(E, o); + R := ring ideal E; + + -- Monomial-direct: basis is in R, all vars present by construction + -- (action is a ring variable, basis includes 1 and extends). + if (E.cache#?"isMonomialAction" and E.cache#"isMonomialAction") + and not (E.cache#?"liftedToRs" and E.cache#"liftedToRs") then ( + return recoverSolutionsFromEigenvectors(Ma, E.cache#"basisList", R); + ); + + -- Polynomial-lifted case: basis is in R_s, map back via s -> 0 to + -- get a basis in R. If every ring variable is a basis monomial, + -- recoverSolutionsFromEigenvectors works. Otherwise recoverSolutions reads + -- missing vars from the template's E / R blocks. + mp := E.cache#"monomialPartition"; + Rs := ring first first mp; + toR := map(R, Rs, {0_R} | apply(numgens R, i -> R_i)); + Blist := apply(mp#2, b -> toR(b)); + BlistSet := set Blist; + if all(numgens R, i -> BlistSet#?(R_i)) then ( + recoverSolutionsFromEigenvectors(Ma, Blist, R) + ) else ( + (Bmat, M) := getEigenMatrix(E, o); + templateMat := getTemplateMatrix(E, o); + recoverSolutions(Bmat, M, E, templateMat) + ) +) +templateSolve(Ideal) := o -> (I) -> templateSolve(random(1,ring I), I, o) +templateSolve(RingElement, Ideal) := o -> (a, J) -> ( + E := eliminationTemplate(a, J); + templateSolve(E, o) +) + +recoverSolutions = method() +recoverSolutions(Matrix, Matrix, EliminationTemplate, Matrix) := (Bmat, M, E, templateMat) -> ( + J := ideal E; + Rnew := ring J; + + mp := E.cache#"monomialPartition"; + Rs := ring first first mp; + + toRnew := map(Rnew, Rs, {0_Rnew} | flatten entries vars Rnew); + toRs := map(Rs, Rnew, apply(numgens Rnew, i -> Rs_(i+1))); + + basisMonsRnew := apply(flatten entries Bmat, m -> toRnew(m)); + + monsE := mp#0; + monsR := mp#1; + monsB := mp#2; + + numE := length monsE; + numR := length monsR; + numB := length monsB; + + -- Use RREF on the full [E|R|B] template to express each excess monomial + -- in terms of B. This handles over-determined templates (numTop > |E|+|R|) + -- naturally, avoids QQ least-squares (not implemented), and matches the + -- approach extractActionFromTemplate uses for residual columns. + FF := coefficientRing Rnew; + Rref := reducedRowEchelonForm templateMat; + -- eReduction_(colIdx) = row in Rref whose pivot is at colIdx, or null. + pivotRowOfCol := new MutableHashTable; + for r from 0 to numRows Rref - 1 do ( + pivCol := position(0..numColumns Rref - 1, c -> Rref_(r,c) != 0_FF); + if pivCol =!= null and not pivotRowOfCol#?pivCol then + pivotRowOfCol#pivCol = r; + ); + -- readMonValFromRref(col, basisVals) returns the CC value of the monomial + -- in column `col` given the CC values of B monomials: row at `col` says + -- 1 * m_col + sum_b Rref[row, numE+numR+b] * b = 0 + -- so m_col = -sum_b Rref[row, numE+numR+b] * b_val. + readMonValFromRref := (col, bVals) -> ( + if not pivotRowOfCol#?col then return null; + row := pivotRowOfCol#col; + val := 0_CC; + for k from 0 to numB - 1 do + val = val - sub(Rref_(row, numE + numR + k), CC) * bVals#k; + val + ); + + solutions := {}; + varsList := flatten entries vars Rnew; + + -- Basis slot holding the monomial 1_Rnew. Eigenvectors normalize by this + -- slot (done upstream by getEigenMatrix), so when this slot is tiny the + -- eigenvector is spurious / numerically degenerate and must be skipped + -- rather than blown up by the normalization. + oneSlot := position(basisMonsRnew, b -> b == 1_Rnew); + + for rootIndex from 0 to numColumns M - 1 do ( + if oneSlot =!= null and abs M_(oneSlot, rootIndex) < 1e-10 then continue; + + monomialValues := new MutableHashTable; + for i from 0 to #basisMonsRnew - 1 do ( + monomialValues#(basisMonsRnew#i) = M_(i, rootIndex); + ); + + -- Basis-monomial values as a vector aligned with monsB. + bVals := apply(numB, j -> monomialValues#(toRnew(monsB#j))); + + root := for v in varsList list ( + if monomialValues#?v then ( + -- v is a basis monomial; read directly. + monomialValues#v + ) + else ( + -- v lives in E or R block; reduce via RREF pivot row. + vRs := toRs(v); + posInE := position(monsE, m -> m == vRs); + posInR := if posInE === null then position(monsR, m -> m == vRs) else null; + colIdx := if posInE =!= null then posInE + else if posInR =!= null then numE + posInR + else null; + local r; + local coeffs; + local val; + if colIdx =!= null then ( + val = readMonValFromRref(colIdx, bVals); + if val === null then ( + -- RREF has no pivot at this column; shouldn't happen + -- for a well-posed template but fall back to the + -- polynomial normal form against the ideal. + r = v % J; + coeffs = last coefficients(r, Monomials => basisMonsRnew); + val = 0_CC; + for j from 0 to numB - 1 do + if monomialValues#?(basisMonsRnew#j) then + val = val + sub(coeffs_(j,0), CC) * monomialValues#(basisMonsRnew#j); + ); + val + ) + else ( + -- Failsafe: v is neither in B nor in E nor in R. + r = v % J; + coeffs = last coefficients(r, Monomials => basisMonsRnew); + val = 0_CC; + for j from 0 to numB - 1 do + if monomialValues#?(basisMonsRnew#j) then + val = val + sub(coeffs_(j,0), CC) * monomialValues#(basisMonsRnew#j); + val + ); + ); + ); + solutions = append(solutions, root); + ); + solutions +) + +beginDocumentation() + +doc /// + Node + Key + EliminationTemplates + Headline + zero-dimensional polynomial solvers based on linear algebra + Description + Text + {\em EliminationTemplates} is a package that supports solvers for the following problem: given a zero-dimensional radical ideal $I \subset R := \mathbb{C} [x_1, \ldots , x_n]$, find approximate values for the isolated solutions $(p_1, \ldots , p_n ) \in V_{\mathbb{C}} (I).$ + The main applications occur when the ideal $I$ occur in a parametric family of problems with similar structure. + + Following the references below, the package is geared twowards implementing a "two-stage" approach, consisting of (1) offline stage and (2) an online stage. + + In the offline stage, the structure of a "template matrix" for $I$ is determined using Groebner basis computations. + + In the online stage, prior knowledge of the template matrix can be used to construct a multiplication matrix for the quotient ring $R/I.$ + From this multiplication matrix, solutions can be extracted using only linear algebra. + References + @UL { + {"Optimizing Elimination Templates by Greedy Parameter Search, Martyushev-Vrablikova-Pajdla", EM " CVPR 2022"}, + {"Efficient solvers for minimal problems by syzygy-based reduction, Larsson-Oskarsson-Astrom", EM " CVPR 2017"} + }@ +/// + +doc /// + Node + Key + EliminationTemplate + Headline + type for elimination template objects + Description + Text + The type `EliminationTemplate` represents objects that store the data required for elimination template computations. + An `EliminationTemplate` object encodes the action variable, the ideal, and a cache for storing computed template data such as shifts, monomial partitions, and template matrices. + These objects are constructed using the function `eliminationTemplate` and are used as input to other functions in this package, such as `getTemplateMatrix`, `getActionMatrix`, and `templateSolve`. + Example + R = QQ[x,y] + J = ideal(x^2+y^2-1, x^2+x*y+y^2-1) + E = eliminationTemplate(x, J) + E +/// + +doc /// + Key + getTemplate + (getTemplate, EliminationTemplate) + [getTemplate, Strategy] + [getTemplate, AdjustParams] + [getTemplate, MonomialOrder] + AdjustParams + Headline + construct the shifts and monomial partition for an elimination template + Usage + (sh, mp) = getTemplate E + Inputs + E:EliminationTemplate + Strategy => String + the algorithm used to compute the template: "Greedy" (default), "Larsson", or null (standard GB) + AdjustParams => Boolean + whether to run the greedy parameter-commit step (α-commit) for Martyushev's strategy + MonomialOrder => Symbol + override the default monomial order of the ring + Outputs + sh:ShiftSet + a list of shift monomials applied to the generators of the ideal + mp:MonomialPartition + a list of three sets of monomials: {excess, residual, basis} + Description + Text + This method is the core of the @TO EliminationTemplates@ pipeline. It identifies + the necessary polynomial shifts and partitions the resulting monomial support + into three blocks: + + * {\bf Excess}: Monomials eliminated during the construction. + * {\bf Residual}: Monomials of the form $a \cdot b_i$ that are not in the basis. + * {\bf Basis}: The monomials spanning the quotient ring $R/I$. + + If the action variable is a polynomial rather than a single variable, + the method automatically performs a lift to the graph ring $R[s] / \langle s - a \rangle$. + Example + R = QQ[x,y]; + I = ideal(x^2-y, y^2-x); + E = eliminationTemplate(x, I); + (sh, mp) = getTemplate(E, Strategy => "Greedy"); + -- View the partitioned support + mp + SeeAlso + getTemplateMatrix + getActionMatrix +/// + +doc /// + Node + Key + eliminationTemplate + (eliminationTemplate, RingElement, Ideal) + Headline + constructor for an EliminationTemplate object + Usage + E = eliminationTemplate(a, J) + Inputs + a:RingElement + the action polynomial defining a multiplication matrix + J:Ideal + a zero-dimensional ideal + Outputs + E:EliminationTemplate + an EliminationTemplate object encoding the data for elimination template computations + Description + Text + This function constructs an EliminationTemplate object, which stores the action variable and ideal, and provides a cache for storing computed template data. + The EliminationTemplate object can be used with other functions in this package to compute template matrices, action matrices, and solve polynomial systems. + Example + R = QQ[x,y] + J = ideal(x^2+y^2-1, x^2+x*y+y^2-1) + E = eliminationTemplate(x, J) +/// + + +doc /// + Node + Key + templateSolve + (templateSolve, EliminationTemplate) + (templateSolve, Ideal) + (templateSolve, RingElement, Ideal) + Headline + polynomial system solver using elimination templates + Usage + sols = templateSolve(et) + sols = templateSolve(J) + sols = templateSolve(a, J) + Inputs + a:RingElement + the action polynomial defining a multiplication matrix + J:Ideal + a zero-dimensional ideal + et:EliminationTemplate + the elimination template for this problem + MonomialOrder=>Thing + the monomial order used on the ambient ring + Strategy=>Thing + @TT "null"@ (default), @TT "\"Larsson\""@, or @TT "\"Greedy\""@ + AdjustParams=>Boolean + @TT "true"@ (default) runs the full Greedy commit; @TT "false"@ + stops at the particular solution α := 0 ("MatrixHi mode") + Outputs + sols:List + a list of solutions; each solution is itself a list of complex + numbers, one per ring variable of the ambient ring, in + declaration order + Description + Text + In the example below, the ideal $J$ defines a zero-dimensional + variety with four points. This method finds numerical + approximations to these four points by solving an eigenvalue + problem, much like the package @TO EigenSolver@. The main + advantage of elimination templates is that the internal template + matrix may be reused for problems of a "similar structure" (see + @TO copyTemplate@). + Example + R = QQ[x,y] + J = ideal(x^2+y^2-1,x^2+x*y+y^2-1) + actVar = x + 2*y + templateSolve(actVar, J) +/// + +doc /// + Node + Key + copyTemplate + (copyTemplate, EliminationTemplate, Ideal) + Headline + copies EliminationTemplate object + Usage + F = copyTemplate(E, J) + Inputs + E:EliminationTemplate + the elimination template to copy + J:Ideal + a zero-dimensional ideal + Outputs + F:EliminationTemplate + an EliminationTemplate object + Description + Text + This method copies an elimination template object, using the same + action variable and basis, but a different defining ideal. The new + ideal is expected to share the Groebner-basis structure of the + original (same leading monomials, same |B|) --- this is the + specialization regime of @TO2{copyTemplate, "copyTemplate"}@. + Note (possible typo in an earlier draft of this docstring): the + example below originally paired @TT "I = ideal(x^4+x*y+y^2-3, x^2*y+y^3-2)"@ + with @TT "J = ideal(x^3+y^2-1, x^2+y^3-1)"@, which violates this + requirement --- @TT "deg(R/I) = 12"@ but @TT "deg(R/J) = 9"@, so no + single template can serve both. The example below uses a + structurally-compatible replacement @TT "J"@. + Example + R = QQ[x,y] + I = ideal(x^4+x*y+y^2-3, x^2*y+y^3-2) + J = ideal(x^4+2*x*y+y^2-1, 3*x^2*y+y^3-5) + E = eliminationTemplate(x,I) + F = copyTemplate(E, J) + SeeAlso + eliminationTemplate +/// + +doc /// + Node + Key + getActionMatrix + (getActionMatrix, EliminationTemplate) + Headline + computes or retrieves a template's action matrix + Usage + A = getActionMatrix E + Inputs + E:EliminationTemplate + an elimination template + Outputs + A:Matrix + the action matrix + Description + Text + For a zero-dimensional polynomial ideal $I \subset R$, multiplication by a general linear form $f \in R$ induces a linear map $R/I \to R/I$, which can be represented using an action matrix. + For an ideal represented by a template matrix, the action matrix may from the template matrix by various triangularization schemes (RREF, LU Decomposition, etc.) + The eigeenvalues eigenvalues can be used to determine the (closed) points in the vanishing locus of $I$, as they give the values of $f$ on these points. + In this implementation, the action matrix is cached inside the template to facilitate quicker computation. + Example + R = QQ[x,y] + I = ideal(x + y - 1, x^2 + y^2 - 1) + E = eliminationTemplate(x,I) + A = getActionMatrix E + eigenvalues A + SeeAlso + EliminationTemplate + getTemplateMatrix +/// + + +doc /// + Node + Key + getTemplateMatrix + (getTemplateMatrix, RingElement, Matrix, Ideal) + (getTemplateMatrix, ShiftSet, MonomialPartition, Ideal) + (getTemplateMatrix, EliminationTemplate) + Headline + computes template matrix + Usage + M = (a, B, J) + M = getTemplateMatrix(sh, mp, J) + getTemplateMatrix(E) + Inputs + a:RingElement + the action polynomial defining a multiplication matrix + B:Matrix + E:EliminationTemplate + the elimination template for this problem + J:Ideal + MonomialOrder=>Thing + the monomial order used on the ambient ring + mp:MonomialPartition + sh:ShiftSet + Strategy=>Thing + the strategy used to compute H0 + Outputs + M:Matrix + Description + Text + This method computes the template matrix corresponding to the inputted elimination template. + Example + R = QQ[x,y] + I = ideal(x^4+x*y+y^2-3, x^2*y+y^3-2) + E = eliminationTemplate(x,I) + getTemplateMatrix(E) + SeeAlso + EliminationTemplate +/// + +doc /// + Key + (ideal, EliminationTemplate) + Headline + access the ideal of an EliminationTemplate + Usage + ideal E + Inputs + E:EliminationTemplate + Outputs + :Ideal + the ideal J from which the template was constructed + Description + Text + This method retrieves the original ideal J associated with the + @TO EliminationTemplate@. + Example + R = QQ[x,y]; + I = ideal(x^2-1, y^2-1); + E = eliminationTemplate(x, I); + ideal E === I +/// + +doc /// + Key + (basis, EliminationTemplate) + [basis, Degree] + [basis, Limit] + [basis, SourceRing] + [basis, Strategy] + [basis, Truncate] + [basis, Variables] + Headline + access the quotient basis of an EliminationTemplate + Usage + basis E + Inputs + E:EliminationTemplate + Outputs + :Matrix + the standard monomials of an elimination template + Description + Text + This method returns a basis of standard mononomials for the ideal represented by an elimination template. + Example + R = QQ[x,y]; + I = ideal(x^2-1, y^2-1); + E = eliminationTemplate(x, I); + getTemplate E; + basis E +/// + +doc /// + Key + (net, EliminationTemplate) + Headline + display an EliminationTemplate + Usage + net E + Inputs + E:EliminationTemplate + Outputs + :Net + Description + Text + This method formats an @TO EliminationTemplate@ for printing. It lists the + action variable and, if they have been computed and cached, the + template matrix and the action matrix. + Example + R = QQ[x,y]; + I = ideal(x^2-1, y^2-1); + E = eliminationTemplate(x, I); + net E +/// + +doc /// + Node + Key + actionVariable + (actionVariable, EliminationTemplate) + Headline + returns the action variable associated to the elimination template + Usage + actionVariable(E) + Inputs + E:EliminationTemplate + the elimination template for this problem + Outputs + a:RingElement + the action variable associated to E + Description + Text + This method outputs the action variable associated to the inputted elimination template E. + Example + R = QQ[x,y] + I = ideal(x^4+x*y+y^2-3, x^2*y+y^3-2) + E = eliminationTemplate(x,I) + actionVariable(E) + SeeAlso + EliminationTemplate +/// + +TEST /// + R = QQ[x,y] + J = ideal(x^2+y^2-1,x^2+x*y+y^2-1) + actVar = x + E = eliminationTemplate(actVar, J) + M = getTemplateMatrix(E) + Ma = getActionMatrix(E) + evals = eigenvalues Ma + assert(all(sort evals, {-1,0,0,1}, (e1, e2) -> abs(e1 - e2) < 1e-4)) +/// + +TEST /// + R = QQ[x,y] + J = ideal(x^3 + y^2 - 1, x - y - 1) + E = eliminationTemplate(x, J) + Mx = getActionMatrix(E) + evals = eigenvalues Mx + assert(all(sort evals, {-2,0,1}, (e1, e2) -> abs(e1 - e2) < 1e-4)) +/// + +TEST /// -- 3-variable benchmark: dense cubic-ish system (deg J = 12, |B| = 12). +-- Smoke test of the basic pipeline (getTemplateMatrix / getActionMatrix). +-- Template-size checks for this system are in the Greedy + AdjustParams=>false +-- TEST below. + R = QQ[x,y,z] + J = ideal(x^3+y^3+z^3-4,x^2-y-z-1,x-y^2+z-3) + E = eliminationTemplate(x, J) + getTemplateMatrix E + getActionMatrix E + eigenvalues getActionMatrix E +/// + +TEST /// -- 5-point essential matrix (Demazure trace identity, deg I = 10). +-- Classical relative-pose benchmark from Nister; template-size reference from +-- Martyushev et al., CVPR 2022 (10 x 20 on std basis). + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; -- essential matrix + I = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E); -- Demazure constraints + l = random(1, R); + sols = templateSolve(l, I) + assert(all(sols, x -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{x}))) +/// + +TEST /// -- Greedy + AdjustParams=>false (alpha := 0 particular solution): +-- paper-reference sizes. Under the monomial-action short-circuit, all strategies already match +-- paper-reference sizes on monomial-action problems; this test pins the +-- alpha:=0 sizes specifically. + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + ET = eliminationTemplate(y, I); + M = getTemplateMatrix(ET, Strategy => "Greedy", AdjustParams => false); + assert(numRows M == 10 and numColumns M == 20) + + J = ideal(x^3+y^3+z^3-4,x^2-y-z-1,x-y^2+z-3) + ET2 = eliminationTemplate(x, J); + N = getTemplateMatrix(ET2, Strategy => "Greedy", AdjustParams => false); + -- Greedy + AdjustParams=>false on this problem uses a min-degree + -- buildHSymbolic H, size 20x33. Default's GB H0 happens to have smaller + -- monomial support here (16x28 in the unified path); which mode wins is + -- problem-dependent. + assert(numRows N == 20 and numColumns N == 33) +/// + +TEST /// -- on 5pt essential (monomial action, 0 free alphas), +-- Greedy flows through the unified getH0 but skips the (s-a) graph lift, +-- so its template matches paper-reference 10x20 (not the old 20x20). + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + ET = eliminationTemplate(y, I); + Mg = getTemplateMatrix(ET, Strategy => "Greedy"); + assert(numRows Mg == 10 and numColumns Mg == 20) +/// + +TEST /// -- getActionMatrix on Greedy templates (alpha:=0 and adjustParams +-- modes): 10x10 with deg I distinct eigenvalues on 5pt essential. + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + d = degree I; + E1 = eliminationTemplate(y, I); + Ma1 = getActionMatrix(E1, Strategy => "Greedy", AdjustParams => false); + assert(numRows Ma1 == d and numColumns Ma1 == d); + assert(#eigenvalues sub(Ma1, CC) == d); + E2 = eliminationTemplate(y, I); + Ma2 = getActionMatrix(E2, Strategy => "Greedy"); + assert(numRows Ma2 == d and numColumns Ma2 == d); + assert(#eigenvalues sub(Ma2, CC) == d); +/// + +TEST /// -- templateSolve via Greedy + AdjustParams=>false on 5pt essential + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + d = degree I; + E1 = eliminationTemplate(y, I); + sols1 = templateSolve(E1, Strategy => "Greedy", AdjustParams => false); + assert(#sols1 == d); + assert(all(sols1, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))); +/// + +TEST /// -- templateSolve via Greedy on 5pt essential + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + d = degree I; + E2 = eliminationTemplate(y, I); + sols2 = templateSolve(E2, Strategy => "Greedy"); + assert(#sols2 == d); + assert(all(sols2, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))); +/// + +TEST /// -- Greedy + AdjustParams=>false with action variable x on 5pt +-- essential. The monomial-action short-circuit handles via the unified +-- [E|R|B] pipeline + recoverSolutionsFromEigenvectors; the legacy graph-ring +-- templateSolve path used to fail on 3-var systems with non-random actions. + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + d = degree I; + E = eliminationTemplate(x, I); + sols = templateSolve(E, Strategy => "Greedy", AdjustParams => false); + assert(#sols == d); + assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))); +/// + +TEST /// -- Greedy with action z on 5pt essential. + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + d = degree I; + E = eliminationTemplate(z, I); + sols = templateSolve(E, Strategy => "Greedy"); + assert(#sols == d); + assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))); +/// + +TEST /// -- Cross-validation: on a 0-free-alpha problem +-- (5pt essential), every strategy skips the (s-a) graph lift on monomial +-- actions and uses the shared [excess|residual|basis] layout. Greedy's +-- buildHSymbolic H and Default's GB H0 happen to produce the same shifts +-- on this problem, so Mg == Md at 10x20. Greedy + AdjustParams=>false +-- (alpha:=0) reaches the same size 10x20 too; the action matrices of the +-- three share the char polynomial. + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(Ee*transpose Ee * Ee - (1/2) * trace(Ee * transpose Ee) * Ee) + ideal(det Ee); + E1 = eliminationTemplate(y, I); + Mh = getTemplateMatrix(E1, Strategy => "Greedy", AdjustParams => false); + E2 = eliminationTemplate(y, I); + Mg = getTemplateMatrix(E2, Strategy => "Greedy"); + E3 = eliminationTemplate(y, I); + Md = getTemplateMatrix(E3); + Ah = getActionMatrix(E1, Strategy => "Greedy", AdjustParams => false); + Ag = getActionMatrix(E2, Strategy => "Greedy"); + Ad = getActionMatrix(E3); + -- Every strategy uses the same downstream on monomial action + -- and produces identical templates on this 0-free-alpha problem. + assert(Mg == Md); + assert(numRows Mh == numRows Mg); + assert(Ag == Ad); + -- Char polynomial of action is invariant under basis permutation. + Rt = QQ[t]; + chiH = det(t * id_(Rt^(numRows Ah)) - sub(Ah, Rt)); + chiG = det(t * id_(Rt^(numRows Ag)) - sub(Ag, Rt)); + assert(chiH == chiG); +/// + +-* +TEST /// -- Greedy on 6 Demazure cubics (no det): free alpha > 0, +-- adjustParams commits alphas to cancel excessive monomials, producing a +-- smaller H0 (fewer monomials per row) than Default's Groebner H0. Both +-- flow through the shared pipeline: Greedy's template should +-- have row count <= Default's (fewer shifts) and both solve to residual +-- < 1e-6. + R = QQ[x,y,z] + Es = apply(4, i -> random(QQ^3, QQ^3)); + Ee = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; + I = ideal(2*Ee*transpose(Ee)*Ee - trace(Ee*transpose(Ee))*Ee); + assert(dim I == 0); + d = degree I; + -- Compute everything up front; defer the CC residual checks to the end, + -- because `CC[gens R]` rebinds the symbols x,y,z and breaks subsequent + -- calls like `eliminationTemplate(y, I)`. + E1 = eliminationTemplate(y, I); + Md = getTemplateMatrix(E1); -- Default + E2 = eliminationTemplate(y, I); + Mg = getTemplateMatrix(E2, Strategy => "Greedy"); + E3 = eliminationTemplate(y, I); + solsH = templateSolve(E3, Strategy => "Greedy", AdjustParams => false); + solsD = templateSolve(E1); + solsG = templateSolve(E2, Strategy => "Greedy"); + -- Greedy invariant: Greedy <= Default in rows on problems with free alphas. + assert(numRows Mg <= numRows Md); + assert(#solsD == d and #solsG == d and #solsH == d); + Rc = CC[gens R]; + gensC = sub(gens I, Rc); + assert(all(solsD, s -> 1e-6 > norm sub(gensC, matrix{s}))); + assert(all(solsG, s -> 1e-6 > norm sub(gensC, matrix{s}))); + assert(all(solsH, s -> 1e-6 > norm sub(gensC, matrix{s}))); +/// +*- + +TEST /// -- Greedy + AdjustParams=>false on the 3-variable docs system. + R = QQ[x,y,z] + J = ideal(x^3+y^3+z^3-4, x^2-y-z-1, x-y^2+z-3) + d = degree J; + E1 = eliminationTemplate(x, J); + solsH = templateSolve(E1, Strategy => "Greedy", AdjustParams => false); + assert(#solsH == d); + assert(all(solsH, s -> 1e-6 > norm sub(sub(gens J, CC[gens R]), matrix{s}))); +/// + +TEST /// -- Paper §3 small example on a 2-var system (deg I = 12): Greedy + +-- AdjustParams=>false produces a 7×19 template and solves to residual < 1e-6. + R = QQ[x,y] + I = ideal(x^4+y^2+x*y-3, x^2*y+y^3-2) + d = degree I; + E = eliminationTemplate(x, I); + M = getTemplateMatrix(E, Strategy => "Greedy", AdjustParams => false); + assert(numColumns M == 19); + assert(numRows M < 19); -- strictly smaller than Default's 19×19 + sols = templateSolve(E, Strategy => "Greedy", AdjustParams => false); + assert(#sols == d); + assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))); +/// + +TEST /// -- #1 F+λ 8pt from Martyushev CVPR 2022 Table 1 over ZZ/32749. +-- Paper target: std 11×19, nstd 7×15. Greedy + AdjustParams=>false achieves +-- 11×20 (matches std up to one column). No tier-3 since we're in ZZ/p. + FF = ZZ/32749 + R = FF[x,y] + d1 = random(FF^8, FF^4); + V1 = matrix{{y*x},{y},{x},{1_R}}; + fv = sub(d1, R) * V1; + Fmat = matrix{{fv_(0,0), fv_(3,0), fv_(5,0)}, + {fv_(1,0), fv_(4,0), fv_(6,0)}, + {fv_(2,0), x, 1_R}}; + I = ideal(det Fmat, y*fv_(2,0) - fv_(7,0)); + E = eliminationTemplate(x, I); + M = getTemplateMatrix(E, Strategy => "Greedy", AdjustParams => false); + assert(numRows M == 11 and numColumns M == 20); +/// + +TEST /// -- change of ideals + R = QQ[x,y] + I = ideal(x^2+y^2-1,x^2+y^3+x*y-2) + E = eliminationTemplate(x+4*y,I) + sols = templateSolve(E) + assert(all(sols, x -> 1e-6 > norm sub(sub(gens I, QQ[gens R]), matrix{x}))) + + J = ideal(x^2+y^2-2,x^2+y^3+3*x*y-5) + F = copyTemplate(E,J) + sols = templateSolve(F) + assert(all(sols, x -> 1e-6 > norm sub(sub(gens J, QQ[gens R]), matrix{x}))) +/// + +TEST /// -- copyTemplate on a Greedy-built template. Greedy populates the +-- same monomial partition / shifts cache as Default, so copyTemplate +-- transplants the structure into a perturbed ideal and the Greedy solve +-- on the copy still hits tier-3 residual. + R = QQ[x,y] + I = ideal(x^2+y^2-1, x^2+y^3+x*y-2) + E = eliminationTemplate(x, I) + -- Populate the greedy cache (graphIdeal / shifts / monomialPartition). + getTemplateMatrix(E, Strategy => "Greedy"); + getActionMatrix(E, Strategy => "Greedy"); + -- getTemplate rebinds x, y to the graph-ring generators while building + -- Rs; restore R so that J is constructed over R (not over the graph + -- ring), which is what copyTemplate expects. + use R; + J = ideal(x^2+y^2-2, x^2+y^3+3*x*y-5) + F = copyTemplate(E, J) + sols = templateSolve(F, Strategy => "Greedy") + assert(all(sols, x -> 1e-6 > norm sub(sub(gens J, CC[gens R]), matrix{x}))) +/// + +TEST /// -- example used for section 3 + R = QQ[x,y] + I = ideal(x^4+y^2+x*y-3, x^2*y+y^3-2) + E = eliminationTemplate(x,I) + sols = templateSolve(E) + assert(all(sols, x -> 1e-6 > norm sub(sub(gens I, QQ[gens R]), matrix{x}))) + actionVariable(E) +/// + +TEST /// + R = QQ[x,y,z] + J = ideal(x^3+y^3+z^3-4,x^2-y-z-1,x-y^2+z-3) + -- Every strategy flows through the same [E|R|B] pipeline; only the H0 + -- computation inside getH0 differs per strategy. + E1 = eliminationTemplate(x, J); + E2 = eliminationTemplate(x, J); + -- Default Strategy + M1 = getActionMatrix(E1); + assert(#eigenvalues M1 == 12) + -- Larsson Strategy + M2 = getActionMatrix(E2, Strategy => "Larsson"); + assert(#eigenvalues M2 == 12) +/// + +TEST /// -- End-to-end: every strategy on 5pt essential + det. Asserts +-- tier-1 (action matrix is d x d), tier-2 (#eigenvalues == d), tier-3 +-- (residual < 1e-6). + setRandomSeed 42 + R = QQ[x,y,z]; + Es = apply(4, i -> random(QQ^3, QQ^3)); + Em = x*Es#0 + y*Es#1 + z*Es#2 + Es#3; + I = ideal(Em*transpose Em * Em - (1/2)*trace(Em*transpose Em)*Em) + ideal(det Em); + d = degree I; + -- Build all four EliminationTemplates up front, before any ring + -- rebinding from building CC[gens R]. + E1 = eliminationTemplate(y, I); + E2 = eliminationTemplate(y, I); + E3 = eliminationTemplate(y, I); + E4 = eliminationTemplate(y, I); + Rc = CC[gens R]; + gensIc = sub(gens I, Rc); + -- Default. + Ma1 = getActionMatrix E1; + assert(numRows Ma1 == d and numColumns Ma1 == d); + assert(#eigenvalues sub(Ma1, CC) == d); + sols1 = templateSolve E1; + assert(#sols1 > 0); + assert(all(sols1, p -> 1e-6 > norm sub(gensIc, matrix{p}))); + -- Larsson. + Ma2 = getActionMatrix(E2, Strategy => "Larsson"); + assert(numRows Ma2 == d and numColumns Ma2 == d); + assert(#eigenvalues sub(Ma2, CC) == d); + sols2 = templateSolve(E2, Strategy => "Larsson"); + assert(all(sols2, p -> 1e-6 > norm sub(gensIc, matrix{p}))); + -- MatrixHi mode (Greedy + AdjustParams => false). + Ma3 = getActionMatrix(E3, Strategy => "Greedy", AdjustParams => false); + assert(numRows Ma3 == d and numColumns Ma3 == d); + assert(#eigenvalues sub(Ma3, CC) == d); + sols3 = templateSolve(E3, Strategy => "Greedy", AdjustParams => false); + assert(all(sols3, p -> 1e-6 > norm sub(gensIc, matrix{p}))); + -- Greedy (default AdjustParams => true). + Ma4 = getActionMatrix(E4, Strategy => "Greedy"); + assert(numRows Ma4 == d and numColumns Ma4 == d); + assert(#eigenvalues sub(Ma4, CC) == d); + sols4 = templateSolve(E4, Strategy => "Greedy"); + assert(all(sols4, p -> 1e-6 > norm sub(gensIc, matrix{p}))); +/// + +TEST /// -- Polynomial action lift: action x+4y on 2-var system, end to end. + R = QQ[x,y]; + I = ideal(x^2+y^2-1, x^2+y^3+x*y-2); + d = degree I; + E = eliminationTemplate(x + 4*y, I); + -- Tier 1: template matrix builds. + M = getTemplateMatrix E; + assert(numRows M > 0 and numColumns M > 0); + -- Tier 2: action matrix has deg I distinct eigenvalues. + Ma = getActionMatrix E; + assert(numRows Ma == d and numColumns Ma == d); + assert(#eigenvalues sub(Ma, CC) == d); + -- Tier 3: recovered solutions satisfy the ideal. + sols = templateSolve E; + assert(all(sols, p -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{p}))); +/// + +TEST /// -- Accessors and copyTemplate on a structurally-compatible specialization. + R = QQ[x,y]; + I = ideal(x^4+x*y+y^2-3, x^2*y+y^3-2); + J = ideal(x^4+2*x*y+y^2-1, 3*x^2*y+y^3-5); + Rc = QQ[gens R]; + gensIc = sub(gens I, Rc); + gensJc = sub(gens J, Rc); + -- Build the template objects up front. + E = eliminationTemplate(x, I); + Ecopy = eliminationTemplate(x, I); + -- Accessor smoke. + assert(actionVariable E == x); + assert(ideal E == I); + -- templateSolve on (RingElement, Ideal). + solsA = templateSolve(x, I); + assert(all(solsA, p -> 1e-6 > norm sub(gensIc, matrix{p}))); + -- copyTemplate on a J that shares I's leading-term structure. + getTemplateMatrix Ecopy; + F = copyTemplate(Ecopy, J); + solsF = templateSolve F; + assert(#solsF == degree I); + assert(all(solsF, p -> 1e-6 > norm sub(gensJc, matrix{p}))); +/// + + +end + + +-------------------------------------------------------------------------- +-- Reproducible code listings from the companion paper. +-- +-- Each block below is the runnable code for one numbered example in the +-- paper. These blocks live past `end`, so loading the package does not +-- execute them; copy-paste a block into an interactive M2 session to +-- reproduce the output. All blocks have been tested against the current +-- package; typos present in earlier drafts of the paper listings are +-- corrected here. +-- +-- Example 4.1 -- specialization of a 2-variable system. + +restart +needsPackage "EliminationTemplates" +R = QQ[x,y] +I = ideal(x^2 + y^2 - 1, x^2 + y^3 + x*y - 2) +E = eliminationTemplate(x + 4*y, I) +sols = templateSolve(E) +assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, QQ[gens R]), matrix{s}))) + +-- Reuse the template on a perturbed ideal with the same initial ideal. +J = ideal(x^2 + y^2 - 2, x^2 + y^3 + 3*x*y - 5) +F = copyTemplate(E, J) +sols = templateSolve(F) +assert(all(sols, s -> 1e-6 > norm sub(sub(gens J, QQ[gens R]), matrix{s}))) + + +-- Example 4.2 -- 5-point essential matrix. + +restart +needsPackage "EliminationTemplates" +setRandomSeed 42 +R = QQ[x, y, z] +Es = apply(4, i -> random(QQ^3, QQ^3)) +Em = x*Es#0 + y*Es#1 + z*Es#2 + Es#3 +I = ideal(Em*transpose Em * Em - (1/2)*trace(Em*transpose Em)*Em) +l = random(1, R) +ET = eliminationTemplate(l, I) +sols = templateSolve(ET) +assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))) + +-- Reuse on a new essential-matrix instance (same ideal shape). +Es2 = apply(4, i -> random(QQ^3, QQ^3)) +Em2 = x*Es2#0 + y*Es2#1 + z*Es2#2 + Es2#3 +J = ideal(Em2*transpose Em2 * Em2 - (1/2)*trace(Em2*transpose Em2)*Em2) +ETp = copyTemplate(ET, J) +sols = templateSolve(ETp) +assert(all(sols, s -> 1e-6 > norm sub(sub(gens J, CC[gens R]), matrix{s}))) + + +-- Example 4.3 -- quaternion camera pose recovery. +-- Typo corrections vs earlier paper drafts: Q2R takes 4 args (not 5); +-- the P0 matrix was missing a closing paren; groundTruthSolution had +-- unbalanced parens; the output annotation o20 should be o19. The +-- listing below is the corrected version. + +restart +needsPackage "EliminationTemplates" +setRandomSeed 42 +FF = QQ + +a = random(FF^3, FF^1) || matrix{{1}} +b1 = random(FF^3, FF^1) || matrix{{1}} +b2 = random(FF^3, FF^1) || matrix{{1}} + +Q2R = (w,x,y,z) -> matrix{ + {w^2+x^2-y^2-z^2, 2*x*y-2*w*z, 2*w*y+2*x*z }, + {2*x*y+2*w*z, w^2-x^2+y^2-z^2, -2*w*x+2*y*z }, + {-2*w*y+2*x*z, 2*w*x+2*y*z, w^2-x^2-y^2+z^2 } +} + +(w0, x0, y0, z0, f0) := (random FF, random FF, random FF, random FF, random FF) +R0 := Q2R(w0, x0, y0, z0) +P0 := diagonalMatrix{f0, f0, 1} * (R0 | matrix{{0},{0},{0}}) +l1 = gens ker transpose(P0 * (a | b1)) +l2 = gens ker transpose(P0 * (a | b2)) + +S = FF[w..z, f] +R = Q2R(w, x, y, z) +P = diagonalMatrix{f, f, 1} * (R | matrix{{0},{0},{0}}) +I = ideal( + w^2 + x^2 + y^2 + z^2 - 1, + transpose l1 * P * a, + transpose l1 * P * b1, + transpose l2 * P * a, + transpose l2 * P * b2 +) + +l = random(1, ring I) +ET = eliminationTemplate(l, I) +sols = templateSolve ET + +groundTruthSolution = matrix{append( + (1/sqrt(w0^2 + x0^2 + y0^2 + z0^2)) * {w0, x0, y0, z0}, + f0 +)} +-- Among the returned sols, exactly one should match the ground truth +-- up to numerical noise. +position(sols, s -> norm(matrix{s} - groundTruthSolution) < 1e-10) + + +-- Example 4.4 -- strategy comparison on a 4-variable problem. +-- Verified sizes on `setRandomSeed 42`: Default 818 x 530, Larsson 286 x 339. +-- (Earlier paper drafts reported 818 x 500 and 286 x 309; the current +-- package retains the residual block as a separate column block, adding +-- |R| = deg I = 30 columns. Row counts are unchanged.) + +restart +needsPackage "EliminationTemplates" +setRandomSeed 42 +R = QQ[w, x, y, lambda] + +mons = {x^2, y^2, lambda^2, x*y, x*lambda, y*lambda} +coeffs = apply(6, i -> random(QQ)) +h = sum(0..#mons-1, i -> coeffs#i * mons#i) + +Fs = apply(4, i -> random(QQ^3, QQ^3)) +F = x*Fs#0 + y*Fs#1 + lambda*Fs#2 + Fs#3 +Q = diagonalMatrix({1, 1, w}) + +I = ideal(F*Q*transpose F*Q*F - (1/2)*trace(F*Q*transpose F*Q)*F) + + ideal(det F) + + ideal(lambda*y - h) + +l = random(1, R) +ET = eliminationTemplate(l, I) + +M1 = getTemplateMatrix(ET) -- 818 x 530 +M2 = getTemplateMatrix(ET, Strategy => "Larsson") -- 286 x 339 + + +-- Example 4.5 -- f+E+f 6-point over ZZ/32749 where greedy is strictly best. + +restart +needsPackage "EliminationTemplates" +setRandomSeed 42 +FF = ZZ/32749 +R = FF[x, y, z] + +A = random(FF^9, FF^3) +X = apply(3, i -> matrix apply(3, j -> + apply(3, k -> sub(A_(3*j+i, k), R)))) +Fmat = X#0 + y*X#1 + z*X#2 +Om = diagonalMatrix{1_R, 1_R, x} +I = ideal(2*Fmat*Om*transpose(Fmat)*Om*Fmat + - trace(Fmat*Om*transpose(Fmat)*Om)*Fmat) + + ideal(det Fmat) + +ET = eliminationTemplate(x, I) + +getTemplateMatrix(ET) -- Default : 200 x 120 +getTemplateMatrix(ET, Strategy => "Larsson") -- Larsson : 53 x 73 +getTemplateMatrix(ET, Strategy => "Greedy", + AdjustParams => false) -- alpha=0 : 93 x 99 +getTemplateMatrix(ET, Strategy => "Greedy") -- Greedy : 31 x 50 + + +-------------------------------------------------------------------------- +-- DEMO: paste these blocks one-at-a-time into an interactive M2 session. +-- Each block is self-contained (starts with `restart`) and exercises one +-- strategy comparison with size annotations and tier-3 (solve) assertions. +-- +-- Expected sizes below are reproducible with `setRandomSeed 42` on QQ. +-- +-- Strategy semantics (post-Increment-3 unified pipeline): +-- Every strategy flows through the [E|R|B] + RREF+pivot +-- extraction. Choice of Strategy only changes how H0 is computed inside +-- getH0: +-- null (Default) : H0 from Groebner change-of-basis +-- "Larsson" : H0 mod syz(F) (CVPR 2017) +-- "Greedy", AdjustParams => false : alpha := 0 particular solution +-- of buildHSymbolic ("MatrixHi mode") +-- "Greedy" : buildHSymbolic + adjustParams +-- (Martyushev CVPR 2022 §4); +-- ZZ/p-pinned inner RREF on QQ input +-- +-- Polynomial actions lift to R_s = R[s] / and run the same +-- pipeline on (s, J_s) in R_s. +-- +-- templateSolve picks recoverSolutionsFromEigenvectors when every ring variable is +-- a basis monomial (monomial-direct, or lifted problems whose basis happens +-- to contain all original vars) and falls back to recoverSolutions (RREF + +-- pivot) otherwise. +-------------------------------------------------------------------------- + +-- Demo 1: 5-point essential matrix (Demazure trace identity + det) +-- deg I = 10. Every strategy reaches paper std size +-- 10x20 on this monomial-action problem. +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +setRandomSeed 42 +R = QQ[x,y,z] +Es = apply(4, i -> random(QQ^3, QQ^3)) +Em = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 +I = ideal(2*Em*transpose(Em)*Em - trace(Em*transpose(Em))*Em, det Em) +degree I -- 10 + +ET = eliminationTemplate(y, I) +getTemplateMatrix ET -- Default : 10 x 20 +getTemplateMatrix(ET, Strategy => "Larsson") -- Larsson : 10 x 20 +getTemplateMatrix(ET, Strategy => "Greedy", AdjustParams => false) -- alpha:=0 : 10 x 20 <- paper +getTemplateMatrix(ET, Strategy => "Greedy") -- Greedy : 10 x 20 (0 free alphas) + +-- End-to-end solve + tier-3 residual check via Greedy. +ET2 = eliminationTemplate(y, I) +sols = templateSolve(ET2, Strategy => "Greedy") +assert(#sols == degree I) +assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))) + + +-- Demo 2: 6 Demazure cubics (no det) -- adjustParams has leverage here. +-- 66 free alphas; adjustParams commits some of them to cut shifts in H0. +-- Every strategy shares the [E|R|B] pipeline. Default 27x34, +-- Larsson / Greedy 24x34 (cut via adjustParams or syzygy reduction), +-- Greedy + AdjustParams=>false (alpha:=0) 25x35. +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +check "EliminationTemplates" +FF = frac(QQ[a,b,c,d]); +R = FF[x,y,MonomialOrder=>Lex]; +l = x - 2*y; +I = ideal(x^2+a*y^2-1, x*y-b); +needsPackage "EliminationTemplates"; +ET = eliminationTemplate(l, I); +M = getTemplateMatrix ET +(P, L, U) = LUdecomposition M; +L +U +getActionMatrix ET +setRandomSeed 42 +R = QQ[x,y,z] +Es = apply(4, i -> random(QQ^3, QQ^3)) +Em = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 +I = ideal(2*Em*transpose(Em)*Em - trace(Em*transpose(Em))*Em) -- no det +degree I -- still 10 (generic random coefficients) + +E1 = eliminationTemplate(y, I) +Md = getTemplateMatrix(E1) -- Default : 27 x 34 +E2 = eliminationTemplate(y, I) +Mg = getTemplateMatrix(E2, Strategy => "Greedy") -- Greedy : 24 x 34 <- shift reduction +assert(numRows Mg <= numRows Md) +E3 = eliminationTemplate(y, I) +Mh = getTemplateMatrix(E3, Strategy => "Greedy", AdjustParams => false) -- alpha:=0 : 25 x 35 + +-- Both solve correctly to residual < 1e-6. +solsG = templateSolve(E2, Strategy => "Greedy") +assert(#solsG == degree I) +assert(all(solsG, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))) + + +-- Demo 3: paper §3 example (2-var system, deg I = 12). +-- Greedy + AdjustParams=>false reaches 7x19 (paper target). +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +R = QQ[x,y] +I = ideal(x^4 + y^2 + x*y - 3, x^2*y + y^3 - 2) +degree I -- 12 + +E = eliminationTemplate(x, I) +getTemplateMatrix E -- Default : 7 x 19 +getTemplateMatrix(E, Strategy => "Greedy", AdjustParams => false) -- alpha:=0 : 7 x 19 +sols = templateSolve(E, Strategy => "Greedy", AdjustParams => false) +assert(#sols == degree I) +assert(all(sols, s -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{s}))) + +-- Cross-check: eigenvalues of the action matrix == x-coordinates of solutions. +Ma = getActionMatrix(E, Strategy => "Greedy", AdjustParams => false) +evals = eigenvalues sub(Ma, CC) +assert(#evals == degree I) + + +-- Demo 4: Paper Table 1 #1 (F+lambda 8pt) over ZZ/p; matches Martyushev std. +-- Shown over ZZ/32749 because Greedy over QQ is slower (coefficient growth). +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +FF = ZZ/32749 +R = FF[x,y] +d1 = random(FF^8, FF^4) +V1 = matrix{{y*x},{y},{x},{1_R}} +fv = sub(d1, R) * V1 +Fmat = matrix{{fv_(0,0), fv_(3,0), fv_(5,0)}, + {fv_(1,0), fv_(4,0), fv_(6,0)}, + {fv_(2,0), x, 1_R}} +I = ideal(det Fmat, y*fv_(2,0) - fv_(7,0)) +E = eliminationTemplate(x, I) +getTemplateMatrix(E, Strategy => "Greedy", AdjustParams => false) -- 11 x 20 (paper std: 11 x 19) + + + + +-- 5-point essential matrix problem: DEBUGGING TEMPLATE SIZE & STRATEGY +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +check "EliminationTemplates" +installPackage("EliminationTemplates", RemakeAllDocumentation => true) +viewHelp EliminationTemplates +R = QQ[x,y,z] +Es = apply(4, i -> random(QQ^3, QQ^3)) +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 -- essential matrix +I = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E, det E); -- Demazure constraints +l = y +ET = eliminationTemplate(l, I) +M = getTemplateMatrix ET +FF=frac(QQ[e_(0,0,0)..e_(3,2,2)]) +Es = apply(4, i -> matrix apply(3, j -> apply(3, k -> e_(i,j,k)))) +R = FF[x,y,z] +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 -- essential matrix +J = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E, det E); -- Demazure constraints +errorDepth=3 +ETP = copyTemplate(ET, J) +printWidth = 1000000 +getTemplateMatrix ETP + +FF = frac(QQ[a,b,c,d]) +R = FF[x,y,MonomialOrder=>Lex] +l = c*x + d*y +I = ideal(x^2+a*y^2-1, x*y-b) +needsPackage "EliminationTemplates" +ET = eliminationTemplate(l, I) +M = getTemplateMatrix ET +(P, L, U) = LUdecomposition M +reducedRowEchelonForm M + +load "Benchmarks.m2"; +runBenchmarks() + + + +-* Development section *- +-- basic solve, compare with known solution +restart +debug needsPackage "EliminationTemplates" +needsPackage "NumericalAlgebraicGeometry" +R=QQ[x,y,z] +J=ideal(x^3+y^3+z^3-4,x^2-y-z-1,x-y^2+z-3) +B=basis(R/J) +getEigenMatrix(x,J) +templateSolve(x, J) +templateSolve(x+2*y+3*z,J) +templateSolve(x,J) -- Why do we have two of these lines? +netList solveSystem J_* + +-- change of basis +restart +debug needsPackage "EliminationTemplates" +R=QQ[x,y] +I=ideal(x^2+y^2-1,x^2+y^3+x*y-2) +J=ideal(x^2+y^2-2,x^2+y^3+3*x*y-5) +B=basis(R/I) +E=eliminationTemplate(x+4*y,I) +getTemplate(E) +getEigenMatrix(E) +sols = templateSolve(E) +assert(all(sols, x -> 1e-6 > norm sub(sub(gens I, QQ[gens R]), matrix{x}))) + +F=copyTemplate(E,J) +getEigenMatrix(F) +sols = templateSolve(F) +assert(all(sols, x -> 1e-6 > norm sub(sub(gens J, QQ[gens R]), matrix{x}))) + +restart +debug needsPackage "EliminationTemplates" +R = QQ[x,y] +J = ideal(x^3 + y^2 - 1, x - y - 1) +errorDepth = 2 +templateSolve(x, J) +actVar = x +getEigenMatrix(x, J) + +restart +debug needsPackage "EliminationTemplates" +R = QQ[x,y] +J = ideal(x^3 + y^2 - 1, x - y - 1) +errorDepth = 0 +templateSolve(x, J) + +restart +debug needsPackage "EliminationTemplates" +R = QQ[x] +J = ideal(x^2-1, x^3-x) +getH0(x, basis(R/J), J, Strategy => "Greedy") + +-- Benchmark tests: just run these three lines +restart +load "Benchmarks.m2"; +runBenchmarks() +-- + +uninstallPackage "EliminationTemplates" +restart +installPackage "EliminationTemplates" +viewHelp "EliminationTemplates" +check "EliminationTemplates" + +help EliminationTemplates +help getTemplate + +viewHelp "EliminationTemplates" + +-- 5-point essential matrix problem: DEBUGGING TEMPLATE SIZE & STRATEGY +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +R = QQ[x,y,z] + +Es = apply(4, i -> random(QQ^3, QQ^3)) +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 -- essential matrix +I = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E, det E); -- Demazure constraints +-- l = random(1, R) +(sh, mp) = getTemplate ET +l = y +ET = eliminationTemplate(l, I) +getTemplateMatrix(ET); -- 27 X 44 +getTemplateMatrix(ET, Strategy => "Greedy"); -- 15 x 44 +getTemplateMatrix(ET, Strategy => "Larsson"); -- 24 x 44 + +-* +-- problem! should be 24 x 34 +Rosie's proposed solution: + 1. Store most recently used strategy in cache of ET + 2. If NEW strategy is passed, recompute +*- + + +-- E+f+k 7pt relative pose +getTemplateMatrix(ET, Strategy => "Greedy"); +needsPackage "EliminationTemplates" +R = QQ[w,x,y,lambda]; +mons = {x^2, y^2, lambda^2, x*y, x*lambda, y*lambda}; +coeffs = apply(6, i -> random(QQ)); +h = sum(0..#mons-1, i -> coeffs#i * mons#i); -- random quadratic function +Fs = apply(4, i -> random(QQ^3, QQ^3)); +F = x * Fs#0 + y * Fs#1 + lambda * Fs#2 + Fs#3; +Q = diagonalMatrix({1, 1, w}); +I = ideal(F * Q * transpose F * Q * F - (1/2) * trace(F * Q * transpose F * Q) * F) + ideal(det F) + ideal(lambda * y - h); +l = random(1, R) +errorDepth = 0 +ET = eliminationTemplate(l, I) +getTemplateMatrix(ET); -- 788 x 530 +getTemplateMatrix(ET, Strategy => "Larsson"); -- 256 x 339 +getTemplateMatrix(ET, Strategy => "Greedy"); -- will exceed runtime limit + + +-- generate a template over finite field +FF = ZZ/3 +R = FF[x,y,z] +Es = apply(4, i -> random(FF^3, FF^3)) +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 -- essential matrix +I = ideal(2 * E*transpose E * E - trace(E * transpose E) * E, det E); -- Demazure constraints +l = y +ET = eliminationTemplate(l, I) +M = getTemplateMatrix ET + +-- try copying this template into a rational problem instance +FF = QQ +Es = apply(4, i -> random(FF^3, FF^3)) +R = QQ[x,y,z] +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3 -- essential matrix +J = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E, det E); -- Demazure constraints +E = copyTemplate(ET, J) +sols = templateSolve(E) +apply(sols, x -> 1e-6 > norm sub(sub(gens J, QQ[gens R]), matrix{x})) + +-- Test case +restart +needsPackage "EliminationTemplates" +R = QQ[x,y,z] +J = ideal(x^3+y^3+z^3-4,x^2-y-z-1,x-y^2+z-3) +-- 3 templates, 3 strategies +E1 = eliminationTemplate(x, J); +E2 = eliminationTemplate(x, J); +E3 = eliminationTemplate(x, J); +getTemplateMatrix(E1); -- 27 X 44 +getTemplateMatrix(E2, Strategy => "Greedy"); -- 15 x 44 +getTemplateMatrix(E3, Strategy => "Larsson") + +-- This example doesn't work :( +loadPackage "EliminationTemplates" +R = QQ[x,y,z]; +E0 = matrix {{7/3, 9, 3}, {5/6, 3, 1/8}, {10/9, 7/5, 3/4}}; +E1 = matrix {{1/6, 5/8, 3/10}, {9/4, 9/7, 1/3}, {7/4, 2, 7/10}}; +E2 = matrix {{8/9, 7/5, 9/4}, {10/3, 9/4, 4/7}, {5/2, 7/5, 2/9}}; +E3 = matrix {{5/6, 1/7, 6}, {6/7, 8/3, 3/10}, {9/8, 1, 4/7}}; +Es = {E0, E1, E2, E3} +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; -- essential matrix +I = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E); -- Demazure constraints +l = 5*x + (3/8)*y + (9/7)*z +sols = templateSolve(l, I); +norms = apply(sols, x -> norm sub(sub(gens I, CC[gens R]), matrix{x})); +all(norms, x -> tol > x) + +--(1/9)*x+(3/5)*y+(5/6)*z + + +tol = 1.0 +done = false +i = 0; +while not done do ( + l = random(1, R); + sols = templateSolve(l, I); + norms = apply(sols, x -> norm sub(sub(gens I, CC[gens R]), matrix{x})); + done = not all(norms, x -> tol > x); + i = i + 1; + ) +print(toString norms); +toString l +print i + + +R = QQ[x, y, z]; +Es = apply(4, i -> random(QQ^3, QQ^3)); +E = x * Es#0 + y * Es#1 + z * Es#2 + Es#3; +I = ideal(E*transpose E * E - (1/2) * trace(E * transpose E) * E, det E); +l = random(1, R); +ET = eliminationTemplate(l, I); +sols = templateSolve(ET); +all(sols, x -> 1e-6 > norm sub(sub(gens I, CC[gens R]), matrix{x})) + +R = QQ[x,y]; +I = ideal(x^4+x*y+y^2-3, x^2*y+y^3-2); +E = eliminationTemplate(x,I) +getTemplateMatrix E +templateSolve E +# oo +J = ideal(x^4 + 2*x*y + y^2-1, 3*x^2*y + y^3 - 5) +templateSolve copyTemplate(E, J) + + +FF = ZZ/32749; +R = FF[x,y,z]; +A = random(FF^9, FF^3); +X = apply(3, i -> matrix apply(3, j -> + apply(3, k -> sub(A_(3*j+i,k), R)))); +Fmat = X#0 + y*X#1 + z*X#2; +Om = diagonalMatrix{1_R, 1_R, x}; +I = ideal(2*Fmat*Om*transpose(Fmat)*Om*Fmat + - trace(Fmat*Om*transpose(Fmat)*Om)*Fmat) + ideal(det Fmat); +ET = eliminationTemplate(x, I); +getTemplateMatrix(ET) -- Default : 200 x 120 +getTemplateMatrix(ET, Strategy => "Larsson") -- Larsson : 53 x 73 +getTemplateMatrix(ET, Strategy => "Greedy", AdjustParams => false) + -- alpha:=0 : 93 x 99 +getTemplateMatrix(ET, Strategy => "Greedy") -- Greedy : 31 x 50 + + +uninstallAllPackages() +restart +path = prepend("./", path) +needsPackage "EliminationTemplates" +installPackage("EliminationTemplates", RemakeAllDocumentation => true) +viewHelp EliminationTemplates +check "EliminationTemplates" diff --git a/M2/Macaulay2/packages/EliminationTemplates/Martyushev.m2 b/M2/Macaulay2/packages/EliminationTemplates/Martyushev.m2 new file mode 100644 index 00000000000..af65d2a8661 --- /dev/null +++ b/M2/Macaulay2/packages/EliminationTemplates/Martyushev.m2 @@ -0,0 +1,675 @@ +-- Martyushev.m2: port of Martyushev's matrixHi + adjustParams (CVPR 2022). +-- +-- Design: +-- - Work in a flat extended ring R = FF[vars..., alphas...] from the start. +-- - Free alpha parameters are ring variables; adjustParams substitutes +-- concrete field values into H directly, without intermediate +-- decompositions. +-- +-- Loaded two ways: +-- (a) from inside EliminationTemplates.m2 newPackage (no needsPackage — that +-- would be a circular dep). +-- (b) by external tests that `debug needsPackage "EliminationTemplates"` +-- first, then `load "EliminationTemplates/Martyushev.m2"`. + +-- ============================================================================ +-- buildH: main function combining matrixHi + adjustParams + constructTemplate +-- +-- Input: action variable, basis B, ideal J +-- Output: (Hoptimized, residualMons, Blist, alphaEnv, extendedRing) +-- where Hoptimized is an nG x nF matrix with symbolic alpha entries that +-- have been partially reduced by the greedy. +-- ============================================================================ +buildGapPolys = method() +buildGapPolys(RingElement, Matrix, Ideal) := (aVar, B, J) -> ( + R := ring J; + aVar2 := sub(aVar, R); + Blist := flatten entries lift(B, R); + Bset := set Blist; + aBlist := apply(Blist, b -> aVar2 * b); + resMons := select(aBlist, m -> not Bset#?m); + gapP := apply(resMons, r -> r - (r % J)); + (gapP, toList resMons, Blist) +); +-- Non-standard basis variant (Direction 4.1 / Larsson 2018). `Blist` is any +-- list of |B| = deg(I) monomials forming a basis of R/J. We express the +-- normal form of each residual in Blist via a change-of-basis through the +-- Gröbner-determined standard basis: NF(r,J) = sum c_i b_i, solve for c_i, +-- then the gap polynomial is r - sum c_i b_i (an element of J). Errors if +-- Blist is linearly dependent mod J. +buildGapPolys(RingElement, List, Ideal) := (aVar, Blist, J) -> ( + R := ring J; + FF := coefficientRing R; + aVar2 := sub(aVar, R); + -- Accept Blist entries either in R or in R/J; lift to R uniformly. + Blist = apply(Blist, b -> sub(b, R)); + stdB := basis(R/J); + stdBlist := flatten entries lift(stdB, R); + nB := #Blist; + if nB != #stdBlist then error ("buildGapPolys: basis size " | toString nB + | " != deg R/J = " | toString #stdBlist); + -- Columns = Blist reduced mod J expressed in stdB coords. + BmatStd := sub(last coefficients(matrix{apply(Blist, b -> b % J)}, + Monomials => matrix{stdBlist}), FF); + if rank BmatStd < nB then error "buildGapPolys: Blist is not a basis of R/J"; + BmatStdInv := inverse BmatStd; + Bset := set Blist; + aBlist := apply(Blist, b -> aVar2 * b); + resMons := select(aBlist, m -> not Bset#?m); + gapP := apply(resMons, r -> ( + rNF := r % J; + rStd := sub(last coefficients(matrix{{rNF}}, + Monomials => matrix{stdBlist}), FF); + cB := BmatStdInv * rStd; + reExpressInB := sum prepend(0_R, apply(nB, i -> sub(cB_(i,0), R) * Blist#i)); + r - reExpressInB + )); + (gapP, toList resMons, Blist) +); + +-- Validate a candidate basis: return true iff Blist is a basis of R/J. +isValidBasis = method() +isValidBasis(List, Ideal) := (Blist, J) -> ( + R := ring J; + FF := coefficientRing R; + Blist = apply(Blist, b -> sub(b, R)); + stdBlist := flatten entries lift(basis(R/J), R); + if #Blist != #stdBlist then return false; + BmatStd := sub(last coefficients(matrix{apply(Blist, b -> b % J)}, + Monomials => matrix{stdBlist}), FF); + rank BmatStd == #stdBlist +); + +-- Build a Greedy template (matrixHi + adjustParams + constructTemplate) on +-- a specified basis Blist of R/J. Returns the template matrix directly over +-- coefficientRing(ring J). Set `"RunGreedy" => false` to skip adjustParams +-- (just MatrixHi's particular solution alpha=0) — ~100x faster, useful for +-- coarse basis-search screening before re-running Greedy on the winner. +buildGreedyTemplateWithBasis = method(Options => {Verbose => false, "RunGreedy" => true}) +buildGreedyTemplateWithBasis(RingElement, Ideal, List) := o -> (aVar, J, Blist) -> ( + R := ring J; + FF := coefficientRing R; + Flist := flatten entries gens J; + (gp, resMons, B) := buildGapPolys(aVar, Blist, J); + if #gp == 0 then return map(FF^0, FF^(#B), 0); + RB := (toList resMons) | B; + (Hsym, Rext, alphaVars, perRow) := buildHSymbolic(Flist, gp, Verbose => o.Verbose); + Hfinal := if o#"RunGreedy" then ( + adjustParams(Flist, Hsym, Rext, (alphaVars, RB), Verbose => o.Verbose) + ) else ( + -- MatrixHi particular solution: substitute all alphas to 0. + if #alphaVars > 0 then ( + finalSubst := map(R, Rext, + apply(numgens R, i -> R_i) | apply(#alphaVars, k -> 0_R)); + matrix apply(numRows Hsym, i -> + apply(numColumns Hsym, j -> finalSubst(Hsym_(i,j)))) + ) else Hsym + ); + (sh, M, V, Eexcess) := buildTemplateFromH(Hfinal, Flist, RB, Verbose => o.Verbose); + M +); + +-- Route (b), Martyushev CVPR 2022 §5: draw `n` random *standard* bases of +-- R/J by choosing a random positive weight vector, computing the Gröbner +-- basis of J under `Weights => w, GRevLex`, and taking the resulting +-- quotient basis. Deduplicates by the monomial set. Returns a list of +-- monomial lists in R (not in R/J); callers can pass any of these to +-- `buildGreedyTemplateWithBasis` or `searchBases`. +randomStandardBases = method(Options => {"Budget" => 5}) +randomStandardBases(Ideal, ZZ) := o -> (J, n) -> ( + R := ring J; + nv := numgens R; + budget := o#"Budget"; + out := new MutableList; + seen := new MutableHashTable; + for i from 0 to n - 1 do ( + w := apply(nv, k -> 1 + random 99); + ok := true; + local B; + -- alarm budget guards against pathological weight vectors that make + -- basis(R/J) run indefinitely; try/else catches both the resulting + -- AlarmInterrupt and any other runtime error. + alarm budget; + try ( + Rprime := newRing(R, MonomialOrder => {Weights => w, GRevLex}); + Jprime := sub(J, Rprime); + Bprime := flatten entries lift(basis(Rprime / Jprime), Rprime); + phi := map(R, Rprime, apply(nv, k -> R_k)); + B = apply(Bprime, b -> phi(b)); + ) else ( ok = false; ); + alarm 0; + if not ok then continue; + key := set B; + if seen#?key then continue; + seen#key = true; + out#(#out) = B; + ); + toList out +); + +-- Route (c), Larsson 2018: sample `n` random *non-standard* bases by picking +-- |B| = deg(R/J) random monomials from an extended monomial pool and keeping +-- those that pass `isValidBasis`. The pool defaults to all monomials of +-- degree up to twice the max degree of the standard basis, plus 1. Times +-- out after `n * 20` attempts to avoid infinite loops on problems where +-- valid non-standard bases are rare. +randomNonstandardBases = method() +randomNonstandardBases(Ideal, ZZ) := (J, n) -> ( + R := ring J; + stdBlist := flatten entries lift(basis(R/J), R); + nB := #stdBlist; + maxDeg := max apply(stdBlist, b -> first degree b); + pool := flatten entries basis(0, 2 * maxDeg + 1, R); + out := new MutableList; + seen := new MutableHashTable; + attempts := 0; + while #out < n and attempts < 20 * n do ( + attempts = attempts + 1; + cand := randomSubset(pool, nB); + if not isValidBasis(cand, J) then continue; + key := set cand; + if seen#?key then continue; + seen#key = true; + out#(#out) = cand; + ); + toList out +); + +-- Parse a Martyushev `_greedyAG/bases/b_` file into a list of monomial +-- lists over the ring `R`. Each input line is of the form +-- [mon1, mon2, ..., mon_d] +-- and is evaluated in R. Lines that fail to parse (e.g. they use a variable +-- not in R) are silently skipped; the caller gets whichever bases were +-- readable. Intended use: run `buildGreedyTemplateWithBasis` on every parsed +-- basis and keep the smallest template. +parseMartyushevBases = method() +parseMartyushevBases(String, Ring) := (path, R) -> ( + content := get path; + use R; + out := for line in lines content list ( + if #line == 0 or first line != "[" then continue; + listStr := replace("\\[", "{", replace("\\]", "}", line)); + ok := true; + local mons; + try ( mons = value listStr; ) else ( ok = false; ); + if ok and instance(mons, List) then continue; + mons + ); + out +); + +-- Basis-search loop. Default: screens each candidate with MatrixHi (fast, +-- O(seconds per basis)), then re-runs Greedy on the winner. Pass +-- `"RunGreedyAll" => true` to run full Greedy on every candidate (slow but may +-- find bases where adjustParams strictly improves over MatrixHi). +searchBases = method(Options => {Verbose => false, "RunGreedyAll" => false}) +searchBases(RingElement, Ideal, List) := o -> (aVar, J, candidates) -> ( + bestRows := infinity; + bestCols := 0; + bestM := null; + bestB := null; + idx := 0; + for B in candidates do ( + idx = idx + 1; + if not isValidBasis(B, J) then continue; + local M; + ok := true; + try ( + M = buildGreedyTemplateWithBasis(aVar, J, B, + "RunGreedy" => o#"RunGreedyAll", Verbose => false); + ) else ( ok = false; ); + if not ok then continue; + if numRows M < bestRows or (numRows M == bestRows and numColumns M < bestCols) then ( + bestRows = numRows M; + bestCols = numColumns M; + bestM = M; + bestB = B; + if o.Verbose then + << " candidate #" << idx << ": " << bestRows << "x" << bestCols + << " (new best)" << endl; + ); + ); + -- If we screened with MatrixHi only, re-run Greedy on the winner. + if bestB =!= null and not o#"RunGreedyAll" then ( + local Mg; + okG := true; + try ( + Mg = buildGreedyTemplateWithBasis(aVar, J, bestB, + "RunGreedy" => true, Verbose => false); + ) else ( okG = false; ); + if okG and (numRows Mg < bestRows or + (numRows Mg == bestRows and numColumns Mg < bestCols)) then ( + bestRows = numRows Mg; + bestCols = numColumns Mg; + bestM = Mg; + if o.Verbose then + << " winner refined by Greedy: " << bestRows << "x" << bestCols << endl; + ); + ); + (bestB, bestM) +); + +-- Build the symbolic H in a FLAT extended ring. +-- Returns (Hsym, Rext, alphaVars, perRowData) where: +-- Rext = FF[base vars..., alpha_0, ..., alpha_{n-1}] +-- Hsym is nG x nF over Rext +-- alphaVars = list of alpha ring elements +-- perRowData[i] = (entMons, alphaRange) for row i +-- +-- IMPORTANT: This handles the degree increment correctly — if the minimum +-- degree H[i,j] doesn't admit a solution, it incrementally raises d until +-- the linear system becomes feasible. This matches Maple's matrixHi. +buildHSymbolic = method(Options => {Verbose => false}) +buildHSymbolic(List, List) := o -> (F, gapPolys) -> ( + R := ring first F; + FF := coefficientRing R; + nF := #F; + nG := #gapPolys; + degF := apply(F, f -> first degree f); + + -- For each row, find the right degree and solve + rowResults := apply(nG, i -> ( + dG := first degree gapPolys#i; + -- Try increasing d until feasible (same as Maple's matrixHi) + d := 0; + dMinMet := 0; + chosenEm := null; + chosenRref := null; + chosenPivots := null; + chosenNParams := 0; + local em; + local nParams; + while d - dMinMet <= 0 do ( + em = apply(nF, j -> flatten entries basis(0, max(0, dG - degF#j) + d, R)); + nParams = sum(em, m -> #m); + targetMons := unique flatten ( + flatten apply(nF, j -> flatten apply(em#j, mk -> flatten entries monomials(mk * F#j))) + | flatten entries monomials(gapPolys#i) + ); + Amat := matrix(FF, apply(targetMons, m -> ( + flatten apply(nF, j -> apply(em#j, mk -> coefficient(m, mk * F#j))) + ))); + bvec := matrix(FF, apply(targetMons, m -> {coefficient(m, gapPolys#i)})); + -- Check feasibility: rank(A) == rank(A|b) + rkA := rank Amat; + rkAug := rank(Amat | bvec); + if rkAug > rkA then ( + -- Infeasible at this degree + dMinMet = d + 1; + d = d + 1; + if d > 30 then error ("matrixHiRow: degree too high"); + ) else ( + chosenEm = em; + chosenNParams = nParams; + chosenRref = reducedRowEchelonForm(Amat | bvec); + break; + ); + ); + em = chosenEm; + nParams = chosenNParams; + rref := chosenRref; + -- Identify pivots + (curR, col) := (0, 0); + pivotCols := while (curR < numrows rref and col < nParams) list ( + oldCol := col; + col = col + 1; + if rref_(curR, oldCol) != 1_FF then continue else ( + curR = curR + 1; + oldCol + ) + ); + pivotSet := set pivotCols; + freeCols := toList select(0..nParams-1, c -> not pivotSet#?c); + -- For each pivot, store: (pivot col, pivot row, list of (freeColIdx, -coef)) + pivotInfo := apply(pivotCols, col -> ( + rowIdx := position(0..numRows rref - 1, r -> rref_(r, col) == 1_FF); + constant := rref_(rowIdx, nParams); + deps := apply(freeCols, fc -> (fc, -rref_(rowIdx, fc))); + (col, constant, deps) + )); + (em, nParams, pivotInfo, freeCols) + )); + + -- Total free params across all rows + totalFree := sum(rowResults, r -> #(r#3)); + if o.Verbose then << "buildHSymbolic: totalFree=" << totalFree << endl; + + -- Build flat extended ring + baseNames := apply(numgens R, i -> toString R_i); + alphaNames := apply(totalFree, k -> "aa" | toString k); + Rext := if totalFree > 0 then + FF[(baseNames | alphaNames) / getSymbol, MonomialOrder => {numgens R, totalFree}] + else R; + toRext := if totalFree > 0 then + map(Rext, R, apply(numgens R, i -> Rext_i)) + else map(R, R); + alphaVars := if totalFree > 0 then apply(totalFree, k -> Rext_(numgens R + k)) else {}; + + -- Build Hsym row by row + alphaOffset := 0; + perRowData := new MutableList from apply(nG, i -> null); + Hrows := apply(nG, i -> ( + (em, nParams, pivotInfo, freeCols) := rowResults#i; + nFreeRow := #freeCols; + rowAlphas := apply(nFreeRow, k -> alphaVars#(alphaOffset + k)); + + -- Mapping from free col idx in this row → global alpha var + freeIdxToAlpha := hashTable apply(nFreeRow, k -> freeCols#k => rowAlphas#k); + + -- Build alpha value expression for each param idx 0..nParams-1 + alphaExpr := new MutableList from apply(nParams, p -> 0_Rext); + -- Set free cols to their corresponding alpha var + scan(nFreeRow, k -> ( + alphaExpr#(freeCols#k) = rowAlphas#k; + )); + -- Set pivot cols to their expression: constant - sum (coef * alpha) + scan(pivotInfo, pi -> ( + (col, constant, deps) := pi; + val := promote(constant, Rext); + scan(deps, dep -> ( + (fc, coef) := dep; + if freeIdxToAlpha#?fc then + val = val + promote(coef, Rext) * freeIdxToAlpha#fc; + )); + alphaExpr#col = val; + )); + + -- Build the row: for each gen j, sum (alphaExpr#p * entMon) over monomials of entMons#j + rowPolys := apply(nF, j -> ( + pStart := sum(j, jj -> #(em#jj)); + sum(#(em#j), k -> alphaExpr#(pStart + k) * toRext(em#j#k)) + )); + + perRowData#i = (em, toList alphaExpr, rowAlphas); + alphaOffset = alphaOffset + nFreeRow; + rowPolys + )); + + Hsym := matrix(Rext, Hrows); + (Hsym, Rext, alphaVars, toList perRowData) +); + +-- ============================================================================ +-- adjustParams: a simpler, direct implementation of Martyushev CVPR 2022 §4. +-- +-- Strategy: For each excessive monomial, directly collect the per-entry +-- coefficient expressions that contribute to it, build a linear system, +-- solve, and substitute into Hsym. +-- ============================================================================ +-- Note: 5-arg method bundled as (F, Hsym, Rext, (alphaVars, RB)) +adjustParams = method(Options => {Verbose => false, "MaxIter" => 1000}) +adjustParams(List, Matrix, Ring, Sequence) := o -> (F, Hsym, Rext, pair) -> ( + (alphaVars, RB) := pair; + R := ring first F; + FF := coefficientRing R; + nF := #F; + nG := numRows Hsym; + baseVars := apply(numgens R, i -> Rext_i); + Fext := apply(F, f -> sub(f, Rext)); + RBset := set apply(RB, m -> sub(m, Rext)); + + Hcurrent := mutableMatrix Hsym; + + -- Get current shift monomials per generator (in vars only) + getShiftMons := (HM) -> ( + apply(nF, j -> ( + unique flatten apply(nG, i -> ( + e := HM_(i,j); + if e == 0 then {} else ( + (mm, cc) := coefficients(e, Variables => baseVars); + flatten entries mm + ) + )) + )) + ); + + -- Get excessive monomials + getExcessive := (HM) -> ( + sh := getShiftMons HM; + allExp := unique flatten apply(nF, j -> ( + flatten apply(sh#j, m -> flatten entries monomials(m * Fext#j, Variables => baseVars)) + )); + select(allExp, m -> not RBset#?m) + ); + + initialExcessive := getExcessive(matrix Hcurrent); + if o.Verbose then + << "adjustParams: " << #initialExcessive << " initial excessive" << endl; + + if #initialExcessive == 0 then return matrix Hcurrent; + + -- Martyushev CVPR 2022 / `adjustParams` (Maple): sort excessives DESC by + -- (|rm|, np) where rm[e] = set of (j,k) positions whose m*F[j] contains e, + -- and np[e] = sum over positions p in rm[e] of (number of excessives using + -- p). Intuition: attack excessives controlled by the most positions first, + -- breaking ties by "downstream impact" so we spend positions that are most + -- contested. Our earlier port sorted ASC and did not compute np. + iterations := 0; + remainingExc := initialExcessive; + + -- Cache `supp(m * F[j])` as a set for reuse across outer iterations. The + -- support only depends on m and F[j]; α-commits don't invalidate it. + suppCache := new MutableHashTable; + suppOf := (j, m) -> ( + key := (j, m); + if not suppCache#?key then + suppCache#key = set flatten entries monomials(m * Fext#j); + suppCache#key + ); + + -- Per-iteration cache of shift monomials per generator column. Rebuilt + -- once at the start of each outer iteration instead of re-extracted per + -- excessive. + currentShifts := null; + refreshShifts := HM -> ( + currentShifts = apply(nF, j -> ( + unique flatten apply(nG, i -> ( + entry := HM_(i,j); + if entry == 0 then {} else ( + (mm, cc) := coefficients(entry, Variables => baseVars); + flatten entries mm + ) + )) + )); + ); + + -- rm[e]: list of (j, m) pairs with m in column j's shift set AND e in + -- supp(m * F[j]). + rmOf := e -> ( + unique flatten apply(nF, j -> ( + apply(select(currentShifts#j, m -> (suppOf(j, m))#?e), m -> (j, m)) + )) + ); + + while iterations < o#"MaxIter" and #remainingExc > 0 do ( + iterations = iterations + 1; + improved := false; + + -- Compute rm and np for every remaining excessive, using cached + -- per-column shift lists and supp(m*F[j]) memoization. + HcurMat := matrix Hcurrent; + refreshShifts(HcurMat); + rmTable := hashTable apply(remainingExc, e -> e => rmOf(e)); + posCounts := new MutableHashTable; + for e in remainingExc do ( + for pos in rmTable#e do ( + posCounts#pos = if posCounts#?pos then posCounts#pos + 1 else 1; + ); + ); + -- Sort DESC by (|rm|, np). M2 sort is ASC by default, so negate keys. + exWithKey := apply(remainingExc, e -> ( + rmList := rmTable#e; + rmSize := #rmList; + np := sum prepend(0, apply(rmList, pos -> posCounts#pos)); + (- rmSize, - np, e) + )); + sortedEx := apply(sort exWithKey, t -> t#2); + + -- Early break (Martyushev): if every H entry is already numeric (no + -- free α survives), adjusting anything further is impossible. + allEntries := flatten apply(nG, i -> apply(nF, j -> Hcurrent_(i,j))); + aliveAlphas := any(allEntries, e -> any(alphaVars, a -> coefficient(a, e) != 0_Rext)); + if not aliveAlphas then break; + + -- Try each in order + tookStep := false; + scan(sortedEx, e -> ( + if tookStep then return; + + -- Collect all entries of H that contribute to e (when expanded via F) + -- For each gen j, find shift monomials m such that coefficient(e, m * F[j]) != 0 + -- Then for each row i, take coefficient(m, H[i,j]) — this is an expression in alphas + -- that must be zero across the sum over rows + + -- The specific equation: sum_i [(total coeff of e contributed by m * F[j] row i)] = 0 + -- More precisely, for each (j, m) pair, for each row i, the contribution to e is + -- coefficient(m, H[i,j]) * coefficient(e, m * F[j]) + -- Setting ALL row contributions to zero means: for each i with coefficient(e, m*F[j]) != 0, + -- coefficient(m, H[i,j]) = 0 + -- (or we'd need the SUM to be zero, but that requires summing rows) + + -- Actually each row of H contributes a separate shifted polynomial to the template, + -- so each row's contribution to e must be independently zero for e to not appear. + -- So we get one equation per (i, j, m) tuple where m * F[j] contains e. + + eqs := flatten apply(nG, i -> ( + flatten apply(nF, j -> ( + entry := Hcurrent_(i,j); + if entry == 0 then return {}; + (mm, cc) := coefficients(entry, Variables => baseVars); + monList := flatten entries mm; + coefList := flatten entries cc; + -- For each monomial m in the entry, check if m * F[j] contains e + eqsForEntry := toList apply(#monList, k -> ( + m := monList#k; + if coefficient(e, m * Fext#j) != 0_Rext then + coefList#k -- the alpha-expression coefficient + else + null + )); + select(eqsForEntry, x -> x =!= null) + )) + )); + + -- Filter to nonzero + nonzeroEqs := select(eqs, x -> x != 0_Rext); + if #nonzeroEqs == 0 then return; + + -- Build linear system in alphaVars + nAlphas := #alphaVars; + sysRows := apply(nonzeroEqs, eq -> ( + -- Extract linear coefficients wrt alphaVars + row := apply(nAlphas, k -> sub(coefficient(alphaVars#k, eq), FF)); + -- Constant term = eq with alphas set to 0 + constSub := map(Rext, Rext, baseVars | apply(nAlphas, k -> 0_Rext)); + constVal := sub(constSub eq, FF); + row | {-constVal} + )); + + -- Drop trivial all-zero rows + sysRows = select(sysRows, r -> not all(r, x -> x == 0_FF)); + if #sysRows == 0 then ( + -- e is already zero, remove from list + remainingExc = select(remainingExc, m -> m != e); + improved = true; + tookStep = true; + return; + ); + + sysMat := matrix(FF, sysRows); + coeffMat := sysMat_{0..nAlphas-1}; + rhsCol := sysMat_{nAlphas}; + + -- Check consistency + if rank(coeffMat | rhsCol) > rank coeffMat then return; -- skip, not solvable + + -- Solve and extract assignment + solRref := reducedRowEchelonForm(coeffMat | rhsCol); + newAlphaVals := new MutableList from apply(nAlphas, k -> null); -- null = unassigned + curR := 0; + for col from 0 to nAlphas - 1 do ( + if curR >= numRows solRref then break; + if solRref_(curR, col) == 1_FF then ( + newAlphaVals#col = sub(solRref_(curR, nAlphas), Rext); + curR = curR + 1; + ); + ); + -- Set unassigned alphas to 0 + substImages := apply(nAlphas, k -> + if newAlphaVals#k =!= null then newAlphaVals#k else 0_Rext); + substMap := map(Rext, Rext, baseVars | substImages); + + -- Apply substitution to Hcurrent + for i from 0 to nG - 1 do + for j from 0 to nF - 1 do + Hcurrent_(i,j) = substMap(Hcurrent_(i,j)); + + improved = true; + tookStep = true; + remainingExc = select(remainingExc, m -> m != e); + if o.Verbose and iterations % 5 == 0 then ( + currExc := getExcessive(matrix Hcurrent); + << " iter " << iterations << ": eliminated e, " << #currExc << " remain" << endl; + ); + )); + + if not improved then break; + + -- Recompute remaining excessive (some may have been zeroed as side effects) + remainingExc = select(remainingExc, e -> ( + all(nF, j -> ( + sh := unique flatten apply(nG, i -> ( + entry := Hcurrent_(i,j); + if entry == 0 then {} else ( + (mm, cc) := coefficients(entry, Variables => baseVars); + flatten entries mm + ) + )); + any(sh, m -> coefficient(e, m * Fext#j) != 0_Rext) + )) + )); + ); + + if o.Verbose then ( + finalExc := getExcessive(matrix Hcurrent); + << "adjustParams: eliminated " << (#initialExcessive - #finalExc) + << " of " << #initialExcessive << " excessive (in " << iterations << " iters)" << endl; + ); + + -- Project back to R (substituting all remaining alphas to 0) + if #alphaVars > 0 then ( + nBase := numgens R; + finalSubst := map(R, Rext, apply(nBase, i -> R_i) | apply(#alphaVars, k -> 0_R)); + matrix apply(nG, i -> apply(nF, j -> finalSubst(Hcurrent_(i,j)))) + ) else matrix Hcurrent +); + +-- ============================================================================ +-- buildTemplateFromH: from H, F, and RB = residualMons | Blist, build the +-- elimination template matrix M over the base field. For each generator F[j], +-- the shift monomials come from column j of H. The template columns are +-- [ excessMons | RB ]. +-- ============================================================================ +buildTemplateFromH = method(Options => {Verbose => false}) +buildTemplateFromH(Matrix, List, List) := o -> (Hmat, F, RB) -> ( + R := ring first F; + FF := coefficientRing R; + nF := #F; + nG := numRows Hmat; + + sh := apply(nF, j -> + toList set flatten apply(nG, i -> flatten entries monomials Hmat_(i,j)) + ); + xF := flatten apply(nF, j -> apply(sh#j, m -> m * F#j)); + V0 := toList set flatten apply(xF, p -> flatten entries monomials p); + RBset := set RB; + Eexcess := rsort select(V0, m -> not RBset#?m); + V := Eexcess | RB; + M := matrix(FF, apply(xF, p -> apply(V, m -> coefficient(m, p)))); + if o.Verbose then + << "buildTemplateFromH: " << numRows M << "x" << numColumns M + << " (excess=" << #Eexcess << ")" << endl << flush; + (sh, M, V, Eexcess) +) + +end diff --git a/M2/Macaulay2/packages/EngineTests.m2 b/M2/Macaulay2/packages/EngineTests.m2 index b2ce33f1dc8..fb2ef950717 100644 --- a/M2/Macaulay2/packages/EngineTests.m2 +++ b/M2/Macaulay2/packages/EngineTests.m2 @@ -12,6 +12,7 @@ newPackage( HomePage => "" } }, Headline => "a test suite for the Macaulay2 engine", + PackageExports => {"Complexes"}, Keywords => {"Miscellaneous"}, AuxiliaryFiles=> true ) diff --git a/M2/Macaulay2/packages/EngineTests/GB.Test.LinearAlgebra.m2 b/M2/Macaulay2/packages/EngineTests/GB.Test.LinearAlgebra.m2 index 5f4afb153c1..7cf8b8c7c2b 100644 --- a/M2/Macaulay2/packages/EngineTests/GB.Test.LinearAlgebra.m2 +++ b/M2/Macaulay2/packages/EngineTests/GB.Test.LinearAlgebra.m2 @@ -272,7 +272,7 @@ TEST /// -- another module GB test -* restart *- -TEST /// -- inhomgeneity. Algorithm => LinearAlgebra, over finite field. Need to implement still +TEST /// -- inhomogeneity. Algorithm => LinearAlgebra, over finite field. Need to implement still kk = ZZ/101; R1 = kk[a..g, MonomialSize=>8]; setRandomSeed 42 diff --git a/M2/Macaulay2/packages/EngineTests/Res.f4.m2 b/M2/Macaulay2/packages/EngineTests/Res.f4.m2 index 72b8ec58850..cb505effb9a 100644 --- a/M2/Macaulay2/packages/EngineTests/Res.f4.m2 +++ b/M2/Macaulay2/packages/EngineTests/Res.f4.m2 @@ -3,7 +3,6 @@ -- Notes: -- Tests are here -- Unit tests for f4 free resolution code are in e/unit-tests/ResTest.cpp --- packages/NonminimalComplexes: has further routines, doc, and maybe some tests. -- March 2018 todo list: -- 1. Allow multi-gradings @@ -72,7 +71,7 @@ TEST /// -- test the zero ideal R = ZZ/101[a..d] I = ideal(0_R) - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) betti'ans = new BettiTally from {(0,{0},0) => 1} assert(betti C == betti'ans) assert(C.dd_1 == 0) @@ -87,7 +86,7 @@ TEST /// assert(betti'ans == minimalBetti (ideal I_*, DegreeLimit=>-1000)) I = trim ideal(0_R) - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) betti'ans = new BettiTally from {(0,{0},0) => 1} assert(betti C == betti'ans) assert(C.dd_1 == 0) @@ -122,7 +121,7 @@ TEST /// -- test the unit ideal R = ZZ/101[a..d] I = ideal(1_R) - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) betti'ans = new BettiTally from {} assert(betti(C,Minimize=>true) == betti'ans) assert(C.dd_1 == 1) @@ -143,7 +142,7 @@ TEST /// -- test linear ideals. R = ZZ/101[a..d] I = ideal(a-d, b+13*a) - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) betti'ans = new BettiTally from {(1,{1},1) => 2, (0,{0},0) => 1, (2,{2},2) => 1} assert(betti(C,Minimize=>true) == betti'ans) assert(numRows C.dd_1 == 1) @@ -157,26 +156,11 @@ TEST /// -- test linear ideal in exterior algebra R = ZZ/101[a..d, SkewCommutative=>true] I = ideal(a-d, b+13*a) - B1 = betti res I - C = res(ideal(I_*), FastNonminimal => true) + B1 = betti res(I, LengthLimit => 7) + C = res(ideal(I_*), LengthLimit => 7, Strategy => Nonminimal) assert(B1 == betti C) B1 = betti res (ideal(I_*), LengthLimit=>7) - B2 = betti res(ideal(I_*), FastNonminimal => true, LengthLimit=>7) - assert(B1 == B2) - assert(C.dd^2 == 0) -/// - -TEST /// - -- nonminimal resolution. - -- test linear ideal in exterior algebra - R = ZZ/101[a..d, SkewCommutative=>true] - I = ideal(a-d, b+13*a) - B1 = betti res I - C = res(ideal(I_*), FastNonminimal => true, LengthLimit => 5) - C.dd^2 - assert(B1 == betti C) - B1 = betti res (ideal(I_*), LengthLimit=>7) - B2 = betti res(ideal(I_*), FastNonminimal => true, LengthLimit=>7) + B2 = betti res(ideal(I_*), Strategy => Nonminimal, LengthLimit=>7) assert(B1 == B2) assert(C.dd^2 == 0) /// @@ -188,7 +172,7 @@ TEST /// I = ideal(a^4-a*c-d, b^3-c^2, a^8-d^2) isHomogeneous I B1 = betti res I - C = res(ideal(I_*), FastNonminimal=>true) + C = res(ideal(I_*), Strategy => Nonminimal) B2 = betti(C, Minimize=>true) B3 = minimalBetti ideal(I_*) assert(B1 == B2) @@ -219,25 +203,25 @@ TEST /// S = ZZ/101[a..d] I = ideal(a*b-a-1, b^3-c*d-3) time res I - res(ideal(I_*), FastNonminimal => true) + res(ideal(I_*), Strategy => Nonminimal) -- don't allow quotient rings S = ZZ/101[a..d] I = ideal(a^2-b*c, b^2-c*d) R = S/I M = coker vars R - assert (try (res(M, FastNonminimal => true); false) else true) + assert (try (res(M, Strategy => Nonminimal); false) else true) -- don't allow Weyl algebras -- don't allow zero heft vectors -- this code never gets to non minimal res code in the engine. S = ZZ/101[a,b,c,d,Degrees=>{-1,1,2,-5}] - assert try (res((ideal gens S)^3, FastNonminimal => true); false) else true; + assert try (res((ideal gens S)^3, Strategy => Nonminimal); false) else true; -- allow multi-gradings S = ZZ/101[a,b,c,d,DegreeRank=>4] - C = res(ideal gens S, FastNonminimal=>true) + C = res(ideal gens S, Strategy => Nonminimal) assert isHomogeneous C assert(degrees C_0 === {{0, 0, 0, 0}}) assert(degrees C_1 === {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}) @@ -249,7 +233,7 @@ TEST /// R = ZZ/32003[a..d, Degrees=>transpose {{1,2,3,4},{1,0,-1,0}}] basis(degree(a*b*c*d)^2,R) I = ideal for i from 1 to 3 list random(degree((a*b*c*d)^2*c), R) - C = res(I, FastNonminimal=>true) + C = res(I, Strategy => Nonminimal) -- need to implement: find all the degrees where C might be not minimal, -- for each: compute rank, fill in a betti table. Note that this Betti table has multigrading. @@ -277,7 +261,7 @@ TEST /// R = ZZ/101[a..d] M = coker vars R gbTrace = 1 - C = res(M, FastNonminimal => true) + C = res(M, Strategy => Nonminimal) betti'ans = new BettiTally from {(0,{0},0) => 1, (3,{3},3) => 4, (1,{1},1) => 4, (4,{4},4) => 1, (2,{2},2) => 6} assert(betti'ans == betti C) for i from 2 to length C do assert(C.dd_(i-1) * C.dd_i == 0) @@ -291,13 +275,14 @@ TEST /// F = R^{0,-1,-2} M = coker map(F,,{{a,0,0},{0,b,0},{0,0,c}}) assert isHomogeneous M - C = res(M, FastNonminimal => true) + C = res(M, Strategy => Nonminimal) + minBettiC = betti(C, Minimize=>true) assert(betti res M == betti C) for i from 2 to length C do assert(C.dd_(i-1) * C.dd_i == 0) M2 = coker map(F,,{{a,0,0},{0,b,0},{0,0,c}}) assert(minimalBetti M2 == betti C) -- doesn't always hold, but does here. - assert(minimalBetti M2 == betti(C, Minimize=>true)) + assert(minimalBetti M2 == minBettiC) assert isHomogeneous C /// @@ -307,7 +292,7 @@ TEST /// M = coker map(F,,{{a,0,0},{0,0,b},{0,c,0}}) M1 = coker map(F,,{{a,0,0},{0,0,b},{0,c,0}}) isHomogeneous M - C = res(M, FastNonminimal => true) + C = res(M, Strategy => Nonminimal) betti res M1 == betti C for i from 2 to length C do assert(C.dd_(i-1) * C.dd_i == 0) assert isHomogeneous C @@ -317,7 +302,7 @@ TEST /// TEST /// R = ZZ/101[a..e] I = monomialCurveIdeal(R,{1,3,7,9}) - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) assert(betti(C, Minimize=>true) == betti res (ideal I_*)) for i from 2 to length C do assert(C.dd_(i-1) * C.dd_i == 0) /// @@ -364,7 +349,7 @@ TEST /// elapsedTime for i from 1 to 1000 do ( J = ideal I_*; - res(J, Strategy=>4); + res(J, Strategy=>Nonminimal); ) debug Core engineMemory() @@ -374,7 +359,7 @@ TEST /// elapsedTime for i from 1 to 10 list ( J = ideal I_*; - res(J, Strategy=>4) + res(J, Strategy=>Nonminimal) ); debug Core engineMemory() @@ -385,7 +370,7 @@ TEST /// m1 = genericMatrix(R,a,3,3) m2 = genericMatrix(R,j,3,3) I = ideal(m1*m2-m2*m1) - elapsedTime C = res(I, FastNonminimal => true) + elapsedTime C = res(I, Strategy => Nonminimal) elapsedTime assert(betti(C,Minimize=>true) == betti res (ideal I_*)) for i from 2 to length C do assert(C.dd_(i-1) * C.dd_i == 0) betti C @@ -397,27 +382,6 @@ TEST /// betti(C, Minimize=>true) debug Core - assert(rawBetti(raw C.Resolution, 1) == betti(C)) - assert(rawBetti(raw C.Resolution, 0) == betti C) - assert(rawBetti(raw C.Resolution, 4) == betti(C, Minimize=>true)) - rawBetti(raw C.Resolution, 5) - --rawBetti(raw C.Resolution, 2) -- not implemented yet - --rawBetti(raw C.Resolution, 3) -- not implemented yet - - kk = coefficientRing R - - assert(rank map(kk,rawResolutionGetMatrix2(raw C,2,3)) == 12) - assert(rank map(kk,rawResolutionGetMatrix2(raw C,2,4)) == 5) - assert(rank map(kk,rawResolutionGetMatrix2(raw C,2,5)) == 1) - - assert(rank map(kk,rawResolutionGetMatrix2(raw C,3,4)) == 9) - assert(rank map(kk,rawResolutionGetMatrix2(raw C,3,5)) == 41) - assert(rank map(kk,rawResolutionGetMatrix2(raw C,3,6)) == 7) - - assert(rank map(kk,rawResolutionGetMatrix2(raw C,4,5)) == 2) - assert(rank map(kk,rawResolutionGetMatrix2(raw C,4,6)) == 69) - assert(rank map(kk,rawResolutionGetMatrix2(raw C,4,7)) == 20) - assert(schreyerOrder target C.dd_2 != 0) assert(schreyerOrder source C.dd_2 != 0) /// @@ -426,7 +390,7 @@ TEST /// kk = ZZ/101 R = kk[vars(0..4)] I = ideal"b2c,abc,a2c,a2de,b3d,bc2de,ac2de,ab2d2e,c3d2e2" - elapsedTime C = res(I, FastNonminimal => true) + elapsedTime C = res(I, Strategy => Nonminimal) C1 = res (ideal I_*) assert(betti C == betti C1) for i from 2 to length C do assert(C.dd_(i-1) * C.dd_i == 0) @@ -438,19 +402,25 @@ TEST /// R = kk[a..f] I = ideal(a*b*c-d*e*f, a*b^2-d*c^2, a*e*f-d^2*b) gbTrace=3 - elapsedTime C = res(I, FastNonminimal => true) + elapsedTime C = res(I, Strategy => Nonminimal) betti(C, Minimize=>true) == betti res ideal(I_*) betti(C) I = ideal I_* C1 = res(ideal gens gb I, Strategy=>1) debug Core - rawBetti(raw C1.Resolution, 1) - rawBetti(raw C1.Resolution, 0) - + debug Complexes + -- Possibly remove this test? Or make access to the raw computation reasonable! + rawcomp = C1.cache.Module.cache.ResolutionObject.RawComputation + b1 = rawBetti(rawcomp, 1) + b2 = rawBetti(rawcomp, 0) + I = ideal I_* C2 = res(ideal gens gb I, Strategy=>0) - rawBetti(raw C2.Resolution, 1) - rawBetti(raw C2.Resolution, 0) + rawcomp = C2.cache.Module.cache.ResolutionObject.RawComputation + b1' = rawBetti(rawcomp, 1) + b2' = rawBetti(rawcomp, 0) + assert(b2 === b2') + -- b1, b1' are different. /// /// @@ -459,7 +429,7 @@ TEST /// I = Grassmannian(2,5,R) gbTrace=0 elapsedTime minimalBetti I - elapsedTime C = res(I, FastNonminimal => true) + elapsedTime C = res(I, Strategy => Nonminimal) betti(C, Minimize=>true) /// @@ -468,7 +438,7 @@ TEST /// R = kk[x_1..x_21] I = Grassmannian(1,6,R) elapsedTime minimalBetti I - elapsedTime C = res(I, FastNonminimal => true) + elapsedTime C = res(I, Strategy => Nonminimal) betti(C, Minimize=>true) /// @@ -481,7 +451,7 @@ TEST /// I = minors(2,M) N = syz gens I minimalBetti coker N - C = res(coker N, FastNonminimal => true) + C = res(coker N, Strategy => Nonminimal) assert(betti(C,Minimize=>true) == betti res coker syz gens I) betti C @@ -493,43 +463,43 @@ TEST /// m2 = genericMatrix(R,j,3,3) I = ideal(m1*m2-m2*m1) elapsedTime minimalBetti I - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) betti C m3 = C.dd_2; gbTrace=0 - res(coker C.dd_2, FastNonminimal => true) - res(coker C.dd_3, FastNonminimal => true) - res(coker C.dd_4, FastNonminimal => true) - res(coker C.dd_5, FastNonminimal => true) - res(coker C.dd_6, FastNonminimal => true) - res(coker C.dd_7, FastNonminimal => true) - res(coker C.dd_8, FastNonminimal => true) - assert(res(coker C.dd_9, FastNonminimal => true) == 0) - assert(res(coker C.dd_10, FastNonminimal => true) == 0) + res(coker C.dd_2, Strategy => Nonminimal) + res(coker C.dd_3, Strategy => Nonminimal) + res(coker C.dd_4, Strategy => Nonminimal) + res(coker C.dd_5, Strategy => Nonminimal) + res(coker C.dd_6, Strategy => Nonminimal) + res(coker C.dd_7, Strategy => Nonminimal) + res(coker C.dd_8, Strategy => Nonminimal) + assert(res(coker C.dd_9, Strategy => Nonminimal) == 0) + assert(res(coker C.dd_10, Strategy => Nonminimal) == 0) betti C gensI = schreyerOrder gens gb I P = gens gb syz gensI; - res(coker P, FastNonminimal => true) + res(coker P, Strategy => Nonminimal) /// TEST /// R = ZZ/101[a..f] m = map(R^0,R^1,0) gbTrace = 3 - res(coker m, FastNonminimal => true) + res(coker m, Strategy => Nonminimal) /// TEST /// R = ZZ/101[a..f] I = ideal(a*b-e*f, a*c*e-f^3, a^2*c*d^2-b^2*e*f^2) time betti res I - elapsedTime C0 = res(ideal I_*, FastNonminimal => true) + elapsedTime C0 = res(ideal I_*, Strategy => Nonminimal) P = gens gb syz gens I - C1 = res(coker P, FastNonminimal => true) + C1 = res(coker P, Strategy => Nonminimal) B1 = minimalBetti coker P B2 = betti res coker P assert(B1 == B2) @@ -545,7 +515,7 @@ TEST /// leadTerm M gens gb M P = gens gb image M - C = res(coker P, FastNonminimal => true) + C = res(coker P, Strategy => Nonminimal) assert(minimalBetti coker P == betti res coker P) /// @@ -554,7 +524,7 @@ TEST /// R = ZZ/101[a..d] I = ideal random(R^1, R^{-2,-3,-4}) P = gens gb syz gens I - betti res(coker P, FastNonminimal => true) + betti res(coker P, Strategy => Nonminimal) minimalBetti coker P F = source schreyerOrder gens I @@ -562,15 +532,14 @@ TEST /// raw F P1 = map(F,,P) isHomogeneous P1 - betti(res(coker P1, FastNonminimal => true), Minimize=>true) + betti(res(coker P1, Strategy => Nonminimal), Minimize=>true) res coker P1 -- this one looks wrong if one does not do the line before this? (MES: I don't see any issue here 3/2018) - -- ?? is this a bug?? + -- ?? is this a bug?? BUG, it seems /// TEST /// setRandomSeed "10" needsPackage "BGG" - needsPackage "OldChainComplexes" S = ZZ/101[x_0..x_5] -- P^5 E = ZZ/101[e_0..e_5, SkewCommutative => true] F = random(S^2, S^{-3,-3}) @@ -579,46 +548,49 @@ TEST /// m = bgg(2,M,E); elapsedTime gens gb m; gbTrace=0 - elapsedTime C1 = res(coker m, FastNonminimal => true, LengthLimit=>7) + elapsedTime C1 = res(coker m, Strategy => Nonminimal, LengthLimit=>7) + B0 = betti(C1, Minimize => true) B1 = minimalBetti(coker m, LengthLimit=>7) - B2 = betti res(coker m) + B2 = betti res(coker m, LengthLimit => 7) assert(B1 == B2) m = bgg(2,M,E); + elapsedTime res(coker m, Strategy => Nonminimal, LengthLimit=>7) elapsedTime C2 = res(coker m, LengthLimit=>6) elapsedTime minimalBetti(coker m, LengthLimit=>6) assert(betti C2 == oo) - betti(C1, Minimize => true) - elapsedTime res(coker m, FastNonminimal=>true, LengthLimit=>7) - betti oo - + m = bgg(3,M,E); elapsedTime gens gb m; elapsedTime B1 = minimalBetti(coker m, LengthLimit=>7) -- 1.4 sec - elapsedTime B2 = betti res(coker m) -- 27 seconds - time C1 = res(coker m, FastNonminimal => true, LengthLimit=>7) -- .65 sec if done without minimalBetti line. - assert(B1 == B2) + -- elapsedTime B2 = betti res(coker m, LengthLimit => 7) -- 27 seconds (in 2026: 10.5 sec M4 mac) + -- time C1 = res(coker m, Strategy => Nonminimal, LengthLimit=>7) -- .65 sec if done without minimalBetti line. + -- assert(B1 == B2) /// TEST /// -- this is a small-ish example used to get the logic of matrix building right + needsPackage "Complexes" setRandomSeed 0 kk = ZZ/101 R = kk[vars(0..3)] I = ideal fromDual random(R^1, R^{-3}); - C = res(I, FastNonminimal => true) - + minimalBetti I + C = res(I, Strategy => Nonminimal) + betti(C, Minimize => true) + I = ideal(I_*) - elapsedTime C1 = res(I, FastNonminimal => true, DegreeLimit=>1) -- DOES NOTHING (i.e. does the whole thing) BUG + elapsedTime C1 = res(I, Strategy => Nonminimal, DegreeLimit=>1) -- DOES NOTHING (i.e. does the whole thing) BUG ---- assert(betti(C,Minimize=>true) != betti(C1,Minimize=>true)) -- totally non-minimal, so maybe it did do something. ACTUALLY: returns without doing ranks betti C1 - elapsedTime C2 = res(I, FastNonminimal => true) - betti C2 == betti C + elapsedTime C2 = res(I, Strategy => Nonminimal) + assert(betti C2 == betti C) assert(C.dd^2 == 0) assert(isHomogeneous C) C1 = betti res ideal(I_*) assert(betti(C,Minimize=>true) == betti(C1,Minimize=>true)) + minimalBetti I assert(minimalBetti I == betti C1) /// @@ -629,9 +601,8 @@ TEST /// setRandomSeed 0 I = ideal fromDual random(R^1, R^{-3}); gbTrace=2 - elapsedTime C = res(I, FastNonminimal => true) - minimalBetti I - betti(C, Minimize=>true) + elapsedTime C = res(I, Strategy => Nonminimal) + assert(minimalBetti I === betti(C, Minimize=>true)) betti C /// @@ -676,11 +647,11 @@ TEST /// stderr << "--EngineTests/Res.f4.m2: *** Warning: bypassing a test that takes too much memory." << endl; exit 0 - elapsedTime C = res(I, FastNonminimal=>true) + elapsedTime C = res(I, Strategy => Nonminimal) gbTrace=2 elapsedTime minimalBetti I - /// + -* TEST *- /// -- takes too much memory -- might take too long ... @@ -690,11 +661,12 @@ TEST /// setRandomSeed 0 I = ideal fromDual random(R^1, R^{-3}); gbTrace=2 - elapsedTime C = res(I, FastNonminimal => true) -- 49.39 seconds on MBP, 11.3 seconds in 2018 - elapsedTime minimalBetti I -- 75 sec in 2018 + elapsedTime C = res(I, Strategy => Nonminimal) -- 49.39 seconds on MBP, 11.3 seconds in 2018, 8.6 sec in 2026. + elapsedTime C = res(I, Strategy => Nonminimal) -- 49.39 seconds on MBP, 11.3 seconds in 2018 + elapsedTime minimalBetti I -- 75 sec in 2018, 8.3 sec (after nonmin res is made) in 2026. - debug needsPackage "NonminimalComplexes" - elapsedTime Cd = constantStrand(C, kk, 8) -- 1.7 sec + debug needsPackage "Complexes" + elapsedTime Cd = constantStrand(C, 8) -- 1.7 sec Mk = Cd.dd_7; M = Mk ** R; elapsedTime M' = transpose M; @@ -705,20 +677,6 @@ TEST /// -- over kk: elapsedTime gens gb Mk; -- >= 104 sec elapsedTime gens gb Mk'; --.01 sec - - debug Core - kkp = ZZp(101, Strategy=>"Ffpack") -- use this ring for M1 - debug Core - comp = fastNonminimalComputation C - elapsedTime M1 = rawResolutionGetMutableMatrix2B(comp, raw kk, 8, 7); - M1 = map(kk,M1); - elapsedTime M2 = sub(M1,kkp); -- uugh, very slow. crashed because of memory usage. - - elapsedTime rank M1 - (numRows M1, numColumns M1) - class M1 - ring M1 - /// -* TEST *- /// @@ -727,7 +685,7 @@ TEST /// R = kk[vars(0..10)] setRandomSeed 0 I = ideal fromDual random(R^1, R^{-3}); - elapsedTime C = res(I, FastNonminimal => true) + elapsedTime C = res(I, Strategy => Nonminimal) elapsedTime B1 = minimalBetti I betti'ans = new BettiTally from { (0,{0},0) => 1, @@ -754,13 +712,13 @@ TEST /// I = ideal fromDual soc; time C = res I time B = betti res I - time F = res(ideal I_*, FastNonminimal=>true) - time B == betti(F, Minimize =>true) + time F = res(ideal I_*, Strategy => Nonminimal) + time B == betti(F, Minimize => true) assert(betti F != betti C) assert(F =!= C) - F = res(ideal I_*, FastNonminimal=>true) - assert(B == betti(F, Minimize =>true)) + F = res(ideal I_*, Strategy => Nonminimal) + assert(B == betti(F, Minimize => true)) C = res I; assert(B == betti C) @@ -768,13 +726,13 @@ TEST /// C1 = res(I, Strategy=>1); C2 = res(I, Strategy=>2); C0 = res(I, Strategy=>0); - C4 = res(I, Strategy=>4); + C4 = res(I, Strategy=>Nonminimal); assert(C2 === C1) assert(C0 === C1) - assert(C4 === C1) + assert(C4 =!= C1) I = ideal I_*; - C4 = res(I, Strategy=>4); + C4 = res(I, Strategy=>Nonminimal); C1 = res(I, Strategy=>1); C2 = res(I, Strategy=>2); C0 = res(I, Strategy=>0); @@ -786,7 +744,7 @@ TEST /// /// restart - debug needsPackage "NonminimalComplexes" + debug needsPackage "Complexes" kk = ZZ/101 R = kk[vars(0..14)] M = genericMatrix(R,a,3,5) @@ -795,12 +753,13 @@ TEST /// gbTrace=2 --minimalBetti I^2 J = I^2; - elapsedTime C = res(J, FastNonminimal=>true) + --elapsedTime C = res(J, Strategy => Nonminimal) + elapsedTime C = res(J, Strategy => Nonminimal) elapsedTime minimalBetti J gbTrace=0 elapsedTime M = submatrixByDegrees(C.dd_3, {6}, {6}); elapsedTime Mk = sub(M,kk); - elapsedTime C6 = constantStrand(C, kk, 6) + elapsedTime C6 = constantStrand(C, 6) assert(Mk == C6.dd_3) -- over R: elapsedTime gens gb M; @@ -815,7 +774,7 @@ TEST /// elapsedTime gens gb Mk'; elapsedTime M = submatrixByDegrees(C.dd_4, {7}, {7}); - elapsedTime C7 = constantStrand(C, kk, 7) + elapsedTime C7 = constantStrand(C, 7) Mk = C7.dd_4; M = Mk ** R; elapsedTime M' = transpose M; @@ -831,7 +790,7 @@ TEST /// time rank Mk -- very slow... time rank Mk' - elapsedTime C7 = constantStrand(C, kk, 8) -- 4.02 sec IMPROVED THIS! + elapsedTime C7 = constantStrand(C, 8) -- 4.02 sec IMPROVED THIS! Mk = C7.dd_5; M = Mk ** R; elapsedTime M' = transpose M; @@ -847,7 +806,7 @@ TEST /// time rank Mk -- very slow... 41 sec time rank Mk' -- also slow, since it is being done by dense methods: 53 sec - elapsedTime C9 = constantStrand(C, kk, 9) -- 4.86 sec + elapsedTime C9 = constantStrand(C, 9) -- 4.86 sec Mk = C9.dd_6; M = Mk ** R; elapsedTime M' = transpose M; @@ -861,8 +820,7 @@ TEST /// elapsedTime gens gb Mk; -- .01 sec elapsedTime gens gb Mk'; -- .01 sec - - elapsedTime C10 = constantStrand(C, kk, 10) -- 7.8 sec + elapsedTime C10 = constantStrand(C, 10) -- 7.8 sec Mk = C10.dd_7; M = Mk ** R; elapsedTime M' = transpose M; @@ -880,13 +838,13 @@ TEST /// R = kk[vars(0..15)] M = genericMatrix(R,a,3,4) I = permanents(2,M) - elapsedTime C = res(I, FastNonminimal=>true) + elapsedTime C = res(I, Strategy => Nonminimal) gbTrace=2 minimalBetti I J = I^2; gens gb J; - elapsedTime C = res(J, FastNonminimal=>true) + elapsedTime C = res(J, Strategy => Nonminimal) gbTrace=2 minimalBetti J /// @@ -900,7 +858,7 @@ TEST /// I = randomMonomialIdeal(splice{20:4}, R) S = kk[vars(0..10), DegreeRank=>11] I = sub(I,S) - C = res(I, FastNonminimal=>true) + C = res(I, Strategy => Nonminimal) Ds = hashTable for i from 1 to length C list i => set keys tally degrees C_i; Es = hashTable for i from 2 to length C list i => toList(Ds#(i-1) * Ds#(i)); diff --git a/M2/Macaulay2/packages/EngineTests/Ring.Test.display.m2 b/M2/Macaulay2/packages/EngineTests/Ring.Test.display.m2 index 858d2854ad9..7995375015f 100644 --- a/M2/Macaulay2/packages/EngineTests/Ring.Test.display.m2 +++ b/M2/Macaulay2/packages/EngineTests/Ring.Test.display.m2 @@ -18,3 +18,36 @@ TEST /// testit(GF(2,4,Strategy=>"FlintBig")) /// +TEST /// + -- test that p_one does the right thing + debug Core + + testit = (R) -> ( + S := R[x]; + f := x^2 - x + 1; + assert Equation(toString raw f, "x2-x+1")) + + testit ZZ + testit(ZZ/101) + testit GF(3,4) + testit QQ + testit RR + testit CC + testit CC_100 + testit(ZZ[y]) +/// + +TEST /// + -- test that p_parens does the right thing + debug Core + + testit = (R) -> ( + S := R[x]; + f := x^3 + (1 + ii)*x^2 + (ii - 1)*x + ii; + assert Equation(toString raw f, "x3+(1+i)x2-(1-i)x+i")) + + testit CC + testit CC_100 + testit(CC[y]) + testit(CC_100[y]) +/// diff --git a/M2/Macaulay2/packages/FastMinors.m2 b/M2/Macaulay2/packages/FastMinors.m2 index 9297e7447d5..5249fa00aab 100644 --- a/M2/Macaulay2/packages/FastMinors.m2 +++ b/M2/Macaulay2/packages/FastMinors.m2 @@ -17,7 +17,7 @@ Version => "1.2.6", Date => "May 15th, 2023", Authors => { } }, --this file is in the public domain Headline => "faster linear algebra operations", - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, PackageExports => {"RandomPoints"}, DebuggingMode => false, Reload=>false, Keywords => {"Linear Algebra"}, @@ -73,7 +73,7 @@ export{ "CodimCheckFunction", "PeriodicCheckFunction", --"RecursiveMinors", - --premade stratgies + --premade strategies "Recursive", "StrategyDefault", "StrategyDefaultNonRandom", @@ -345,7 +345,7 @@ chooseRandomNonzeroSubmatrix(ZZ, Matrix) := opts -> (n1, M1) -> ( --curList = flatten entries curM1; entryList = entries transpose matrix nonzeroEntries(curM1); if #entryList == 0 then return null; - curEntry = entryList#(random(#entryList)); + curEntry = randomElement entryList; --print (curList#curMax); curRow = curEntry#0; curCol = curEntry#1; @@ -535,7 +535,7 @@ chooseMinorSmallestDegree(ZZ, MutableMatrix) := o -> (n1, M1) -> ( randomMaxPosition = method(Options=>{}); randomMaxPosition(List) := o -> (L1) -> ( newList := apply(#L1, i -> {L1#i, random((#L1)^2), i}); - newList2 := random(newList); + newList2 := shuffle(newList); j := maxPosition(newList2); return ((newList2#j)#2); ); @@ -544,7 +544,7 @@ randomMaxPosition(List) := o -> (L1) -> ( randomMinPosition = method(Options=>{}); randomMinPosition(List) := o -> (L1) -> ( newList := apply(#L1, i -> {L1#i, random((#L1)^2), i}); - newList2 := random(newList); + newList2 := shuffle(newList); j := minPosition(newList2); return ((newList2#j)#2); ); @@ -553,7 +553,7 @@ randomMinPosition(List) := o -> (L1) -> ( randomMinPositions = method(Options=>{}); randomMinPositions(ZZ, List) := o -> (n1, L1) -> ( newList := apply(#L1, i -> {L1#i, random((#L1)^2), i}); - newList2 := random(newList); + newList2 := shuffle(newList); newList3 := sort(newList2); return apply(take(n1, newList3), z -> z#0); ); @@ -622,10 +622,8 @@ chooseRandomSubmatrix = method(Options=>{}); chooseRandomSubmatrix(ZZ, Matrix) := opts -> (n1, M1) -> ( - rowL := random(toList(0..(numRows M1 - 1))); - colL := random(toList(0..(numColumns M1 - 1))); - rowL = sort take(rowL, n1); - colL = take(colL, n1); + rowL := randomSubset(numRows M1, n1); + colL := shuffle(toList(0.. (myOrder, R1) -> ( if (debugLevel > 0) then print "reorderPolynomialRing: it is a polynomialRing"; coeff := coefficientRing R1; genList := generators R1; - newGenList := random(genList); + newGenList := shuffle(genList); return coeff[newGenList, MonomialOrder => myOrder];) else (return R1); ); diff --git a/M2/Macaulay2/packages/ForeignFunctions.m2 b/M2/Macaulay2/packages/ForeignFunctions.m2 index 97b4035e416..372af579473 100644 --- a/M2/Macaulay2/packages/ForeignFunctions.m2 +++ b/M2/Macaulay2/packages/ForeignFunctions.m2 @@ -1,5 +1,5 @@ -- ForeignFunctions package for Macaulay2 --- Copyright (C) 2022-2025 Doug Torrance +-- Copyright (C) 2022-2026 Doug Torrance -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License @@ -16,8 +16,8 @@ newPackage("ForeignFunctions", Headline => "foreign function interface", - Version => "0.6", - Date => "November 10, 2025", + Version => "0.7", + Date => "February 5, 2026", Authors => {{ Name => "Doug Torrance", Email => "dtorrance@piedmont.edu", @@ -44,6 +44,11 @@ newPackage("ForeignFunctions", -* +0.7 (2026-02-05, M2 1.26.05) +* fix garbage collection of mpzT and mpfrT objects +* update foreignSymbol test so that it doesn't require mpfi since it may not be + available (e.g., if it's statically linked) + 0.6 (2025-11-10, M2 1.25.11) * update GPL 2 text (FSF no longer has a physical address) @@ -2549,11 +2554,8 @@ TEST /// ------------------- -- foreignSymbol -- ------------------- -mpfi = openSharedLibrary "mpfi" -(foreignFunction(mpfi, "mpfi_set_error", void, int)) 5 -assert Equation(value foreignSymbol(mpfi, "mpfi_error", int), 5) -assert Equation(value foreignSymbol("mpfi_error", int), 5) -(foreignFunction(mpfi, "mpfi_reset_error", void, void))() +environ = foreignSymbol("environ", charstarstar) +assert all(value environ, x -> instance(x, String)) /// -- TODO: add test when #2683 fixed @@ -2632,3 +2634,10 @@ mpfi = openSharedLibrary "mpfi" assert instance(value describe mpfi, SharedLibrary) assert instance(value toExternalString mpfi, SharedLibrary) /// + +TEST /// +-- finalizing gmp and mpfr objects +scan(1000, i -> mpzT 2^100) +scan(1000, i -> mpfrT numeric(100, pi)) +collectGarbage() +/// diff --git a/M2/Macaulay2/packages/GKMVarieties/Tests_GKMVarieties.m2 b/M2/Macaulay2/packages/GKMVarieties/Tests_GKMVarieties.m2 index 22200aa2073..7f8b345efd7 100644 --- a/M2/Macaulay2/packages/GKMVarieties/Tests_GKMVarieties.m2 +++ b/M2/Macaulay2/packages/GKMVarieties/Tests_GKMVarieties.m2 @@ -8,7 +8,7 @@ Tests for GKMVarieties.m2 -- flagGeomTuttePolynomial test -------------------------------- TEST /// -ML = (random drop(drop(allMatroids 4,1),-1))_{0,1} +ML = randomSubset(take(allMatroids 4, {1, 15}), 2) TML = apply(ML, m -> {tuttePolynomial m, flagGeomTuttePolynomial flagMatroid({m})}) -- 2.5 seconds assert all(TML, l -> (map(ring first l, ring last l, gens ring first l))(last l) == first l) N = flagMatroid(matrix{{1,1,1},{1,0,0}},{1,2}) diff --git a/M2/Macaulay2/packages/GameTheory.m2 b/M2/Macaulay2/packages/GameTheory.m2 index 0c89d40d428..0870a634ea9 100644 --- a/M2/Macaulay2/packages/GameTheory.m2 +++ b/M2/Macaulay2/packages/GameTheory.m2 @@ -352,7 +352,7 @@ monomialFromIndex (List, ZZ, Ring):= (L, i, R) ->( -- for 0<=k<=n-1, k!=u, obtaining a list of d_u - 1 monomials. -- The function returns the sum of all these lists of d_u - 1 monomials, as a list --- of d_u - 1 polynomoials, which are the polynomials of equilibrium conditions +-- of d_u - 1 polynomials, which are the polynomials of equilibrium conditions -- from the payoff tensor T of player u. -- Note: accumulatedHash is not implemented with 'zeroTensor' to avoid pre-filling diff --git a/M2/Macaulay2/packages/GradedLieAlgebras.m2 b/M2/Macaulay2/packages/GradedLieAlgebras.m2 index 5a1c2acca72..8618e230145 100644 --- a/M2/Macaulay2/packages/GradedLieAlgebras.m2 +++ b/M2/Macaulay2/packages/GradedLieAlgebras.m2 @@ -151,10 +151,6 @@ export { -- Also, the elements of Q#diff belong to ambient(Q)=F. -- The ambient of Q=L/I where class I=LieIdeal is L, The elements of Q#diff belong to ambient(Q)=L. -- The ideal Q#ideal=I is an ideal of L. - - -recursionLimit=10000; - ---------------------------------------- -- diff --git a/M2/Macaulay2/packages/GradedLieAlgebras/doc.m2 b/M2/Macaulay2/packages/GradedLieAlgebras/doc.m2 index 8043e106258..409008f6c66 100644 --- a/M2/Macaulay2/packages/GradedLieAlgebras/doc.m2 +++ b/M2/Macaulay2/packages/GradedLieAlgebras/doc.m2 @@ -2009,7 +2009,7 @@ Usage y = a x Inputs a: RingElement - $a$ is an element in {\tt L#Field}, where $L$ is of type {\tt Lielgebra} + $a$ is an element in {\tt L#Field}, where $L$ is of type {\tt LieAlgebra} x: LieElement $x$ is of type $L$ Outputs diff --git a/M2/Macaulay2/packages/GraphicalModels.m2 b/M2/Macaulay2/packages/GraphicalModels.m2 index 38b6c663731..8b8464ee558 100644 --- a/M2/Macaulay2/packages/GraphicalModels.m2 +++ b/M2/Macaulay2/packages/GraphicalModels.m2 @@ -684,8 +684,8 @@ gaussianRing MixedGraph := Ring => opts -> (g) -> ( p := toSymbol opts.pVariableName; k := toSymbol opts.kVariableName; kk := opts.Coefficients; - if (not gaussianRingList#?(kk,s,k,l,p,vv)) then ( - --(kk,s,k,l,p,vv) uniquely identifies gaussianRing in case of MixedGraph input. + if (not gaussianRingList#?(kk,s,k,l,p,dd,bb,uu)) then ( + --(kk,s,k,l,p,dd,bb,uu) uniquely identifies gaussianRing in case of MixedGraph input. sL := delete(null, flatten apply(vv, x-> apply(vv, y->if pos(vv,x)>pos(vv,y) then null else s_(x,y)))); kL := join(apply(U, i->k_(i,i)),delete(null, flatten apply(U, x-> apply(toList uu#x, y->if pos(vv,x)>pos(vv,y) then null else k_(x,y))))); lL := delete(null, flatten apply(vv, x-> apply(toList dd#x, y->l_(x,y)))); @@ -715,8 +715,8 @@ gaussianRing MixedGraph := Ring => opts -> (g) -> ( R.graphType=class g; R.graph= g; -- fill into internal gaussianRingList - gaussianRingList#((kk,s,k,l,p,vv)) = R;); - gaussianRingList#((kk,s,k,l,p,vv)) + gaussianRingList#((kk,s,k,l,p,dd,bb,uu)) = R;); + gaussianRingList#((kk,s,k,l,p,dd,bb,uu)) ) @@ -3590,6 +3590,13 @@ R = gaussianRing G assert(sort gens R === sort {l_(b,c), l_(b,d), l_(c,d), p_(a,a), p_(b,b), p_(c,c), p_(d,d), p_(a,d), s_(a,a), s_(a,b), s_(a,c), s_(a,d), s_(b,b), s_(b,c), s_(b,d), s_(c,c), s_(c,d), s_(d,d)}) /// +-- Test caching issue GH#3553 +TEST /// +R1 = gaussianRing digraph({a,b,c}, {(a,b)}) +R2 = gaussianRing digraph({a,b,c}, {(a,b),(b,c),(a,c)}) +assert(R1 =!= R2) +/// + ----------------------------------------------- --- TEST undirectedEdgesMatrix----------------- ----------------------------------------------- diff --git a/M2/Macaulay2/packages/Graphs.m2 b/M2/Macaulay2/packages/Graphs.m2 index 5199b80788a..debc14d9833 100644 --- a/M2/Macaulay2/packages/Graphs.m2 +++ b/M2/Macaulay2/packages/Graphs.m2 @@ -1171,7 +1171,7 @@ topologicalSort Digraph := List => D -> topologicalSort(D, "") topologicalSort (Digraph, String) := List => (D,s) -> ( if instance(D, Digraph) then ( s = toLower s; - processor := if s == "random" then random + processor := if s == "random" then shuffle else if s == "min" then sort else if s == "max" then rsort else if s == "degree" then L -> last \ sort transpose {apply(L, v -> degree(D, v)), L} @@ -1581,7 +1581,7 @@ reindexBy (Graph, String) := Graph => (G, s) -> ( ); return graph (V', edges G) ); - if s == "random" then return graph (random vertexSet G, edges G, EntryMode => "edges"); + if s == "random" then return graph (shuffle vertexSet G, edges G, EntryMode => "edges"); if s == "components" then return graph (flatten connectedComponents G, edges G, EntryMode => "edges"); if s == "sort" then return graph (sort vertexSet G, edges G, EntryMode => "edges"); ) @@ -1653,7 +1653,7 @@ reindexBy (Digraph, String) := Digraph => (D, s) -> ( ); return digraph (V', edges D, EntryMode => "edges") ); - if s == "random" then return digraph(random vertexSet D, edges D, EntryMode => "edges"); + if s == "random" then return digraph(shuffle vertexSet D, edges D, EntryMode => "edges"); if s == "sort" then return digraph(sort vertexSet D, edges D, EntryMode => "edges"); ) diff --git a/M2/Macaulay2/packages/GroebnerStrata.m2 b/M2/Macaulay2/packages/GroebnerStrata.m2 index 5febf79ea58..c8bc9148f54 100644 --- a/M2/Macaulay2/packages/GroebnerStrata.m2 +++ b/M2/Macaulay2/packages/GroebnerStrata.m2 @@ -8,7 +8,7 @@ newPackage( {Name => "Kristine Jones", Email => "kejones84@gmail.com"}}, Headline => "computing Groebner loci in Hilbert schemes", PackageImports => {"Elimination"}, - PackageExports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, DebuggingMode => false, Keywords => {"Commutative Algebra"} ) @@ -344,7 +344,8 @@ nonminimalMaps(Ideal) := Sequence => (F) -> ( A := coefficientRing T; T' := first flattenRing T; F' := sub(F, T'); - C := res(F', Strategy => 5); -- must be over a finite field. + for f in F'_* do if leadCoefficient f != 1 then error "expected a putative monic Groebner basis"; + C := res(F', Strategy => NonminimalWithGB); -- must be over a finite field. -- now let's place this into a new ring, with degree 0 coefficients. A'' := (coefficientRing A)[gens A, Degrees => {numgens A : 0}]; T'' := A'' [gens T, Join => false]; @@ -355,7 +356,7 @@ nonminimalMaps(Ideal) := Sequence => (F) -> ( f := sub(C.dd_i, T''); newMaps#(i-1) = map(source newMaps#(i-2),, f); ); - CF := chainComplex toList newMaps; -- this is one thing returned. + CF := complex toList newMaps; -- this is one thing returned. -- the other is a list of degree zero maps, indexed via: (homological degree, internal degree). H := hashTable flatten for lev from 1 to length C list ( set1 := set (degrees CF_lev)/first; @@ -951,7 +952,7 @@ doc /// $A$. The lead terms of the generators of $I$ should be the initial ideal of $I$, and should be monic. Outputs - C:ChainComplex + C:Complex A complex over a polynomial ring where any parameters in the base ring are set to have degree 0, and the variables of the ring of $I$ are set to have degree one. H:HashTable diff --git a/M2/Macaulay2/packages/HigherCIOperators.m2 b/M2/Macaulay2/packages/HigherCIOperators.m2 index 43a9908009d..624c40a6ee5 100644 --- a/M2/Macaulay2/packages/HigherCIOperators.m2 +++ b/M2/Macaulay2/packages/HigherCIOperators.m2 @@ -9,7 +9,7 @@ newPackage( Keywords => {"Commutative Algebra"}, DebuggingMode => false, PackageImports => { "CompleteIntersectionResolutions" }, - PackageExports =>{"MCMApproximations", "OldChainComplexes"} + PackageExports =>{"MCMApproximations", "Complexes"} ) export {"exteriorMultiplication", @@ -25,12 +25,12 @@ trueKoszul=method() trueKoszul Matrix := ff -> ( len := numcols ff; gg := map(target ff,source ff, (i,j)-> (-1)^j*ff_(len-1-j)_0); -- was source ff - K1 := koszul gg; + K1 := koszulComplex gg; Klist := apply(len, i-> map(K1_i, K1_(i+1), (-1)^i*transpose K1.dd_(len -i))); mapList := {map((ring gg)^1,,matrix Klist_0)}; scan(toList(1..len-1), i-> mapList = append(mapList, map(source last mapList, , matrix Klist_i))); - chainComplex mapList + complex mapList ) -*syzygyModule = method() @@ -39,14 +39,14 @@ syzygyModule (ZZ, Module) := (d,M)->( coker F.dd_(d+1)) *- ciOperatorResolution=method() -ciOperatorResolution(ChainComplex, ChainComplex) := (A,L) ->( +ciOperatorResolution(Complex, Complex) := (A,L) ->( u :=higherCIOperators(A,L); - chainComplex apply(toList(1..length A), + complex apply(toList(1..length A), k->makeALDifferential(k,A,L,u)) ) makeALDifferential= method() -makeALDifferential(ZZ,ChainComplex, ChainComplex, HashTable) := +makeALDifferential(ZZ,Complex, Complex, HashTable) := (j, A,L,u) ->( --make the differential from the j-th diag A_j**L_0++A_(j-1)**L_1... --to the (j-1)-st diagonal A_(j-1)**L_0++... @@ -67,7 +67,7 @@ makeALDifferential(ZZ,ChainComplex, ChainComplex, HashTable) := higherCIOperators = method() -higherCIOperators(ChainComplex, ChainComplex) := (A,L) ->( +higherCIOperators(Complex, Complex) := (A,L) ->( --L is a koszul complex, resolution of coker ff over S --A is the lift to S of (part of) a resolution over R = S/ideal ff --u#{n,p,q}: A_p **L_q --> A_(p-n)**L_(q+n-1) @@ -232,7 +232,7 @@ doc /// ff:Matrix Matrix with the elements on which to build the Koszul complex Outputs - K:ChainComplex + K:Complex Description Text The usual Koszul command produces a complex with the basis @@ -241,11 +241,11 @@ doc /// Example S = ZZ/101[a,b,c,d] ff = matrix{{a,b,c,d}} - (koszul ff).dd_2 + (koszulComplex ff).dd_2 (trueKoszul ff).dd_2 basis(2,(ZZ/101[a,b,c,d, SkewCommutative => true])) SeeAlso - koszul + koszulComplex /// doc /// @@ -267,7 +267,7 @@ doc /// The basis of each $\wedge^p k^n$ is given in lex order. Caveat This is not the order of the basis in the output of - koszul ff; rather, use trueKoszul ff. + koszulComplex ff; rather, use trueKoszul ff. SeeAlso trueKoszul /// @@ -275,15 +275,15 @@ doc /// doc /// Key higherCIOperators - (higherCIOperators, ChainComplex, ChainComplex) + (higherCIOperators, Complex, Complex) Headline "creates the HashTable of higher CI operators on a lifted resolution" Usage u = higherCIOperators(A,L) Inputs - A:ChainComplex + A:Complex lifted resolution from complete intersection $S\to R$ - L:ChainComplex + L:Complex Koszul complex resolving R over S Outputs u:HashTable @@ -344,7 +344,7 @@ doc /// doc /// Key makeALDifferential - (makeALDifferential,ZZ,ChainComplex, ChainComplex, HashTable) + (makeALDifferential,ZZ,Complex, Complex, HashTable) Headline "makes the differential used in ciOperatorResolution" Usage @@ -352,9 +352,9 @@ doc /// Inputs j:ZZ which differential - A:ChainComplex + A:Complex lift of resolution from complete intersection S-->R - L:ChainComplex + L:Complex Koszul complex resolving R over S u:HashTable output of higherCIOperators @@ -369,18 +369,18 @@ doc /// doc /// Key ciOperatorResolution - (ciOperatorResolution, ChainComplex, ChainComplex) + (ciOperatorResolution, Complex, Complex) Headline "lift resolution from complete intersection using higher ci-operators" Usage AL = ciOperatorResolution(A,L) Inputs - A:ChainComplex + A:Complex lift over $S\to R$ of an R-free resolution - L:ChainComplex + L:Complex algebra resolution of R over S; for now, Koszul complex Outputs - AL:ChainComplex + AL:Complex resolution over S of same module Description Text @@ -410,7 +410,7 @@ doc /// M = highSyzygy (R**N); AA = res(M, LengthLimit => 5); Alist = apply(length AA, i-> lift(AA.dd_(i+1), S)); - A = chainComplex Alist; + A = complex Alist; L = trueKoszul ff; AL = ciOperatorResolution(A,L) G = res pushForward(map(R,S),M) diff --git a/M2/Macaulay2/packages/HighestWeights.m2 b/M2/Macaulay2/packages/HighestWeights.m2 index eeef8297d5a..2c42f2de17b 100644 --- a/M2/Macaulay2/packages/HighestWeights.m2 +++ b/M2/Macaulay2/packages/HighestWeights.m2 @@ -25,7 +25,6 @@ newPackage( HomePage => "http://math.galetto.org"}}, Headline => "decompose free resolutions and graded modules with a semisimple Lie group action", Keywords => {"Lie Groups and Lie Algebras", "Homological Algebra"}, - PackageImports => {"OldChainComplexes"}, PackageExports => {"WeylGroups", "Complexes"}, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry", @@ -57,7 +56,7 @@ end --0.1: wrote a basic working version of the package! :-) --0.2: completed documentation and tests --0.2.1: suppressed minimality test for propagate ---0.2.2: now with auxiliary files and freudenthal decomposition +--0.2.2: now with auxiliary files and Freudenthal decomposition --0.2.3: new propagateWeights function --0.2.4: preliminary HW decomposition for resolutions (no multigrading) --0.2.5: HW decomposition for (multi)-graded rings diff --git a/M2/Macaulay2/packages/HighestWeights/main.m2 b/M2/Macaulay2/packages/HighestWeights/main.m2 index 9b37d880c15..443e42e9e60 100644 --- a/M2/Macaulay2/packages/HighestWeights/main.m2 +++ b/M2/Macaulay2/packages/HighestWeights/main.m2 @@ -219,7 +219,8 @@ highestWeightsDecomposition (Complex, ZZ, List) := HashTable => o -> (C,i,L) ->( h := new MutableHashTable from {i => L}; temp := (h#i,id_(C_i)); --if the complex is a resolution skip MinimalityTest - if C.?Resolution then mt:=false else mt=true; + ---- MES---- if C.?Resolution then mt:=false else mt=true; + mt := true; --propagate weights backwards from homological dimension i for j from i+1 to maxRight when C_j!=0 do ( temp = propagateWeights(temp_1*C.dd_j,temp_0,MinimalityTest=>mt); diff --git a/M2/Macaulay2/packages/HolonomicSystems/DOC/Dsystems.m2 b/M2/Macaulay2/packages/HolonomicSystems/DOC/Dsystems.m2 index de05c4dbf9c..896c403de28 100644 --- a/M2/Macaulay2/packages/HolonomicSystems/DOC/Dsystems.m2 +++ b/M2/Macaulay2/packages/HolonomicSystems/DOC/Dsystems.m2 @@ -121,7 +121,7 @@ doc /// Description Text Given a $d \times n$ integer matrix $A = (a_{ij})$ and a Weyl algebra in $n$ variables, produce the $d$ corresponding Euler operators $E_i = \sum_{j=1}^n a_{ij}x_jdj$. - An optional list $b$ imposes a multigrading so that one can look for solutions to the Euler operatros of multidegree $b$. + An optional list $b$ imposes a multigrading so that one can look for solutions to the Euler operators of multidegree $b$. Example D = makeWeylAlgebra(QQ[x,y,z]) A = matrix{{2,-7,5},{14,8,-1}} diff --git a/M2/Macaulay2/packages/HyperplaneArrangements.m2 b/M2/Macaulay2/packages/HyperplaneArrangements.m2 index 0936edea9ef..2f326a03d6f 100644 --- a/M2/Macaulay2/packages/HyperplaneArrangements.m2 +++ b/M2/Macaulay2/packages/HyperplaneArrangements.m2 @@ -23,7 +23,7 @@ newPackage( Headline => "manipulating finite sets of hyperplanes", Keywords => {"Algebraic Geometry", "Matroids"}, DebuggingMode => false, - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, PackageExports => {"Matroids"} ) diff --git a/M2/Macaulay2/packages/IncidenceCorrespondenceCohomology.m2 b/M2/Macaulay2/packages/IncidenceCorrespondenceCohomology.m2 index cee3f71924d..6155ad65833 100644 --- a/M2/Macaulay2/packages/IncidenceCorrespondenceCohomology.m2 +++ b/M2/Macaulay2/packages/IncidenceCorrespondenceCohomology.m2 @@ -97,7 +97,7 @@ shiftn = (list3, n) ->(for i from 0 to (#list3-1) list ({list3#i#0-n, list3#i#1, tensorfqa = (list3, q, a, det1) ->( --list of divided multidegrees dividedp := compositions(2, a); - --list of frobenius multidegrees of D^aU + --list of Frobenius multidegrees of D^aU fqa := for i from 0 to a list ({dividedp#i#0*q, dividedp#i#1*q}); --this will be the new list to be returned newlist := flatten for i from 0 to (#fqa -1) list( @@ -368,7 +368,7 @@ gradedFdr = (S, d, r) ->( --auxiliary Laurent polynomial ring --keeps track of shifts and multiplicities for standard generators ---of the Han-Monsky reprezentation ring +--of the Han-Monsky representation ring q := local q; HMRing = ZZ[q,Inverses => true, MonomialOrder=>Lex] --if HMRing is a terrible name, we should replace it, ideas? @@ -848,7 +848,7 @@ base10p = (p, num) ->( --Input: q = p^e a power of an integer prime or q = 0, positive integer d, --and R=n or R = ZZ[x_1..x_n] either a positive integer or a polynomial ring --Output: If R is a polynomial ring, then the function outputs the q-truncated symmetric polynomial ---where the q=0 is intrepreted to be not truncated +--where the q=0 is interpreted to be not truncated --If R=n an integer then, the output is the number of monomials in this polynomial --(i.e. the sum of the coefficients of the monomial terms in this polynomial) hqd = (q, d, R) ->( @@ -884,7 +884,7 @@ hqd = (q, d, R) ->( --Input: q=p^e a power of a prime integer p or q = 0, positive integers a,b, --and R=n or R = ZZ[x_1..x_n] either a positive integer or a polynomial ring --Output: If R is a polynomial ring, then the function outputs the q-truncated Schur polynomial for (a,b) ---where the q=0 is intrepreted to be not truncated +--where the q=0 is interpreted to be not truncated --If R=n an integer then, the output is the sum of the coefficients of the monomial terms in this polynomial sqab = (q,a, b, R) ->(hqd(q, a, R)*hqd(q, b, R)-hqd(q, a+1, R)*hqd(q, b-1, R)) diff --git a/M2/Macaulay2/packages/IntegerProgramming.m2 b/M2/Macaulay2/packages/IntegerProgramming.m2 index 97161fdb67b..205da2636a8 100644 --- a/M2/Macaulay2/packages/IntegerProgramming.m2 +++ b/M2/Macaulay2/packages/IntegerProgramming.m2 @@ -443,7 +443,7 @@ doc /// Text {\bf Remark.} Maximization and minimization (integer) linear programs are equivalent by multiplying objective functions by $-1$. - This package treats every problem as a minimization problem and leaves the multiplication for maximiation problems to the user. + This package treats every problem as a minimization problem and leaves the multiplication for maximization problems to the user. References [CLO] David A. Cox, John Little, and Donal O'Shea. Using Algebraic Geometry. Second edition. Graduate Texts in Mathematics, 185. Springer, New York, 2005. diff --git a/M2/Macaulay2/packages/IntegralClosure.m2 b/M2/Macaulay2/packages/IntegralClosure.m2 index b3273be7a73..ffb4f5c3e23 100644 --- a/M2/Macaulay2/packages/IntegralClosure.m2 +++ b/M2/Macaulay2/packages/IntegralClosure.m2 @@ -466,17 +466,14 @@ randomMinors(ZZ,ZZ,Matrix) := (n,d,M) -> ( then return (minors(d,M))_*; L := {}; -- L will be a list of minors, specified by the pair of lists "rows" and "cols" dets := {}; -- the list of determinants taken so far - rowlist := toList(0..r-1); - collist := toList(0..c-1); - ds := toList(0..d-1); for i from 1 to n do ( -- choose a random set of rows and of columns, add it to L -- only if it doesn't appear already. When a pair is added to L, -- the corresponding minor is added to "dets" while ( - rows := sort (random rowlist)_ds ; - cols := sort (random collist)_ds ; + rows := randomSubset(r, d); + cols := randomSubset(c, d); for p in L do (if (rows,cols) == p then break true); false) do(); @@ -493,13 +490,10 @@ nonzeroMinor(ZZ,Matrix) := opts -> (d,M) -> ( c := numcols M; if d > min(r,c) then return null; candidate := 0_(ring M); - rowlist := toList(0..r-1); - collist := toList(0..c-1); - ds := toList(0..d-1); for i from 1 to opts.Limit do( -- choose a random set of rows and of columns, test the determinant. - rows := sort (random rowlist)_ds ; - cols := sort (random collist)_ds ; + rows := randomSubset(r, d); + cols := randomSubset(c, d); candidate = det (M^rows_cols); if candidate != 0 then return(candidate); ); diff --git a/M2/Macaulay2/packages/InvariantRing.m2 b/M2/Macaulay2/packages/InvariantRing.m2 index b9e1c8ae562..16be31c09f1 100644 --- a/M2/Macaulay2/packages/InvariantRing.m2 +++ b/M2/Macaulay2/packages/InvariantRing.m2 @@ -1,5 +1,5 @@ -* - Copyright 2020, Luigi Ferraro, Federico Galetto, + Copyright 2020-2026, Luigi Ferraro, Federico Galetto, Francesca Gandini, Hang Huang, Matthew Mastroeni, Xianglong Ni. You may redistribute this file under the terms of the GNU General Public @@ -9,12 +9,12 @@ newPackage( "InvariantRing", - Version => "2.0", - Date => "November 11, 2020", + Version => "2.4", + Date => "May 1, 2026", Authors => { {Name => "Luigi Ferraro", - Email => "lferraro@ttu.edu", - HomePage => "http://www.math.ttu.edu/~lferraro/" + Email => "luigi.ferraro@utrgv.edu", + HomePage => "https://faculty.utrgv.edu/luigi.ferraro/" }, {Name => "Federico Galetto", Email => "f.galetto@csuohio.edu", @@ -30,7 +30,7 @@ newPackage( }, {Name => "Thomas Hawes", Email => "thomas.hawes@maths.ox.ac.uk"}, {Name => "Matthew Mastroeni", - Email => "mmastro@okstate.edu", + Email => "mmastro@iastate.edu", HomePage => "https://mnmastro.github.io/" }, {Name => "Xianglong Ni", @@ -47,49 +47,62 @@ newPackage( "published article URI" => "https://msp.org/jsag/2024/14-1/p02.xhtml", "published article DOI" => "10.2140/jsag.2024.14.5", "published code URI" => "https://msp.org/jsag/2024/14-1/jsag-v14-n1-x02-InvariantRing.m2", + "repository code URI" => "https://github.com/Macaulay2/M2/blob/master/M2/Macaulay2/packages/InvariantRing.m2", "release at publication" => "d708862d93052a37b7f9ff1e584a9b57d1cb7870", "version at publication" => "2.0", "volume number" => "14", "volume URI" => "https://msp.org/jsag/2024/14-1/" }, AuxiliaryFiles => true, - DebuggingMode => false + DebuggingMode => false, + PackageExports => { + "Graphs" + } ) export { - "GroupAction", - "finiteAction", - "FiniteGroupAction", + -- New Types + "GroupAction", + "FiniteGroupAction", + "DiagonalAction", + "LinearlyReductiveAction", + + --FiniteGroups.m2 + "finiteAction", "group", "isAbelian", "permutationMatrix", "schreierGraph", - "words", - "cyclicFactors", - "DiagonalAction", + "words", + + --AbelianGroups.m2 + "cyclicFactors", "diagonalAction", "equivariantHilbert", "equivariantHilbertSeries", - "weights", + "weights", + + --LinearlyReductiveGroups.m2 "actionMatrix", "groupIdeal", "hilbertIdeal", - "linearlyReductiveAction", - "LinearlyReductiveAction", + "linearlyReductiveAction", + + --Invariants.m2 "action", "definingIdeal", "DegreeBound", "invariants", "invariantRing", "isInvariant", - "reynoldsOperator", - "UseLinearAlgebra", + "reynoldsOperator", "RingOfInvariants", - "UseCoefficientRing", - "UseNormaliz", - "UsePolyhedra", + "UseCoefficientRing", + "UsePolyhedra", + + --Hawes.m2 "hironakaDecomposition", "molienSeries", "primaryInvariants", @@ -99,7 +112,6 @@ export { "PrintDegreePolynomial" } - needsPackage("Elimination") needsPackage("Normaliz") needsPackage("Polyhedra") diff --git a/M2/Macaulay2/packages/InvariantRing/AbelianGroups.m2 b/M2/Macaulay2/packages/InvariantRing/AbelianGroups.m2 index 6b5f9db670b..4efe7a5d90c 100644 --- a/M2/Macaulay2/packages/InvariantRing/AbelianGroups.m2 +++ b/M2/Macaulay2/packages/InvariantRing/AbelianGroups.m2 @@ -7,70 +7,91 @@ the License, or any later version. *- +------------------------------------------- + +DiagonalAction = new Type of GroupAction -- the DiagonalAction object -DiagonalAction = new Type of GroupAction +diagonalAction = method() -- Constructor for DiagonalAction object ------------------------------------------- ---- DiagonalAction methods ------------------- +--- DiagonalAction Constructors ----------- ------------------------------------------- -diagonalAction = method() - +------------------------------- +---- diagonalAction constructor +-->- W1 & W2 - Weight matrices +-->- g - Order of the group +-->- R - Ring diagonalAction (Matrix, Matrix, List, PolynomialRing) := DiagonalAction => (W1, W2, d, R) -> ( + --!-- Ensures inputs are valid --!-- if not isField coefficientRing R then ( - error "diagonalAction: Expected the last argument to be a polynomial ring over a field." + error "diagonalAction: Expected the last argument to be a polynomial ring over a field." ); if ring W1 =!= ZZ or ring W2 =!= ZZ then ( - error "diagonalAction: Expected the first and second arguments to be matrices of integer weights." + error "diagonalAction: Expected the first and second arguments to be matrices of integer weights." ); if numColumns W1 =!= dim R or numColumns W2 =!= dim R then ( - error "diagonalAction: Expected the number of columns of each matrix to equal the dimension of the polynomial ring." + error "diagonalAction: Expected the number of columns of each matrix to equal the dimension of the polynomial ring." ); if numRows W2 =!= #d then ( - error "diagonalAction: Expected the number of rows of the second argument to equal the size of the list." + error "diagonalAction: Expected the number of rows of the second argument to equal the size of the list." ); if any(d, j -> not instance(j, ZZ) or j <= 0) then ( - error "diagonalAction: Expected the second argument to be a list of positive integers." + error "diagonalAction: Expected the second argument to be a list of positive integers." ); p := char R; if p > 0 and any(d, j -> j%p == 0) then ( - error "diagonalAction: Diagonal action is not defined when the characteristic divides the order of one of the cyclic factors." - ); + error "diagonalAction: Diagonal action is not defined when the characteristic divides the order of one of the cyclic factors." + ); + --!-- ---------------------- --!-- + ----- - Creates the object - --!-- r := numRows W1; g := numRows W2; - z := getSymbol "z"; - C := ZZ[Variables=> r + g,VariableBaseName=>z, - MonomialOrder=> {GroupLex => r,GroupLex => g}, - Inverses=>true]; + diagVariable := getSymbol "ζ"; + C := ZZ[Variables => r + g, + VariableBaseName => diagVariable, + MonomialOrder => {GroupLex => r,GroupLex => g}, + Inverses => true]; new DiagonalAction from { - cache => new CacheTable, - (symbol cyclicFactors) => d, - (symbol degreesRing) => C monoid degreesRing R, - (symbol numgens) => g, - (symbol ring) => R, - (symbol rank) => r, - (symbol weights) => (W1, W2) + cache => new MutableHashTable, + (symbol cyclicFactors) => d, + (symbol degreesRing) => C monoid degreesRing R, + (symbol numgens) => g, + (symbol ring) => R, + (symbol rank) => r, + (symbol weights) => (W1, W2) } - ) +) +------------------------------- +---- diagonalAction constructor +-->- W - Weight matrices +-->- g - Order of the group +-->- R - Ring diagonalAction (Matrix, List, PolynomialRing) := DiagonalAction => (W, d, R) -> ( if ring W =!= ZZ then ( - error "diagonalAction: Expected the first argument to be a matrix of integer weights." + error "diagonalAction: Expected the first argument to be a matrix of integer weights." ); r := numRows W - #d; if r < 0 then ( - error "diagonalAction: The number of rows of the matrix cannot be smaller than the size of the list." + error "diagonalAction: The number of rows of the matrix cannot be smaller than the size of the list." ); W1 := W^(apply(r, i -> i)); W2 := W^(apply(#d, i -> r + i)); diagonalAction(W1, W2, d, R) - ) - -diagonalAction (Matrix, PolynomialRing) := DiagonalAction => (W, R) -> diagonalAction(W, {}, R) +) +---- Additional constructors when parameters have special properties +diagonalAction (Matrix, PolynomialRing) := DiagonalAction => (W, R) -> diagonalAction(W, {}, R) +diagonalAction (Matrix, ZZ, PolynomialRing) := DiagonalAction => (W, z, R) -> diagonalAction(W, apply(numRows W, i -> z), R) +------------------------------------------- +--- DiagonalAction Methods ---------------- ------------------------------------------- +------------------------------- +---- net DiagonalAction +-->- Used to print out a diagonalAction object net DiagonalAction := D -> ( torus := ""; cyclicGroups := ""; @@ -78,55 +99,68 @@ net DiagonalAction := D -> ( g := D.numgens; local weightMatrix; if r > 0 then ( - torus = (expression coefficientRing D.ring)^(expression "*"); - if r > 1 then torus = (expression ("("|net torus|")"))^(expression r) - ); + torus = (expression coefficientRing D.ring)^(expression "*"); + if r > 1 then ( + torus = (expression ("("|net torus|")"))^(expression r) + ); + ); if g > 0 then ( - cyclicGroups = cyclicGroups|horizontalJoin apply(g, i -> ( - if i == g - 1 then (net ZZ|"/"|net D.cyclicFactors#i) - else (net ZZ|"/"|net D.cyclicFactors#i|" x ") - ) + cyclicGroups = cyclicGroups|horizontalJoin apply(g, i -> + ( if i == g - 1 then (net ZZ|"/"|net D.cyclicFactors#i) + else (net ZZ|"/"|net D.cyclicFactors#i|" x ") + ) ); - if r > 0 then ( - torus = net torus|" x "; - weightMatrix = D.weights - ) - else weightMatrix = last D.weights + if r > 0 then ( + torus = net torus|" x "; + weightMatrix = D.weights + ) + else weightMatrix = last D.weights; ) else weightMatrix = first D.weights; stack {(net D.ring)|" <- "|net torus|net cyclicGroups|" via ","", net weightMatrix} - ) +) -cyclicFactors = method() +---- cyclicFactors +-->- Method to access the cyclicFactors of a DiagonalAction +cyclicFactors = method() cyclicFactors DiagonalAction := List => D -> D.cyclicFactors + +-->- Override of "degreesRing" function for DiagonalAction degreesRing DiagonalAction := Ring => D -> D.degreesRing +-->- Override for "numgens" function for DiagonalAction numgens DiagonalAction := ZZ => D -> D.numgens +-->- Override for "rank" function for DiagonalAction rank DiagonalAction := ZZ => D -> D.rank +---- weights +-->- Method to access the cyclicFactors of a DiagonalAction weights = method() - weights DiagonalAction := Matrix => D -> D.weights +------------------------------------------- +--- Equivariant Hilbert Series Methods ---- +------------------------------------------- equivariantHilbertSeries = method(Options => {Order => infinity}, TypicalValue => Divide) equivariantHilbertSeries DiagonalAction := op -> T -> ( - if unique degrees ring T =!= {{1}} then - error "Only implemented for standard graded polynomial rings"; + if unique degrees ring T =!= {{1}} then error "Only implemented for standard graded polynomial rings"; ord := op.Order; if ord === infinity then ( - equivariantHilbertRational(T) + equivariantHilbertRational(T) ) else ( - equivariantHilbertPartial(T,ord-1) + equivariantHilbertPartial(T,ord-1) ) - ) +) +-- computes equivariant hilbert series as rational function +-- INPUT: T, DiagonalAction equivariantHilbertRational = T -> ( n := dim T; W1 := first weights T; @@ -134,7 +168,7 @@ equivariantHilbertRational = T -> ( d := cyclicFactors T; if not zero W2 then ( W2 = matrix apply(entries W2,d,(row,m)->apply(row,i->i%m)); - ); + ); W := W1 || W2; R := degreesRing T; C := coefficientRing R; @@ -143,42 +177,44 @@ equivariantHilbertRational = T -> ( Divide{1,den} ) +-- computes equivariant hilbert series up to a given degree +-- INPUT: T, DiagonalAction equivariantHilbertPartial = (T, d) -> ( if not T.cache.?equivariantHilbert then ( - T.cache.equivariantHilbert = 1_(degreesRing T); + T.cache.equivariantHilbert = 1_(degreesRing T); ); currentDeg := first degree T.cache.equivariantHilbert; (M,C) := coefficients T.cache.equivariantHilbert; if (d > currentDeg) then ( - R := degreesRing T; - r := rank T; - g := numgens T; - cf := cyclicFactors T; - den := value denominator equivariantHilbertSeries T; - denDeg := first degree den; - B := last coefficients den; - if cf =!= {} then ( - CR := coefficientRing R; - phi := map(CR,R); - CRab := ZZ[Variables=>g]; - CRab = CRab / ideal apply(g,i -> CRab_i^(cf_i)-1); - psi := map(CRab,CR,toList(r:1)|(gens CRab)); - psi' := map(CR,CRab,apply(g, i-> CR_(r+i))); - (m,c) := coefficients(phi B,Variables=>apply(g, i-> CR_(r+i))); - m = psi' psi m; - B = m*c; - ); - for i from currentDeg+1 to d do ( - p := -sum(1..min(i,denDeg),k -> C_(i-k,0)*B_(k,0) ); - if cf =!= {} then ( - (m,c) = coefficients(phi p,Variables=>apply(g, i-> CR_(r+i))); - m = psi' psi m; - p = (m*c)_(0,0); - ); - M = M | matrix{{R_0^i}}; - C = C || matrix{{p}}; - ); + R := degreesRing T; + r := rank T; + g := numgens T; + cf := cyclicFactors T; + den := value denominator equivariantHilbertSeries T; + denDeg := first degree den; + B := last coefficients den; + if cf =!= {} then ( + CR := coefficientRing R; + phi := map(CR,R); + CRab := ZZ[Variables=>g]; + CRab = CRab / ideal apply(g,i -> CRab_i^(cf_i)-1); + psi := map(CRab,CR,toList(r:1)|(gens CRab)); + psi' := map(CR,CRab,apply(g, i-> CR_(r+i))); + (m,c) := coefficients(phi B,Variables=>apply(g, i-> CR_(r+i))); + m = psi' psi m; + B = m*c; + ); + for i from currentDeg+1 to d do ( + p := -sum(1..min(i,denDeg),k -> C_(i-k,0)*B_(k,0) ); + if cf =!= {} then ( + (m,c) = coefficients(phi p,Variables=>apply(g, i-> CR_(r+i))); + m = psi' psi m; + p = (m*c)_(0,0); + ); + M = M | matrix{{R_0^i}}; + C = C || matrix{{p}}; + ); ); q := first flatten entries (M_{0..d}*C^{0..d}); T.cache.equivariantHilbert = q - ) +) diff --git a/M2/Macaulay2/packages/InvariantRing/AbelianGroupsDoc.m2 b/M2/Macaulay2/packages/InvariantRing/AbelianGroupsDoc.m2 index 8d15ea325c5..b8366ae2729 100644 --- a/M2/Macaulay2/packages/InvariantRing/AbelianGroupsDoc.m2 +++ b/M2/Macaulay2/packages/InvariantRing/AbelianGroupsDoc.m2 @@ -11,15 +11,17 @@ document { Key => {diagonalAction, (diagonalAction, Matrix, PolynomialRing), + (diagonalAction, Matrix, ZZ, PolynomialRing), (diagonalAction, Matrix, List, PolynomialRing), (diagonalAction, Matrix, Matrix, List, PolynomialRing) }, Headline => "diagonal group action via weights", - Usage => "diagonalAction(W, R), diagonalAction(W, d, R), diagonalAction(W1, W2, d, R)", + Usage => "diagonalAction(W, R), diagonalAction(W, z, R), diagonalAction(W, d, R), diagonalAction(W1, W2, d, R)", Inputs => { "W" => Matrix => {"of weights of the diagonal group action"}, "W1" => Matrix => {"of weights for the torus action"}, "W2" => Matrix => {"of weights for the finite abelian action"}, + "z" => ZZ => {"of the order of an elementary abelian group"}, "d" => List => {"of orders of cyclic abelian factors in the decomposition of the diagonal group"}, "R" => PolynomialRing => {"on which the group acts"} @@ -67,24 +69,54 @@ document { }, EXAMPLE { - "R = QQ[x_1..x_4]", - "W = matrix{{0,1,-1,1},{1,0,-1,-1}}", + "R = QQ[x_1..x_4];", + "W = matrix{{0,1,-1,1},{1,0,-1,-1}};", "T = diagonalAction(W, R)" }, PARA { - "Here is an example of a product of two cyclic groups of order 3 - acting on a three-dimensional vector space:" + "Here are examples with products of cyclic groups + acting on a three-dimensional vector space. + The orders of the cyclic factors can be passed as + a list of integers or as a single integer when they are + all the same." }, EXAMPLE { - "R = QQ[x_1..x_3]", - "d = {3,3}", - "W = matrix{{1,0,1},{0,1,1}}", - "A = diagonalAction(W, d, R)", + "R = QQ[x_1..x_3];", + "d = {2,5}; z = 3;", + "W = matrix{{1,0,1},{0,1,1}};", + "A = diagonalAction(W, d, R)", + "B = diagonalAction(W, z, R)" }, - } + PARA { + "Here is an example of a diagonal action by the product of + a two-dimensional torus with a cyclic group of order 3 + acting on a two-dimensional vector space:" + }, + + EXAMPLE { + "R = QQ[x_1, x_2]", + "d = {3}", + "W1 = matrix{{1,-1}, {-1,1}}", + "W2 = matrix {{1,0}}", + "D = diagonalAction(W1, W2, d, R)" + }, + + PARA { + "Finally, a diagonal action may be constructed with a single + weights matrix obtained by vertically stacking the weights + matrix for the torus action on top of the weight matrix for + the finite abelian action. The following redefines the same + action as in the previous example." + }, + + EXAMPLE { + "W = W1 || W2", + "diagonalAction(W, d, R)" + } +} document { Key => {DiagonalAction}, @@ -103,7 +135,8 @@ document { document { Key => { equivariantHilbertSeries, - (equivariantHilbertSeries, DiagonalAction) + (equivariantHilbertSeries, DiagonalAction), + [equivariantHilbertSeries, Order] }, Headline => "equivariant Hilbert series for a diagonal action", Usage => "equivariantHilbertSeries D", @@ -122,19 +155,18 @@ document { "this function returns the equivariant Hilbert series of the coordinate ring", TEX /// $K[V]$ ///, "as a rational function of", - TEX /// $z_0, \ldots, z_{r-1}, t$ ///, + TEX /// $\zeta_0, \ldots, \zeta_{r-1}, T$ ///, "where", TEX /// $r$ ///, "is the rank of the torus. The series in", - TEX /// $t$ ///, + TEX /// $T$ ///, "which is the coefficient of", - TEX /// $z_0^0\cdots z_{r-1}^0$ ///, - "gives the ordinary Hilbert series of", - TEX /// $K[V]^T$ ///, - ". The option ", + TEX /// $\zeta_0^0\cdots \zeta_{r-1}^0$ ///, + "gives the ordinary Hilbert series of the torus-invariant subring. ", + "The option ", TT "Order => N", " can be used to compute the series up to the ", - TEX /// $t$ ///, + TEX /// $T$ ///, "-degree ", TT "N-1", "." @@ -153,7 +185,7 @@ document { "T = diagonalAction(W, R)", "equivariantHilbertSeries T", "S = equivariantHilbertSeries(T, Order => 7)", - "sub(S, {z_0 => 0, z_1 => 0})" + "sub(S, {ζ_0 => 0, ζ_1 => 0})" }, } diff --git a/M2/Macaulay2/packages/InvariantRing/FiniteGroups.m2 b/M2/Macaulay2/packages/InvariantRing/FiniteGroups.m2 index 381bd4de369..34727427b8d 100644 --- a/M2/Macaulay2/packages/InvariantRing/FiniteGroups.m2 +++ b/M2/Macaulay2/packages/InvariantRing/FiniteGroups.m2 @@ -46,29 +46,26 @@ numgens FiniteGroupAction := ZZ => G -> G.numgens ------------------------------------------- -isAbelian = method() +isAbelian = method(Options => true) -isAbelian FiniteGroupAction := { } >> opts -> (cacheValue (symbol isAbelian)) (G -> runHooks(FiniteGroupAction, symbol isAbelian, G) ) +isAbelian FiniteGroupAction := { } >> opts -> (cacheValue (symbol isAbelian)) ( + G -> runHooks((isAbelian, FiniteGroupAction), G) ) -addHook(FiniteGroupAction, symbol isAbelian, G -> break ( +addHook((isAbelian, FiniteGroupAction), G -> ( X := G.generators; n := #X; - if n == 1 then( - true - ) - else( - all(n - 1, i -> all(n - 1 - i, j -> (X#j)*(X#(n - 1 - i)) == (X#(n - 1 - i))*(X#j) ) ) - ) - )) + if n == 1 then true + else all(n - 1, i -> all(n - 1 - i, j -> (X#j)*(X#(n - 1 - i)) == (X#(n - 1 - i))*(X#j) ) ) + )) -generateGroup = method() +generateGroup = method(Options => true) -generateGroup FiniteGroupAction := { } >> opts -> (cacheValue (symbol generateGroup)) (G -> runHooks(FiniteGroupAction, symbol generateGroup, G) ) +generateGroup FiniteGroupAction := {} >> opts -> (cacheValue (symbol generateGroup)) (G -> runHooks((generateGroup, FiniteGroupAction), G) ) -addHook(FiniteGroupAction, symbol generateGroup, G -> break ( +addHook((generateGroup, FiniteGroupAction), G -> ( m := numgens G; n := dim G; K := coefficientRing ring G; @@ -111,40 +108,37 @@ addHook(FiniteGroupAction, symbol generateGroup, G -> break ( ------------------------------------------- -schreierGraph = method() +schreierGraph = method(Options => true) -schreierGraph FiniteGroupAction := { } >> opts -> (cacheValue (symbol schreierGraph)) (G -> runHooks(FiniteGroupAction, symbol schreierGraph, G) ) +schreierGraph FiniteGroupAction := {} >> opts -> (cacheValue (symbol schreierGraph)) (G -> runHooks((schreierGraph, FiniteGroupAction), G) ) -addHook(FiniteGroupAction, symbol schreierGraph, - G -> break (generateGroup G)_0 - ) +addHook((schreierGraph, FiniteGroupAction), G -> (generateGroup G)_0 ) ------------------------------------------- -group = method() +group = method(Options => true) -group FiniteGroupAction := { } >> opts -> (cacheValue (symbol group)) (G -> runHooks(FiniteGroupAction, symbol group, G) ) +group FiniteGroupAction := { } >> opts -> (cacheValue (symbol group)) (G -> runHooks((group, FiniteGroupAction), G) ) + +addHook((group, FiniteGroupAction), G -> keys first schreierGraph G ) -addHook(FiniteGroupAction, symbol group, - G -> break keys first schreierGraph G - ) ------------------------------------------- -words = method() +words = method(Options => true) -words FiniteGroupAction := { } >> opts -> (cacheValue (symbol words)) (G -> runHooks(FiniteGroupAction, symbol words, G) ) +words FiniteGroupAction := { } >> opts -> (cacheValue (symbol words)) (G -> runHooks((words, FiniteGroupAction), G) ) + +addHook((words, FiniteGroupAction), G -> applyValues((generateGroup G)_1, val -> first val) ) -addHook(FiniteGroupAction, symbol words, - G -> break applyValues((generateGroup G)_1, val -> first val) - ) ------------------------------------------- -relations FiniteGroupAction := { } >> opts -> (cacheValue (symbol relations)) (G -> runHooks(FiniteGroupAction, symbol relations, G) ) +relations FiniteGroupAction := { } >> opts -> (cacheValue (symbol relations)) ( + G -> runHooks((relations, FiniteGroupAction), G) ) -addHook(FiniteGroupAction, symbol relations, G -> break ( +addHook((relations, FiniteGroupAction), G -> ( relators := values last generateGroup G; W := apply(relators, r -> first r); relators = flatten apply(#W, i -> apply(drop(relators#i, 1), a -> {W#i,a} ) ); @@ -159,57 +153,43 @@ addHook(FiniteGroupAction, symbol relations, G -> break ( unique relators )) + ------------------------------------------- -permutationMatrix = method() +permutationMatrix = method(Options => {EntryMode => "one-line"}) -permutationMatrix String := Matrix => s -> ( - n := #s; - p := apply(n, i -> ( - v := value(s#i); - if v <= 0 or v > n then ( - error "permutationMatrix: Expected a string of positive integers - representing a permutation." - ) - else v - ) - ); - if #(unique p) =!= n then ( - error "permutationMatrix: Expected a string of distinct integers." - ); - matrix apply(n, i -> - apply(n, j -> if p#j - 1 == i then 1 else 0) +permutationMatrix Array := Matrix => opts -> p -> ( + if opts.EntryMode == "cycle" then permutationMatrix(max p, p) + else ( + n := max p; + if #p =!= n or set (1..n) =!= set p then ( + error "permutationMatrix: Expected a sequence of positive integers + representing a permutation." + ); + matrix apply(n, i -> apply(n, j -> if p#j - 1 == i then 1 else 0) ) ) ) -permutationMatrix (ZZ, Array) := Matrix => (n, c) -> permutationMatrix(n, {c}) - -permutationMatrix (ZZ, List) := Matrix => (n, p) -> ( - if n <= 0 then (error "permutationMatrix: Expected the first input to be a positive integer."); - if any(p, c -> not instance(c, Array) or any(c, i -> i <= 0 or i > n)) then ( - error "permutationMatrix: Expected the second input to be a list of arrays - with integer entries between 1 and the first input." +permutationMatrix (ZZ, Array) := Matrix => opts -> (n, c) -> ( + if n <= 0 then error "permutationMatrix: Expected a positive integer."; + if #c == 0 then error "permutationMatrix: Expected a nonempty array,"; + if #(set c) =!= #c or not isSubset(set c, set(1..n)) then ( + error "permutationMatrix: Expected an array of distinct integers + between 1 and the first input." ); - if any(p, c -> #(unique toList c) =!= #c) then (error "permutationMatrix: Expected each sequence in - the list to have distinct entries."); - s := new MutableHashTable from apply(n, i -> i + 1 => i + 1); - scan(p, c -> ( - k := #c; - u := hashTable pairs s; - scan(k, j -> ( - if j < k - 1 then s#(c_j) = u#(c_(j+1)) - else s#(c_j) = u#(c_0) - ) - ) + permutationMatrix new Array from apply(n, i -> + if (set c)#?(i + 1) then ( + k := position(c, j -> j == i + 1); + if k == #c - 1 then c#0 else c#(k + 1) ) - ); - s = horizontalJoin apply(values s, i -> toString i); - permutationMatrix toString s - ) - -permutationMatrix Array := Matrix => c -> permutationMatrix(max c, c) + else i + 1 + ) + ) + +permutationMatrix (ZZ, List) := Matrix => opts -> (n, p) -> product apply(p, c -> permutationMatrix(n, c) ) -permutationMatrix List := Matrix => p -> permutationMatrix(max (p/max), p) +permutationMatrix List := Matrix => opts -> p -> permutationMatrix(max (p/max), p) + diff --git a/M2/Macaulay2/packages/InvariantRing/FiniteGroupsDoc.m2 b/M2/Macaulay2/packages/InvariantRing/FiniteGroupsDoc.m2 index 3b1779337a9..ec4eb87a95a 100644 --- a/M2/Macaulay2/packages/InvariantRing/FiniteGroupsDoc.m2 +++ b/M2/Macaulay2/packages/InvariantRing/FiniteGroupsDoc.m2 @@ -47,7 +47,7 @@ document { }, EXAMPLE { - "P = permutationMatrix toString 231", + "P = permutationMatrix [2, 3, 1]", "C3 = finiteAction(P, R)", }, } @@ -72,7 +72,8 @@ document { } document { - Key => {(generators, FiniteGroupAction)}, + Key => {(generators, FiniteGroupAction), + [generators, CoefficientRing]}, Headline => "generators of a finite group", Usage => "generators G", Inputs => { @@ -297,21 +298,22 @@ document { document { - Key => {permutationMatrix, - (permutationMatrix, String), + Key => {permutationMatrix, + (permutationMatrix, Array), (permutationMatrix, ZZ, Array), (permutationMatrix, ZZ, List), - (permutationMatrix, Array), - (permutationMatrix, List) + (permutationMatrix, List), + [permutationMatrix, EntryMode] }, Headline => "convert a one-line notation or cyclic notation of a permutation to a matrix representation", - Usage => "permutationMatrix s, permutationMatrix(n , c)", + Usage => "permutationMatrix c, \n permutationMatrix(n , c), \n permutationMatrix(n, p), \n permutationMatrix p", Inputs => { - "s" => String => {"an array or a list of arrays giving a one-line notation or cyclic notation of a permutation"}, - "n" => ZZ => {"giving the number of integers getting permuted"}, - "c" => List => {"of arrays giving a cyclic notation of a permutation"} + "c" => Array => {"of positive integers representing a permutation in one-line notation or + representing a cyclic permutation"}, + "p" => List => {"of arrays representing a permutation as a product of cycles"}, + "n" => ZZ => {"giving the number of integers being permuted"}, }, Outputs => { Matrix => {"the matrix representation of the permutation"} @@ -324,25 +326,34 @@ document { }, EXAMPLE { - "M = permutationMatrix toString 213", + "M = permutationMatrix [2, 1, 3]", }, PARA { - "The following example converts the cyclic notation of the same transposition into a matrix representation. Without ",TT "n"," the function assumes ",TT "n"," is the largest integer that appears in your array or list of arrays." + "The following example converts the cyclic notation of the same transposition into a matrix representation." }, EXAMPLE { "M = permutationMatrix(3,[1,2])", - "M = permutationMatrix [1,2]", }, + + PARA { + "If ",TT "n"," is the largest integer that appears in your array, the value of ", TT "n", " can be omitted by + using the option ", TT "EntryMode => \"cycle\"", "." + }, + + EXAMPLE { + "M = permutationMatrix([1,2], EntryMode => \"cycle\")", + }, + PARA { "The following example converts the cyclic notation of a permutation of 4 into a matrix representation." }, EXAMPLE { "M = permutationMatrix(4,{[1,2],[3,4]})", - "M = permutationMatrix {[1,2],[3,4]}", }, } + document { Key => {(relations, FiniteGroupAction)}, Headline => "relations of a finite group", @@ -357,7 +368,7 @@ document { "This function is provided by the package ", TO InvariantRing,". ", PARA { - "Use this function to get the relations among elements of a group. Each element is represented by a word of minimal length in the Coxeter generators. And each relation is represented by a list of two words that equates the group element represented by those two words." + "Use this function to get the relations among elements of a group. Each element is represented by a word of minimal length in the Coxter generators. And each relation is represented by a list of two words that equates the group element represented by those two words." }, PARA { "The following example defines the permutation action diff --git a/M2/Macaulay2/packages/InvariantRing/HawesDoc.m2 b/M2/Macaulay2/packages/InvariantRing/HawesDoc.m2 index 644d9e7cbd6..e78fbc1b206 100644 --- a/M2/Macaulay2/packages/InvariantRing/HawesDoc.m2 +++ b/M2/Macaulay2/packages/InvariantRing/HawesDoc.m2 @@ -37,8 +37,8 @@ document { TT "({f", SUB TT "1", TT ",...," , TT "f", SUB TT "n", TT "}, {g", SUB TT "1", TT ",...," , TT "g", SUB TT "r", TT"})", " of primary and secondary invariants such that ", - TT "R", SUP TT "G", TT "=A", TT "g", SUB TT "1", TEX "\\oplus", - TT "...", TEX "\\oplus", TT "A", TT "g", SUB TT "r", ", where ", TT "A=K[", + TT "R", SUP TT "G", TT "=A", TT "g", SUB TT "1", "$\\oplus$", + TT "...", "$\\oplus$", TT "A", TT "g", SUB TT "r", ", where ", TT "A=K[", TT "f", SUB TT "1", TT ",...," , TT "f", SUB TT "n", TT "]", " and ", TT "K", " is the field of coefficients of ", TT "R", "." @@ -63,7 +63,7 @@ document { }, PARA{ "From the output one sees that ", TT "QQ[x,y]", SUP(TT "C4"), - TT "=QQ[f", SUB(TT "1"), TT "f", SUB(TT "2"), TT "]", TEX "\\oplus", TT "QQ[f", + TT "=QQ[f", SUB(TT "1"), TT "f", SUB(TT "2"), TT "]", "$\\oplus$", TT "QQ[f", SUB(TT "1"), TT "f", SUB(TT "2"), TT "](x", SUP(TT "4"), TT "+y", SUP(TT "4"), TT ")", ", where ", TT "f", SUB(TT "1"), TT "=x", SUP(TT "2"), TT "+y", SUP(TT "2"), " and ",TT "f", SUB(TT "2"), TT "=xy", diff --git a/M2/Macaulay2/packages/InvariantRing/InvariantRingDoc.m2 b/M2/Macaulay2/packages/InvariantRing/InvariantRingDoc.m2 index 7282278ee12..b59ff1a880e 100644 --- a/M2/Macaulay2/packages/InvariantRing/InvariantRingDoc.m2 +++ b/M2/Macaulay2/packages/InvariantRing/InvariantRingDoc.m2 @@ -32,6 +32,10 @@ document { HREF{"https://deepblue.lib.umich.edu/handle/2027.42/151589","Ideals of Subspace Arrangements"}, ". Thesis (Ph.D.)-University of Michigan. 2019. ISBN: 978-1392-76291-2. pp 29-34." }, + {"A new algorithm for invariants of elementary abelian groups by Arasha, Cassell, Dolorfino, Gandini, Novak, Qin, and Strom. See ", + HREF{"https://github.com/gordonovak/algorithms"}, + " for more information." + }, {"King's algorithm and the linear algebra method for invariants of finite groups: ", "Derksen, H. & Kemper, G. (2015). ", HREF{"https://link.springer.com/book/10.1007%2F978-3-662-48422-7","Computational Invariant Theory"}, @@ -66,8 +70,30 @@ document { auxiliary file Hawes.m2 (with documentation in the file HawesDoc.m2) and has been updated to work with the new types." - } - } + }, + {BOLD "2.1: ", "speeds up checking whether a polynomial + is invariant under a finite group action, and + fixes a warning that appeared when computing invariants + of finite groups (the authors thank N. Iammarino, T. Yu, + and Q. Zhao for the fix)." + }, + {BOLD "2.2: ", "changed input of ", TO permutationMatrix, + ", minor documentation and internal code changes." + }, + {BOLD "2.3: ", "minor documentation updates."}, + {BOLD "2.4: ", "a new algorithm for invariants of elementary + abelian $p$-groups."} + }, + Contributors=>{ + "The following people worked on the algorithm for invariants of elementary + abelian $p$-groups introduced in version 2.4: + Sasha Arasha, + Marcus Cassell, + Mal Dolorfino, + Gordie Novak, + Daniel Qin, + and Sumner Strom." + }, } document { diff --git a/M2/Macaulay2/packages/InvariantRing/Invariants.m2 b/M2/Macaulay2/packages/InvariantRing/Invariants.m2 index c2016b87b94..120cb227b78 100644 --- a/M2/Macaulay2/packages/InvariantRing/Invariants.m2 +++ b/M2/Macaulay2/packages/InvariantRing/Invariants.m2 @@ -14,9 +14,9 @@ RingOfInvariants = new Type of HashTable invariantRing = method(Options => { - Strategy => UseNormaliz, - UseLinearAlgebra => false, + Strategy => "Default", UseCoefficientRing => false, + UsePolyhedra => false, DegreeBound => infinity }) @@ -112,120 +112,329 @@ reynoldsOperator (RingElement, DiagonalAction) := RingElement => (f, D) -> sum s ------------------------------------------- +------------------------------------------- +--- NEW April 20th, 2026 ------------------ +--- Elementary Invariants Methods --------- +------------------------------------------- + + +-->-- Function: seedMinimal --<-- +-- Checks if a given candidate is minimal given the list of seeds, starting at the index, "startIndex" +--> INPUT: +-- Seeds : List[List[ZZ]] › A list of current seeds that are invariant +-- Candidate : List[ZZ] › A seed that may be added to the Seeds list +-- startIndex : ZZ › The index in the Seeds list that you want to start minimizing from +--> OUTPUT: +-- Returns ({-1} | candidate) if our candidate is minimal. +-- Returns {-2} if our candidate is not minimal. +-- Returns ({-3, index}) if a seed is not minimal, with the index of the seed. +-- Returns ({-4, index} | newSeed) if our candidate helps reduce a seed. +seedMinimal := (Seeds, candidate, startIndex) -> ( + i := startIndex; + numSeeds := #Seeds; + while i < numSeeds do ( -- Iterate through all seeds + candMinimal := true; -- We start by assuming our candidate & seed are divisible by each other + seedIsMinimal := true; + + for k from 0 to #candidate - 1 do ( + if (candidate#k > Seeds#i#k) then ( + candMinimal = false; -- If our candidate ever has a greater power than the seed, its not minimal + ) + else if (Seeds#i#k > candidate#k) then ( + seedIsMinimal = false; -- If our seed ever has a greater power than the candidate, our candidate is safe (for now) + ); + ); + if seedIsMinimal then ( -- If our seed is exclusively less than our candidate, we: + newCandidate := candidate - Seeds#i; -- Reduce our candidate by our seed, as that will still be invariant + if (newCandidate === candidate) then ( + i = i + 1; -- No change: move forward to avoid infinite loop + ) + else ( + candidate = newCandidate; + if (all (candidate, i -> i == 0)) then return {-2, 0}; -- If the candidate fully reduces, we return -2. + i = startIndex; -- Reduce our index by restarting so we can test candidate again. + ); + ) + else if candMinimal then ( -- If our candidate is minimal, we may need to discard our seed instead. + removalSeed := Seeds#i; + while (all(removalSeed - candidate, i -> i >= 0) and removalSeed =!= removalSeed - candidate) do ( + removalSeed = removalSeed - candidate; + ); + if (all (removalSeed, i -> i == 0)) then return {-3, i}; -- If the seed fully reduces, we return -3 + return {-4, i} | removalSeed; -- Otherwise, we return -3, the index of the seed, and the updated seed. + ) + else ( + i = i + 1; -- Normal case: move forward + ); + ); + return {-1, 0} | candidate ; +) + +-->--elementaryInvariants function--<-- +--> INPUT: D (a diagonalAction) +--> OUTPUT: L (a list of invariants) +elementaryInvariants := D -> ( + --------------------- + -- Seed Generation -- + --------------------- + + -->- Grab our variables W, R, Z from D -<-- + W := D.weights_1; + + R := ring D; + Z := (D.cyclicFactors)#0; + + -->- Find our m and n from the weight matrix -<-- + n := numColumns W; m := numRows W; + + -->- STEP 1 -<-- + -->- Now, we find a n x n submatrix of W with nonzero determinant --<- + nonZeroSM := matrix{{0}}; -- Start with an empty submatrix (SM stands for submatrix) + colList := {}; -- This empty list will track the columns we don't use for the submatrix + for i from 0 to (n - m) do ( -- Iterate from 0 to n - m (we don't want our matrix out of bounds) + candidateSM := submatrix(W, toList(i .. i+m-1)); + if (determinant candidateSM != 0) then ( + nonZeroSM = candidateSM; -- If candidateSM has nonzero determinant, it is now our nonZero det submatrix + colList = toList(0 .. i-1) | toList(i+m .. n-1); -- Grabs the columns we didn't use. + break; -- ends the loop + ) + ); + + -->- Return error if this submatrix doesn't exist --<-- + -- Fred G: April 26, 2027; currently the Elementary strategy can only be called + -- if the weight matrix has maximal rank, which guarantees the existence of an + -- n x n submatrix with nonzero determinant, so this error is never triggered + -- I chose to add the maximal rank condition because the default strategy + -- Derksen-Gandini returns a result even when the matrix does not have maximal + -- rank, so this error created inconsistent outputs + -- Francesca suggests row-reducing the matrix then removing zero rows to ensure + -- we always have a matrix with maximal rank; this is okay mathematically because + -- the invariant rings are isomorphic, but technically it changes the action. + -- There is also the issue that row-reducing requires moving to QQ instead of ZZ + -- which may introduce denominators, so we would have to deal with that. + -- All of this could be considered for a later update. + if (nonZeroSM == matrix{{0}}) then ( + error ("Non-zero submatrix of this weight matrix could not be found.\n"); + return {}; + ); + + -->- STEP 2 -<-- + seedList := {}; -- Creates a list for the seed invariants in exponent vec form + + for v in colList do ( -- Iterates through all columns we didn't use for nonZeroSM + seedInvariant := {}; -- Current seed invariant we are calculating + seedMatrix := nonZeroSM | matrix(W_v); -- Matrix we extract the seed invariant from (where W_v is our additional vector) + signFlip := 1; + for i from 0 to m do ( -- This loops lets us remove one of the columns from the matrix to calculate the plücker + pluckerMatrix := submatrix(seedMatrix, toList(0 .. i -1) | toList (i + 1 .. m)); -- Find plucker matrix + e := for j from 0 to n-1 list (if j == i then 1 else 0); -- Standard basis vector + seedInvariant = seedInvariant | {signFlip * determinant(pluckerMatrix) * e}; -- Calculate vector + signFlip = signFlip * -1; -- Flip the sign after each iteration. + ); + seedList = seedList | {sum seedInvariant} -- Adds the summed seed invariant vec to our list + ); + + -- Now, seedList contains our list of seed Invariants, so we move onto expansion. + + -------------------- + -- Seed Expansion -- + -------------------- + ringVars := gens R; -- So we don't need to call "gens" each time we need the variables of the ring + seedList = for l in seedList list apply(l, x -> ((x % Z) + Z) % Z); -- Mods our seeds out by Z + trashList := {0} | seedList; -- List to keep track of duplicate invariants + purePowers := apply(#ringVars, i -> 0); -- List to keep track of pure powers. + powerIndex := null; -- added by FG to fix unexported symbol error + + + --> Starting with seed expansion <-- + -- Note that the "drop" function is used in combination with the seedminimal function in this loop. + -- This is because seed minimal appends a "result" and "index" value to the beginning of a seed. + -- Thus by saying drop(candidate, 2), we get rid of those information values. + for s when s < #seedList do ( -- We use a "when" loop here because size of newList will change + startingSeed := seedList#s; + for k to #seedList - 1 do ( -- We can use a static loop here because we won't add any elements in here. + for p from 1 to (Z - 1) do ( + candidateSeed := (seedList#k) * p; -- Put our seed to the power of p. + candidateSeed = (startingSeed + candidateSeed) % Z; -- Multiply two seeds & mod out by Z. + if (not all(candidateSeed, i -> i == 0) and not any(trashList, t -> (candidateSeed == {t}))) then ( + minimality := seedMinimal(seedList, candidateSeed, 0); + result := minimality#0; + -- If result = -3 or -4, that means one of our seeds was not minimal given our candidate + while (result == -3 or result == -4) do ( -- So we must loop to sort out the seeds and get our candidate & seeds minimized + editIndex := minimality#1; -- minimality#2 holds the index of the seed we need to adjust. + if (result == -3) then ( -- {-3} -> Our seed is not minimal, so we must remove it. + seedList = take(seedList, editIndex) | drop(seedList, editIndex+1); + ) + else if (result == -4) then ( -- {-4} -> We found a reduction for our seed, so we must replace the old one. + newSeed := drop(minimality, 2); + seedList = replace(editIndex, newSeed, seedList); -- Replace our old seed with the new one. + if (number(newSeed, e -> e != 0) == 1) then ( -- Check if our seed is a pure power of some kind. + powerIndex = position(newSeed, e -> e != 0); + purePowers = replace(powerIndex, newSeed#powerIndex, purePowers); + ); + + ); + --> Then we call our seedMinimal function again, this time starting from the editIndex to save time. + minimality = seedMinimal(seedList, candidateSeed, editIndex+1); + result = minimality#0; + ); + + if (result == -1) then ( -- If our candidate is minimal, we add it to the seed list. + newCandSeed := drop(minimality, 2); + candidateSeed = newCandSeed; + if (number(newCandSeed, e -> e != 0) == 1) then ( -- check if seed is pure power of some kind + powerIndex = position(newCandSeed, e -> e != 0); + if (powerIndex =!= null and powerIndex < #purePowers) then ( + purePowers = replace(powerIndex, newCandSeed#powerIndex, purePowers); + ); + ); + seedList = append(seedList, newCandSeed); -- Add our seed to the list. + ); + -- If our result is -2, we do nothing because our candidate is bunk. + ); + trashList = append(trashList, candidateSeed) -- Always add our candidate to the trashList for efficiency. + ); + ); + ); + + --> Then we add the pure powers to the list, checking if they are minimal via. our purePowers list. + for i from 0 to (#ringVars - 1) do ( + if (Z % (purePowers#i) != 0 ) then ( + seedList = seedList | {for k to (#ringVars - 1) list (if i == k then Z else 0)}; + ); + ); + + -->-- Now, we turn each of the exponent vectors into their polynomials in the ring. --<-- + polyList := {}; + for i in seedList do ( + n := 1; + for j to #i - 1 do (n = n * (((ringVars)#j)^(i#j))); + polyList = polyList | {n}; + ); + + return polyList; -- Return our list +) + +------------------------------------------- +--- invariants for DiagonalAction --------- +------------------------------------------- + invariants = method(Options => { - Strategy => UseNormaliz, - UseLinearAlgebra => false, + Strategy => "Default", UseCoefficientRing => false, + UsePolyhedra => false, DegreeBound => infinity, DegreeLimit => {}, SubringLimit => infinity - }) + } +) invariants DiagonalAction := List => o -> D -> ( + d := cyclicFactors D; (W1, W2) := weights D; + -- As of April 2026, the elementary generation method is when + -- called by the user with Strategy=>"Elementary", as long as: + -- i) there is no torus action: zero(W1) + -- ii) there are cyclic factors: d =!= {} + -- iii) all cyclic factors have the same order: all(d, i -> d#0 == i) + -- iv) the weight matrix has maximal rank: rank W2 == min(numRows W2,numColumns W2) + if (o.Strategy === "Elementary") and + zero(W1) and d =!= {} and all(d, i -> d#0 == i) + and rank W2 == min(numRows W2,numColumns W2) + then ( + return elementaryInvariants D; + ); + --*-* Otherwise, continue with Derksen-Gandini algorithm *-*-- R := ring D; kk := coefficientRing R; p := char kk; - d := cyclicFactors D; r := rank D; if p > 0 and o.UseCoefficientRing then ( - q := kk#order; - if any(d, j -> q%j =!= 1) then ( - print "-- Diagonal action is not defined over the given coefficient ring. \n-- Returning invariants over an infinite extension field over which the action is defined." - ) - else ( - D' := diagonalAction(W1||W2, apply(r, i -> q - 1)|d, R); - return invariants D' - ) - ); + q := kk#order; + if any(d, j -> q%j =!= 1) then ( + print "-- Diagonal action is not defined over the given coefficient ring. \n-- Returning invariants over an infinite extension field over which the action is defined."; + ) + else ( + D' := diagonalAction(W1||W2, apply(r, i -> q - 1)|d, R); + return invariants D'; + ) + ); R = kk[R_*, MonomialOrder => GLex]; g := numgens D; n := dim D; mons := R_*; local C, local S, local U; local v, local m, local v', local u; - if g > 0 then ( - t := product d; - - reduceWeight := w -> vector apply(g, i -> w_i%d#i); - - C = apply(n, i -> reduceWeight W2_i); - - S = new MutableHashTable from apply(C, w -> w => {}); - scan(#mons, i -> S#(reduceWeight W2_i) = S#(reduceWeight W2_i)|{mons#i}); - U = R_*; - - while #U > 0 do( - m = min U; - v = first exponents m; - k := max positions(v, i -> i > 0); - v = reduceWeight(W2*(vector v)); - - while k < n do( - u = m*R_k; - v' = reduceWeight(v + W2_k); - if (not S#?v') then S#v' = {}; - if all(S#v', m' -> u%m' =!= 0_R) then ( - S#v' = S#v'|{u}; - if first degree u < t then U = U | {u} - ); - k = k + 1; - ); - U = delete(m, U); - ); - if S#?(0_(ZZ^g)) then mons = S#(0_(ZZ^g)) else mons = {} - ); + t := product d; + reduceWeight := w -> vector apply(g, i -> w_i%d#i); + C = apply(n, i -> reduceWeight W2_i); + S = new MutableHashTable from apply(C, w -> w => {}); + scan(#mons, i -> S#(reduceWeight W2_i) = S#(reduceWeight W2_i)|{mons#i}); + U = R_*; + while #U > 0 do( + m = min U; + v = first exponents m; + k := max positions(v, i -> i > 0); + v = reduceWeight(W2*(vector v)); + while k < n do( + u = m*R_k; + v' = reduceWeight(v + W2_k); + if (not S#?v') then S#v' = {}; + if all(S#v', m' -> u%m' =!= 0_R) then ( + S#v' = S#v'|{u}; + if first degree u < t then U = U | {u} + ); + k = k + 1; + ); + U = delete(m, U); + ); + if S#?(0_(ZZ^g)) then mons = S#(0_(ZZ^g)) else mons = {} + ); if r == 0 then return apply(mons, m -> sub(m, ring D) ); - W1 = W1*(transpose matrix (mons/exponents/first)); - if o.Strategy == UsePolyhedra then ( - if r == 1 then C = convexHull W1 else C = convexHull( 2*r*W1|(-2*r*W1) ); - C = (latticePoints C)/vector; - ) - else if o.Strategy == UseNormaliz then ( - if r == 1 then C = (normaliz(transpose W1, "polytope"))#"gen" - else C = (normaliz(transpose (2*r*W1|(-2*r*W1)), "polytope"))#"gen"; - C = transpose C_(apply(r, i -> i)); - C = apply(numColumns C, j -> C_j) - ); - + if o.UsePolyhedra then ( + if r == 1 then C = convexHull W1 else C = convexHull( 2*r*W1|(-2*r*W1) ); + C = (latticePoints C)/vector; + ) + else ( + if r == 1 then C = (normaliz(transpose W1, "polytope"))#"gen" + else C = (normaliz(transpose (2*r*W1|(-2*r*W1)), "polytope"))#"gen"; + C = transpose C_(apply(r, i -> i)); + C = apply(numColumns C, j -> C_j) + ); S = new MutableHashTable from apply(C, w -> w => {}); scan(#mons, i -> S#(W1_i) = S#(W1_i)|{mons#i}); U = new MutableHashTable from S; - nonemptyU := select(keys U, w -> #(U#w) > 0); - while #nonemptyU > 0 do( - v = first nonemptyU; - m = first (U#v); - - scan(#mons, i -> ( - u := m*mons#i; - v' := v + W1_i; - if ((U#?v') and all(S#v', m' -> ( - if u%m' =!= 0_R then true - else if g > 0 then ( - m'' := u//m'; - v'' := reduceWeight(W2*(vector first exponents m'')); - v'' =!= 0_(ZZ^g) - ) - else false - ) - ) - ) - then( - S#v' = S#v'|{u}; - U#v' = U#v'|{u}; - ) - ) - ); - U#v = delete(m, U#v); - nonemptyU = select(keys U, w -> #(U#w) > 0) - ); + while #nonemptyU > 0 do( + v = first nonemptyU; + m = first (U#v); + + scan(#mons, i -> ( + u := m*mons#i; + v' := v + W1_i; + if ((U#?v') and all(S#v', m' -> ( + if u%m' =!= 0_R then true + else if g > 0 then ( + m'' := u//m'; + v'' := reduceWeight(W2*(vector first exponents m'')); + v'' =!= 0_(ZZ^g) + ) + else false + ))) then ( + S#v' = S#v'|{u}; + U#v' = U#v'|{u}; + ) + )); + U#v = delete(m, U#v); + nonemptyU = select(keys U, w -> #(U#w) > 0) + ); if S#?(0_(ZZ^r)) then mons = S#(0_(ZZ^r)) else mons = {}; return apply(mons, m -> sub(m, ring D) ) - ) + +) ------------------------------------------- @@ -246,7 +455,7 @@ manualTrim (List) := List => L -> ( ------------------------------------------- -- Computes an *additive* basis for the degree d part of the -- invariant ring following Algorithm 4.5.1 of Derksen-Kemper. -invariants (LinearlyReductiveAction, List) := List => o -> (V,d) -> ( +invariants (LinearlyReductiveAction, List) := List => o -> (V, d) -> ( M := actionMatrix V; Q := ring V; A := groupIdeal V; @@ -323,12 +532,13 @@ invariants FiniteGroupAction := List => o -> G -> ( error "Only implemented for standard graded polynomial rings"; if o.DegreeBound < b then b = o.DegreeBound; local M; - for d from 1 to b do ( + for d from 1 to b+1 do ( Gb := gb(promote(ideal S,R),DegreeLimit=>d); I := monomialIdeal leadTerm Gb; M = reverse select(flatten entries (basis(d,R)%I),m->not zero m); + if d == b+1 then break; if M === {} then break else ( - if o.UseLinearAlgebra then ( + if o.Strategy == "LinearAlgebra" then ( for f in invariants(G,d) do ( g := f % Gb; if not zero g then ( @@ -336,7 +546,8 @@ invariants FiniteGroupAction := List => o -> G -> ( Gb = forceGB ( (gens Gb) | matrix{{g}} ); ); ); - ) else ( + ) + else ( for m in M do ( f := reynoldsOperator(m,G); g := f % Gb; @@ -348,7 +559,7 @@ invariants FiniteGroupAction := List => o -> G -> ( ); ); ); - if M =!= {} then print" + if (M =!= {} and b < #(group G)) then print" Warning: stopping condition not met! Output may not generate the entire ring of invariants. Increase value of DegreeBound. @@ -382,7 +593,11 @@ invariants(FiniteGroupAction, ZZ) := List => o -> (G,d) -> ( isInvariant = method() -isInvariant (RingElement, FiniteGroupAction) := Boolean => (f, G) -> reynoldsOperator(f, G) == f +--checking invariants with reynolds operator can be slow for large groups +--isInvariant (RingElement, FiniteGroupAction) := Boolean => (f, G) -> reynoldsOperator(f, G) == f +--instead it is enough to check invariance under group generators +isInvariant (RingElement, FiniteGroupAction) := Boolean => (f, G) -> + all(gens G, g -> sub(f, (vars ring G)*(transpose g) ) == f ) isInvariant (RingElement, DiagonalAction) := Boolean => (f, D) -> ( if not instance(f, ring D) then ( diff --git a/M2/Macaulay2/packages/InvariantRing/InvariantsDoc.m2 b/M2/Macaulay2/packages/InvariantRing/InvariantsDoc.m2 index 169fea86872..e9b134f4e2e 100644 --- a/M2/Macaulay2/packages/InvariantRing/InvariantsDoc.m2 +++ b/M2/Macaulay2/packages/InvariantRing/InvariantsDoc.m2 @@ -81,7 +81,6 @@ document { Inputs => { "G" => GroupAction, "R" => PolynomialRing => {"on which the group acts"}, - Strategy => {"the strategy used to compute the invariant ring"} }, Outputs => { RingOfInvariants => {"the ring of invariants of the given group action"} @@ -137,7 +136,6 @@ document { Inputs => { "G" => GroupAction => {"a specific type of group action on a polynomial ring"}, - Strategy => {"the strategy used to compute diagonal invariants, options are UsePolyhedra or UseNormaliz."} }, Outputs => { "L" => List => {"a minimal set of generating invariants for the group action"} @@ -171,7 +169,7 @@ document { document { Key => { - (invariants, DiagonalAction) + (invariants, DiagonalAction) }, Headline => "computes the generating invariants of a group action", @@ -180,7 +178,6 @@ document { Inputs => { "D" => DiagonalAction => {"a diagonal action on a polynomial ring"}, - Strategy => {"the strategy used to compute diagonal invariants, options are UsePolyhedra or UseNormaliz."} }, Outputs => { "L" => List => {"a minimal set of generating invariants for the group action"} @@ -243,6 +240,15 @@ document { ". Thesis (Ph.D.)-University of Michigan. 2019. ISBN: 978-1392-76291-2. pp 29-34."} }, + PARA { + "Version 2.4 includes a new algorithm to compute invariants + of elementary abelian $p$-groups. For more information, see:" + }, + + UL { + {HREF{"https://github.com/gordonovak/algorithms"}} + }, + PARA { "Here is an example of a one-dimensional torus acting on a two-dimensional vector space:" @@ -267,6 +273,15 @@ document { "A = diagonalAction(W, d, R)", "invariants A" }, + + PARA { + "To call the new algorithm for elementary abelian $p$-groups + use the option ", TT "Strategy=>\"Elementary\"" , "." + }, + + EXAMPLE { + "invariants(A,Strategy=>\"Elementary\")" + }, PARA { "Here is an example of a diagonal action by the product of @@ -289,6 +304,55 @@ document { isInvariant } } + +document { + Key => { + [invariants, Strategy], [invariantRing, Strategy] + }, + Headline => "choose the strategy for computing invariants", + + PARA { + "The default strategy for computing invariants of a finite + group action uses the Reynolds operator, however + this may be slow for large groups. Using the option ", + TT "Strategy => \"LinearAlgebra\"", " uses the linear algebra + method for computing invariants of a given degree by calling ", + TO (invariants, FiniteGroupAction, ZZ), ". This may + provide a speedup at lower degrees, especially if the + user-provided generating set for the group is small." + }, + + PARA { + "The following example computes the invariants of the + symmetric group on 4 elements. Note that using + different strategies may lead to different sets of + generating invariants." + }, + + EXAMPLE { + "R = QQ[x_1..x_4]", + "L = apply({[2, 1, 3, 4], [2, 3, 4, 1]}, permutationMatrix);", + "S4 = finiteAction(L, R)", + "elapsedTime invariants S4", + "elapsedTime invariants(S4, Strategy => \"LinearAlgebra\")" + }, + + PARA { + "Version 2.4 introduces a new algorithm to compute invariants + of elementary abelian $p$-groups. + To call this algorithm, use the option ", + TT "Strategy=>\"Elementary\"", "; see ", + TO (invariants, DiagonalAction), " for an example." + }, + + SeeAlso => { + diagonalAction, + invariants, + invariantRing, + reynoldsOperator + } + + } document { Key => { @@ -298,7 +362,6 @@ document { Usage => "invariants G", Inputs => { "G" => FiniteGroupAction, - Strategy => {"the strategy used to compute diagonal invariants, options are UsePolyhedra or UseNormaliz."} }, Outputs => { "L" => List => {"a minimal set of generating invariants for the group action"} @@ -321,11 +384,11 @@ document { "The following example computes the invariants of the alternating group on 4 elements." }, - EXAMPLE { - "R = QQ[x_1..x_4]", - "L = apply({\"2314\",\"2143\"},permutationMatrix);", - "A4 = finiteAction(L,R)", - "netList invariants A4" + EXAMPLE { + "R = QQ[x_1..x_4]", + "L = apply({[2, 3, 1, 4], [2, 1, 4, 3]}, permutationMatrix);", + "A4 = finiteAction(L, R)", + "netList invariants A4" }, SeeAlso => { @@ -364,10 +427,10 @@ document { }, EXAMPLE { "R = QQ[x_1..x_4]", - "L = apply({\"2134\",\"2341\"},permutationMatrix);", - "S4 = finiteAction(L,R)", + "L = apply({[2, 1, 3, 4], [2, 3, 4, 1]}, permutationMatrix);", + "S4 = finiteAction(L, R)", "elapsedTime invariants S4", - "elapsedTime invariants(S4,DegreeBound=>4)" + "elapsedTime invariants(S4, DegreeBound => 4)" }, Caveat => { "If the value provided for this option is too small, @@ -387,7 +450,7 @@ document { [invariants, UseCoefficientRing], [invariantRing, UseCoefficientRing], UseCoefficientRing }, Headline => "option to compute invariants over the given coefficient ring", - Usage => "invariants G", + Usage => "invariants D", Inputs => {"D" => DiagonalAction}, Outputs => { "L" => List => {"a minimal set of generating invariants for the group action"} @@ -438,11 +501,11 @@ document { document { Key => { - [invariants, UseLinearAlgebra], [invariantRing, UseLinearAlgebra], UseLinearAlgebra + [invariants, UsePolyhedra], [invariantRing, UsePolyhedra], UsePolyhedra }, - Headline => "strategy for computing invariants of finite groups", - Usage => "invariants G", - Inputs => {"G" => FiniteGroupAction}, + Headline => "use Polyhedra package for invariants of tori", + Usage => "invariants D", + Inputs => {"D" => DiagonalAction}, Outputs => { "L" => List => {"a minimal set of generating invariants for the group action"} }, @@ -452,36 +515,18 @@ document { }, PARA { - "This optional argument determines the strategy used to - compute generating invariants of a finite group action. - The default strategy uses the Reynolds operator, however - this may be slow for large groups. Setting this argument - to ", TO true, " uses the linear algebra method for - computing invariants of a given degree by calling ", - TO (invariants, FiniteGroupAction, ZZ), ". This may - provide a speedup at lower degrees, especially if the - user-provided generating set for the group is small." - }, - - PARA { - "The following example computes the invariants of the - symmetric group on 4 elements. Note that using - different strategies may lead to different sets of - generating invariants." + "For a diagonal action, the computation of invariants relies on + finding integral points in a convex hull constructed + from a weight matrix. By default, the package ", TO Normaliz, + " is used for finding integral points. It is also possible + to use the package ", TO Polyhedra, " for finding integral points + by passing the option ", TT "UsePolyhedra => true", "." }, - - EXAMPLE { - "R = QQ[x_1..x_4]", - "L = apply({\"2134\",\"2341\"},permutationMatrix);", - "S4 = finiteAction(L,R)", - "elapsedTime invariants S4", - "elapsedTime invariants(S4,UseLinearAlgebra=>true)" - }, SeeAlso => { - finiteAction, - invariantRing, - isInvariant + diagonalAction, + invariants, + invariantRing } } @@ -497,8 +542,7 @@ document { Inputs => { "G" => FiniteGroupAction, - "d" => ZZ => {"a degree or multidegree"}, - Strategy => {"the strategy used to compute diagonal invariants, options are UsePolyhedra or UseNormaliz."} + "d" => ZZ => {"a degree or multidegree"} }, Outputs => { "L" => List => {"an additive basis for a graded component of the ring of invariants"} @@ -559,8 +603,7 @@ document { Inputs => { "V" => LinearlyReductiveAction, - "d" => ZZ => {"a degree or multidegree"}, - Strategy => {"the strategy used to compute diagonal invariants, options are UsePolyhedra or UseNormaliz."} + "d" => ZZ => {"a degree or multidegree"} }, Outputs => { "L" => List => {"an additive basis for a graded component of the ring of invariants"} @@ -616,8 +659,7 @@ document { Usage => "invariants V", Inputs => { - "V" => LinearlyReductiveAction, - Strategy => {"the strategy used to compute diagonal invariants, options are UsePolyhedra or UseNormaliz."} + "V" => LinearlyReductiveAction }, Outputs => { "L" => List => {"of invariants generating the Hilbert ideal"} @@ -842,7 +884,7 @@ document { EXAMPLE { "R = ZZ/3[x_0..x_6]", - "P = permutationMatrix toString 2345671", + "P = permutationMatrix [2, 3, 4, 5, 6, 7, 1]", "C7 = finiteAction(P, R)", "reynoldsOperator(x_0*x_1*x_2^2, C7)", }, @@ -863,7 +905,9 @@ document { document { Key => {definingIdeal, - (definingIdeal, RingOfInvariants)}, + (definingIdeal, RingOfInvariants), + [definingIdeal, Variable] + }, Headline => "presentation of a ring of invariants as polynomial ring modulo the defining ideal", @@ -936,7 +980,9 @@ document { } document { - Key => {(hilbertSeries, RingOfInvariants)}, + Key => {(hilbertSeries, RingOfInvariants), + [hilbertSeries, Order], + [hilbertSeries, Reduce]}, Headline => "Hilbert series of the invariant ring", @@ -953,7 +999,7 @@ document { "This function is provided by the package ", TO InvariantRing,". ", PARA { - "This method computes the Hilbert series of the ring of invariants." + "This method computes the hilbert series of the ring of invariants." }, EXAMPLE { @@ -965,16 +1011,3 @@ document { }, } -document { - Key => {UseNormaliz, UsePolyhedra}, - Headline => "option for diagonal invariants", - "This option is provided by the package ", TO InvariantRing,". ", - PARA { - "The computation of diagonal invariants relies on - finding integral points in a convex hull constructed - from a weight matrix. This option selects the package - used for finding integral points. See ", - TO (invariants,DiagonalAction), - " for usage." - }, - } diff --git a/M2/Macaulay2/packages/InvariantRing/LinearlyReductiveGroupsDoc.m2 b/M2/Macaulay2/packages/InvariantRing/LinearlyReductiveGroupsDoc.m2 index 2ef3e758f20..b1a1be2cfd2 100644 --- a/M2/Macaulay2/packages/InvariantRing/LinearlyReductiveGroupsDoc.m2 +++ b/M2/Macaulay2/packages/InvariantRing/LinearlyReductiveGroupsDoc.m2 @@ -114,6 +114,10 @@ document { ". Heidelberg: Springer. pp 159-164"} }, + PARA { + "and works in arbitrary characteristic.", + }, + PARA { "The next example constructs a cyclic group of order 2 as a set of two affine points. Then it introduces an @@ -155,7 +159,11 @@ document { generators for the Hilbert ideal." }, Caveat => "The generators of the Hilbert ideal computed - by this function need not be invariant." + by this function need not be invariant. + + Although this method could in principle be used for any group + that can be represented as an affine variety, it tends to be + slow; other specialized methods are likely to be faster." } diff --git a/M2/Macaulay2/packages/InvariantRing/Tests.m2 b/M2/Macaulay2/packages/InvariantRing/Tests.m2 index 300db5bff95..5813ed6a5ec 100644 --- a/M2/Macaulay2/packages/InvariantRing/Tests.m2 +++ b/M2/Macaulay2/packages/InvariantRing/Tests.m2 @@ -104,15 +104,12 @@ assert(set invariants T1 === invariants1) /// -- Test 9 - --- this test often fails, because the result depends on what hashcode values are, so we comment it out for now. - --- TEST /// --- R2 = QQ[x_1..x_4] --- T2 = diagonalAction(matrix{{0,1,-1,1},{1,0,-1,-1}}, R2) --- invariants2 = set {x_1*x_2*x_3,x_1^2*x_3*x_4} --- assert(set invariants T2 === invariants2) --- /// +TEST /// +R2 = QQ[x_1..x_4] +T2 = diagonalAction(matrix{{0,1,-1,1},{1,0,-1,-1}}, R2) +invariants2 = set {x_1*x_2*x_3,x_1^2*x_3*x_4} +assert(set invariants T2 === invariants2) +/// ------------------------------------------- @@ -170,7 +167,10 @@ W1 = matrix{{1,0,-1},{0,1,-1}} W2 = matrix{{0,1,1},{1,0,1}} d = {3,3} D = diagonalAction(W1,W2,d,R) -degRing = degreesRing D +-- get degree variable +T = (degreesRing D)_0 +-- get torus character variables +z = gens coefficientRing degreesRing D e = equivariantHilbertSeries D assert(value denominator e === 1+(-z_0*z_3-z_1*z_2-z_0^(-1)*z_1^(-1)*z_2*z_3)*T+(z_0*z_1*z_ @@ -203,7 +203,10 @@ TEST /// R = QQ[x_1..x_4] W = matrix{{0,1,-1,1},{1,0,-1,-1}} D = diagonalAction(W, R) -degRing = degreesRing D +-- get degree variable +T = (degreesRing D)_0 +-- get torus character variables +z = gens coefficientRing degreesRing D e = equivariantHilbertSeries D assert(value denominator e === 1+(-z_0-z_0*z_1^(-1)-z_1-z_0^(-1)*z_1^(-1))*T+(z_0^2*z_1^(-1 @@ -245,7 +248,10 @@ R = QQ[x_1..x_3] d = {3,3} W = matrix{{1,0,1},{0,1,1}} D = diagonalAction(W, d, R) -degRing = degreesRing D +-- get degree variable +T = (degreesRing D)_0 +-- get torus character variables +z = gens coefficientRing degreesRing D e = equivariantHilbertSeries D assert(value denominator e === 1+(-z_0*z_1-z_0-z_1)*T+(z_0^2*z_1+z_0*z_1^2+z_0*z_1)*T^2-z_0 @@ -281,8 +287,8 @@ R=QQ[x_1..x_4] S5=finiteAction({A,B},R) assert(#(group S5) === 120) assert(not isAbelian S5) -C=permutationMatrix toString 3124 -D=permutationMatrix toString 2143 +C=permutationMatrix [3, 1, 2, 4] +D=permutationMatrix [2, 1, 4, 3] A4=finiteAction({C,D},R) assert(#(group A4) === 12) assert(not isAbelian A4) @@ -327,8 +333,8 @@ assert(invariants D4 === {x*y,x^4+y^4}) TEST /// R = QQ[x,y,z] -r=permutationMatrix toString 312 -s=permutationMatrix toString 213 +r=permutationMatrix [3, 1, 2] +s=permutationMatrix [2, 1, 3] S3 = finiteAction({r,s},R) assert(isInvariant(x*y*z,S3)) assert(isInvariant(x+y+z,S3)) @@ -355,8 +361,8 @@ assert(value denominator H === sub((1-T)^3, ring value denominator H)) TEST /// K=GF(101) R=K[x,y,z] -r=permutationMatrix toString 312 -s=permutationMatrix toString 213 +r=permutationMatrix [3, 1, 2] +s=permutationMatrix [2, 1, 3] S3 = finiteAction({r,s},R) setRandomSeed 0 P=primaryInvariants(S3, Dade=>true) @@ -405,7 +411,7 @@ assert( -- Test 23 -- Checks the dadeHSOP routine by checking that the list of polynomials output -- has the expected output. Namely: --- they are invariant polynomials, +-- they are invariant polynimials, -- they form a homogeneous system of parameters for the polynomial ring -- they have degrees equal to the cardinality of the group (which should occur -- with probability 1)* @@ -431,3 +437,21 @@ assert( apply(P,degree)==toList(#P:{#(group D4)}) ) /// + +------------------------------------------------------------------------ +--- Tests for elementary invarianst strategy (April 2026) -------------- +------------------------------------------------------------------------ + +-- Test 24 +-- checks that the new strategy for elementary invariants returns +-- the same results as the previous strategy by Derksen and Gandini +TEST /// +p=11 +R = QQ[x_1..x_3] +W = matrix{{1,0,1},{0,1,1}} +L = {p,p} +T = diagonalAction(W,L,R) +inv = invariants T +einv = invariants(T, Strategy => "Elementary") +assert(set inv == set einv) +/// diff --git a/M2/Macaulay2/packages/InverseSystems.m2 b/M2/Macaulay2/packages/InverseSystems.m2 index b44a71a894c..85fc15d17bf 100644 --- a/M2/Macaulay2/packages/InverseSystems.m2 +++ b/M2/Macaulay2/packages/InverseSystems.m2 @@ -379,7 +379,7 @@ Description Method: To represent finitely generated S-submodule of $D'$ as an S-module we use the map of modules S/(x_1^d,\dots, x_n^d) -> D' sending $x^a$ to - contract(x^a, product(n, j-> x_i^{d-1})), and its inverse, + contract(x^a, product(1 .. n, i -> x_i^(d-1))), and its inverse, which is of course defined only on divided monomials of small degree. Caveat @@ -470,14 +470,14 @@ Usage M1 = inverseSystem I Inputs M:Matrix - if r rows, then represents a submodule of D'^r + if $r$ rows, then represents a submodule of $D'^r$ M:RingElement I:Ideal Outputs I1:Ideal - if r=1 + if $r=1$ I1:Module - submodule of S^r + submodule of $S^r$ M1:Matrix Description Text @@ -485,27 +485,27 @@ Description ideals and modules. For that application see @TO Gorenstein@. - Let S = k[x_1..x_n] be a standard graded polynomial ring, - and let D be its dual, the divided power algebra, - regarded as an S-module. Let M be an rxm matrix of polynomials, - and let I be an ideal of S. + Let $S = k[x_1,\ldots,x_n]$ be a standard graded polynomial ring, + and let $D$ be its dual, the divided power algebra, + regarded as an $S$-module. Let $M$ be an $r \times m$ matrix of polynomials, + and let $I$ be an ideal of $S$. - From a submodule of D^r to a submodule of S^r (or to an ideal, if r=1): + From a submodule of $D^r$ to a submodule of $S^r$ (or to an ideal, if $r=1$): - We think of the columns of M as generators of an S-submodule MM of D^r, + We think of the columns of M as generators of an $S$-submodule MM of $D^r$, and - inverseSystem M returns the annihilator of MM in S^r = Hom_{graded}(D^r,k). + inverseSystem M returns the annihilator of MM in $S^r = \mathrm{Hom}_{\text{graded}}(D^r,k)$. In the default behavior a monomial $x^a$ in an entry of the matrix M is taken to represent - $a!x^(a) \in D'$, where, - $a = (a_1,\dots,a_n)$ then $a! = a_1!*\dots*a_n!$. Use + $a!x^{(a)} \in D'$, where, + $a = (a_1,\dots,a_n)$ then $a! = a_1!\times\dots\times a_n!$. Use inverseSystem(M, DividedPowers => false) - to make the monomials of entries of M represent the dual basis of the monomial basis of S, - that is, the divided powers of the generators of D as an algebra. + to make the monomials of entries of M represent the dual basis of the monomial basis of $S$, + that is, the divided powers of the generators of $D$ as an algebra. - From an ideal of S to a submodule of D: + From an ideal of $S$ to a submodule of $D$: If $I$ is an ideal of $S$, homogeneous or not, we regard $I$ as an ideal of the localization $S'$ of $S$ at $(x_1,\dots,x_n)$. If $S'/I$ is of @@ -517,22 +517,22 @@ Description M1 = inverseSystem(I, DividedPowers => false) - each return a 1 x m matrix whose entries are + each return a $1 \times m$ matrix whose entries are the minimal generators of the annihilator of $I$ in $D$. In the matrix $M$ a term $x^a$ is to be interpreted as - $a! x^(a)$, while in the matrix $M'$ it is interpreted - as $x^(a)$. Of course the first computation is only + $a! x^{(a)}$, while in the matrix $M'$ it is interpreted + as $x^{(a)}$. Of course the first computation is only valid if all the powers of variables appearing in the generators of $I$ are < char k. To make these computations it is necessary to represent some sufficiently - large finitely generated S-submodule of $D$ (this will automatically be + large finitely generated $S$-submodule of $D$ (this will automatically be an $S'$-submodule. To do this we use the map of modules - D-> S/(x_1^d,\dots, x_n^d) sending $x^{(a)}$ to - contract(x^a, product(n, j-> x_i^{d-1})), defined only when the variables - in $x^{(a)}$ appear only with powers < d. + $D\to S/(x_1^d,\dots, x_n^d)$ sending $x^{(a)}$ to + contract(x^a, product(1 .. n, i -> x_i^(d-1))), defined only when the variables + in $x^{(a)}$ appear only with powers < $d$. Example setRandomSeed 0 @@ -565,10 +565,10 @@ Caveat it should not be used in the default mode unless the characteristic is greater than the highest degree to which a variable appears. - To make $x^a$ represent $x^(a)$, + To make $x^a$ represent $x^{(a)}$, for example in small characteristics use - inverseSystem(Matrix, DividedPowers=>false) + inverseSystem(Matrix, DividedPowers=>true) (which was the default behavior of the old script "fromDual"). diff --git a/M2/Macaulay2/packages/JSON.m2 b/M2/Macaulay2/packages/JSON.m2 index 616f6d7b594..b999358bed4 100644 --- a/M2/Macaulay2/packages/JSON.m2 +++ b/M2/Macaulay2/packages/JSON.m2 @@ -1,5 +1,5 @@ -- JSON package for Macaulay2 --- Copyright (C) 2022-2025 Doug Torrance +-- Copyright (C) 2022-2026 Doug Torrance -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License @@ -17,15 +17,15 @@ newPackage( "JSON", Headline => "JSON encoding and decoding", - Version => "0.5", - Date => "November 10, 2025", + Version => "0.6", + Date => "February 28, 2026", Authors => {{ Name => "Doug Torrance", Email => "dtorrance@piedmont.edu", HomePage => "https://webwork.piedmont.edu/~dtorrance"}}, Keywords => {"System"}, - PackageImports => {"Parsing"}, PackageExports => {"Text"}, + PackageImports => {"Parsing"}, AuxiliaryFiles => true) --------------- @@ -34,6 +34,14 @@ newPackage( -* +0.6 (2026-02-28, M2 1.26.05) +* parsing is now handled by jansson in the interpreter, which speeds things up + considerably (if M2 is built w/o jansson support, then we fall back on the + old behavior) +* breaking changes: + - "null" now returns as null, not nil + - \0 is no longer allowed in object keys + 0.5 (2025-11-10, M2 1.25.11) * add GPL copyright header * move tests to "tests" subdirectory @@ -72,11 +80,10 @@ export { "ValueSeparator", } -exportFrom_Parsing "nil" - ----------------------------------------------------------------- +--------------------------------------------------------------- -- parser based on https://datatracker.ietf.org/doc/html/rfc8259 ---------------------------------------------------------------- +-- only used if we build M2 w/o jansson support -- whitespace wsP = *orP(" ", "\t", "\n", "\r") @@ -112,14 +119,11 @@ unescapedP = Parser(c -> if c === null then null else ( if x < 0x20 or x == 0x22 or x == 0x5c or x > 0x10ffff then null else terminalParser c)) -deformat = x -> ( - if last x === "/" then "/" - else value concatenate("\"", x, "\"")) -escapedP = deformat % ("\\" @ +escapedP = ((l, r) -> if r === "/" then r else concatenate(l, r)) % ("\\" @ orP("\"", "\\", "/", "b", "f", "n", "r", "t", andP("u", hexDigitP, hexDigitP, hexDigitP, hexDigitP))) charP = unescapedP | escapedP -stringP = ((l, x, r) -> concatenate x) % andP("\"", *charP, "\"") +stringP = ((l, x, r) -> value concatenate(l, x, r)) % andP("\"", *charP, "\"") -- objects memberP = ((k, gets, v) -> k => v) % andP( @@ -153,9 +157,23 @@ utf8Analyzer = Analyzer(s -> ( i = i + 1; r))) +-- hack so we can transform nil -> null +processJSON = method() +processJSON Thing := identity +processJSON Symbol := x -> null -- should only ever be nil +processJSON List := x -> apply(x, processJSON) +processJSON HashTable := x -> applyValues(x, processJSON) + fromJSON = method() -fromJSON String := jsonTextP : utf8Analyzer -fromJSON File := fromJSON @@ get + +-- did we build w/ jansson support? +fromJSON0 = value(?? Core#"private dictionary"#"fromJSON0") +if fromJSON0 === null then ( + fromJSON String := processJSON @@ (jsonTextP : utf8Analyzer); + fromJSON File := fromJSON @@ get + ) else ( + fromJSON String := + fromJSON File := fromJSON0) -------------- -- encoding -- @@ -170,15 +188,15 @@ toJSON = method( ValueSeparator => null, Sort => false}) -toJSON Thing := toJSON MutableHashTable := o -> format @@ toString +toJSON Thing := +toJSON Symbol := +toJSON MutableHashTable := o -> format @@ toString toJSON String := o -> format toJSON RR := o -> format_0 toJSON Number := o -> format_0 @@ numeric toJSON ZZ := toJSON Boolean := toJSON Nothing := o -> toString -toJSON Symbol := o -> x -> ( - if x === nil then "null" else format toString x) toJSON Hypertext := o -> format @@ html maybeNewline = o -> if o.Indent === null then "" else newline @@ -231,7 +249,7 @@ doc /// @TO toJSON@ and @TO fromJSON@, for converting Macaulay2 things to valid JSON data and vice versa. Example - toJSON {hashTable{"foo" => "bar"}, 1, 3.14159, true, false, nil} + toJSON {hashTable{"foo" => "bar"}, 1, 3.14159, true, false, null} fromJSON oo /// @@ -278,7 +296,7 @@ doc /// given Macaulay2 thing. If the @TT "Indent"@ option is @TT "null"@ (the default), then there are no newlines or indentation. Example - x = hashTable {"foo" => {1, 2, {pi, true, false, nil}}} + x = hashTable {"foo" => {1, 2, {pi, true, false, null}}} toJSON x Text If the @TT "Indent"@ option is an integer, then newlines are added between @@ -328,8 +346,7 @@ doc /// Description Text The JSON data provided in the given string or file is parsed using the - @TO "Parsing"@ package with the context-free grammar specified by - @HREF{"https://datatracker.ietf.org/doc/html/rfc8259", "RFC 8259"}@. + @HREF("https://github.com/akheron/jansson", "Jansson")@ library. The type of the return value will vary depending on the data. Numbers will result in @TT "ZZ"@ or @TT "RR"@ objects, as appropriate. @@ -347,11 +364,9 @@ doc /// fromJSON "true" fromJSON "false" Text - Due to the implementation of the @TT "Parsing"@ package, @TO "null"@ - cannot be a return value, and so the symbol @TO "nil"@ is returned - when JSON's @TT "null"@ is given. + JSON's @TT "null"@ will result in Macaulay2's @TO null@. Example - fromJSON "null" + fromJSON "null" === null Text Objects will result in hash tables. Example @@ -377,7 +392,6 @@ testdir = tmpdir | "/JSONTestSuite/test_parsing" tsts = select(readDirectory(testdir), f -> match("\\.json$", f)) -needsPackage "Parsing" -- for nil debug Core -- for commentize outdir = (needsPackage "JSON")#"source directory" | "JSON" @@ -391,6 +405,8 @@ https://github.com/nst/JSONTestSuite" outfile = openOut(outdir | "/tests/parse.m2") outfile << commentize copyrightBanner << endl for tst in sort select(tsts, f -> match("^y_", f)) do ( + -- TODO: allow \0 in keys (need jansson 2.14) + if tst == "y_object_escaped_null_in_key.json" then continue; outfile << endl << commentize tst << endl; testjson = get(testdir | "/" | tst); outfile << "assert BinaryOperation(symbol ===, fromJSON " << diff --git a/M2/Macaulay2/packages/JSON/tests/encode.m2 b/M2/Macaulay2/packages/JSON/tests/encode.m2 index 1992c77c156..a66b2c04b8b 100644 --- a/M2/Macaulay2/packages/JSON/tests/encode.m2 +++ b/M2/Macaulay2/packages/JSON/tests/encode.m2 @@ -12,7 +12,6 @@ assert Equation(toJSON "¡pʃɹoÊ 'oʃʃÇH", "\"¡pʃɹoÊ 'oʃʃÇH\"") assert Equation(toJSON true, "true") assert Equation(toJSON false, "false") assert Equation(toJSON null, "null") -assert Equation(toJSON nil, "null") -- arrays assert Equation(toJSON {1, 2, 3}, "[1, 2, 3]") diff --git a/M2/Macaulay2/packages/JSON/tests/parse.m2 b/M2/Macaulay2/packages/JSON/tests/parse.m2 index e75c55cb415..d7a58c2b660 100644 --- a/M2/Macaulay2/packages/JSON/tests/parse.m2 +++ b/M2/Macaulay2/packages/JSON/tests/parse.m2 @@ -19,10 +19,10 @@ assert BinaryOperation(symbol ===, fromJSON "[\"a\"]", {"a"}) assert BinaryOperation(symbol ===, fromJSON "[false]", {false}) -- y_array_heterogeneous.json -assert BinaryOperation(symbol ===, fromJSON "[null, 1, \"1\", {}]", {nil,1,"1",new HashTable from {}}) +assert BinaryOperation(symbol ===, fromJSON "[null, 1, \"1\", {}]", {,1,"1",new HashTable from {}}) -- y_array_null.json -assert BinaryOperation(symbol ===, fromJSON "[null]", {nil}) +assert BinaryOperation(symbol ===, fromJSON "[null]", {null}) -- y_array_with_1_and_newline.json assert BinaryOperation(symbol ===, fromJSON "[1\n]", {1}) @@ -31,7 +31,7 @@ assert BinaryOperation(symbol ===, fromJSON "[1\n]", {1}) assert BinaryOperation(symbol ===, fromJSON " [1]", {1}) -- y_array_with_several_null.json -assert BinaryOperation(symbol ===, fromJSON "[1,null,null,null,2]", {1,nil,nil,nil,2}) +assert BinaryOperation(symbol ===, fromJSON "[1,null,null,null,2]", {1,,,,2}) -- y_array_with_trailing_space.json assert BinaryOperation(symbol ===, fromJSON "[2] ", {2}) @@ -111,9 +111,6 @@ assert BinaryOperation(symbol ===, fromJSON "{}", new HashTable from {}) -- y_object_empty_key.json assert BinaryOperation(symbol ===, fromJSON "{\"\":0}", new HashTable from {"" => 0}) - -- y_object_escaped_null_in_key.json -assert BinaryOperation(symbol ===, fromJSON "{\"foo\\u0000bar\": 42}", new HashTable from {"foo\0bar" => 42}) - -- y_object_extreme_numbers.json assert BinaryOperation(symbol ===, fromJSON "{ \"min\": -1.0e+28, \"max\": 1.0e+28 }", new HashTable from {"max" => .99999999999999996p53e28, "min" => -.99999999999999996p53e28}) @@ -133,10 +130,10 @@ assert BinaryOperation(symbol ===, fromJSON "{\n\"a\": \"b\"\n}", new HashTable assert BinaryOperation(symbol ===, fromJSON "[\"\\u0060\\u012a\\u12AB\"]", {"`Īካ"}) -- y_string_accepted_surrogate_pair.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\uD801\\udc37\"]", {"í í°·"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\uD801\\udc37\"]", {"ð·"}) -- y_string_accepted_surrogate_pairs.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\ud83d\\ude39\\ud83d\\udc8d\"]", {"í ½í¸¹í ½í²"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\ud83d\\ude39\\ud83d\\udc8d\"]", {"😹ðŸ’"}) -- y_string_allowed_escapes.json assert BinaryOperation(symbol ===, fromJSON "[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]", {"\"\\/\b\f\n\r\t"}) @@ -157,7 +154,7 @@ assert BinaryOperation(symbol ===, fromJSON "[\"\\\\a\"]", {"\\a"}) assert BinaryOperation(symbol ===, fromJSON "[\"\\\\n\"]", {"\\n"}) -- y_string_escaped_control_character.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\u0012\"]", {""}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\u0012\"]", {"\u0012"}) -- y_string_escaped_noncharacter.json assert BinaryOperation(symbol ===, fromJSON "[\"\\uFFFF\"]", {"ï¿¿"}) @@ -169,7 +166,7 @@ assert BinaryOperation(symbol ===, fromJSON "[\"asd\"]", {"asd"}) assert BinaryOperation(symbol ===, fromJSON "[ \"asd\"]", {"asd"}) -- y_string_last_surrogates_1_and_2.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\uDBFF\\uDFFF\"]", {"􏿿"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\uDBFF\\uDFFF\"]", {"ô¿¿"}) -- y_string_nbsp_uescaped.json assert BinaryOperation(symbol ===, fromJSON "[\"new\\u00A0line\"]", {"new line"}) @@ -181,7 +178,7 @@ assert BinaryOperation(symbol ===, fromJSON "[\"ô¿¿\"]", {"ô¿¿"}) assert BinaryOperation(symbol ===, fromJSON "[\"ï¿¿\"]", {"ï¿¿"}) -- y_string_null_escape.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\u0000\"]", {"\0"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\u0000\"]", {"\u0000"}) -- y_string_one-byte-utf-8.json assert BinaryOperation(symbol ===, fromJSON "[\"\\u002c\"]", {","}) @@ -199,7 +196,7 @@ assert BinaryOperation(symbol ===, fromJSON "[\"asd \"]", {"asd "}) assert BinaryOperation(symbol ===, fromJSON "\" \"", " ") -- y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\uD834\\uDd1e\"]", {"í ´í´ž"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\uD834\\uDd1e\"]", {"ð„ž"}) -- y_string_three-byte-utf-8.json assert BinaryOperation(symbol ===, fromJSON "[\"\\u0821\"]", {"à ¡"}) @@ -232,10 +229,10 @@ assert BinaryOperation(symbol ===, fromJSON "[\"â‚㈴â‚\"]", {"â‚㈴â‚"}) assert BinaryOperation(symbol ===, fromJSON "[\"\\u0022\"]", {"\""}) -- y_string_unicode_U+1FFFE_nonchar.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\uD83F\\uDFFE\"]", {"🿾"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\uD83F\\uDFFE\"]", {"🿾"}) -- y_string_unicode_U+10FFFE_nonchar.json -assert BinaryOperation(symbol ===, fromJSON "[\"\\uDBFF\\uDFFE\"]", {"􏿾"}) +assert BinaryOperation(symbol ===, fromJSON "[\"\\uDBFF\\uDFFE\"]", {"ô¿¾"}) -- y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json assert BinaryOperation(symbol ===, fromJSON "[\"\\u200B\"]", {"​"}) @@ -268,7 +265,7 @@ assert BinaryOperation(symbol ===, fromJSON "42", 42) assert BinaryOperation(symbol ===, fromJSON "-0.1", -.10000000000000001p53) -- y_structure_lonely_null.json -assert BinaryOperation(symbol ===, fromJSON "null", nil) +assert BinaryOperation(symbol ===, fromJSON "null", null) -- y_structure_lonely_string.json assert BinaryOperation(symbol ===, fromJSON "\"asd\"", "asd") diff --git a/M2/Macaulay2/packages/JSONRPC.m2 b/M2/Macaulay2/packages/JSONRPC.m2 index b3a836aaec3..b50253a7280 100644 --- a/M2/Macaulay2/packages/JSONRPC.m2 +++ b/M2/Macaulay2/packages/JSONRPC.m2 @@ -1,5 +1,5 @@ -- JSONRPC package for Macaulay2 --- Copyright (C) 2025 Doug Torrance +-- Copyright (C) 2025-2026 Doug Torrance -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -16,8 +16,8 @@ newPackage("JSONRPC", Headline => "JSON-RPC server", - Version => "0.1", - Date => "May 19, 2025", + Version => "0.2", + Date => "February 18, 2026", Authors => {{ Name => "Doug Torrance", Email => "dtorrance@piedmont.edu", @@ -25,6 +25,21 @@ newPackage("JSONRPC", Keywords => {"System"}, PackageImports => {"JSON"}) +--------------- +-- ChangeLog -- +--------------- + +-* + +0.2 (2026-02-18, M2 1.26.05) +* Use trap for error messages +* Update for JSON v0.6 (nil -> null) + +0.1 (2025-05-19, M2 1.25.11) +* Initial release + +*- + export { -- classes "JSONRPCError", @@ -102,7 +117,7 @@ handleRequestHelper(JSONRPCServer, HashTable) := (server, request) -> ( request#?"id" and not ( instance(request#"id", String) or instance(request#"id", ZZ) or - request#"id" === nil)) + request#"id" === null)) then handleRequestHelper(server, null) -- invalid request else if not server#?(request#"method") then ( @@ -139,17 +154,15 @@ callMethod(JSONRPCMethod, List, Thing) := (m, params, ID) -> ( i -> if i >= #params then null else params#i) else params); if #inp == 1 then inp = inp#0; - r := (try m#"function" inp - -- TODO: use lastError here once it's available - -- afterwards, validate params and only throw this - -- error when they're bad - -- also update JSONRPCError doc node - else JSONRPCError(-32602, "Invalid params")); - if instance(r, JSONRPCError) - then m#"server"#"logger" concatenate( - "method \"", m#"name", "\" failed with error: ", toJSON r) + -- TODO: validate params and throw the following if they're bad: + -- JSONRPCError(-32602, "Invalid params") + (r, err) := trap m#"function" inp; + if err =!= null then ( + r = JSONRPCError(-32603, "Internal error: " | toString err); + m#"server"#"logger" concatenate( + "method \"", m#"name", "\" failed with error: ", toJSON r)) else m#"server"#"logger" concatenate( - "method \"", m#"name", "\" returned: ", toJSON r); + "method \"", m#"name", "\" returned: ", toJSON r); makeResponse(m#"server", r, ID)) callMethod(JSONRPCMethod, HashTable, Thing) := (m, params, ID) -> ( @@ -383,21 +396,13 @@ doc /// data. This class ensures that errors are properly formatted according to the JSON-RPC 2.0 specification and can be easily included in responses to clients. - - Consider the following example. The default response doesn't include a - very useful error message. Example server = new JSONRPCServer - registerMethod(server, "divide", (x, y) -> x/y) - handleRequest(server, makeRequest("divide", {1, 0}, 1)) - Text - Let's replace it with a more useful one. - Example registerMethod(server, "divide", (x, y) -> ( if zero y then JSONRPCError(-32001, "division by zero") else x/y)) handleRequest(server, makeRequest("divide", {1, 0}, 1)) - handleRequest(server, makeRequest("divide", {22, 7}, 1)) + handleRequest(server, makeRequest("divide", {22, 7}, 2)) Text Note that the error codes -32000 to -32099 are reserved for use by JSON-RPC servers, so @CODE "errCode"@ should lie in this interval @@ -650,6 +655,9 @@ assertJSONRPC = (request, expected) -> assert BinaryOperation(symbol ===, registerMethod(server, "foo", () -> JSONRPCError(1234, "bar")) assertJSONRPC("{\"jsonrpc\": \"2.0\", \"method\": \"foo\", \"id\": 1}", "{\"error\": {\"code\": 1234, \"message\": \"bar\"}, \"jsonrpc\": \"2.0\", \"id\": 1}") +registerMethod(server, "divide", (x,y) -> x/y) +assertJSONRPC("{\"jsonrpc\": \"2.0\", \"method\": \"divide\", \"params\": [1, 0], \"id\": 2}", + "{\"error\": {\"code\": -32603, \"message\": \"Internal error: division by zero\"}, \"jsonrpc\": \"2.0\", \"id\": 2}") /// TEST /// diff --git a/M2/Macaulay2/packages/K3Carpets.m2 b/M2/Macaulay2/packages/K3Carpets.m2 index b84831470ee..91300db5bb7 100644 --- a/M2/Macaulay2/packages/K3Carpets.m2 +++ b/M2/Macaulay2/packages/K3Carpets.m2 @@ -28,7 +28,7 @@ newPackage( export { "schreyerName", - -- should be moved to an other package, e.g. NonminimalComplexes + -- should be moved to an other package, e.g. Complexes? "analyzeStrand", "carpetDet", "resonanceDet", @@ -1024,7 +1024,7 @@ $$ TO carpetBettiTable, TO analyzeStrand, TO degenerateK3BettiTables, - TO schreyerName, -- should be moved to an other package, e.g. NonminimalComplexes + TO schreyerName, -- should be moved to an other package, e.g. Complexes? TO allGradings, TO carpetDet, TO resonanceDet, diff --git a/M2/Macaulay2/packages/K3Surfaces.m2 b/M2/Macaulay2/packages/K3Surfaces.m2 index e5fd13e31a3..eb039a1a0b9 100644 --- a/M2/Macaulay2/packages/K3Surfaces.m2 +++ b/M2/Macaulay2/packages/K3Surfaces.m2 @@ -1,5 +1,11 @@ -if version#"VERSION" < "1.18" then error "this package requires Macaulay2 version 1.18 or newer"; +-* + Copyright 2022-2026, Giovanni Staglianò and Michael Hoff. + + You may redistribute this file under the terms of the GNU General Public + License as published by the Free Software Foundation, either version 2 of + the License, or (at your option) any later version. +*- newPackage( "K3Surfaces", @@ -15,14 +21,14 @@ newPackage( DebuggingMode => false ) -if SpecialFanoFourfolds.Options.Version < "2.6" then ( - < ( - << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << "Embedded K3 surface" << endl; + << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << "Embedded K3 surface of genus " << sectionalGenus S << endl; ); map (LatticePolarizedK3surface,ZZ,ZZ) := o -> (S,a,b) -> ( @@ -75,7 +81,7 @@ map (LatticePolarizedK3surface,ZZ,ZZ) := o -> (S,a,b) -> ( if d == 0 and n == -2 then if b != 0 then error "the K3 surface is nodal"; H := hyperplane S; C := S#"curve"; - phi := mapDefinedByDivisor(Var S,{(H,a),(C,b)}); + phi := if a > 0 and b < 0 then rationalMap((-b)*(C % (Var S)), a) else mapDefinedByDivisor(Var S,{(H,a),(C,b)}); if dim target phi =!= genus(S,a,b) then error("expected map to PP^"|(toString genus(S,a,b))|", but got map to PP^"|toString(dim target phi)); S.cache#("map",a,b) = phi ); @@ -158,77 +164,58 @@ latticePolarizedK3surface (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,L latticePolarizedK3surface (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,List) := (T,C,H,gdn) -> latticePolarizedK3surface(new EmbeddedK3surface from T,C,H,gdn); latticePolarizedK3surface (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,Nothing,List) := (T,C,H,gdn) -> latticePolarizedK3surface(new EmbeddedK3surface from T,C,H,gdn); -K3 = method(Options => {CoefficientRing => ZZ/65521, Verbose => false, Singular => null}); - -makegeneralK3 = (f,p,g) -> ( - K3surf := new EmbeddedK3surface from image f; - assert(sectionalGenus K3surf == g and degree K3surf == 2*g-2 and dim ambient K3surf == g and dim p <= 0 and isSubset(p,K3surf)); - -- if g <= 12 then assert(degree p == 1); - f#"image" = K3surf; - K3surf.cache#"mapK3" = f; - K3surf.cache#"pointK3" = p; - K3surf.cache#"GeneralK3" = true; - K3surf +K3 = method(Options => {CoefficientRing => ZZ/65521, Verbose => false, Strategy => null, Singular => null}); + +buildEmbeddedK3fromFourfold = (labelX,typeX,g,K,OptVerb,OptStr,OptSing) -> ( + cmdF := if typeX === "Gushel-Mukai" + then "gushelMukaiFourfold" + else if typeX === "cubic" + then "cubicFourfold" + else error "expected 'cubic' or 'Gushel-Mukai'"; + if OptVerb then ( + << "-- construction of general K3 surface of genus " << g << " and degree " << 2*g-2 << " in PP^" << g << endl; + << "-- creating general " << typeX << " fourfold of discriminant " << 2*g-2 << "..." << endl; + cmdF = cmdF | "(\"" | labelX | "\", " | (toString K) | ")"; + << "-- command: " << cmdF << endl; + ); + -- X := specialFourfold(labelX,K); + X := if typeX === "cubic" then cubicFourfold(labelX, K) else gushelMukaiFourfold(labelX, K); + S := associatedK3surface(X,Verbose=>OptVerb,Strategy=>OptStr,Singular=>OptSing); + if last building S === null then S = associatedK3surface(X,Verbose=>OptVerb,Strategy=>OptStr,Singular=>OptSing); + if not isStandardK3surface S then error "invariant mismatch for standard K3 surface"; + assert(instance(S,EmbeddedProjectiveVariety) and sectionalGenus S == g); + S.cache#"mapK3" = last building S; + S.cache#"GeneralK3" = true; + new EmbeddedK3surface from S ); K3 ZZ := o -> g -> ( K := o.CoefficientRing; - local X; local p; local Ass; if member(g,{3,4,5,6,7,8,9,10,12}) then ( - (X,p) = randomPointedMukaiThreefold(g,CoefficientRing=>K); + (X,p) := randomPointedMukaiThreefold(g,CoefficientRing=>K); j := parametrize random({1},p); - X = j^* X; p = j^* p; - return makegeneralK3(super 1_X,p,g); + S := j^* X; + if not isStandardK3surface S then error "invariant mismatch for standard K3 surface"; + assert(sectionalGenus S == g); + S = new EmbeddedK3surface from S; + S.cache#"mapK3" = super(1_S); + S.cache#"GeneralK3" = true; + return S; ); - if g == 11 then ( - if o.Verbose then <<"-- constructing general K3 surface of genus "<o.Verbose,Singular=>o.Singular); - if o.Verbose then <<"-- *** --"<o.Verbose,Singular=>o.Singular); - if o.Verbose then <<"-- *** --"<o.Verbose,Singular=>o.Singular); - if o.Verbose then <<"-- *** --"<o.Verbose,Singular=>o.Singular); - if o.Verbose then <<"-- *** --"< X -> ( - d := discriminant X; - if (not isAdmissible d) and (not isAdmissibleGM d) then <<"--warning: expected an admissible integer for the discriminant"<o.Verbose,Singular=>o.Singular); - if o.Verbose then <<"-- *** --"< ( + if not S.cache#?"Hodge-special fourfold" then error "recoverFourfold: the K3 surface is not constructed from a Hodge-special fourfold"; + S.cache#"Hodge-special fourfold" ); +K3 GushelMukaiFourfold := K3 CubicFourfold := o -> X -> associatedK3surface(X,Verbose=>o.Verbose,Strategy=>o.Strategy,Singular=>o.Singular); + K3 (ZZ,ZZ,ZZ) := o -> (g,d,n) -> ( -- if o.Verbose then <<"-- constructing K3 surface with rank 2 lattice defined by the intersection matrix "< {EmbeddedK3surface}, Headline => "the class of all embedded K3 surfaces", SeeAlso => {LatticePolarizedK3surface,(symbol SPACE,LatticePolarizedK3surface,Sequence)}} -document {Key => {K3,(K3,ZZ,ZZ,ZZ),[K3,Verbose],[K3,CoefficientRing],[K3,Singular]}, +document {Key => {K3,(K3,ZZ,ZZ,ZZ),[K3,Verbose],[K3,CoefficientRing],[K3,Singular],[K3,Strategy]}, Headline => "make a lattice-polarized K3 surface", Usage => "K3(g,d,n) K3(g,d,n,CoefficientRing=>K)", @@ -916,13 +903,13 @@ Outputs => {EmbeddedK3surface => {"a general K3 surface defined over ",TEX///$K$ EXAMPLE {"K3 9"}, SeeAlso => {(K3,ZZ,ZZ,ZZ)}} -document {Key => {(K3,SpecialCubicFourfold),(K3,SpecialGushelMukaiFourfold)}, +document {Key => {(K3,CubicFourfold),(K3,GushelMukaiFourfold)}, Headline => "K3 surface associated to a cubic or GM fourfold", Usage => "K3 X", -Inputs => {"X" => SpecialCubicFourfold => {"or ",ofClass SpecialGushelMukaiFourfold}}, -Outputs => {EmbeddedK3surface => {"a K3 surface associated to ",TEX///$X$///}}, -PARA {"This function calls the function ",TO associatedK3surface,"."}, -EXAMPLE {"X = specialFourfold \"tau-quadric\";", "K3 X", "associatedK3surface X"}, +Inputs => {"X" => CubicFourfold => {"or ",ofClass GushelMukaiFourfold}}, +Outputs => {{"a K3 surface associated to ",TEX///$X$///}}, +PARA {"This is a shortcut for the function ",TO associatedK3surface,"."}, +EXAMPLE {"X = specialFourfold \"tau-quadric\";", "K3 X"}, SeeAlso => {(associatedK3surface),(K3,ZZ)}} document {Key => {(genus,LatticePolarizedK3surface,ZZ,ZZ),(genus,EmbeddedK3surface,ZZ,ZZ)}, diff --git a/M2/Macaulay2/packages/Kronecker.m2 b/M2/Macaulay2/packages/Kronecker.m2 index acac7e1bc85..5b6bd1ad572 100644 --- a/M2/Macaulay2/packages/Kronecker.m2 +++ b/M2/Macaulay2/packages/Kronecker.m2 @@ -5,7 +5,7 @@ newPackage( Headline => "Kronecker and rational normal forms", Authors => {{Name => "Edward Carter", Email => "edward.carter@gmail.com"}}, - PackageExports =>{"OldChainComplexes"}, + PackageExports =>{"Complexes"}, Keywords => {"Commutative Algebra"}, DebuggingMode => false ) @@ -812,8 +812,8 @@ GradedModuleMap | GradedModuleMap := (f,g) -> ( map(target f, source f ++ source g, j -> f_j | g_j, Degree => d) ); -min(GradedModule) := M -> min chainComplex M; -max(GradedModule) := M -> max chainComplex M; +min(GradedModule) := M -> min complex M; +max(GradedModule) := M -> max complex M; ker(GradedModuleMap) := o -> f -> ( M := source f; diff --git a/M2/Macaulay2/packages/LatticePolytopes.m2 b/M2/Macaulay2/packages/LatticePolytopes.m2 index 0b851cf3bd8..2d2e81c6723 100644 --- a/M2/Macaulay2/packages/LatticePolytopes.m2 +++ b/M2/Macaulay2/packages/LatticePolytopes.m2 @@ -86,12 +86,12 @@ cayley(Polyhedron,Polyhedron,Polyhedron,ZZ):=(P1,P2,P3,k)->( cayley(Matrix,Matrix,Matrix,ZZ):=(M1,M2,M3,k)->( cayley({M1,M2,M3},k)); --- INPUT : A list of Polyhedras or Matrices +-- INPUT : A list of Polyhedra or Matrices -- OUTPUT : Polyhedron - The Cayley sum with height 1 of the elements in the list cayley(List):=(Plist)->( cayley(Plist,1)); --- INPUT : A list of Polyhedras or Matrices and an integer k +-- INPUT : A list of Polyhedra or Matrices and an integer k -- OUTPUT : Polyhedron - The Cayley sum with height k of the elements in the list cayley(List,ZZ):=(Plist,k)->( if all(Plist,p -> (class p === Matrix)) then Plist=apply(Plist,convexHull); @@ -652,7 +652,7 @@ epsilonBounds(Polyhedron,ZZ):=(P,n)->( intInP:=unique(intsect_pos); if (#intInP==2) then( fVec:=intInP_0-intInP_1; - fv:=gcd(apply(flatten entries(fVec),x->promote(x,QQ))); + fv:=gcd(apply(flatten entries(fVec),x->lift(x,QQ))); Fvs=append(Fvs,fv); ); ); diff --git a/M2/Macaulay2/packages/LexIdeals.m2 b/M2/Macaulay2/packages/LexIdeals.m2 index 7ec3ec302c0..84700c2ef7e 100644 --- a/M2/Macaulay2/packages/LexIdeals.m2 +++ b/M2/Macaulay2/packages/LexIdeals.m2 @@ -9,7 +9,7 @@ newPackage( HomePage => "http://www.math.okstate.edu/~chris"} }, Headline => "lexicographic-type monomial ideals", - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, Keywords => {"Commutative Algebra"}, DebuggingMode => false ) diff --git a/M2/Macaulay2/packages/LieAlgebraRepresentations.m2 b/M2/Macaulay2/packages/LieAlgebraRepresentations.m2 index 4ac83f5c01b..9f6cde7382f 100644 --- a/M2/Macaulay2/packages/LieAlgebraRepresentations.m2 +++ b/M2/Macaulay2/packages/LieAlgebraRepresentations.m2 @@ -2,8 +2,8 @@ -- licensed under GPL v2 or any later version newPackage( "LieAlgebraRepresentations", - Version => "1.0", - Date => "Nov 8, 2025", + Version => "1.01", + Date => "May 1, 2026", AuxiliaryFiles=>true, Headline => "Lie algebra representations and characters", Authors => { diff --git a/M2/Macaulay2/packages/LieAlgebraRepresentations/LieAlgebraBases/lieAlgebraBasisTypeAFH.m2 b/M2/Macaulay2/packages/LieAlgebraRepresentations/LieAlgebraBases/lieAlgebraBasisTypeAFH.m2 index 11e525f5bdb..1d84469d4a1 100644 --- a/M2/Macaulay2/packages/LieAlgebraRepresentations/LieAlgebraBases/lieAlgebraBasisTypeAFH.m2 +++ b/M2/Macaulay2/packages/LieAlgebraRepresentations/LieAlgebraBases/lieAlgebraBasisTypeAFH.m2 @@ -3,6 +3,10 @@ Hin = (i,n) -> ( Eijm(i,i,n) - Eijm(i+1,i+1,n)); +-- The next five functions about weights are for getting the basis elements +-- into lex level order +-- They aren't used for computing weights in representationWeights + -- Want to change between Dynkin basis of the weight lattice and L_i basis -- Use the formula omega_j = L_1+...+L_j from [FH, Section 15] -- Very similar to the formula for type C in [FH, Section 17.2] @@ -80,7 +84,8 @@ slnBasisElements = (n) -> ( ); - +-* +Not used anymore slnDualBasis = (n,B) -> ( -- Create the basis elements Hcoeffs := entries(inverse(1/1*cartanMatrix("A",n-1))); @@ -93,12 +98,12 @@ slnDualBasis = (n,B) -> ( sigma:=slnPermutation(n); apply(sigma, i -> unorderedDualBasis_i) ); - +*- slnBasisLabels = (n) -> ( -- Create the basis elements - Hbasis := apply(n-1, i -> "H_"|toString(i+1)); + Hbasis := apply(n-1, i -> "Ha_"|toString(i+1)); Xbasis := flatten apply(n, i -> delete(null,apply(n, j -> if i delete(null,apply(n, j -> if j ( Eijm(n+i,2*n,2*n+1) - Eijm(2*n,i,2*n+1)); ------------------------------------------------ +-- The next five functions about weights are for getting the basis elements +-- into lex level order +-- They aren't used for computing weights in representationWeights DtoLMatrixTypeB = memoize((n) -> ( M:=apply(n, i -> apply(n, j -> if j ( so2n1BasisLabels = (n) -> ( -- Create the labels - Hbasis := apply(n, i -> "H_a_"|toString(i)); + Hbasis := apply(n, i -> "Ha_"|toString(i)); Xbasis := flatten apply(n, i -> delete(null,apply(n, j -> if j!=i then "X_"|toString(i+1,j+1) ))); Ybasis := flatten apply(n, i -> delete(null,apply(n, j -> if i delete(null,apply(n, j -> if i ( Eijm(i,n+j,2*n)+Eijm(j,n+i,2*n)); typeCZijn = (i,j,n) -> ( Eijm(n+i,j,2*n)+Eijm(n+j,i,2*n)); +-- The next five functions about weights are for getting the basis elements +-- into lex level order +-- They aren't used for computing weights in representationWeights -- Want to change between Dynkin basis of the weight lattice and L_i basis -- Use the formula for type C in [FH, Section 17.2], p. 259 @@ -86,7 +89,7 @@ sp2nBasisElements = (n) -> ( ); - +-* sp2nDualBasis = (n) -> ( B:={}; Hbasis := apply(n, i -> 1/2*typeCHin(i,n)); @@ -100,12 +103,12 @@ sp2nDualBasis = (n) -> ( sigma:=sp2nPermutation(n); apply(sigma, i -> unorderedDualBasis_i) ); - +*- sp2nBasisLabels = (n) -> ( B:={}; - Hbasis := apply(n, i -> "H_"|toString(i)); + Hbasis := apply(n, i -> "Ha_"|toString(i)); Xbasis := flatten apply(n, i -> delete(null,apply(n, j -> if j!=i then "X_"|toString(i,j) ))); Ybasis := flatten apply(n, i -> delete(null,apply(n, j -> if i delete(null,apply(n, j -> if i ( Eijm(i,j,2*n)-Eijm(n+j,n+i,2*n)); typeDYijn = (i,j,n) -> ( Eijm(i,n+j,2*n)-Eijm(j,n+i,2*n)); typeDZijn = (i,j,n) -> ( Eijm(n+i,j,2*n)-Eijm(n+j,i,2*n)); - +-- The next five functions about weights are for getting the basis elements +-- into lex level order +-- They aren't used for computing weights in representationWeights -- Want to change between Dynkin basis of the weight lattice and L_i basis -- Use the formula for type D in [FH, ??] @@ -85,7 +87,7 @@ so2nBasisElements = (n) -> ( ); - +-* so2nDualBasis = (n) -> ( -- Create the basis elements Hbasis := apply(n, i -> typeDHin(i,n)); @@ -97,12 +99,12 @@ so2nDualBasis = (n) -> ( sigma:=so2nPermutation(n); apply(sigma, i -> unorderedDualBasis_i) ); - +*- so2nBasisLabels = (n) -> ( -- Create the basis elements - Hbasis := apply(n, i -> "H_"|toString(i)); + Hbasis := apply(n, i -> "Ha_"|toString(i)); Xbasis := flatten apply(n, i -> delete(null,apply(n, j -> if j!=i then "X_"|toString(i,j) ))); Ybasis := flatten apply(n, i -> delete(null,apply(n, j -> if i delete(null,apply(n, j -> if iLusztig); + LABLusztig = lieAlgebraBasis("A",2,"Method"=>"Lusztig"); LABLusztig#"BasisElements" /// @@ -1591,6 +1593,49 @@ doc /// +doc /// + Key + (dual,LieAlgebraRepresentation) + Headline + creates the dual representation of a Lie algebra representation + Usage + dual rho + Inputs + rho:LieAlgebraRepresentation + Outputs + rhostar:LieAlgebraRepresentation + Description + + Text + The dual representation (with respect to the dual basis) is the negative transpose. + + Example + sl6 = simpleLieAlgebra("A",5) + Std = standardRepresentation(sl6); + rho1 = exteriorPower(2,Std); + rho2 = dual rho1; + peek(rho1#"Module") + peek(rho2#"Module") + M1 = (rho1#"RepresentationMatrices")_6 + M2 = (rho2#"RepresentationMatrices")_6 + M2==-transpose(M1) + rho3 = exteriorPower(4,Std); + isomorphismOfRepresentations(rho2, rho3) +/// + +TEST /// + sl6 = simpleLieAlgebra("A",5) + Std = standardRepresentation(sl6); + rho1 = exteriorPower(2,Std); + rho2 = dual rho1; + assert((rho1#"Module")#"DecompositionIntoIrreducibles"==new VirtualTally from {{0, 1, 0, 0, 0} => 1}) + assert((rho2#"Module")#"DecompositionIntoIrreducibles"==new VirtualTally from {{0, 0, 0, 1, 0} => 1}) + assert(all(dim sl6, i -> (rho2#"RepresentationMatrices")_i==-transpose((rho1#"RepresentationMatrices")_i))) +/// + + + + doc /// Key trivialRepresentation @@ -2419,7 +2464,7 @@ doc /// Let $\rho: SL_n \rightarrow GL(V)$ be a representation where $V$ is irreducible of highest weight $\lambda$. Then $\dim (V \otimes V^{*})^{SL_n} = 1$. Text - We have a conjectural combinatorial formula for this invariant polynomial in the Gelfand-Tsetlin basis of $V$. See https://faculty.fordham.edu/dswinarski/InvariantPolynomialsAndMukaiModels/InvariantPolynomialConjecture.pdf. + We have a combinatorial formula for this invariant polynomial in the Gelfand-Tsetlin basis of $V$. See https://faculty.fordham.edu/dswinarski/InvariantPolynomialsAndMukaiModels/InvariantPolynomialFormula.pdf. Text @@ -2635,8 +2680,7 @@ doc /// TEST /// - assert(spinRepresentationMatrices(3)== -{matrix {{-1/2, 0, 0, 0, 0, 0, 0, 0}, {0, 1/2, 0, 0, 0, 0, 0, 0}, {0, 0, 1/2, 0, 0, 0, 0, 0}, {0, 0, 0, -1/2, 0, 0, 0, 0}, {0, 0, 0, 0, 1/2, 0, 0, 0}, {0, 0, 0, 0, 0, -1/2, 0, 0}, {0, 0, 0, 0, 0, 0, -1/2, 0}, {0, 0, 0, 0, 0, 0, 0, 1/2}}, matrix {{-1/2, 0, 0, 0, 0, 0, 0, 0}, {0, 1/2, 0, 0, 0, 0, 0, 0}, {0, 0, -1/2, 0, 0, 0, 0, 0}, {0, 0, 0, 1/2, 0, 0, 0, 0}, {0, 0, 0, 0, -1/2, 0, 0, 0}, {0, 0, 0, 0, 0, 1/2, 0, 0}, {0, 0, 0, 0, 0, 0, -1/2, 0}, {0, 0, 0, 0, 0, 0, 0, 1/2}}, matrix {{-1/2, 0, 0, 0, 0, 0, 0, 0}, {0, -1/2, 0, 0, 0, 0, 0, 0}, {0, 0, 1/2, 0, 0, 0, 0, 0}, {0, 0, 0, 1/2, 0, 0, 0, 0}, {0, 0, 0, 0, -1/2, 0, 0, 0}, {0, 0, 0, 0, 0, -1/2, 0, 0}, {0, 0, 0, 0, 0, 0, 1/2, 0}, {0, 0, 0, 0, 0, 0, 0, 1/2}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, -1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, -1, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, -1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0/1}}, matrix {{0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0/1}}}) + assert(spinRepresentationMatrices(3)=={map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, -1, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, -1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, -1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, -1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{-1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, -1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, -1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, -1, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, -1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, -1}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}),map(QQ^8,QQ^8,{{0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0}})}) /// @@ -2672,8 +2716,8 @@ doc /// TEST /// - assert(halfspinRepresentationMatrices(3,0)=={map(QQ^4,QQ^4,{{-1/2, 0, 0, 0}, {0, 1/2, 0, 0}, {0, 0, 1/2, 0}, {0, 0, 0, -1/2}}),map(QQ^4,QQ^4,{{-1/2, 0, 0, 0}, {0, 1/2, 0, 0}, {0, 0, -1/2, 0}, {0, 0, 0, 1/2}}),map(QQ^4,QQ^4,{{-1/2, 0, 0, 0}, {0, -1/2, 0, 0}, {0, 0, 1/2, 0}, {0, 0, 0, 1/2}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, -1}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 1, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, -1, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}})}) - assert(halfspinRepresentationMatrices(3,1)=={map(QQ^4,QQ^4,{{1/2, 0, 0, 0}, {0, -1/2, 0, 0}, {0, 0, -1/2, 0}, {0, 0, 0, 1/2}}),map(QQ^4,QQ^4,{{-1/2, 0, 0, 0}, {0, 1/2, 0, 0}, {0, 0, -1/2, 0}, {0, 0, 0, 1/2}}),map(QQ^4,QQ^4,{{-1/2, 0, 0, 0}, {0, -1/2, 0, 0}, {0, 0, 1/2, 0}, {0, 0, 0, 1/2}}),map(QQ^4,QQ^4,{{0, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, -1, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 1, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, -1}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}})}) + assert(halfspinRepresentationMatrices(3,0)=={map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, -1}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{-1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, -1}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 1, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, -1, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}})}) + assert(halfspinRepresentationMatrices(3,1)=={map(QQ^4,QQ^4,{{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{-1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}}),map(QQ^4,QQ^4,{{0, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, -1, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 1, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, -1}, {0, 0, 0, 0}, {0, 0, 0, 0}}),map(QQ^4,QQ^4,{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}})}) /// diff --git a/M2/Macaulay2/packages/LieAlgebraRepresentations/lieAlgebraBases.m2 b/M2/Macaulay2/packages/LieAlgebraRepresentations/lieAlgebraBases.m2 index 962296368c3..ef492c15cf3 100644 --- a/M2/Macaulay2/packages/LieAlgebraRepresentations/lieAlgebraBases.m2 +++ b/M2/Macaulay2/packages/LieAlgebraRepresentations/lieAlgebraBases.m2 @@ -146,7 +146,7 @@ makeDualBasisFunction = (LAB) -> ( -- level moved to lieAlgebraModules.m2 --- Implement fromula from de Graaf, page 98 +-- Implement formula from de Graaf, page 98 star = (j, a) -> ( R:=ring(a); ea:=first exponents(a); diff --git a/M2/Macaulay2/packages/LieAlgebraRepresentations/representationsCasimirReynolds.m2 b/M2/Macaulay2/packages/LieAlgebraRepresentations/representationsCasimirReynolds.m2 index b1e0784a85e..864c2bdbc8b 100644 --- a/M2/Macaulay2/packages/LieAlgebraRepresentations/representationsCasimirReynolds.m2 +++ b/M2/Macaulay2/packages/LieAlgebraRepresentations/representationsCasimirReynolds.m2 @@ -18,6 +18,14 @@ lieAlgebraRepresentation(LieAlgebraModule,LieAlgebraBasis,List):=(V,LAB,L) -> ( ); +dual LieAlgebraRepresentation := {} >> o -> rho -> ( + new LieAlgebraRepresentation from { + "Module"=>starInvolution(rho#"Module"), + "Basis"=>rho#"Basis", + "RepresentationMatrices"=>apply(rho#"RepresentationMatrices", M -> -transpose(M)) + } +); + trivialRepresentation = method( TypicalValue => LieAlgebraRepresentation diff --git a/M2/Macaulay2/packages/LieAlgebraRepresentations/spinRepresentations.m2 b/M2/Macaulay2/packages/LieAlgebraRepresentations/spinRepresentations.m2 index ef99396f316..f78ffd1110e 100644 --- a/M2/Macaulay2/packages/LieAlgebraRepresentations/spinRepresentations.m2 +++ b/M2/Macaulay2/packages/LieAlgebraRepresentations/spinRepresentations.m2 @@ -52,7 +52,11 @@ spinRepresentationMatrices(ZZ) := o -> (n) -> ( R:=o#CoefficientRing; I:=identityMatrix(2^n,R); WedgeBasis:=wedgeBasis(n); - Hbasis := apply(n, i -> wedgeByek(i+1,WedgeBasis,R)*contractByemk(i+1,WedgeBasis,R) - (1/2)*I); + -- First create the Hs in the first basis I used + Hs := apply(n, i -> wedgeByek(i+1,WedgeBasis,R)*contractByemk(i+1,WedgeBasis,R) - (1/2)*I); + -- Now make the basis Ha1..Han + Hbasis := apply(n-1, i -> Hs_i-Hs_(i+1)); + Hbasis = append(Hbasis, Hs_(n-2)+Hs_(n-1)); Xbasis := flatten apply(n, i -> delete(null,apply(n, j -> if j!=i then wedgeByek(i+1,WedgeBasis,R)*contractByemk(j+1,WedgeBasis,R)))); Ybasis := flatten apply(n, i -> delete(null,apply(n, j -> if i delete(null,apply(n, j -> if i "David Eisenbud", Email => "de@msri.org"}, {Name => "Navid Nemati", Email => "Navid.Nemati@inria.fr"}}, Headline => "find the multigraded truncations that give linear resolutions", - PackageExports => {"OldChainComplexes", "Truncations", "TateOnProducts"}, + PackageExports => {"Complexes", "TateOnProducts", "Truncations"}, DebuggingMode => false, Keywords => {"Commutative Algebra"}, Certification => { @@ -168,9 +168,9 @@ coarseMultigradedRegularity' ChainComplex := F -> ( partialRegularities = method() partialRegularities Module := M -> partialRegularities res prune M -partialRegularities ChainComplex := F-> ( +partialRegularities Complex := F-> ( t := degreeLength ring F; - range := toList(min F..max F-1); + range := toList(min F..max F); apply(t, i -> max flatten apply(range, j -> ( apply(degrees(F_j), ell -> ell_i - j) ))) @@ -179,7 +179,7 @@ partialRegularities ChainComplex := F-> ( -* --version that allows generation in multiple multidegrees isLinearComplex = method() -isLinearComplex ChainComplex := F -> ( +isLinearComplex Complex := F -> ( if F == 0 then return true; t := degreeLength ring F; range := toList(min F..max F-1); @@ -191,11 +191,12 @@ isLinearComplex ChainComplex := F -> ( isLinearComplex = method() --requires generators in single degree -isLinearComplex ChainComplex := F -> ( +isLinearComplex Complex := F -> ( + F = prune F; -- removes trailing zero modules. if F == 0 then return true; t := degreeLength ring F; - range := toList(min F..max F-1); - if #(unique degrees F_(range_0)) != 1 then return false; + range := toList(min F..max F); + if #(unique degrees F_(min F)) != 1 then return false; dF := apply(range, i -> (degrees(F_i))/sum); mindF := dF_(min F)_0; all(range, i -> max(dF_i) === mindF+i-range_0) @@ -330,13 +331,13 @@ isQuasiLinear BettiTally := opts -> T -> ( any(allowed_hdeg, em -> all(em + d - ell_1, j -> j >=0)) )) ) -isQuasiLinear ChainComplex := opts -> F -> isQuasiLinear(betti F, opts) +isQuasiLinear Complex := opts -> F -> isQuasiLinear(betti F, opts) ------------------------- --bounds on linear truncations region supportOfTor = method() -supportOfTor ChainComplex := F -> ( +supportOfTor Complex := F -> ( for i from min F to max F list ( degs := unique degrees F_i; if degs == {} then continue else degs @@ -508,7 +509,7 @@ M = { {{{0,0}},{{-1,0},{0,-1}},{{-2,0},{-1,-1},{0,-2}},{{0,-4}},{}} }; --twists appearing in chain complexes -C = apply(M, L -> chainComplex(apply(#L - 1, i -> map(S^(L_i),S^(L_(i+1)),0)))); +C = apply(M, L -> complex(apply(#L - 1, i -> map(S^(L_i),S^(L_(i+1)),0)))); A = { false, true, @@ -646,7 +647,7 @@ doc /// Key partialRegularities (partialRegularities,Module) - (partialRegularities,ChainComplex) + (partialRegularities,Complex) Headline calculates Castelnuovo-Mumford regularity in each component of a multigrading Usage @@ -654,7 +655,7 @@ Usage Inputs M: Module a multigraded module - F: ChainComplex + F: Complex the minimal free resolution of a module Outputs : List @@ -682,13 +683,13 @@ SeeAlso doc /// Key isLinearComplex - (isLinearComplex,ChainComplex) + (isLinearComplex,Complex) Headline tests whether a complex of graded modules is linear Usage isLinearComplex F Inputs - F: ChainComplex + F: Complex Outputs : Boolean true if @TT "F"@ is a linear complex, false if it is not @@ -1022,7 +1023,7 @@ Key IrrelevantIdeal (isQuasiLinear,List,Module) (isQuasiLinear,BettiTally) - (isQuasiLinear,ChainComplex) + (isQuasiLinear,Complex) [isQuasiLinear,IrrelevantIdeal] Headline checks whether degrees in the resolution of a truncation are at most those of the irrelevant ideal @@ -1055,7 +1056,7 @@ Description isQuasiLinear(d,M) Text The condition comes from Theorem 2.9 in Berkesch, Erman, and Smith's paper "Virtual Resolutions for a Product of Projective Spaces." - The @TT "ChainComplex"@ and @TT "BettiTally"@ usages take the resolution of the truncation (or some other virtual resolution) directly. + The @TT "Complex"@ and @TT "BettiTally"@ usages take the resolution of the truncation (or some other virtual resolution) directly. Caveat If the resolution of the truncation is longer than the resolution of $S/B$ then @TT "isQuasiLinear"@ will return false. SeeAlso @@ -1067,7 +1068,7 @@ doc /// Key supportOfTor (supportOfTor,Module) - (supportOfTor,ChainComplex) + (supportOfTor,Complex) Headline computes multidegrees in the support of Tor_i(M,k), where k is the residue field Usage @@ -1075,7 +1076,7 @@ Usage Inputs M: Module a multigraded module - F: ChainComplex + F: Complex the minimal resolution of a module Outputs L: List diff --git a/M2/Macaulay2/packages/LocalRings.m2 b/M2/Macaulay2/packages/LocalRings.m2 index 010f7cc594c..dbe7f41bbfb 100644 --- a/M2/Macaulay2/packages/LocalRings.m2 +++ b/M2/Macaulay2/packages/LocalRings.m2 @@ -36,8 +36,7 @@ newPackage( }, Headline => "operations over a local ring R_p", Keywords => {"Commutative Algebra"}, - PackageImports => {"OldChainComplexes"}, - PackageExports => {"PruneComplex", "Saturation", "Complexes"}, + PackageExports => {"Saturation", "Complexes"}, AuxiliaryFiles => true ) diff --git a/M2/Macaulay2/packages/LocalRings/doc.m2 b/M2/Macaulay2/packages/LocalRings/doc.m2 index 63b8115a2c5..6d73b479c92 100644 --- a/M2/Macaulay2/packages/LocalRings/doc.m2 +++ b/M2/Macaulay2/packages/LocalRings/doc.m2 @@ -47,7 +47,7 @@ Description @TO syz@, @TO resolution@, @TO mingens@, @TO minimalPresentation@, @TO trim@, @TO (length, Module)@, @TO isSubset@, @TO inducedMap@, @TO (quotient, Matrix, Matrix)@, @TO (remainder, Matrix, Matrix)@, @TO "Saturation :: quotient(Module,Module)"@, @TO saturate@, @TO annihilator@. - Most of these routines rely on the functions @TO liftUp@ and @TO "PruneComplex :: pruneComplex"@ and + Most of these routines rely on the functions @TO liftUp@ and @TO "Complexes :: pruneComplex"@ and take advantage of Nakayama's lemma and flatness of local rings. In addition, methods such as @TO map@, @TO modulo@, @TO subquotient@, @TO kernel@, @TO cokernel@, @@ -57,7 +57,7 @@ Caveat Quotients of local rings are not implemented yet. Moreover, certain functions (such as symbol%, radical, minimalPrimes, leadingCoefficient) are ambiguous or not yet defined. SeeAlso - "PruneComplex :: PruneComplex" + "Complexes :: pruneComplex" Subnodes LocalRing /// diff --git a/M2/Macaulay2/packages/LocalRings/examples.m2 b/M2/Macaulay2/packages/LocalRings/examples.m2 index 57d53295d7b..0cdef93e2a8 100644 --- a/M2/Macaulay2/packages/LocalRings/examples.m2 +++ b/M2/Macaulay2/packages/LocalRings/examples.m2 @@ -217,7 +217,7 @@ assert({1,1,0,0} == for i from 0 to 3 list hilbertSamuelFunction(max RP, N, i)) -- III. Additional examples: restart -debug needsPackage "PruneComplex" +debug needsPackage "Complexes" needsPackage "LocalRings" R = ZZ/32003[a..f] I = ideal"abc-def,ab2-cd2-c,acd-b3-1" @@ -562,17 +562,12 @@ length N ------------------------------------------------------------------------------------------------------- restart needsPackage "LocalRings" -needsPackage "PruneComplex" debugLevel=1 -debug PruneComplex debug LocalRings -elapsedTime check PruneComplex -- 17 elapsedTime check LocalRings -- 15 -uninstallPackage "PruneComplex" uninstallPackage "LocalRings" -installPackage "PruneComplex" installPackage "LocalRings" ------------------------------------------------------------------------------------------------------- @@ -584,7 +579,7 @@ make -j4 all-in-d all-in-e all-in-bin restart debug Core -debug needsPackage "PruneComplex" +debug needsPackage "Complexes" debug needsPackage "LocalRings" R = ZZ/32003[vars(0..3)] I = monomialCurveIdeal(R, {1, 3, 4}) diff --git a/M2/Macaulay2/packages/LocalRings/legacy.m2 b/M2/Macaulay2/packages/LocalRings/legacy.m2 index 4b735594f8f..3a721f7be79 100644 --- a/M2/Macaulay2/packages/LocalRings/legacy.m2 +++ b/M2/Macaulay2/packages/LocalRings/legacy.m2 @@ -97,33 +97,47 @@ localResolution Module := options -> (M) -> ( if not R.?maxIdeal then error "use setMaxIdeal first!"; maxlength := resolutionLength(R,options); if M.cache.?localResolution - then (C := M.cache.localResolution; - C.length = length C) - else ( - C = new ChainComplex; - C.ring = R; - -- f := presentation M; - -- we have replaced presentation in the previous line by minimalPresentation. This has fixed a reported bug where - -- localResolution returned a non-minimal presentation. - -- We have included the above mentioned example in the tests section. - f := relations minimalPresentation M; - C#0 = target f; - C#1 = source f; - C.dd#1 = f; - M.cache.localResolution = C; - C.length = 1; - ); - i := C.length; - while i < maxlength and C.dd_i != 0 do ( - g := localsyz C.dd_i; + then ( + C := M.cache.localResolution; + -- if a longer length is desired: we need to continue processing below + if true then return M.cache.localResolution; + ); + if not M.cache#?"LocalResolutionMaps" then ( + M.cache#"LocalResolutionMaps" = new MutableHashTable; + ----M.cache#"LocalResolutionModules" = new MutableHashTable; + -- C = new ChainComplex; + -- C.ring = R; + -- --f := presentation M; + -- we have replaced presentation in the previous line by minimalPresentation. This has fixed a reported bug where + -- localResolution returned a non-minimal presentation. + -- We have included the above mentioned example in the tests section. + f := relations minimalPresentation M; + ----M.cache#"LocalResolutionModules"#0 = target f; + ----M.cache#"LocalResolutionModules"#1 = source f; + M.cache#"LocalResolutionMaps"#1 = f; + -- C#0 = target f; + -- C#1 = source f; + -- C.dd#1 = f; + -- M.cache.localResolution = C; + -- C.length = 1; + -- i := C.length; + ); + i := max keys M.cache#"LocalResolutionMaps"; + while i < maxlength and M.cache#"LocalResolutionMaps"#i != 0 do ( + g := localsyz M.cache#"LocalResolutionMaps"#i; shield ( i = i+1; - C.dd#i = g; - C#i = source g; - C.length = i; + ----M.cache#"LocalResolutionModules"#i = source g; + M.cache#"LocalResolutionMaps"#i = g; + -- C.dd#i = g; + -- C#i = source g; + -- C.length = i; ); ); - C) + len := max keys M.cache#"LocalResolutionMaps"; + resmaps := for i from 1 to len list M.cache#"LocalResolutionMaps"#i; + M.cache.localResolution = complex resmaps -- also returns it + ) beginDocumentation() @@ -272,7 +286,7 @@ document { } | apply(keys options localResolution, o -> toString o => {"see ", TO [resolution, o]}), PARA "This method has option inputs that it inherits from ", TO resolution, ".", Outputs => { - ChainComplex + Complex }, PARA "This function iterates ", TO localsyz, " to obtain a resolution over the local ring.", EXAMPLE lines /// diff --git a/M2/Macaulay2/packages/LocalRings/localring.m2 b/M2/Macaulay2/packages/LocalRings/localring.m2 index 334df75e5d7..d40d3cdfe2b 100644 --- a/M2/Macaulay2/packages/LocalRings/localring.m2 +++ b/M2/Macaulay2/packages/LocalRings/localring.m2 @@ -17,7 +17,7 @@ -- -- NOTE : Elementary operations in a local ring are defined in e/localring.hpp and handled here. -- Most operations (syz, res, mingens, etc.) are in packages/LocalRings.m2 and rely heavily --- on liftUp in packages/LocalRings.m2 and on pruneComplex from packages/PruneComplex.m2 +-- on liftUp in packages/LocalRings.m2 and on pruneComplex from packages/Complexes/PruneComplex.m2. -- Legacy code from 2008 is stored in packages/LocalRings/legacy.m2 and is still loaded. --------------------------------------------------------------------------- @@ -91,5 +91,14 @@ localRing(EngineRing, Ideal) := (R, P) -> if R.?generatorExpressions then RP.generatorExpressions = R.generatorExpressions; if R.?indexSymbols then RP.indexSymbols = applyValues(R.indexSymbols, r -> promote(r,RP)); if R.?indexStrings then RP.indexStrings = applyValues(R.indexStrings, r -> promote(r,RP)); + setupPromote( + f -> numerator f / denominator f, + RP, frac R); + liftFromFractionFieldMap := map(RP, frac R); + setupLift(f -> ( + if isMember(denominator f, RP.maxIdeal) + then error "expected a denominator outside the maximal ideal" + else liftFromFractionFieldMap f), + frac R, RP); RP ) diff --git a/M2/Macaulay2/packages/LocalRings/tests.m2 b/M2/Macaulay2/packages/LocalRings/tests.m2 index b32ad776aea..c7164acb6d9 100644 --- a/M2/Macaulay2/packages/LocalRings/tests.m2 +++ b/M2/Macaulay2/packages/LocalRings/tests.m2 @@ -1,7 +1,7 @@ --============================ Tests Sections ===================================-- TEST /// -- test for localRing, res, **, == - debug needsPackage "PruneComplex" + debug needsPackage "Complexes" R = ZZ/32003[vars(0..3)] I = monomialCurveIdeal(R, {1, 3, 4}) CI = freeResolution I @@ -190,7 +190,7 @@ TEST /// -- chain homotopy over local rings x*y^2+5019*x*y+3216*y^2-13233*z^2+3723*x, 13424*x^2*y+936*x*y^2+10667*y*z^2+14913*x^2-8521*x*y-15541*y^2-12289*x}}) - F = chainComplex { gens J, syz gens J, syz syz gens J } + F = complex { gens J, syz gens J, syz syz gens J } f0 = J_0 s0 = map(R^1, 0, 0) L = for i to 3 list ( @@ -624,6 +624,17 @@ end-- gens gb Iloc /// +TEST /// +-- promoting/lifting to/from fraction field +S = QQ[x] +p = ideal x +R = S_p +F = frac R +assert(promote(x_R, F) === x_F) +assert(lift(x_F, R) === x_R) +assert not liftable(1/x, R) +/// + end-- -- Development stuff diff --git a/M2/Macaulay2/packages/MRDI.m2 b/M2/Macaulay2/packages/MRDI.m2 new file mode 100644 index 00000000000..217cc9f6609 --- /dev/null +++ b/M2/Macaulay2/packages/MRDI.m2 @@ -0,0 +1,1069 @@ +-- MRDI package for Macaulay2 +-- Copyright (C) 2025-2026 Doug Torrance + +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License +-- as published by the Free Software Foundation; either version 2 +-- of the License, or (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. + +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, see . + +-- This package was originally written for use by the Macaulean project and its +-- development was funded by Renaissance Philantropy's AI for Math Fund. +-- https://www.renaissancephilanthropy.org/ai-for-math-fund + +newPackage( + "MRDI", + Version => "0.1", + Date => "April 25, 2026", + Headline => "serializing algebraic data with .mrdi files", + Authors => { + { + Name => "Doug Torrance", + Email => "dtorrance@piedmont.edu", + HomePage => "https://webwork.piedmont.edu/~dtorrance" + }}, + PackageImports => {"JSON"}, + Keywords => {"System"}) + +export { + -- methods + "addLoadMethod", + "addNamespace", + "addSaveMethod", + "loadMRDI", + "saveMRDI", + "validateMRDI", + + -- symbols + "Namespace", + "ToString", + "UseID", + } + +importFrom(Core, { + "noMethod", + "nullf"}) + +------------ +-- saving -- +------------ + +-- universally unique identifiers +-- https://www.rfc-editor.org/rfc/rfc9562 +uuidsByThing = new MutableHashTable +thingsByUuid = new MutableHashTable +pad0 = (n, s) -> concatenate((n - #s):"0", s) +randnibbles = k -> pad0(k, changeBase(random 2^(4*k), 16)) +thingToUuid = x -> uuidsByThing#x ??= ( + i := concatenate( + randnibbles 8, "-", randnibbles 4, "-4", randnibbles 3, "-", + changeBase(8 + random 4, 16), randnibbles 3, "-", randnibbles 12); + thingsByUuid#i = x; + i) +uuidToThing = (i, f) -> thingsByUuid#i ??= ( + x := f(); + uuidsByThing#x = i; + x) +isUuid = i -> match("^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$", i) + +namespaces = new MutableHashTable +loadMethods = new MutableHashTable + +addNamespace = method() +addNamespace(String, String, String) := (ns, url, v) -> ( + namespaces#ns = {url, v}; + loadMethods#ns = new MutableHashTable; + Thing#{ns, UseID} = false;) + +addNamespace("Macaulay2", "https://macaulay2.com", version#"VERSION") +addNamespace("Oscar", "https://github.com/oscar-system/Oscar.jl", "1.6.0") + +saveMRDI = method( + Dispatch => Thing, + Options => { + FileName => null, + ToString => true, + Namespace => "Macaulay2"}) +saveMRDI Thing := o -> x -> ( + if not namespaces#?(o.Namespace) + then error("unknown namespace: ", o.Namespace); + refs := new MutableHashTable; + mrdi := toMRDI(o.Namespace, x, refs); + r := (if o.ToString then toJSON else identity) merge( + hashTable { + "_ns" => hashTable { + o.Namespace => namespaces#(o.Namespace)}, + if useID(o.Namespace, x) then "id" => thingToUuid x, + if #refs > 0 then "_refs" => new HashTable from refs}, + mrdi, + (x, y) -> error "unexpected key collision"); + if o.FileName =!= null then o.FileName << r << endl << close; + r) + +-- low-level unexported function +-- input: ns: string (namespace) +-- x: the object to serialize +-- refs: mutable hash table (keys = uuids of refs) +-- output: hash table representing x (type & data only) +-- side effect: new refs are added to refs +-- use addSaveMethod to define for a given class +toMRDI = (ns, x, refs) -> ( + if (f := lookup({ns, saveMRDI}, class x)) === null + then error noMethod({ns, saveMRDI}, x,) + else f(x, refs)) + +useID = (ns, x) -> ( + if (u := lookup({ns, UseID}, class x)) === null + then error noMethod({ns, UseID}, x,) + else if not instance(u, Boolean) + then error("expected ", {ns, UseID}, " for ", class x, + " to be true or false") + else u) + +toMRDIorUuid = (ns, x, refs) -> ( + r := toMRDI(ns, x, refs); + if useID(ns, x) then ( + i := thingToUuid x; + refs#i = r; + i) + else r) + +-- low-level unexported method +-- same interface as toMRDI, but attempts to separate out objects we'd like +-- to serialize from json-level objects that we're using to describe +-- other objects +processMRDI = method() +processMRDI(String, Thing, MutableHashTable) := toMRDIorUuid +processMRDI(String, String, MutableHashTable) := (ns, x, refs) -> x +processMRDI(String, Nothing, MutableHashTable) := (ns, x, refs) -> null +processMRDI(String, ZZ, MutableHashTable) := (ns, x, refs) -> toString x +processMRDI(String, List, MutableHashTable) := (ns, x, refs) -> ( + if class x === List then apply(x, y -> processMRDI(ns, y, refs)) + else toMRDIorUuid(ns, x, refs)) +processMRDI(String, HashTable, MutableHashTable) := (ns, x, refs) -> ( + if class x === HashTable then applyValues(x, v -> processMRDI(ns, v, refs)) + else toMRDIorUuid(ns, x, refs)) + + +addSaveMethod = method(Options => { + UseID => false, + Name => toString @@ class, + Namespace => "Macaulay2"}) + +getType = method() +getType(Function, Thing) := (f, x) -> f x +getType(String, Thing) := (s, x) -> s + + +addSaveMethod Type := o -> T -> ( + addSaveMethod(T, nullf, nullf, o)) +addSaveMethod(Type, Function) := o -> (T, dataf) -> ( + addSaveMethod(T, nullf, dataf, o)) +addSaveMethod(Type, Function, Function) := o -> (T, paramsf, dataf) -> ( + T#{o.Namespace, saveMRDI} = (x, refs) -> ( + if o.UseID then thingToUuid x; -- save uuid + params := processMRDI(o.Namespace, paramsf x, refs); + data := processMRDI(o.Namespace, dataf x, refs); + hashTable { + "_type" => ( + if params =!= null then hashTable { + "name" => getType(o.Name, x), + "params" => params} + else getType(o.Name, x)), + if data =!= null then "data" => data}); + T#{o.Namespace, UseID} = o.UseID;) + +addSaveMethod(ZZ, identity) + +addSaveMethod(Ring, + R -> ( + if isMember(R, {ZZ, QQ}) then toString R + else error "not implemented yet")) + +addSaveMethod(QuotientRing, + R -> ( + if isFinitePrimeField R then char R + else error "not implemented yet")) + +addSaveMethod(GaloisField, + F -> hashTable { + "char" => F.char, + "degree" => F.degree}, + UseID => true) + +addSaveMethod(PolynomialRing, + coefficientRing, + R -> hashTable { + "variables" => toString \ gens R}, + UseID => true) + +mrdiCoefficient = method() +mrdiCoefficient ZZ := identity +mrdiCoefficient QQ := x -> {numerator x, denominator x} + +mrdiListForm = f -> apply(listForm f, + (mon, coeff) -> {mon, mrdiCoefficient coeff}) + +addSaveMethod(RingElement, + ring, + mrdiListForm, + Name => "RingElement") + +addSaveMethod(Ideal, + ring, + I -> apply(I_*, mrdiListForm)) + +addSaveMethod(Matrix, ring, A -> apply(entries A, row -> mrdiListForm \ row)) + +------------- +-- loading -- +------------- + +uuidsToCreate = new MutableHashTable + +loadMRDI = method() +-- TODO: schema validation +loadMRDI String := loadMRDI @@ fromJSON +loadMRDI HashTable := r -> ( + ns := first keys r#"_ns"; + if not loadMethods#?ns then error("unknown namespace: ", ns); + -- save info about refs we haven't created yet + if r#?"_refs" then scanPairs(r#"_refs", + (i, s) -> if not thingsByUuid#?i then uuidsToCreate#i = s); + if r#?"id" then uuidToThing(r#"id", () -> fromMRDI(ns, r)) + else fromMRDI(ns, r)) + +-- unexported helper function +-- inputs: string (namespace) and object to de-serialize +-- outputs: a de-serialized M2 object +fromMRDI = method() +fromMRDI(String, HashTable) := (ns, r) -> ( + -- if it has a _type key, then it's an object to de-serialize + if r#?"_type" then ( + (name, params) := ( + if instance(r#"_type", HashTable) + then (r#"_type"#"name", r#"_type"#"params") + else (r#"_type", null)); + if not loadMethods#ns#?name then error ("unknown type: ", name); + loadMethods#ns#name( + if params =!= null then fromMRDI(ns, params), + if r#?"data" then fromMRDI(ns, r#"data"))) + -- otherwise, de-serialize its values + else applyValues(r, fromMRDI_ns)) +fromMRDI(String, String) := (ns, s) -> ( + -- if the string is a uuid, then return the corresponding object + if isUuid s then uuidToThing(s, () -> ( + if uuidsToCreate#?s + then fromMRDI(ns, remove(uuidsToCreate, s)) + else error("unknown uuid: ", s))) + -- otherwise, just return the string + else s) +fromMRDI(String, List) := (ns, x) -> apply(x, fromMRDI_ns) + +-- input function takes two args: params (de-serialized) & data +addLoadMethod = method(Options => {Namespace => "Macaulay2"}) +addLoadMethod(String, Function) := o -> (type, f) -> ( + if not loadMethods#?(o.Namespace) + then error("unknown namespace: ", o.Namespace); + loadMethods#(o.Namespace)#type = f) +addLoadMethod(List, Function) := o -> (types, f) -> ( + scan(types, type -> addLoadMethod(type, f, o))) + +addLoadMethod("ZZ", (params, data) -> value data) +addLoadMethod("Ring", (params, data) -> ( + if data == "ZZ" then ZZ + else if data == "QQ" then QQ + else error "unknown ring")) +addLoadMethod("QuotientRing", (params, data) -> ZZ/(value data)) +addLoadMethod("GaloisField", (params, data) -> ( + GF(value data#"char", value data#"degree"))) +addLoadMethod("PolynomialRing", (params, data) -> ( + params[Variables => data#"variables"])) + +mrdiToCoefficient = method(Dispatch => Type) +mrdiToCoefficient ZZ := R -> value +mrdiToCoefficient QQ := R -> a -> value a#0 / value a#1 + +mrdiToPolynomial = (R, f) -> ( + if #f == 0 then 0_R + else sum(f, term -> times( + (mrdiToCoefficient coefficientRing R) term#1, + R_(value \ toList term#0)))) +addLoadMethod("RingElement", (params, data) -> ( + mrdiToPolynomial(params, data))) +addLoadMethod("Ideal", (params, data) -> ( + ideal apply(data, mrdiToPolynomial_params))) +addLoadMethod("Matrix", (params, data) -> ( + matrix applyTable(data, mrdiToPolynomial_params))) + +-- for debugging w/ "methods" +LoadMethod = new SelfInitializingType of List +net LoadMethod := lookup(net, Sequence) +locate LoadMethod := x -> locate loadMethods#(x#0#0)#(x#1) +code LoadMethod := code @@ locate + +importFrom(Core, "previousMethodsFound") +oldmethods = lookup(methods, List) +methods List := x -> ( + if #x == 2 and x#1 === loadMRDI then ( + previousMethodsFound = new NumberedVerticalList from ( + if loadMethods#?(x#0) then ( + apply(keys loadMethods#(x#0), k -> LoadMethod(x, k))) + ?? {})) + else oldmethods x) + +----------- +-- Oscar -- +----------- + +oscarRings = hashTable { + ZZ => "ZZRing", + QQ => "QQField", + } +addSaveMethod(Ring, + Name => R -> oscarRings#R ?? error "unknown ring", + Namespace => "Oscar") + +addSaveMethod(ZZ, + x -> ZZ, + toString, + Name => "ZZRingElem", + Namespace => "Oscar") + +addSaveMethod(QQ, + x -> QQ, + x -> concatenate(toString numerator x, "//", toString denominator x), + Name => "QQFieldElem", + Namespace => "Oscar") + +-- Oscar differentiates between univariate and multivariate polynomial rings, +-- but multivariate rings can have just 1 variable, so we just always use that +addSaveMethod(PolynomialRing, + coefficientRing, + R -> hashTable {"symbols" => toString \ gens R}, + Name => "MPolyRing", + UseID => true, + Namespace => "Oscar") + +addSaveMethod(RingElement, + ring, + f -> apply(listForm f, mon -> {mon#0, mon#1}), + Name => "MPolyRingElem", + Namespace => "Oscar") + +addLoadMethod("Base.Int", (params, data) -> value data, Namespace => "Oscar") +addLoadMethod("ZZRingElem", + (params, data) -> value data, + Namespace => "Oscar") +addLoadMethod("QQFieldElem", + (params, data) -> ( + x := separate("//", data); + if #x == 2 then value x#0 / value x#1 + else value x#0 / 1), + Namespace => "Oscar") +addLoadMethod("String", (params, data) -> data, Namespace => "Oscar") +addLoadMethod("Float64", (params, data) -> value data, Namespace => "Oscar") +addLoadMethod("ZZRing", (params, data) -> ZZ, Namespace => "Oscar") +addLoadMethod("QQField", (params, data) -> QQ, Namespace => "Oscar") +addLoadMethod("FiniteField", + (params, data) -> ( + if params =!= null then error "not implemented yet" + else ZZ/(value data)), + Namespace => "Oscar") +addLoadMethod({"PolyRing", "MPolyRing"}, + (params, data) -> ( + -- TODO: handled indexed variables, e.g., x[1], x[2], x[3] + params[Variables => data#"symbols"]), + Namespace => "Oscar") +addLoadMethod({"PolyRingElem", "MPolyRingElem"}, + (params, data) -> mrdiToPolynomial(params, data), + Namespace => "Oscar") + +---------------- +-- validating -- +---------------- + +-- https://www.oscar-system.org/schemas/mrdi.json + +-- all JSON objects must have keys as strings +validateObject = x -> scanKeys(x, k -> ( + if not instance(k, String) + then error("expected all keys to be strings, but got ", k))) + +validateData = method() +validateData Thing := x -> error("invalid data: ", x) +validateData String := x -> null +validateData HashTable := x -> ( + validateObject x; + scanKeys(x, k -> if not match("^[a-zA-Z0-9_]*", k) + then error("expected an alphanumeric key, but got ", k))) +validateData List := x -> validateData \ x +-- TODO: validate polymake schema + +validateMRDI = method() +validateMRDI Thing := x -> error("expected an object, but got ", x) +validateMRDI String := validateMRDI @@ fromJSON +validateMRDI HashTable := x -> ( + validateObject x; + if not x#?"_type" then error "expected a '_type' key"; + if instance(x#"_type", String) then null + else if instance(x#"_type", HashTable) then ( + validateObject x#"_type"; + if x#"_type"#?"name" then ( + if not instance(x#"_type"#"name", String) + then error("expected value of 'name' to be a string")); + if x#"_type"#?"params" then validateData x#"_type"#"params") + else error("expected value of '_type' to be a string or object"); + scan({"_ns", "_refs"}, k -> ( + if x#?k then ( + if not instance(x#k, HashTable) + then error("expected value of '", k, "' to be an object"); + validateObject x#k))); + if x#?"_refs" then scanPairs(x#"_refs", (k, v) -> ( + if not isUuid k + then error("expected all keys of '_refs' to be UUID's, but got ", k); + validateMRDI v)); + ) + +------------------- +-- documentation -- +------------------- + +beginDocumentation() + +doc /// +Key + MRDI +Headline + serialization using the mrdi file format +Description + Text + The @EM "MRDI"@ package provides tools for serializing and deserializing + mathematical objects in Macaulay2 using the MRDI file format, a JSON-based + format for storing and sharing results in computer algebra without losing + accuracy. The format was developed as part of the + @HREF("https://www.mardi4nfdi.de/", + "Mathematics Research Data Initiative (MaRDI)")@ + and is described in the paper: + + Antony Della Vecchia, Michael Joswig, and Benjamin Lorenz, + @HREF("https://doi.org/10.1007/978-3-031-64529-7_25", + "A FAIR file format for mathematical software")@, + @EM "Mathematical software—ICMS 2024"@, 234–244, Lecture Notes in + Comput. Sci., 14749, Springer, Cham. + + Each serialized object carries a namespace (@TT "_ns"@) identifying + the originating software system and version, a type descriptor + (@TT "_type"@) that may include recursive parameters, the actual + data, and optionally a set of references (@TT "_refs"@) keyed by + UUIDs. + + The package can serialize and deserialize integers, rings + (@TO ZZ@, @TO QQ@, finite prime fields, Galois fields), polynomial rings, + ring elements, ideals, and matrices. It can also load and save objects + using the OSCAR namespace, enabling interoperability with the + @HREF("https://www.oscar-system.org/", "OSCAR")@ computer algebra system. + + The namespace mechanism also makes it possible to define custom + serialization formats for exchanging data with other software systems. + Example + R = QQ[x,y,z,w] + I = monomialCurveIdeal(R, {1,2,3}) + s = saveMRDI I + loadMRDI s +Subnodes + saveMRDI + loadMRDI + Namespace + validateMRDI +/// + +doc /// +Key + saveMRDI + (saveMRDI, Thing) + [saveMRDI, FileName] + [saveMRDI, Namespace] + [saveMRDI, ToString] + ToString +Headline + serialize a Macaulay2 object to MRDI JSON format +Usage + saveMRDI x +Inputs + x:Thing + a Macaulay2 object to serialize (must have a save method + registered via @TO addSaveMethod@) + FileName => String + if given, the JSON output is written to this file + Namespace => String + the namespace to use for serialization + ToString => Boolean + if @TO true@, then the output is a JSON string; + if @TO false@, then the output is @ofClass HashTable@ representing + the JSON structure +Outputs + :{String, HashTable} +Description + Text + This function serializes a Macaulay2 object into a JSON string following + the MRDI file format specification. The output includes a namespace + (@TT "_ns"@) identifying the software system and its version, a type + descriptor (@TT "_type"@), and the data. + + For parametric types such as @TO PolynomialRing@, + @TO RingElement@, @TO Ideal@, and @TO Matrix@, the type + descriptor includes a @TT "params"@ field referencing the + parent ring. Rings and other objects marked with + @TO UseID@ are assigned UUIDs so that multiple objects + sharing the same ring refer to it by UUID rather than + repeating its full description. + Text + We may serialize a simple integer. + Example + saveMRDI 5 + Text + We can also serialize a polynomial ring element. + Example + R = QQ[x,y,z]; + f = x^2 + y*z - 3*x + saveMRDI f + Text + Ideals are serialized similarly. + Example + I = ideal(x^2 - y, y^2 - z) + saveMRDI I + Text + The output can be written directly to a file using the @TO FileName@ option. + Example + fn = temporaryFileName() | ".mrdi" + saveMRDI(f, FileName => fn) + removeFile fn + Text + The @TO Namespace@ option selects a different namespace for + serialization, such as OSCAR. + Example + saveMRDI(5, Namespace => "Oscar") + Text + Setting @TT "ToString => false"@ returns the hash table + representation instead of a JSON string. This + Example + saveMRDI(5, ToString => false) + Text + To see which types have built-in save methods for a given namespace, + call @TO methods@ as follows. + Example + methods {"Macaulay2", saveMRDI} + methods {"Oscar", saveMRDI} + Text + Additional types can be supported by calling @TO addSaveMethod@. +Caveat + Not all Macaulay2 types have save methods defined. Attempting + to serialize an unsupported type will produce an error. + Quotient rings other than finite prime fields are not yet + supported. +Subnodes + addSaveMethod +SeeAlso + loadMRDI +/// + +doc /// +Key + loadMRDI + (loadMRDI, String) + (loadMRDI, HashTable) +Headline + deserialize a Macaulay2 object from MRDI format +Usage + loadMRDI s +Inputs + s:{String, HashTable} + a JSON string in the MRDI file format or + parsed JSON hash table (e.g., from @TO "JSON::fromJSON"@) +Outputs + :Thing -- the deserialized Macaulay2 object +Description + Text + This method parses an MRDI-formatted JSON string (or hash table) and + reconstructs the corresponding Macaulay2 object. The namespace field + (@TT "_ns"@) in the JSON determines which set of load methods is used for + deserialization. + + This function handles the @TT "_refs"@ section of the JSON + to reconstruct shared references via UUIDs. For example, + when an ideal and a ring element both refer to the same + polynomial ring, the ring is constructed once and shared. + Text + A polynomial ring element can be round-tripped through the format. + Example + R = QQ[x,y,z,w]; + f = x^2 + y*z + s = saveMRDI f + g = loadMRDI s + f === g + Text + The same works for ideals. + Example + I = monomialCurveIdeal(R, {1,2,3}) + s = saveMRDI I + J = loadMRDI s + I === J + Text + Objects can be loaded from a file as well using @TO get@. + Example + fn = temporaryFileName() | ".mrdi" + saveMRDI(I, FileName => fn) + J = loadMRDI get fn + I === J + removeFile fn + Text + A parsed hash table can also be passed directly, for instance + when using @TT "ToString => false"@ with @TO saveMRDI@. + Example + h = saveMRDI(42, ToString => false) + loadMRDI h + Text + The MRDI format supports cross-system interoperability. + Objects serialized by the OSCAR computer algebra system + can also be loaded. + Example + loadMRDI "{\"_ns\":{\"Oscar\":[\"https://github.com/oscar-system/Oscar.jl\",\"1.5.0\"]},\"_type\":\"ZZRingElem\",\"data\":\"42\"}" + Text + To see which types have built-in load methods for a given namespace, + call @TO methods@ as follows. + Example + methods {"Macaulay2", loadMRDI} + methods {"Oscar", loadMRDI} + Text + Additional types can be supported by calling @TO addLoadMethod@. +Caveat + If the JSON string references a namespace or type for which + no load method has been registered, an error is produced. + Use @TO addLoadMethod@ to register handlers for custom types + and @TO addNamespace@ to register new namespaces. +Subnodes + addLoadMethod +SeeAlso + saveMRDI +/// + +doc /// +Key + addSaveMethod + (addSaveMethod, Type) + (addSaveMethod, Type, Function) + (addSaveMethod, Type, Function, Function) + UseID + [addSaveMethod, UseID] + [addSaveMethod, Name] + [addSaveMethod, Namespace] +Headline + register a method for serializing a type to MRDI format +Usage + addSaveMethod T + addSaveMethod(T, dataFunc) + addSaveMethod(T, paramsFunc, dataFunc) +Inputs + T:Type + the Macaulay2 type to be serialized + dataFunc:Function + a function that takes an object of type @TT "T"@ and + returns the data to be stored in the @TT "data"@ field + of the MRDI JSON + paramsFunc:Function + a function that takes an object of type @TT "T"@ and + returns the parameter object (e.g., the coefficient ring + of a polynomial ring), which will itself be serialized + recursively + UseID => Boolean + if @TT "true"@, objects of this type are assigned UUIDs + so they can be referenced rather than duplicated + Name => Function + a function (or string) that determines the type name string + for the @TT "_type"@ field. By default, the @TO class@ of + each object (as a string) is used. + Namespace => String + the namespace to register this save method under +Description + Text + This function registers a serialization method for the given type so + that @TO saveMRDI@ knows how to convert objects of that + type into MRDI JSON format. + + The zero-argument form @TT "addSaveMethod T"@ + is for types with no data and no parameters (the MRDI + output will contain only a @TT "_type"@ field). + + The one-argument form @TT "addSaveMethod(T, dataFunc)"@ + is for basic (non-parametric) types whose MRDI + representation needs only a @TT "_type"@ name and + @TT "data"@. The @TT "dataFunc"@ receives the object and + should return the data portion. + + The two-argument form @TT "addSaveMethod(T, paramsFunc, dataFunc)"@ + is for parametric types. The @TT "paramsFunc"@ returns an + object representing the type's parameter (which will be + recursively serialized into the @TT "_type.params"@ field), + and @TT "dataFunc"@ returns the data. + + The @TO UseID@ option causes objects of this type to be + assigned RFC 9562 version 4 UUIDs upon serialization. + This is important for types like polynomial rings + that may be shared by many objects: the ring is stored + once in the @TT "_refs"@ section and referenced by UUID + elsewhere. + + The @TO Namespace@ option allows registering save methods + for different namespaces. This is how OSCAR serialization + support is implemented alongside Macaulay2's own namespace. + + Here we register a save method for a custom namespace. + Example + addNamespace("MySystem", "https://example.com", "1.0") + addSaveMethod(ZZ, identity, Name => "MyInt", Namespace => "MySystem") + saveMRDI(42, Namespace => "MySystem") +SeeAlso + saveMRDI +/// + +doc /// +Key + addLoadMethod + (addLoadMethod, String, Function) + (addLoadMethod, List, Function) + [addLoadMethod, Namespace] +Headline + register a method for deserializing a type from MRDI format +Usage + addLoadMethod(typeName, f) +Inputs + typeName:{String, List} + the type name as it appears in the @TT "_type"@ field + of the MRDI JSON, or a list of strings to add multiple + load methods at the same time + f:Function + a function @TT "(params, data) -> Thing"@ that + reconstructs the object + Namespace => String + the namespace to register this method under +Description + Text + This methods registers a deserialization method so that @TO loadMRDI@ + knows how to reconstruct objects of a given type within a + given namespace. + + The namespace allows the same file format to be used across + different computer algebra systems. The Macaulay2 namespace + is @TT "\"Macaulay2\""@ and the OSCAR namespace is + @TT "\"Oscar\""@. Each namespace maintains its own type + registry. Use @TO addNamespace@ to register a new namespace + before adding load methods to it. + + The loading function @VAR "f"@ receives two + arguments: + + @UL { + LI {TT "params", ": For parametric types (e.g., ", TO RingElement, + "), this is the already-deserialized parameter (e.g., the parent ", + "ring). For non-parametric types (e.g., ", TO ZZ, "), this is ", + TO null, "."}, + LI {TT "data", ": The contents of the ", TT "data", " field from the ", + "JSON, recursively deserialized."}}@ + + The @TO List@ form allows registering the same function for + multiple type names at once, which is useful when systems + use different names for equivalent types. + Text + Here we register a load method for a custom namespace. + Example + addNamespace("MySystem", "https://example.com", "1.0") + addLoadMethod("MyInt", + (params, data) -> value data, + Namespace => "MySystem") + loadMRDI "{\"_ns\":{\"MySystem\":[\"https://example.com\",\"1.0\"]},\"_type\":\"MyInt\",\"data\":\"42\"}" +SeeAlso + loadMRDI +/// + +doc /// +Key + addNamespace + (addNamespace, String, String, String) +Headline + register a namespace for MRDI serialization +Usage + addNamespace(ns, url, ver) +Inputs + ns:String -- the namespace identifier + url:String -- the URL associated with the namespace + ver:String -- the version string for the namespace +Description + Text + This method registers a new namespace for use with @TO saveMRDI@ and + @TO loadMRDI@. A namespace must be registered before any save or load methods + can be added to it using @TO addSaveMethod@ or @TO addLoadMethod@. + + The namespace, URL, and version are written into the + @TT "_ns"@ field of the MRDI JSON output. + + The @TT "\"Macaulay2\""@ and @TT "\"Oscar\""@ namespaces + are registered automatically when the package is loaded. + Example + addNamespace("MySystem", "https://example.com", "1.0") + addLoadMethod("MyInt", + (params, data) -> value data, + Namespace => "MySystem") + loadMRDI "{\"_ns\":{\"MySystem\":[\"https://example.com\",\"1.0\"]},\"_type\":\"MyInt\",\"data\":\"42\"}" +/// + +doc /// +Key + validateMRDI + (validateMRDI, Thing) + (validateMRDI, String) + (validateMRDI, HashTable) +Headline + validate an MRDI JSON object against the format specification +Usage + validateMRDI s +Inputs + s:{String, HashTable} + a JSON string or parsed JSON hash table to validate +Description + Text + Validates that a JSON string or hash table conforms to the + @HREF("https://www.oscar-system.org/schemas/mrdi.json", + "MRDI file format specification")@. + This checks structural requirements such as: + + @UL { + LI {"the presence of a ", TT "_type", " key"}, + LI {"that ", TT "_type", " is a string or an object with string-valued ", TT "name"}, + LI {"that ", TT "_ns", " and ", TT "_refs", " are objects (if present)"}, + LI {"that all keys of ", TT "_refs", " are valid UUIDs"}, + LI {"that referenced objects are themselves valid MRDI"}}@ + + The function produces an error if validation fails and returns + @TO null@ on success. + Example + s = saveMRDI 5 + validateMRDI s +/// + +doc /// +Key + Namespace +Headline + option specifying the namespace for MRDI serialization +Description + Text + A symbol used as an option to @TO addSaveMethod@, + @TO addLoadMethod@, and @TO saveMRDI@ to specify which + namespace to use. + + Different namespaces allow the same MRDI file format to + represent objects from different computer algebra systems. + The @TT "\"Macaulay2\""@ and @TT "\"Oscar\""@ namespaces + are built in. Custom namespaces can be registered with + @TO addNamespace@. + + When passed to @TO saveMRDI@, this determines which set of + save methods is used and what appears in the @TT "_ns"@ field + of the JSON output. + + When passed to @TO addSaveMethod@ or @TO addLoadMethod@, this + determines which namespace the method is registered under. + Text + The same object can be serialized under different namespaces. + Example + saveMRDI 5 + saveMRDI(5, Namespace => "Oscar") +Subnodes + addNamespace +/// + + +----------- +-- tests -- +----------- + +TEST /// +-- loadMRDI saveMRDI x should return x +checkMRDI = x -> ( + assert BinaryOperation(symbol ===, loadMRDI saveMRDI x, x); + -- check FileName option + fn := temporaryFileName() | ".mrdi"; + saveMRDI(x, FileName => fn); + assert BinaryOperation(symbol ===, loadMRDI get fn, x); + removeFile fn; + -- check ToString option + h := saveMRDI(x, ToString => false); + assert instance(h, HashTable); + assert BinaryOperation(symbol ===, loadMRDI h, x)) +checkMRDI 5 +checkMRDI ZZ +checkMRDI QQ +checkMRDI(ZZ/101) +checkMRDI GF(2, 3) +checkMRDI(QQ[x]) +checkMRDI(QQ[x][y][z]) +R = QQ[x,y,z,w] +I = monomialCurveIdeal(R, {1, 2, 3}) +checkMRDI I_0 +checkMRDI I +checkMRDI gens I +-- rational coefficients +checkMRDI((1/2)*x + (3/4)*y - 7/3) +checkMRDI ideal((1/2)*x^2 - (3/5)*y, (7/11)*x*y + 1) +checkMRDI matrix {{(1/2)*x, (3/4)*y}, {(5/6)*x*y, (7/8)*x^2}} +-- identity elements +checkMRDI 1_R +checkMRDI id_(R^3) +-- zero elements +checkMRDI 0_R +checkMRDI ideal 0_R +checkMRDI map(R^2, R^3, 0) +/// + +-* code to generate strings for the next test: + +printWidth = 0 +getFormattedMRDI = x -> ( + format replace(regexQuote version#"VERSION", "@VERSION@", saveMRDI x)) +scan({ + 5, + ZZ, + QQ, + ZZ/101, + GF(2, 3), + QQ[x], + QQ[x][y][z], + (R = QQ[x,y,z,w]; I = monomialCurveIdeal(R, {1, 2, 3}); I_0), + I, + gens I + }, x -> << "checkMRDI " << getFormattedMRDI x << endl) + +*- + +TEST /// +-- saveMRDI loadMRDI x should return x (possibly up to reordering of elements) +needsPackage "JSON" +checkMRDI = x -> ( + x = replace("@VERSION@", version#"VERSION", x); + y := saveMRDI loadMRDI x; + assert BinaryOperation(symbol ===, fromJSON x, fromJSON y)) +checkMRDI "{\"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_type\": \"ZZ\", \"data\": \"5\"}" +checkMRDI "{\"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_type\": \"Ring\", \"data\": \"ZZ\"}" +checkMRDI "{\"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_type\": \"Ring\", \"data\": \"QQ\"}" +checkMRDI "{\"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_type\": \"QuotientRing\", \"data\": \"101\"}" +checkMRDI "{\"_type\": \"GaloisField\", \"data\": {\"degree\": \"3\", \"char\": \"2\"}, \"id\": \"366eef8c-095b-4675-bc4c-c815a6706f52\", \"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}}" +checkMRDI "{\"_type\": {\"params\": {\"_type\": \"Ring\", \"data\": \"QQ\"}, \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"x\"]}, \"id\": \"31292984-9503-4034-9a78-7badbc3d5710\", \"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}}" +checkMRDI "{\"_type\": {\"params\": \"8731803f-89bd-4ff7-a599-79375b33cf4c\", \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"z\"]}, \"id\": \"27447205-6c41-4ed5-91ba-f7b96c0a65ce\", \"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_refs\": {\"8731803f-89bd-4ff7-a599-79375b33cf4c\": {\"_type\": {\"params\": \"81e005bb-a348-423a-a627-e96ff29a3597\", \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"y\"]}}, \"81e005bb-a348-423a-a627-e96ff29a3597\": {\"_type\": {\"params\": {\"_type\": \"Ring\", \"data\": \"QQ\"}, \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"x\"]}}}}" +checkMRDI "{\"_type\": {\"params\": \"cfaa114f-9d5a-44e1-abbb-a0ee2ca94fe4\", \"name\": \"RingElement\"}, \"data\": [[[\"0\", \"0\", \"2\", \"0\"], [\"1\", \"1\"]], [[\"0\", \"1\", \"0\", \"1\"], [\"-1\", \"1\"]]], \"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_refs\": {\"cfaa114f-9d5a-44e1-abbb-a0ee2ca94fe4\": {\"_type\": {\"params\": {\"_type\": \"Ring\", \"data\": \"QQ\"}, \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"x\", \"y\", \"z\", \"w\"]}}}}" +checkMRDI "{\"_type\": {\"params\": \"cfaa114f-9d5a-44e1-abbb-a0ee2ca94fe4\", \"name\": \"Ideal\"}, \"data\": [[[[\"0\", \"0\", \"2\", \"0\"], [\"1\", \"1\"]], [[\"0\", \"1\", \"0\", \"1\"], [\"-1\", \"1\"]]], [[[\"0\", \"1\", \"1\", \"0\"], [\"1\", \"1\"]], [[\"1\", \"0\", \"0\", \"1\"], [\"-1\", \"1\"]]], [[[\"0\", \"2\", \"0\", \"0\"], [\"1\", \"1\"]], [[\"1\", \"0\", \"1\", \"0\"], [\"-1\", \"1\"]]]], \"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_refs\": {\"cfaa114f-9d5a-44e1-abbb-a0ee2ca94fe4\": {\"_type\": {\"params\": {\"_type\": \"Ring\", \"data\": \"QQ\"}, \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"x\", \"y\", \"z\", \"w\"]}}}}" +checkMRDI "{\"_type\": {\"params\": \"cfaa114f-9d5a-44e1-abbb-a0ee2ca94fe4\", \"name\": \"Matrix\"}, \"data\": [[[[[\"0\", \"0\", \"2\", \"0\"], [\"1\", \"1\"]], [[\"0\", \"1\", \"0\", \"1\"], [\"-1\", \"1\"]]], [[[\"0\", \"1\", \"1\", \"0\"], [\"1\", \"1\"]], [[\"1\", \"0\", \"0\", \"1\"], [\"-1\", \"1\"]]], [[[\"0\", \"2\", \"0\", \"0\"], [\"1\", \"1\"]], [[\"1\", \"0\", \"1\", \"0\"], [\"-1\", \"1\"]]]]], \"_ns\": {\"Macaulay2\": [\"https://macaulay2.com\", \"@VERSION@\"]}, \"_refs\": {\"cfaa114f-9d5a-44e1-abbb-a0ee2ca94fe4\": {\"_type\": {\"params\": {\"_type\": \"Ring\", \"data\": \"QQ\"}, \"name\": \"PolynomialRing\"}, \"data\": {\"variables\": [\"x\", \"y\", \"z\", \"w\"]}}}}" +/// + +TEST /// +-- save/load Oscar objects +checkMRDI = x -> assert BinaryOperation(symbol ===, + loadMRDI saveMRDI(x, Namespace => "Oscar"), x) +checkMRDI ZZ +checkMRDI QQ +checkMRDI 5 +checkMRDI(1/2) +R = ZZ[x,y,z,w] +checkMRDI R +checkMRDI random(3, R) + +-- objects we can load but can't save yet +checkLoad = (x, mrdi) -> assert BinaryOperation(symbol ===, x, loadMRDI mrdi) +checkLoad("hello", "{\"_ns\":{\"Oscar\":[\"https://github.com/oscar-system/Oscar.jl\",\"1.6.0\"]},\"_type\":\"String\",\"data\":\"hello\"}") +checkLoad(3.14, "{\"_ns\":{\"Oscar\":[\"https://github.com/oscar-system/Oscar.jl\",\"1.6.0\"]},\"_type\":\"Float64\",\"data\":\"3.14\"}") +checkLoad(ZZ/101, "{\"_ns\":{\"Oscar\":[\"https://github.com/oscar-system/Oscar.jl\",\"1.6.0\"]},\"_type\":\"FiniteField\",\"data\":\"101\"}") +checkLoad(3/4, "{\"_ns\":{\"Oscar\":[\"https://github.com/oscar-system/Oscar.jl\",\"1.6.0\"]},\"_type\":{\"name\":\"QQFieldElem\",\"params\":{\"_type\":\"QQField\"}},\"data\":\"3//4\"}") +checkLoad(7_QQ, "{\"_ns\":{\"Oscar\":[\"https://github.com/oscar-system/Oscar.jl\",\"1.6.0\"]},\"_type\":{\"name\":\"QQFieldElem\",\"params\":{\"_type\":\"QQField\"}},\"data\":\"7\"}") +/// + +TEST /// +-- save/load errors +checkError = (f, msg) -> ( + (ret, err) := trap f(); + assert Equation(msg, toString err)) +checkError( + () -> saveMRDI(5, Namespace => "NonExistent"), + "unknown namespace: NonExistent") +checkError( + () -> loadMRDI "{\"_ns\":{\"UnknownSystem\":[\"https://example.com\",\"1.0\"]},\"_type\":\"ZZ\",\"data\":\"5\"}", + "unknown namespace: UnknownSystem") +checkError( + () -> loadMRDI "{\"_ns\":{\"Macaulay2\":[\"https://macaulay2.com\",\"1.0\"]},\"_type\":\"NoSuchType\",\"data\":\"5\"}", + "unknown type: NoSuchType") +/// + +TEST /// +-- custom namespace +addNamespace("TestSystem", "https://example.com/test", "0.1") +addSaveMethod(ZZ, identity, Name => "TestInt", Namespace => "TestSystem") +addLoadMethod("TestInt", (params, data) -> value data, Namespace => "TestSystem") +s = saveMRDI(99, Namespace => "TestSystem") +validateMRDI s +assert Equation(99, loadMRDI s) +needsPackage "JSON" +h = fromJSON s +assert(h#"_ns"#?"TestSystem") +/// + + +TEST /// +-- validation +validateMRDI saveMRDI 5 +validateMRDI saveMRDI ZZ +validateMRDI saveMRDI QQ +validateMRDI saveMRDI (ZZ/101) +validateMRDI saveMRDI GF(2, 3) +validateMRDI saveMRDI (QQ[x]) +validateMRDI saveMRDI (QQ[x][y][z]) +R = QQ[x,y,z,w] +I = monomialCurveIdeal(R, {1, 2, 3}) +validateMRDI saveMRDI I_0 +validateMRDI saveMRDI I +validateMRDI saveMRDI gens I +validateMRDI saveMRDI(ZZ, Namespace => "Oscar") +validateMRDI saveMRDI(QQ, Namespace => "Oscar") +validateMRDI saveMRDI(5, Namespace => "Oscar") +validateMRDI saveMRDI(1/2, Namespace => "Oscar") +R = ZZ[x,y,z,w] +validateMRDI saveMRDI(R, Namespace => "Oscar") +validateMRDI saveMRDI(random(3, R), Namespace => "Oscar") +/// + +TEST /// +-- validation errors +checkError = (mrdi, msg) -> ( + (ret, err) := trap validateMRDI mrdi; + assert Equation(msg, toString err)) +checkError( + "{\"_ns\":{\"Macaulay2\":[\"https://macaulay2.com\",\"1.0\"]}}", + "expected a '_type' key") +checkError( + "{\"_type\":42}", + "expected value of '_type' to be a string or object") +checkError( + "{\"_type\":\"ZZ\",\"_refs\":{\"not-a-uuid\":{\"_type\":\"ZZ\"}}}", + "expected all keys of '_refs' to be UUID's, but got not-a-uuid") +checkError( + "{\"_type\":\"ZZ\",\"_ns\":\"bad\"}", + "expected value of '_ns' to be an object") +checkError( + "[1,2,3]", + "expected an object, but got {1,2,3}") +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/changes.m2 b/M2/Macaulay2/packages/Macaulay2Doc/changes.m2 index 2faf0824b22..673c51bf855 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/changes.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/changes.m2 @@ -6,6 +6,7 @@ document { Key => "changes to Macaulay2, by version", Subnodes => { -- TO "changes made for the next release", + TO "changes, 1.26.05", TO "changes, 1.25.11", TO "changes, 1.25.05", TO "changes, 1.24.11", @@ -66,6 +67,77 @@ changesHelper List := opt -> pkgnames -> ( << ".\" }," << endl))) +document { + Key => "changes, 1.26.05", + UL { + LI { "new packages:", + UL { + LI { TO "EliminationTemplates::EliminationTemplates", ", a package by Manav Batavia, Cheng Chen, Wanchun / Rosie Shen, Anna Natalie Chlopecki, Tim Duff, Will Huang, and Aolong Li for elimination templates, has been added." }, + LI { TO "MacaulayPosets::MacaulayPosets", ", a package by Penelope Beall and Yu Olivier Li for Macaulay posets, has been added." }, + LI { TO "MRDI::MRDI", ", a package by Doug Torrance for serializing algebraic data with .mrdi files, has been added." }, + LI { TO "Padic::Padic", ", a package by Doug Torrance for p-adic numbers, has been added." }, + LI { TO "SimplicialModules::SimplicialModules", ", a package by Keller VandeBogert and Michael DeBellevue for working in the category of simplicial modules, has been added." }, + LI { TO "WittVectors::WittVectors", ", a package by Anne Fayolle, Abhay Goel, Devlin Mallory, Eamon Quinlan-Gallego, and Teppei Takamatsu for computations with Witt vectors, Frobenius lifts, and quasi-F-splittings, has been added." }, + }, + }, + LI { "packages that have been published and certified:", + UL { + LI { star, " ", TO "ThinSincereQuivers::ThinSincereQuivers", ", a package by Mary Barker and Patricio Gallardo for constructing flow polytopes and their associated quivers, has been published." }, + } + }, + LI { "improved packages:", + UL { + LI { TO "BettiCharacters::BettiCharacters", " has been updated to version 2.6. Changes include significant optimizations of the core algorithm, a new option for semidirect products of finite groups acting on tori, and methods for hyperoctahedral groups. This version introduces breaking changes to the ", TT "Character", " type, making it incompatible with previous versions." }, + LI { TO "CellularResolutions::CellularResolutions", " has been updated to version 1.1 with improved documentation." }, + LI { TO "Complexes::Complexes", " has been updated to version 1.0 and is now the default homological algebra package. To construct a complex, use ", TO "Complexes::complex", ", which returns a ", TO "Complexes::Complex", " object. This replaces ", TO "OldChainComplexes::chainComplex", " and ", TO "OldChainComplexes::ChainComplex", ". For the legacy behavior, the ", TO "OldChainComplexes::OldChainComplexes", " package is still available. Add ", M2CODE "HomologicalAlgebraPackage = \"OldChainComplexes\"", " to your ", TO "initialization file", " to make it the default." }, + LI { TO "DGAlgebras::DGAlgebras", " has been updated to version 2.0, introducing ", TO "DGAlgebras::DGModule", " and related types with core homological operations, adding minimal semifree DG resolutions, overhauling documentation of key constructors with examples, and expanding test coverage." }, + LI { TO "ForeignFunctions::ForeignFunctions", " has been updated to version 0.7 with improved garbage collection of GMP and MPFR objects." }, + LI { TO "InvariantRing::InvariantRing", " has been updated to version 2.4, which includes a new algorithm for invariants of elementary abelian $p$-groups, as well as bugfixes and documentation improvements. The ", TT "permutationMatrix", " method now takes arrays as inputs." }, + LI { TO "JSON::JSON", " has been updated to version 0.6 with significant improvements to parsing speed thanks to the ", HREF("https://github.com/akheron/jansson", "Jansson"), " library."}, + LI { TO "JSONRPC::JSONRPC", " has been updated to version 0.2 with improved error messages." }, + LI { TO "LieAlgebraRepresentations::LieAlgebraRepresentations", " has been updated to version 1.01, adding a dual construction for representations, fixing Cartan-basis inconsistencies in spin and half-spin matrix computations, and updating documentation to reflect a now-proven formula." }, + LI { TO "MatchingFields::MatchingFields", " has been updated to version 1.3 with overhauls to the internals of the main datatype, simpliyfing the logic and removing duplicated code." }, + LI { TO "PieriMaps::PieriMaps", " has been updated to version 2.0, adding dual (projection-direction) Pieri and Littlewood-Richardson maps with point evaluators, supporting multiple basis conventions with conversions, and providing equivariance/well-definedness checks and symbolic representations of maps." }, + LI { TO "Probability::Probability", " has been updated to version 0.7 with minor updates." }, + LI { TO "RInterface::RInterface", " has been updated to version 0.2, adding hash table-environment conversion, string-based evaluation via ", TO "RInterface::RValue", ", a persistent ", TO "RInterface::RContext", " for shared environments, formula construction with ", TO "RInterface::RObject ~ RObject", ", and improved documentation and testing." }, + LI { TO "SchurComplexes::SchurComplexes", " has been updated to version 1.2, fixing a bug with function ", TO "SchurComplexes::schurComplex", " keeps track of homological degree. Also, ", TO "SchurComplexes::schurComplex ", " has been updated to output a graded complex, based on the grading on the input complex." }, + LI { TO "SchurFunctors::SchurFunctors", " has been updated to version 1.0, adding a full Weyl-functor framework parallel to the Schur side (with new modules, operations, and characters), augmenting Schur-side utilities, fixing decomposition and predicate bugs, improving straightening performance, and expanding documentation." }, + LI { TO "SchurRings::SchurRings", " has been updated to version 2.0, adding new character ring variants for symplectic, orthogonal, and rational GL groups (with full API support, branching/specialization maps, and inter-conversions), introducing a monomial-basis option with Kostka-based multiplication, and including minor efficiency improvements while preserving existing functionality."}, + LI { TO "SpecialFanoFourfolds::SpecialFanoFourfolds", " has been updated to version 2.8, adding support for the ", TO "SpecialFanoFourfolds::DoublySpecialCubicFourfold", " class (cubic fourfolds in the intersection of two Hassett divisors)." }, + LI { TO "SpectralSequences::SpectralSequences", " has been updated to version 2.02 and now uses the ", TO "Complexes::Complexes", " package." }, + LI { TO "Tableaux::Tableaux", " has been updated to version 0.6, merging the class ", TT "SkewTableau", " into ", TO "Tableaux::YoungTableau", " and bringing in several features from the package ", TO "SpechtModule::SpechtModule", "."}, + LI { TO "TerraciniLoci::TerraciniLoci", " has been updated to version 0.5 with minor updates to citation and author info." }, + LI { TO "Visualize::Visualize", " has been updated to version 1.9 and now uses updated versions of various JavaScript libraries." }, + }, + }, + LI { "functionality added or improved:", + UL { + LI { "The ", TO Mutex, " class has been added to help prevent race conditions in multithreaded code." }, + LI { "Multiple assignment may now be used to assign to elements of mutable lists and hash tables and for using and installing assignment methods. In addition, null entries may be used on the left-hand side to skip assignment. For example, if ", VAR "x", " is a mutable list and ", VAR "y", " is a mutable hash table, then ", CODE "(x#0, , y.a) = (1, 2, 3)", " is equivalent to ", CODE "x#0 = 1; y.a = 3", ", ignoring the 2."}, + LI { "The ", TO polylog, " function has been added for computing polylogarithms." }, + LI { "The ", TO symbol trap, " keyword and the ", M2CODE "except", "/", M2CODE "do", " options to ", TO symbol try, " have been added for improved error handling. In particular, it is now possible to \"trap\" an error as an ", TO Error, " object instead of raising it." }, + LI { "It is now possible to ", TO2(installMethod, "install"), " a nullary (0-argument) method using the syntactic sugar ", CODE "f() := () -> ..." }, + LI { "It is now possible to construct a mutable list with a specific number of null entries using ", TO "new MutableList from ZZ", "." }, + LI { "The ", TO parse, " function has been added for viewing the parse tree of Macaulay2 code." }, + LI { "The ", TO norm, " function now supports computing $\\ell^p$ norms for finite $p$." }, + LI { "The ", TO shuffle, " function has been added for shuffling lists and the ", TO randomElement, " function for selecting random elements from lists. Note that ", TO (random, List), ", which currently behaves like ", TO shuffle, ", will have its behavior changed to that of ", TO randomElement, " in a future release." }, + LI { "A new number type, ", TO CCi, ", has been added for working with complex intervals." }, + }, + }, + LI { "functionality changed in a way that could break code:", + UL { + LI { "The ", TO symbol ~, " operator is now a binary or prefix unary operator instead of a postfix unary operator. Use ", TO symbol ^~, " for sheafification." }, + LI { "The ", TT "OldPolyhedra", " and ", TT "OldToricVectorBundles", " packages are no longer distributed. Use ", TO "Polyhedra::Polyhedra", " and ", TO "ToricVectorBundles::ToricVectorBundles", " instead." }, + LI { "It is no longer possible to ", TO promote, " from ", TO RR, " to ", TO QQ, "." }, + LI { "Matrix multiplication now preserves degree." }, + LI { "The ", TO (symbol _, VisibleList, List), " method for subsetting lists now preserves class, e.g., ", CODE "[1,2,3,4]_{0,1}", " now returns an ", TO Array, ", not a ", TO List, ", as it did previously." }, + LI { "The ", TT "NonminimalComplexes", " and ", TT "PruneComplex", " packages have been removed. Their functionality has been moved to ", TO "Complexes::Complexes", "." }, + LI { "The ", M2CODE "resolution", " function (and its synonym, ", M2CODE "res", ") are now synonyms for ", TO "Complexes::freeResolution", ". The ", TO LengthLimit, " option is required for rings where there are infinite free resolutions such as quotients of polynomial rings and skew commutative polynomial rings. Also, ", M2CODE "Strategy => Nonminimal", " replaces ", M2CODE "FastNonminimal => true", "." }, + }, + }, + } + } + document { Key => "changes, 1.25.11", UL { @@ -110,7 +182,7 @@ document { LI { TO "Probability::Probability", " has been updated to version 0.6."}, LI { TO "Python::Python", " has been updated to version 1.0 with significant updates, including the new ", TO "Python::PythonContext", " class and the method ", TO "Python::pythonRunScript", "."}, LI { TO "SCMAlgebras::SCMAlgebras", " has been updated to version 1.1, adding new methods. It can now compute the unmixed layer of an ideal, check for unmixedness, and check for canonical Cohen-Macaulayness."}, - LI { TO "SRdeformations::SRdeformations", " has been updated to version 0.53, using ", TO "Polyhedra::Polyhedra", " instead of ", TO "OldPolyhedra::OldPolyhedra", "."}, + LI { TO "SRdeformations::SRdeformations", " has been updated to version 0.53, using ", TO "Polyhedra::Polyhedra", " instead of ", TT "OldPolyhedra", "."}, LI { TO "TerraciniLoci::TerraciniLoci", " has been updated to version 0.4."}, LI { TO "ToricHigherDirectImages::ToricHigherDirectImages", " has been updated to version 1.1, fixing unexpected behavior from the output of ", TO "ToricHigherDirectImages::HDI", "."}, LI { TO "ToricTopology::ToricTopology", " has been updated to version 1.1, adding a new class ", TO "ToricTopology::MomentAngleComplex", " and associated methods to compute equivariant cohomology, Betti numbers, and Euler characteristic, as well as minor fixes to the rest of the package."}, @@ -212,7 +284,7 @@ document { LI { "functionality changed in a way that could break code:", UL { LI { "The package ", TT "Divisor", " has been renamed as ", TO "WeilDivisors :: WeilDivisors", "." }, - LI { "The method ", TO "Isomorphism :: isIsomorphism", " now only returns true or false. ", + LI { "The method ", TO "Isomorphism :: isIsomorphic", " now only returns true or false. ", "To retrieved the computed isomorphism, use the method ", TO "Isomorphism :: isomorphism", "." }, LI { "The method ", TO (symbol\\, Matrix, Matrix), " is now a shortcut for ", TO (quotient', Matrix, Matrix), ". ", "The previous functionality is still available via ", TO (symbol//, Matrix, Matrix), ", which is a shortcut for ", @@ -1161,7 +1233,7 @@ document { The function ", TO "Truncations::truncate(List,Module)", " has been made functorial, but it no longer allows partial degrees to be given." }, LI { TO "FrobeniusThresholds::FrobeniusThresholds", ", a package by Erin Bela, Alberto F. Boix, Juliette Bruce, Drew Ellingson, Daniel Hernandez, Zhibek Kadyrsizova, Moty Katzman, Sara Malec, Matthew Mastroeni, Maral Mostafazadehfard, Marcus Robinson, Karl Schwede, Dan - Smolkin, Pedro Teixeira and Emily Witt for calculation of Frobenious thresholds, has been added." }, + Smolkin, Pedro Teixeira and Emily Witt for calculation of Frobenius thresholds, has been added." }, LI { TO "ToricInvariants::ToricInvariants", ", a package by Martin Helmer for computing Euclidean distance degrees, polar degrees, degree and codimension of the dual, and Chern-Mather classes of toric varieties, has been added." }, LI { TO "SegreClasses::SegreClasses", ", a package by Martin Helmer and Corey Harris for testing containment of varieties and @@ -1331,9 +1403,9 @@ document { LI { TO "Complexes::Complexes", ", a package by Gregory G. Smith and Mike Stillman for chain complexes, has been added." }, LI { TO "GroebnerWalk::GroebnerWalk", ", a package by Dylan Peifer for computing Gröbner bases via the Gröbner walk, has been added." }, LI { TO "Matroids::Matroids", ", a package by Justin Chen for computations with matroids, has been added." }, - LI { TO "NonminimalComplexes::NonminimalComplexes", ", a package by Frank Schreyer and Mike Stillman for obtaining the non-minimal strands of a non-minimal resolution of a homogeneous module, has been added." }, + LI { "NonminimalComplexes::NonminimalComplexes", ", a package by Frank Schreyer and Mike Stillman for obtaining the non-minimal strands of a non-minimal resolution of a homogeneous module, has been added." }, LI { TO "NumericalImplicitization::NumericalImplicitization", ", a package by Justin Chen and Joe Kileel for computing invariants of images of polynomial maps, has been added." }, - LI { TO "PruneComplex::PruneComplex", ", a package by Mahrud Sayrafi and Mike Stillman for pruning chain complexes over polynomial and local rings, has been added." }, + LI { "PruneComplex::PruneComplex", ", a package by Mahrud Sayrafi and Mike Stillman for pruning chain complexes over polynomial and local rings, has been added." }, LI { TO "RandomMonomialIdeals::RandomMonomialIdeals", ", a package by Despina Stasi, Dane Wilburne, Tanner Zielinski, Daniel Kosmas, Parker Joncus, Richard Osborn, Monica Yun, and Genevieve Hummel for generating Erdos-Renyi-type random monomial ideals, has been added." }, LI { TO "ReflexivePolytopesDB::ReflexivePolytopesDB", ", a package by Mike Stillman for simple access to Kreuzer-Skarke database of reflexive polytopes of dimensions 3 and 4, has been added." }, LI { TO "SymbolicPowers::SymbolicPowers", ", a package by Eloisa Grifo for calculations involving symbolic powers, has been added." }, @@ -1486,7 +1558,7 @@ document { "An important difference is that objects of these classes can no longer be used as keys into hash tables, since these objects are implemented as mutable hash tables. In particular ", TO symbol===, " no longer works on cones. Instead, use a sorted list of e.g. vertices and lineality space.", - PARA{"If you need the old behavior, load the package ", TO "OldPolyhedra::OldPolyhedra", ". + PARA{"If you need the old behavior, load the package ", TT "OldPolyhedra", ". However, if possible, change your code to run with the new package."} } } @@ -2892,7 +2964,8 @@ document { saving time. See ", TO (quotientRemainder,RingElement,RingElement), "." }, LI { - "The binary representation of a real number is now available using ", TO (promote,RR,QQ), ". + "The binary representation of a real number is now available using ", TT "promote(RR,QQ)", ". + (note: this was reverted in a later version.) The code for ", TO (lift,RR,QQ), " has been tightened up so a rational number is provided that provides exactly the same real number when promoted." }, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_arithmetic.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_arithmetic.m2 index 1bdac8b86ee..1e0629ed983 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_arithmetic.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_arithmetic.m2 @@ -33,8 +33,8 @@ document { document { Key => {numeric,(numeric,Matrix),(numeric,ZZ,Matrix), (numeric, Vector),(numeric, ZZ, Vector), - (numeric, ZZ, CC), (numeric, RR), (numeric, RRi), - (numeric, ZZ, RRi), + (numeric, ZZ, CC), (numeric, RR), (numeric, RRi), (numeric, CCi), + (numeric, ZZ, RRi), (numeric, ZZ, CCi), (numeric, CC), (numeric, ZZ, VisibleList), (numeric, VisibleList), (numeric, ZZ, Constant), (numeric, Constant), (numeric, InfiniteNumber, Constant), @@ -75,40 +75,45 @@ document { document { Key => {realPart, (realPart,Number), (realPart,QQ), (realPart,ZZ), - (realPart,InexactNumber)}, + (realPart,InexactNumber),"(realPart,CC)","(realPart,CCi)"}, Headline => "real part", Usage => "realPart z", - Inputs => {"z" => "an integer, rational, real or complex number"}, - Outputs => {"the real part of the complex number z."}, + Inputs => {"z" => "an integer, rational, real or complex number, or a real or complex interval"}, + Outputs => {"the real part of the complex number z or an interval containing the real parts of real or complex interval z"}, EXAMPLE { "realPart(3/4)", - "realPart(1.5+2*ii)" + "realPart(1.5+2*ii)", + "realPart(interval(1+2*ii,3+4*ii))" }, SeeAlso => {CC, imaginaryPart} } document { Key => {imaginaryPart,(imaginaryPart,Number), (imaginaryPart,QQ), - (imaginaryPart,ZZ), (imaginaryPart,InexactNumber)}, + (imaginaryPart,ZZ), (imaginaryPart,InexactNumber),"(imaginaryPart,CCi)","(imaginaryPart,CC)"}, Headline => "imaginary part", Usage => "imaginaryPart z", - Inputs => {"z" => "an integer, rational, real or complex number"}, - Outputs => {"the imaginary part of the complex number z."}, + Inputs => {"z" => "an integer, rational, real or complex number or a real or complex interval"}, + Outputs => {"the imaginary part of the complex number z or an interval containing the complex parts of real or complex interval z."}, EXAMPLE { "imaginaryPart(3/4)", - "imaginaryPart(1.5+2*ii)" + "imaginaryPart(1.5+2*ii)", + "imaginaryPart(interval(1+2*ii,3+4*ii))" }, SeeAlso => {CC, realPart} } document { - Key => {conjugate,(conjugate,CC),(conjugate,Number),(conjugate,Constant)}, + Key => {conjugate,(conjugate,CC),(conjugate,Number),(conjugate,CCi),(conjugate,Constant)}, Headline => "complex conjugate", Usage => "conjugate z", Inputs => {"z"}, - Outputs => {CC => {"the complex conjugate of ", TT "z"}}, + Outputs => {CC => {"the complex conjugate of ", TT "z"}, + CCi => {"a complex interval containing all complex conjugate of points of ", TT "z"} +}, EXAMPLE { "conjugate(1+2.5*ii)", - "conjugate 3" + "conjugate 3", + "conjugate interval(1+2*ii,3+4*ii)" } } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_atomic.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_atomic.m2 index eda1b3f7db3..4176492c7fa 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_atomic.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_atomic.m2 @@ -13,6 +13,7 @@ doc /// between $-2^{31}$ and $2^{31} - 1$. SeeAlso "parallel programming with threads and tasks" + Mutex Subnodes (NewFromMethod, AtomicInt, ZZ) (NewFromMethod, ZZ, AtomicInt) diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_intervals.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_intervals.m2 index 04bd35307d9..b0d0cee78c1 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_intervals.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_intervals.m2 @@ -1,6 +1,11 @@ doc /// Key - left + left + (left, CCi) + (left, Matrix) + (left, Number) + (left, RRi) + (left, RingElement) Headline left endpoint of an interval Usage @@ -12,6 +17,8 @@ doc /// Description Text Returns the left endpoint of the input interval. + Example + left interval(2,3) SeeAlso right midpoint @@ -20,7 +27,12 @@ doc /// doc /// Key - right + right + (right, CCi) + (right, Matrix) + (right, Number) + (right, RRi) + (right, RingElement) Headline right endpoint of an interval Usage @@ -32,26 +44,59 @@ Outputs Description Text Returns the right endpoint of the input interval. + Example + right interval(2,3) SeeAlso left midpoint diameter /// +-- helper methods not intended to be called by the user +undocumented {(midpoint, Ring), (midpoint, PolynomialRing), + (midpoint, QuotientRing), (midpoint, Module)} + doc /// Key - midpoint + midpoint + (midpoint, CCi) + (midpoint, Matrix) + (midpoint, Number) + (midpoint, RRi) + (midpoint, RingElement) Headline midpoint of an interval Usage x = midpoint I Inputs - I:RRi + I:{RRi,CCi} Outputs - x:RR + x:{RR,CC} Description Text Returns the midpoint (the average of the endpoints) of the input interval. + Example + interval(2,4) + midpoint oo + Text + For complex intervals, the center of the rectangle in the complex + plane is returned. + Example + interval(2 + 3*ii, 4 + 7*ii) + midpoint oo + Text + The midpoint of a matrix is the matrix containing the midpoints of + its entries. + Example + A = matrix{{interval(1,3), interval(3,5)}, {interval(5,7), interval(7,9)}} + midpoint A + Text + The midpoint of a polynomial is obtained by taking the midpoints of the + coefficients. + Example + R = RRi[x,y,z] + f = interval(1,3)*x + interval(3,5)*y + interval(5,7)*z + midpoint f SeeAlso left right @@ -61,18 +106,28 @@ SeeAlso doc /// Key diameter + (diameter, CCi) (diameter, RRi) Headline diameter of an interval Usage x = diameter I Inputs - I:RRi + I:{RRi, CCi} Outputs x:RR Description Text Returns the diameter (the difference between the endpoints) of the input interval. + Example + interval (2, 7) + diameter oo + Text + For a complex interval, the length of the diagonal in the complex + plane is returned. + Example + interval (0, 3 + 4*ii) + diameter oo SeeAlso left right @@ -83,6 +138,10 @@ doc /// Key (intersect, RRi) (intersect, RRi, RRi) + (intersect, CCi) + (intersect, CCi, CCi) + (intersect, CCi, RRi) + (intersect, RRi, CCi) [(intersect, RRi), Precision] [(intersect, RRi, RRi), Precision] Headline @@ -99,27 +158,39 @@ Outputs Description Text Returns the intersection of any number of input intervals. + Example + intersect(interval(1,3),interval(2,4)) + intersect(interval(1,2+3*ii),interval(2*ii,3+2*ii)) SeeAlso intersect /// doc /// Key + (isMember, QQ, CCi) + (isMember, RR, CCi) + (isMember, ZZ, CCi) + (isMember, CC, CCi) (isMember, QQ, RRi) (isMember, ZZ, RRi) (isMember, RR, RRi) + (isMember, CC, RRi) Headline membership test in an interval Usage isMember(x,I) Inputs - x:{QQ,ZZ,RR} - I:RRi + x:{QQ,ZZ,RR,CC} + I:CCi Outputs :Boolean Description Text Returns true if the input number is in the interval. + Example + isMember(1,interval(2,3)) + isMember(2,interval(1,3)) + isMember(1+2*ii,interval(0,2+3*ii)) SeeAlso isEmpty /// @@ -127,6 +198,7 @@ SeeAlso doc /// Key (isEmpty, RRi) + (isEmpty, CCi) Headline empty test for an interval Usage @@ -138,112 +210,96 @@ Outputs Description Text Returns true if the input interval is empty, i.e., the left endpoint is to the right of the right endpoint. + Example + isEmpty interval(1,2) + isEmpty interval(2,1) + isEmpty interval(1,2*ii) SeeAlso isMember /// doc /// Key + (isSubset, CCi, CCi) (isSubset, RRi, RRi) + (isSubset, RRi, CCi) + (isSubset, CCi, RRi) Headline subset test for intervals Usage x = isSubset(I,J) Inputs - I:RRi - J:RRi + I:CCi + J:CCi Outputs x:Boolean Description Text Returns true if interval I is a subset of interval J. + Example + isSubset(interval(2,3),interval(1,4)) + isSubset(interval(1,3),interval(2,4)) + isSubset(interval(0,4+4*ii),interval(1+ii,2+3*ii)) SeeAlso isMember /// -undocumented{(span,RRi), (span, QQ), (span, RR), (span, ZZ)} - doc /// Key span -Headline - construct smallest interval -Description - Text - Returns the smallest interval containing the inputs (which can include intervals). Typically, the returned interval is not empty. See @TO (span, Sequence)@ and @TO (span, List)@ -SeeAlso - interval - (span, Sequence) - (span, List) - toRRi -/// - -doc /// -Key - (span, List) - [(span,List),Precision] + (span,CCi,CCi) + (span,CCi,RRi) + (span,List) + (span,Number) + (span,Number,Number) + (span,RRi,CCi) + (span,RRi,RRi) + (span,Sequence) Headline construct smallest interval Usage I = span(L) I = span(L,Precision => prec) Inputs - L:List - containing @TO Number@ (including @TO RRi@) + L:{List, Sequence} + containing @TO Number@ objects Precision => ZZ specifies the desired precision of the output, a value of {\tt -1} uses the minimum precision of the inputs. Outputs - I:RRi + I:{RRi, CCi} Description Text Returns the smallest interval containing the inputs (which can include intervals). Typically, the returned interval is not empty. + Example + span(1,4,interval(2,5),interval(-3)) + span(1 + 3*ii, pi, 4 + ii) SeeAlso interval - (span, Sequence) - toRRi -/// - -doc /// -Key - (span, Sequence) - [(span,Sequence),Precision] -Headline - construct smallest interval -Usage - I = span(S) - I = span(S,Precision => prec) -Inputs - S:Sequence - containing @TO Number@ (including @TO RRi@) - Precision => ZZ - specifies the desired precision of the output, a value of {\tt -1} uses the minimum precision of the inputs. -Outputs - I:RRi -Description - Text - Returns the smallest interval containing the inputs (which can include intervals). Typically, the returned interval is not empty. -SeeAlso - interval - (span, List) toRRi + toCCi /// doc /// Key interval (interval,Array) - (interval,QQ) - (interval,QQ,QQ) - (interval,QQ,RR) - (interval,QQ,ZZ) + (interval,CC) + (interval,CC,CC) + (interval,CC,RR) + (interval,CC,RRi) + (interval,CCi) + (interval,CCi,Number) + (interval,Number) + (interval,Number,CCi) + (interval,Number,Number) (interval,RR) - (interval,RR,QQ) + (interval,RR,CC) (interval,RR,RR) - (interval,RR,ZZ) - (interval,ZZ) - (interval,ZZ,QQ) - (interval,ZZ,RR) - (interval,ZZ,ZZ) + (interval,RR,RRi) + (interval,RRi) + (interval,RRi,CC) + (interval,RRi,RR) + (interval,RRi,RRi) [interval,Precision] Headline construct an interval @@ -251,9 +307,9 @@ Usage I = interval(n) I = interval(l,r) I = interval([l,r]) - I = interval(n,Precition => prec) - I = interval(l,r,Precition => prec) - I = interval([l,r],Precition => prec) + I = interval(n,Precision => prec) + I = interval(l,r,Precision => prec) + I = interval([l,r],Precision => prec) Inputs n:Number l:Number @@ -264,7 +320,35 @@ Outputs I:RRi Description Text - Returns an interval as small as possible containing {\tt n} or from {\tt l} to {\tt r}. Note that if {\tt l} is to the right of {\tt r}, the constructed interval is empty. + If given one argument, returns a real or complex interval as small as possible containing {\tt n}. + Example + interval 3 + interval(2 + 5*ii) + Text + If given two real arguments (or an array with two entries), the interval + from {\tt l} to {\tt r}. Note that if {\tt l} is to the right of + {\tt r}, the constructed interval is empty. + Example + interval(2, 3) + interval(5, 4) + interval [7, 8] + Text + If given two arguments, at least one of which is a complex number + and neither of which is an interval, then the rectangle in the + complex plane whose lower left hand corner is the first argument + and upper right hand corner is the second argument is returned. + Example + interval(2 + 3*ii, 5 + 4*ii) + Text + If given two real arguments, at least one of which is an interval, + then the first argument gives the real part and the second argument + the imaginary part of a complex interval. + Example + interval(interval(3, 4), interval(5, 6)) + Text + The @M2CODE "Precision"@ option sets the precision of the output. + Example + interval(Precision => 100, 5) SeeAlso (span, List) (span, Sequence) @@ -294,6 +378,8 @@ SeeAlso (span, List) (span, Sequence) interval + left + right /// doc /// @@ -320,3 +406,29 @@ SeeAlso interval toRRi /// + +doc /// +Key + toCCi +Headline + construct an interval +Usage + I = toCCi(n) + I = toCCi(re,im) + I = toCCi(prec,re,im) +Inputs + n:CC + re:RRi + im:RRi + prec:ZZ +Outputs + I:CCi +Description + Text + Returns an interval as small as possible containing {\tt n} or from {\tt l} to {\tt r}. Note that if {\tt l} is to the right of {\tt r}, the constructed interval is empty. This is a more low-level function and @TO interval@ or span should be used instead. +SeeAlso + (span, List) + (span, Sequence) + interval +/// + diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_module.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_module.m2 index 3eca8e520f9..82cf0b209e3 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_module.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_module.m2 @@ -156,7 +156,7 @@ document { }, } -document { Key => (module, Ring), +document { Key => {(module, Ring), (module, RingFamily)}, Usage => "module R", Inputs => {"R"}, Outputs => {{"the free module of rank 1 over the ring R"}}, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 new file mode 100644 index 00000000000..d9cc1315d0e --- /dev/null +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 @@ -0,0 +1,138 @@ +doc /// + Key + Mutex + Headline + the class of mutexes + Description + Text + A @EM "mutex"@ (short for @EM "mutual exclusion"@) is a synchronization + primitive used to prevent multiple threads from accessing shared + data at the same time. It ensures that only one thread can hold + the lock at a time, which helps avoid race conditions in + concurrent programs. + + In Macaulay2, a @M2CODE "Mutex"@ object can be used to protect critical + sections of code. When a thread locks a mutex, other threads attempting + to lock it will wait until it is unlocked. + + For example, suppose multiple threads try to modify the same string. + Each thread will need to get the current value of the string, make its + modification, and then save the new value. However, there is a good + chance that another thread might save its updated value after another + thread has fetched it but before it saved the new value. We can + see this in the code below. + Example + msgs = "" + sayhello = i -> msgs |= "hello from thread #" | toString i | newline + T = apply(10, i -> schedule(() -> sayhello i)) + while not all(T, isReady) do null + stack sort lines msgs + Text + We likely ended up with fewer than the expected number of 10 messages. + We can get around this issue by using a mutex to lock the string so + that only one thread can modify it at a time. + Example + m = new Mutex + msgs = "" + T = apply(10, i -> schedule(() -> (lock m; sayhello i; unlock m))) + while not all(T, isReady) do null + stack sort lines msgs + Text + With the mutex, all 10 messages are present. + SeeAlso + "parallel programming with threads and tasks" + AtomicInt + Subnodes + (NewMethod, Mutex) + (lock, Mutex) + (tryLock, Mutex) + (unlock, Mutex) +/// + +doc /// + Key + (NewMethod, Mutex) + Headline + construct a mutex + Usage + new Mutex + Outputs + :Mutex + Description + Text + Construct a new @TO Mutex@ object. + Example + m = new Mutex +/// + +doc /// + Key + lock + (lock, Mutex) + Headline + lock a mutex + Usage + lock m + Inputs + m:Mutex + Description + Text + Locks a mutex. + Example + m = new Mutex + lock m + Text + If the mutex is already locked, then the thread blocks until it is + unlocked. This is not interruptible. + SeeAlso + (tryLock, Mutex) + (unlock, Mutex) +/// + +doc /// + Key + tryLock + (tryLock, Mutex) + Headline + try locking a mutex + Usage + tryLock m + Inputs + m:Mutex + Description + Text + Tries locking a mutex. + Example + m = new Mutex + tryLock m + Text + If the mutex is already locked, then an error is raised. + Example + stopIfError = false + tryLock m + SeeAlso + (lock, Mutex) + (unlock, Mutex) +/// + +doc /// + Key + unlock + (unlock, Mutex) + Headline + unlock a mutex + Usage + unlock m + Inputs + m:Mutex + Description + Text + Unlocks a mutex. + Example + m = new Mutex + lock m + unlock m + SeeAlso + (lock, Mutex) + (tryLock, Mutex) +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_rings.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_rings.m2 index 4015bf1ad84..7229f5584c6 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_rings.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_rings.m2 @@ -197,7 +197,7 @@ document { "Macaulay2 provides for fraction fields of integral domains.", PARA{}, "In some cases, normal forms of fractions makes sense, but in general - for fraction fieldss of quotient rings, there is no notion of + for fraction fields of quotient rings, there is no notion of normal form for a fraction. In other words, fractions may be equal without displaying the same numerator and denominator.", @@ -450,11 +450,6 @@ document { Key => RealField, PARA { "A real number ring is a ring whose elements are real numbers of variable precision." } } -undocumented { - (NewOfFromMethod,ComplexField,Nothing,ZZ), - (NewOfFromMethod,RealField,Nothing,ZZ) - } - document { Key => ComplexField, Headline => "the class of all complex fields", PARA { "A complex number ring is a ring whose elements are complex numbers of variable precision." } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions.m2 index 0698e319e32..19756e9cd03 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions.m2 @@ -195,6 +195,7 @@ load "./functions/numgens-doc.m2" load "./functions/options-doc.m2" load "./functions/override-doc.m2" load "./functions/pack-doc.m2" +load "./functions/parse-doc.m2" load "./functions/part-doc.m2" load "./functions/parts-doc.m2" load "./functions/partition-doc.m2" @@ -208,6 +209,7 @@ load "./functions/pfaffians-doc.m2" load "./functions/pivots-doc.m2" load "./functions/poincare-doc.m2" load "./functions/polarize-doc.m2" +load "./functions/polylog-doc.m2" load "./functions/position-doc.m2" load "./functions/positions-doc.m2" load "./functions/preimage-doc.m2" @@ -248,6 +250,7 @@ load "./functions/setup-doc.m2" load "./functions/setupLift-doc.m2" load "./functions/setupPromote-doc.m2" load "./functions/show-doc.m2" +load "./functions/shuffle-doc.m2" load "./functions/sign-doc.m2" load "./functions/sin-doc.m2" load "./functions/sinh-doc.m2" diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/Beta-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/Beta-doc.m2 index e7659807d6d..124c704a351 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/Beta-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/Beta-doc.m2 @@ -1,13 +1,7 @@ doc /// Key Beta - (Beta,CC,CC) - (Beta,CC,RR) - (Beta,RR,CC) - (Beta,RR,RR) - (Beta,RR,RRi) - (Beta,RRi,RR) - (Beta,RRi,RRi) + (Beta,InexactNumber,InexactNumber) Headline Beta function Usage @@ -31,21 +25,7 @@ doc /// doc /// Key regularizedBeta - (regularizedBeta,CC,CC,CC) - (regularizedBeta,CC,CC,RR) - (regularizedBeta,CC,RR,CC) - (regularizedBeta,CC,RR,RR) - (regularizedBeta,RR,CC,CC) - (regularizedBeta,RR,CC,RR) - (regularizedBeta,RR,RR,CC) - (regularizedBeta,RR,RR,RR) - (regularizedBeta,RR,RR,RRi) - (regularizedBeta,RR,RRi,RR) - (regularizedBeta,RR,RRi,RRi) - (regularizedBeta,RRi,RR,RR) - (regularizedBeta,RRi,RR,RRi) - (regularizedBeta,RRi,RRi,RR) - (regularizedBeta,RRi,RRi,RRi) + (regularizedBeta,InexactNumber,InexactNumber,InexactNumber) Headline regularized beta function Usage @@ -71,7 +51,7 @@ doc /// doc /// Key inverseRegularizedBeta - (inverseRegularizedBeta,RR,RR,RR) + (inverseRegularizedBeta,InexactNumber,InexactNumber,InexactNumber) Headline inverse of the regularized beta function Usage diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/Gamma-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/Gamma-doc.m2 index a516c879660..97cc30ba6df 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/Gamma-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/Gamma-doc.m2 @@ -1,16 +1,8 @@ doc /// Key Gamma - (Gamma,CC) - (Gamma,CC,CC) - (Gamma,CC,RR) - (Gamma,RR) - (Gamma,RR,CC) - (Gamma,RR,RR) - (Gamma,RR,RRi) - (Gamma,RRi) - (Gamma,RRi,RR) - (Gamma,RRi,RRi) + (Gamma,InexactNumber) + (Gamma,InexactNumber,InexactNumber) Headline Gamma function Usage @@ -41,13 +33,7 @@ doc /// doc /// Key regularizedGamma - (regularizedGamma,CC,CC) - (regularizedGamma,CC,RR) - (regularizedGamma,RR,CC) - (regularizedGamma,RR,RR) - (regularizedGamma,RR,RRi) - (regularizedGamma,RRi,RR) - (regularizedGamma,RRi,RRi) + (regularizedGamma,InexactNumber,InexactNumber) Headline upper regularized gamma function Usage @@ -72,7 +58,7 @@ doc /// doc /// Key inverseRegularizedGamma - (inverseRegularizedGamma,RR,RR) + (inverseRegularizedGamma,InexactNumber,InexactNumber) Headline inverse of the upper regularized gamma function Usage @@ -99,6 +85,7 @@ doc /// lngamma (lngamma, RR) (lngamma, CC) + (lngamma, CCi) (lngamma, RRi) (lngamma, Number) Headline diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/apply-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/apply-doc.m2 index 09b620638d3..c2260b4cb29 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/apply-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/apply-doc.m2 @@ -4,10 +4,6 @@ --- The apply(HashTable,Function) in the "ways to use" section doesn't seem --- to have disappeared in my documentation, even though it is obsolete. -undocumented { - (apply, Thing, Command), -} - document { Key => apply, Headline => "apply a function to each element", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/atan-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/atan-doc.m2 index f85b4f489a6..dd21f4fbb1c 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/atan-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/atan-doc.m2 @@ -3,8 +3,7 @@ --- notes: document { - Key => {atan2,(atan2,RR,RR),(atan2, RRi, RRi), - (atan2,RR,RRi), (atan2,RRi,RR) + Key => {atan2,(atan2,InexactNumber,InexactNumber) }, Headline => "compute an angle of a certain triangle", Usage => "atan2(y,x)\natan2(y,I)\natan2(J,x)\natan2(J,I)", @@ -22,7 +21,7 @@ document { } document { - Key => {atan,(atan,RR),(atan,CC),(atan, RRi)}, + Key => {atan,(atan,InexactNumber)}, Headline => "compute the arctangent of a number", Usage => "atan x\natan I", Inputs => { "x" => RR, "I" => RRi}, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/cos-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/cos-doc.m2 index 3a11982d5a2..46b97cb3ea7 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/cos-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/cos-doc.m2 @@ -3,7 +3,7 @@ --- notes: document { - Key => {cos, (cos,CC),(cos,RR),(cos, RRi)}, + Key => {cos, (cos,InexactNumber)}, Headline => "compute the cosine", Usage => "cos x\ncos I", Inputs => { "x" => RR,"I"=>RRi}, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/document-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/document-doc.m2 index b6a5887b01e..47055cf4e97 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/document-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/document-doc.m2 @@ -33,6 +33,7 @@ Node Subnodes=>List SourceCode=>List ExampleFiles=>List + Citation=>List Consequences Item formatted documentation is created and stored in the @TO2 {"currentPackage", "current package"}@ @@ -118,6 +119,7 @@ Node [document, Subnodes] [document, SourceCode] [document, ExampleFiles] + [document, Citation] Node Key @@ -459,6 +461,27 @@ Node [newPackage, AuxiliaryFiles] /// +doc /// +Node + Key + Citation + [document, Citation] + Headline + package citation information + Description + Text + This option gives the BibTeX code to be used to cite the package being + documented. Note this is only used in the main documentation node for + the package and is ignored otherwise. + Code + EXAMPLE { PRE ////Citation => {//////@misc{mypkg, + author = "John Doe", + title = "My Macaulay2 package", + year = "2025" }//////}//// } + SeeAlso + "PackageCitations::cite" +/// + doc /// Node Key diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/erf-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/erf-doc.m2 index 02226d7576e..19a4aa30b7f 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/erf-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/erf-doc.m2 @@ -1,9 +1,7 @@ doc /// Key erf - (erf,CC) - (erf,RR) - (erf,RRi) + (erf,InexactNumber) Headline error function Usage @@ -26,9 +24,7 @@ doc /// doc /// Key erfc - (erfc,CC) - (erfc,RR) - (erfc,RRi) + (erfc,InexactNumber) Headline complementary error function Usage @@ -51,8 +47,7 @@ doc /// doc /// Key inverseErf - (inverseErf,RR) - (inverseErf,RRi) + (inverseErf,InexactNumber) Headline inverse error function Usage diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/help-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/help-doc.m2 index 5ea6309dd13..490484df652 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/help-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/help-doc.m2 @@ -226,6 +226,7 @@ Node (about, String) (about, Symbol) (about, Type) + (about, Keyword) Headline search the documentation Usage @@ -371,8 +372,8 @@ Node { M2CODE "help \"getting started\"", "" }, { M2CODE "help \"a first Macaulay2 session\"", "" }, { M2CODE "help coker", "-- show documentation for coker" }, - { M2CODE "help about Ext", "-- show documentation about Ext" }, - { M2CODE "help about(\"Yoneda\", Body=>true)", "-- show documentation mentioning \"Yoneda\"" }, + { M2CODE "headlines about Ext", "-- show a list of documentation headlines about Ext" }, + { M2CODE "headlines about(\"Yoneda\", Body=>true)", "-- show a list of documentation headlines mentioning \"Yoneda\"" }, { M2CODE "printWidth = 80", "-- set print width to 80 characters" }, { M2CODE "viewHelp", "-- view documentation in a browser" }, { M2CODE "viewHelp coker", "-- view documentation for coker in browser" }, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/lift-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/lift-doc.m2 index f4e2138b827..09300bdead3 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/lift-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/lift-doc.m2 @@ -38,6 +38,10 @@ document { (lift,Matrix,CC_*,QQ), (lift,Matrix,CC_*,RR_*), (lift,Matrix,CC_*,ZZ), + (lift,Matrix,CCi_*,CC_*), + (lift,Matrix,CCi_*,QQ), + (lift,Matrix,CCi_*,RR_*), + (lift,Matrix,CCi_*,ZZ), (lift,Matrix,Number), (lift,Matrix,QQ,QQ), (lift,Matrix,QQ,ZZ), @@ -49,11 +53,25 @@ document { (lift, RR, QQ), (lift, RR, ZZ), (lift, ZZ, ZZ), - (lift, RRi, QQ), - (lift, RRi, RR_*), - (lift, RRi, ZZ), + (lift, RRi, QQ), + (lift, RRi, RR_*), + (lift, RRi, ZZ), + (lift, RR, RR'), + (lift, CCi, RR'), + (lift, CC, CC'), + (lift, CCi, CC'), + (lift, RRi, RRi'), + (lift, CC, RRi'), + (lift, CCi, RRi'), + (lift, CCi, CCi'), + (lift, CCi, QQ), + (lift, CCi, ZZ), + (lift, Constant, Number), + (lift, Constant, InexactNumber), (symbol ^, Number, Ring), (symbol ^, Number, RingFamily), + (symbol ^, RingElement, Ring), + (symbol ^, RingElement, RingFamily), (lift,Matrix,RRi',QQ), (lift,Matrix,RRi',RR'), (lift,Matrix,RRi',ZZ) @@ -118,19 +136,16 @@ document { lift(3.0,QQ) ///, PARA{ - "A continued fraction method is used to lift a real number to a rational number, whereas - ", TO "promote", " uses the internal binary representation.", + "A continued fraction method is used to lift a real number to a rational number." }, EXAMPLE lines /// lift(123/2341.,QQ) - promote(123/2341.,QQ) factor oo ///, PARA { "For numbers and ring elements, an alternate syntax with ", TO "^", " is available, analogous to the use of ", TO "_", " for ", TO "promote", "." }, EXAMPLE lines /// .0001^QQ - .0001_QQ ///, SeeAlso => { baseRings, promote }, Subnodes => { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/liftable-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/liftable-doc.m2 index 8874e161923..3fbf4756d2d 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/liftable-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/liftable-doc.m2 @@ -7,7 +7,13 @@ undocumented {(liftable, Number, Number), (liftable, Number, RingElement), (liftable, QQ, ZZ), (liftable, CC, RR_*), (liftable, Number, InexactNumber), - (liftable, RRi, QQ),(liftable, RRi, RR),(liftable, RRi, ZZ)} + (liftable, RRi, QQ), + (liftable, CC, RRi'), + (liftable, CCi, RRi'), + (liftable, RRi, RR'), + (liftable, CCi, CC'), + (liftable, CCi, RR'), + (liftable, CCi, QQ)} document { Key => {liftable}, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/locate-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/locate-doc.m2 index 8990d21d3e6..00f65f25e1c 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/locate-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/locate-doc.m2 @@ -16,6 +16,7 @@ Node (locate, Package) (locate, List) (locate, ZZ) + (locate, Error) Headline locate source code Usage diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/map-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/map-doc.m2 index fc64332705e..175dc570d33 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/map-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/map-doc.m2 @@ -7,7 +7,8 @@ (map,GaloisField,GaloisField) *- -undocumented {(map, RingFamily, Thing, Thing),(map, Thing, RingFamily, Thing)} +undocumented {(map, RingFamily, Thing, Thing),(map, Thing, RingFamily, Thing), + (map, RingFamily, Thing), (map, Thing, RingFamily)} document { Key => { map, "morphisms" }, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/matrix-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/matrix-doc.m2 index a043b55c673..bfd01bfd459 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/matrix-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/matrix-doc.m2 @@ -192,13 +192,33 @@ document { SeeAlso => {mutableMatrix, mutableIdentity, MutableMatrix} } -document { - Key => {(matrix, RingElement),(matrix, Number)}, - Headline => "make a matrix from a ring element", - Usage => "matrix r", - Inputs => { "r" }, - Outputs => { Matrix => {"the one by one matrix with ", TT "r", " as its single entry"} }, - EXAMPLE lines /// - matrix 48 - /// - } +doc /// + Key + (matrix, Ring, RingElement) + (matrix, Ring, Number) + (matrix, Number) + (matrix, RingElement) + (matrix, RingFamily, RingElement) + (matrix, RingFamily, Number) + Headline + make a matrix from a ring element + Usage + matrix(R, f) + matrix f + Outputs + :Matrix -- the one by one matrix with @VAR "f"@ as its single entry + Inputs + R:{Ring, RingFamily} + f:{RingElement, Number} + Description + Example + matrix 48 + R = QQ[x,y] + matrix(x^2 - y^2) + Text + Specify a ring @VAR "R"@ to get a matrix over @VAR "R"@ instead of the + ring of @VAR "f"@. + Example + matrix(QQ, 48) + matrix(R, 48) +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/parse-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/parse-doc.m2 new file mode 100644 index 00000000000..8acee8900dc --- /dev/null +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/parse-doc.m2 @@ -0,0 +1,35 @@ +doc /// + Key + parse + Headline + get a concrete syntax tree of Macaulay2 code + Usage + parse s + Inputs + s:String -- Macaulay2 code + Outputs + :List + Description + Text + This function parses a string @VAR "s"@ containing Macaulay2 code and + returns a list representing the corresponding concrete syntax tree. + Each element of the list corresponds a statement. Except for very simple + code, they will have a nested structure. The first element is a tag + representing the type of node and later elements are its children. + + Consider the following: + Example + parse "2 + 3" + Text + We see that this is a @CODE "Binary"@ node with three children: the + left hand side, the operator, and the right hand side. All three + are @CODE "Token"@ nodes. + + The next example shows how Macaulay2 deals with the + @wikipedia "danging else"@ problem: + Example + parse "if a then if b then s1 else s2" + SeeAlso + pseudocode + disassemble +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/polylog-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/polylog-doc.m2 new file mode 100644 index 00000000000..a2c061c1706 --- /dev/null +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/polylog-doc.m2 @@ -0,0 +1,19 @@ +doc /// + Key + polylog + Headline + polylogarithm function + Usage + polylog_s z + Inputs + s:Number + z:Number + Description + Text + The @wikipedia "polylogarithm"@ function is defined by + $\operatorname{Li}_s(z) = \sum_{k=1}^\infty\frac{z^k}{k^s}$. + For example, $\operatorname{Li}_s(1)= \zeta(s)$. + Example + polylog_2 1 + zeta 2 +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/presentation-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/presentation-doc.m2 index c978f2277fd..cfdeba39a23 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/presentation-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/presentation-doc.m2 @@ -4,6 +4,9 @@ undocumented { (presentation,QuotientRing,QuotientRing), + (presentation,Ring), + (presentation,InexactField), + (presentation,InexactFieldFamily), (presentation,PolynomialRing), (presentation,PolynomialRing,PolynomialRing), (presentation,QuotientRing,PolynomialRing) diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/promote-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/promote-doc.m2 index 12fc357c243..66d4ba2ed09 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/promote-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/promote-doc.m2 @@ -19,40 +19,57 @@ promote(Vector,type of RingElement) *- undocumented {(promote,CC,CC_*), - (promote, Matrix, InexactNumber),(promote, Number, InexactNumber), + (promote,CC,CCi_*), + (promote, Matrix, InexactNumber), + (promote, QQ, RR), + (promote, ZZ, RR), + (promote, Number, CC), (promote, Ideal, Number), (promote, Ideal, RingElement), (promote, List, QQ, CC_*), + (promote, List, QQ, CCi_*), (promote, List, QQ, QQ), (promote, List, QQ, RR_*), (promote, List, RR_*, CC_*), + (promote, List, RR_*, CCi_*), (promote, List, RR_*, RR_*), (promote, List, CC_*, CC_*), + (promote, List, CC_*, CCi_*), (promote, List, ZZ, CC_*), + (promote, List, ZZ, CCi_*), (promote, List, ZZ, QQ), (promote, List, ZZ, RR_*), (promote, List, ZZ, ZZ), (promote,Matrix,CC_*,CC_*), + (promote,Matrix,CC_*,CCi_*), (promote,Matrix,Number), (promote,Matrix,QQ,CC_*), + (promote,Matrix,QQ,CCi_*), (promote,Matrix,QQ,QQ), (promote,Matrix,QQ,RR_*), (promote,Matrix,RingElement), (promote,Matrix,RR_*,CC_*), + (promote,Matrix,RR_*,CCi_*), (promote,Matrix,RR_*,RR_*), (promote,Matrix,ZZ,CC_*), + (promote,Matrix,ZZ,CCi_*), (promote,Matrix,ZZ,QQ), (promote,Matrix,ZZ,RR_*), (promote,Matrix,ZZ,ZZ), (promote,MonoidElement,RingElement), (promote,QQ,CC_*), + (promote,QQ,CCi_*), (promote,QQ,QQ), (promote, QQ, RingElement), (promote,QQ,RR_*), (promote,RR,CC_*), + (promote,RR,CCi_*), (promote,RR,RR_*), (promote, RRi, RRi_*), + (promote, RRi, CCi_*), + (promote, CCi, CCi_*), (promote,ZZ,CC_*), + (promote,ZZ,CCi_*), (promote,ZZ,QQ), (promote,ZZ,RingElement), (promote,ZZ,RR_*), @@ -94,8 +111,7 @@ undocumented {(promote,CC,CC_*), document { Key => {promote, - (symbol _, RingElement, Ring),(symbol _,Number,Ring), - (promote,RR,QQ)}, + (symbol _, RingElement, Ring),(symbol _,Number,Ring)}, Headline => "promote to another ring", Usage => "promote(f,R)", Inputs => { @@ -117,17 +133,6 @@ document { promote(f,S) G = map(S,R); G(f) ///, - PARA { - "Promotion of real numbers to rational numbers is accomplished by using all of the bits of - the internal representation." - }, - EXAMPLE lines /// - promote(101.,QQ) - promote(.101,QQ) - factor denominator oo - ooo + 0. - oo === .101 - ///, PARA { "For promotion of ring elements, there is the following shorter notation." }, @@ -148,6 +153,19 @@ document { "A special feature is that if ", TT "f", " is rational, and ", TT "R", " is not an algebra over ", TO "QQ", ", then an element of ", TT "R", " is provided by attempting the evident division.", + PARA { + "Prior to version 1.26.05, promotion of real numbers to rational numbers was accomplished by using all of the bits of + the internal representation. This feature was removed since there is no natural map from $\\RR\\to\\QQ$. + However, this functionality is still available using the unexported function ", CODE "internalRepresentation", "." + }, + EXAMPLE lines /// + debug Core + internalRepresentation 101. + internalRepresentation .101 + factor denominator oo + ooo + 0. + oo === .101 + ///, SeeAlso => {baseRings, lift, liftable, "substitution and maps between rings", substitute, (symbol**,Matrix,Ring) }, Subnodes => { TO setupPromote }, diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/random-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/random-doc.m2 index 8af9c5a1c15..9d4f2aedf7e 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/random-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/random-doc.m2 @@ -17,11 +17,11 @@ Node (random, ZZ, ZZ) (random, QQ) (random, Type) - (random, List) (random, ZZ, Ideal) (random, ZZ, Ring) (random, Module) (random, Module, Module) + randomElement setRandomSeed randomSubset @@ -178,19 +178,23 @@ Node Node Key - (random, List) + randomElement + (randomElement, List) +-- (random, List) Headline - shuffle a list randomly + select a random element of a list Usage - random L + randomElement L Inputs L:List Outputs - :List - a new list containing the elements of @TT "L"@ in a shuffled random order + :Thing -- random element of @VAR "L"@ Description Example - random toList (0 .. 12) + randomElement toList (0 .. 12) + Text + In the near future, this will also be the behavior of + @TO (random, List)@. SeeAlso setRandomSeed diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/randomSubset-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/randomSubset-doc.m2 index 549130bc173..8bebf19b9e0 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/randomSubset-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/randomSubset-doc.m2 @@ -31,10 +31,14 @@ doc /// Example randomSubset(4, 2) randomSubset 5 + Text + The order of elements in @VAR "x"@ is preserved. For an arbitrary + order, use @TO (shuffle, List, ZZ)@ instead. References Knuth, Donald E. @EM "The Art of Computer Programming: Seminumerical Algorithms, Volume 2"@. Addison-Wesley Professional, 2014. (Algorithm S, Section 3.4.2) SeeAlso random + shuffle /// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/ring-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/ring-doc.m2 index f5b0382f986..d60a9cc0276 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/ring-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/ring-doc.m2 @@ -6,7 +6,8 @@ document { Key => {ring, (ring,Vector), (ring,RingElement), (ring, GroebnerBasis),(ring, Number), (ring,Module),(ring,Matrix), - (ring,MutableMatrix),(ring,Ideal), (ring,CC),(ring,RR),(ring,RRi) + (ring,MutableMatrix),(ring,Ideal), (ring,CC),(ring,RR),(ring,RRi), + (ring,CCi) }, Headline => "get the associated ring of an object", Usage => "ring M", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/set-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/set-doc.m2 index cfb02bc8e75..c199c9f3bdd 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/set-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/set-doc.m2 @@ -105,8 +105,8 @@ document { document { Key => {(symbol -, Set, Set), - (symbol -, Set, List), - (symbol -, List, Set)}, + (symbol -, Set, VisibleList), + (symbol -, VisibleList, Set)}, Headline => "set difference", Usage => "x - y", Inputs => { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/setupPromote-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/setupPromote-doc.m2 index 82ed4bf28b6..de4637c4866 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/setupPromote-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/setupPromote-doc.m2 @@ -1,25 +1,38 @@ --- status: DRAFT --- author(s): PZJ ---- notes: - -undocumented {(setupPromote,RingMap,Ring,Ring,Function), (setupPromote,Function,Ring,Ring,Function), (setupPromote,Function,Ring,Ring), (setupPromote,RingMap,Ring,Ring), (setupPromote,Ring,Ring)} - +--- notes: doc /// Key setupPromote (setupPromote,RingMap) + (setupPromote,RingMap,Ring,Ring,Function) + (setupPromote,Function,Ring,Ring,Function) + (setupPromote,Function,Ring,Ring) + (setupPromote,RingMap,Ring,Ring) + (setupPromote,Ring,Ring) Headline set up promote from one ring to another Usage setupPromote f + setupPromote(f, R, S) + setupPromote(f, R, S, d) + setupPromote(R, S) Inputs - f: RingMap + f:{RingMap, Function} + R:Ring + S:Ring + d:Function Description Text - This defines promotion from one ring to another as the application of a ring map. - After calling @TT "setupPromote"@, any operation that is given an element of the source of @TT "f"@ but - expects an element of the target of @TT "f"@ will automatically @TO "promote"@ it by applying @TT "f"@. + This defines promotion from one ring @VAR "R"@ to another ring @VAR "S"@ as + the application of a ring map or function @VAR "f"@. After calling + @M2CODE "setupPromote"@, any operation that is given an element of @VAR "R"@ + but expects an element of @VAR "S"@ will automatically @TO "promote"@ it by + applying @VAR "f"@. + + Note that @VAR "R"@ and @VAR "S"@ must be specified when @VAR "f"@ is + @ofClass Function@, but may be omitted when it is @ofClass RingMap@. Example R=QQ[x_1,x_2] R'=QQ[e_1,e_2,Degrees=>{1,2}] @@ -27,6 +40,30 @@ doc /// promote(e_1^2,R) e_1*x_1 e_2==x_1*x_2 + Text + If @VAR "f"@ is omitted, then the map created by @TO (map, Ring, Ring)@ + that maps variables in @VAR "R"@ to variables in @VAR "S"@ with the same name + is used. + Example + R = QQ[x] + S = QQ[x,y] + setupPromote(R, S) + promote(R_0, S) + Text + The optional argument @VAR "d"@ is a function that translates degrees + from @VAR "R"@ to @VAR "S"@. It is used to set up + @M2CODE "promote(List, R, S)"@, which translates degree lists when + promoting free modules. When @VAR "f"@ is @ofClass Function@, there is + no @TO DegreeMap@ option available, so @VAR "d"@ must be specified + explicitly if the degree map is not the default (which pads or truncates + degrees). Consider the following example. + Example + R = QQ[x] + S = QQ[a, b, Degrees => {{1,0},{0,1}}] + setupPromote(r -> sub(r, x => b), R, S) + promote(R^{1,2}, S) -- wrong degrees! + setupPromote(r -> sub(r, x => b), R, S, d -> {0, d#0}) + promote(R^{1,2}, S) SeeAlso setupLift /// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/shuffle-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/shuffle-doc.m2 new file mode 100644 index 00000000000..f1c7fa98978 --- /dev/null +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/shuffle-doc.m2 @@ -0,0 +1,42 @@ +doc /// + Key + shuffle + (shuffle, List) + (shuffle, MutableList) + (shuffle, List, ZZ) + (random, List) + Headline + shuffle a list randomly + Usage + shuffle L + shuffle(L, n) + Inputs + L:List + n:ZZ + Outputs + :List + a new list containing the elements of @TT "L"@ in a shuffled random order + Description + Example + shuffle toList (0 .. 12) + Text + Mutable lists are shuffled in place. + Example + x = new MutableList from 0..12; peek x + shuffle x; peek x + Text + Is @VAR "n"@ is given, then a shuffled random sublist of length + @VAR "n"@ is returned. Contrast this with @TO randomSubset@, + which preserves the order. + Example + shuffle(toList(0..12), 4) + Text + Currently, @TO random@ has this behavior when given a list. + However, in the near future, this will change to the behavior of + @TO randomElement@. + Example + random toList(0..12) + SeeAlso + setRandomSeed + randomSubset +/// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/sin-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/sin-doc.m2 index e4aeff6bfc6..1fe5825e654 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/sin-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/sin-doc.m2 @@ -3,7 +3,7 @@ --- notes: Is the comment about being used as an example still relevant? document { - Key => {sin,(sin,CC),(sin,RR),(sin, RRi)}, + Key => {sin,(sin,InexactNumber)}, -- this node is used as an example in the documentation node Inputs and Outputs. Headline => "compute the sine", Usage => "sin x\nsin I", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/sinh-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/sinh-doc.m2 index ad273d0aa79..cefa1766c1a 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/sinh-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/sinh-doc.m2 @@ -3,7 +3,7 @@ --- notes: include example? document { - Key => {sinh,(sinh,CC),(sinh,RR),(sinh, RRi)}, + Key => {sinh,(sinh,InexactNumber)}, Headline => "compute the hyperbolic sine", Usage => "sinh x\nsinh I", Inputs => { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/tan-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/tan-doc.m2 index b53f3290395..3eaa63ff556 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/tan-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/tan-doc.m2 @@ -3,7 +3,7 @@ --- notes: document { - Key => {tan,(tan,CC),(tan,RR), (tan,RRi)}, + Key => {tan,(tan,InexactNumber)}, Headline => "compute the tangent", Usage => "tan x\ntan I", Inputs => { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/tanh-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/tanh-doc.m2 index 432c7984dd0..3305f26edd4 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/tanh-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/tanh-doc.m2 @@ -3,7 +3,7 @@ --- notes: include example? document { - Key => {tanh, (tanh,CC),(tanh,RR),(tanh, RRi)}, + Key => {tanh, (tanh,InexactNumber)}, Headline => "compute the hyperbolic tangent", Usage => "tanh x\ntanh I", Inputs => { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/loads.m2 b/M2/Macaulay2/packages/Macaulay2Doc/loads.m2 index c6704de792a..285dbdb7320 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/loads.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/loads.m2 @@ -62,6 +62,7 @@ load "./operators.m2" load "./shared.m2" load "./doc_iterators.m2" load "./doc_atomic.m2" +load "./doc_mutex.m2" load "./options.m2" -- this must come last load "./undocumented.m2" diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators.m2 index 2a60ef84356..bd3814ec638 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators.m2 @@ -36,24 +36,31 @@ document { /// } -document { - Key => {abs,(abs, RR),(abs, CC),(abs, ZZ),(abs, QQ),(abs, RRi),(abs, Constant)}, - Headline => "absolute value function", - Usage => "abs x\nabs I", - Inputs => { - "x" => "a number", - "I" => RRi - }, - Outputs => { - {"the absolute value of ", TT "x"}, - RRi => {"an interval containing the absolute values of all the point of ", TT "I"} - }, - TT "abs x", " computes the absolute value of ", TT "x", ".", - EXAMPLE { - "abs(-pi)", - "abs(1+ii)" - }, - } +doc /// + Key + abs + (abs, Number) + (abs, Constant) + Headline + absolute value function + Usage + abs x + abs I + Inputs + x:{RR,CC} + I:{RRi,CCi} + Outputs + :RR + the absolute value of @TT "x"@ + :RRi + an interval containing the absolute values of all the points of @TT "I"@ + Description + Example + abs(-pi) + abs(1+ii) + abs(interval(1,2)) + abs(interval(1+2*ii,3+4*ii)) +/// document { Key => (exp,RingElement), @@ -67,45 +74,96 @@ document { /// } -document { - Key => {exp,(exp,RR),(exp,CC),(exp,RRi)}, - Headline => "exponential function", - Usage => "exp x\nexp I", - Inputs => { "x" => RR ,"I"=>RRi}, - Outputs => { { "the exponential of ", TT "x" }, - RRi=>{"an interval containing the exponentials of points of ", TT "I"} } , - EXAMPLE lines /// - exp 1p300 - exp(pi*ii) - /// - } +doc /// + Key + exp + (exp, InexactNumber) + Headline + exponential function + Usage + exp x + exp I + Inputs + x:{RR,CC} + I:{RRi,CCi} + Outputs + :{RR,CC} + the exponential of @TT "x"@ + :{RRi,CCi} + an interval containing the exponentials of all the points of @TT "I"@ + Description + Example + exp 1p300 + exp(pi*ii) + exp(interval(1,2)) + exp(interval(1+2*ii,3+4*ii)) +/// -document { - Key => {log,(log, RR),(log,CC),(log, RRi),(log, RR, RR),(log, RRi, RRi), - (log,RR,CC),(log,RR,RRi),(log,RRi,RR)}, - Headline => "logarithm function", - Usage => "log x\nlog(b,x)\nlog_b x\nlog I\nlog(b,I)\nlog_b I\nlog(J,x)\nlog_J x\nlog(J,I)\nlog_J I", -Inputs => { "x" => RR, "b" => RR => {"the base for the logarithm"}, "I" => RRi, "J" => RRi => {"an interval of bases for the logarithm"} }, -Outputs => { { "the logarithm of ", TT "x"}, RRi => {"an interval containing the logarithms of points of ", TT "I"}, RRi => {"an interval containing the logarithms for bases in ", TT "J"} }, - EXAMPLE lines /// - log 10 - log_2 10 - log_10 2p100 - /// - } -document { - Key => {sqrt,(sqrt, CC),(sqrt, RR), (sqrt, RRi)}, - Headline => "square root function", -Usage => "sqrt x\nsqrt I", - Inputs => { "x" => RR, "I" => RRi }, - Outputs => { { "the square root of ", TT "x"}, -RRi => { "an interval containing the square roots of the points of ", TT "I" } -}, - EXAMPLE lines /// - sqrt 2p200 - sqrt (+ii) - /// - } +doc /// + Key + log + (log,InexactNumber) + (log,InexactNumber,InexactNumber) + Headline + logarithm function + Usage + log x + log(b,x) + log_b x + log I + log(b,I) + log_b I + log(J,x) + log_J x + log(J,I) + log_J I + Inputs + x:{RR,CC} + b:RR + the base for the logarithm + I:{RRi,CCi} + J:RRi + an interval of bases for the logarithm + Outputs + :{RR,CC} + the logarithm of @TT "x"@ + :{RRi,CCi} + an interval containing the logarithms of all the points of @TT "I"@ + :{RR,CC,RRi,CCi} + an interval containing the logarithms for bases in @TT "J"@ + Description + Example + log 10 + log_2 10 + log_10 2p100 + log interval(2,3) + log interval(1+2*ii,3+4*ii) +/// + +doc /// + Key + sqrt + (sqrt, InexactNumber) + Headline + square root function + Usage + sqrt x + sqrt I + Inputs + x:{RR,CC} + I:{RRi,CCi} + Outputs + :{RR,CC} + the square root of @TT "x"@ + :{RRi,CCi} + an interval containing the square roots of all the points of @TT "I"@ + Description + Example + sqrt 2p200 + sqrt(+ii) + sqrt(interval(2,3)) + sqrt(interval(1+2*ii,3+4*ii)) +/// doc /// Key @@ -456,20 +514,14 @@ doc /// Headline logical not Usage - n~ + ~n Inputs n:ZZ Outputs :ZZ -- the bitwise complement of @TT "n"@ Description Example - 7~ - Text - Note that @TT "~"@ has @TO2 {"precedence of operators", - "higher precedence"}@ than @TT "-"@, so enclose negative integers in - parentheses. - Example - (-12)~ + ~7 SeeAlso (symbol &, ZZ, ZZ) (symbol |, ZZ, ZZ) @@ -488,8 +540,9 @@ document { document { Key => symbol ~, - Headline => "a unary postfix operator", + Headline => "a unary or binary operator, often used for bitwise negation", Subnodes => { TO (symbol ~, ZZ) }, + SeeAlso => { "Python::~ PythonObject", "RInterface::~ RObject"} } document { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/assignment.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/assignment.m2 index 43b8be41929..a6a1925ca55 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/assignment.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/assignment.m2 @@ -180,7 +180,9 @@ document { (x,y) = f 9 x y - /// + ///, + PARA {"Multiple assignment may also be used for other uses of ", CODE "=", + " such as assigning to elements of mutable lists and hash tables and using/installing assignment methods."} }, SYNOPSIS { Heading => "assignment to an element of a mutable list", @@ -359,8 +361,8 @@ document { }}, "The first line of the following example illustrates the syntax above.", EXAMPLE lines /// - String ~ = peek; - "foo" ~ = "value" + String ^~ = peek; + "foo" ^~ = "value" ///, PARA "Warning: the installation of new methods may supplant old ones, changing the behavior of Macaulay2." }, @@ -381,8 +383,8 @@ document { References to currently installed assignment methods are given below.", "The second line of the following example illustrates the syntax above.", EXAMPLE lines /// - String ~ = peek; - "foo" ~ = "value" + String ^~ = peek; + "foo" ^~ = "value" /// }, SeeAlso => {":=", "<-", "globalAssignmentHooks" } @@ -482,13 +484,7 @@ document { Consequences => { { "new local variables ", TT "x", ", ", TT "y", ", ", TT "z", ", ... are created" }, { "the expressions c,d,e,... are assigned to the variables x,y,z,..., respectively, as above." }, - { "If the left hand side has more elements than the right hand side, then the extra symbols on the left side are given the value ", TO "null", "." }, - { "If the left hand side has fewer elements than the right hand side, then the last symbol on the left hand side is given - as value a sequence containing the trailing elements of the right hand side." - }, - { "If the right hand side is not a sequence, then it is assigned to the first symbol on the left, and the remaining symbols are assigned the - value ", TO "null", "." - } + { "the number of expressions must match the number of variables." } }, PARA "Multiple assignment effectively means that functions can return multiple values usefully.", EXAMPLE lines /// @@ -496,7 +492,8 @@ document { (r,s) := f 9 r s - /// + ///, + PARA {"Multiple assignment may also be used for other uses of ", CODE ":=", " such as method installation."}, }, SYNOPSIS { Heading => "installing methods for binary operators", @@ -610,8 +607,8 @@ document { }}, "The first line of the following example illustrates the syntax above.", EXAMPLE lines /// - String ~ := peek; - "foo" ~ + String ^~ := peek; + "foo" ^~ ///, PARA "Warning: the installation of new methods may supplant old ones, changing the behavior of Macaulay2." }, @@ -630,8 +627,8 @@ document { }, "The second line of the following example illustrates the syntax above.", EXAMPLE lines /// - String ~ := peek; - "foo" ~ + String ^~ := peek; + "foo" ^~ /// }, SYNOPSIS { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/augmented_assignment.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/augmented_assignment.m2 index ac72219219f..65d113c5043 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/augmented_assignment.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/augmented_assignment.m2 @@ -18,6 +18,12 @@ doc /// Example importFrom(Core, "augmentedAssignmentOperators"); augmentedAssignmentOperators + Text + Multiple augmented assignment is also supported. + Example + (x,y) = (3, 4) + (x,y) += (5, 6) + (x,y) SeeAlso "installing augmented assignment methods" /// diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/caret.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/caret.m2 index c022bb1ba68..4d66ea98a88 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/caret.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/caret.m2 @@ -10,7 +10,6 @@ undocumented { (symbol ^, QQ, InfiniteNumber), (symbol ^, RR, InfiniteNumber), (symbol ^, Constant, Constant), - (symbol ^, Constant, RingFamily), (symbol ^, Constant, InexactNumber), (symbol ^, Constant, Number), (symbol ^, InexactNumber, Constant), diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/division.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/division.m2 index 74226001747..6e4658a142c 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/division.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/division.m2 @@ -29,28 +29,41 @@ document { Key => { symbol /, (symbol /, CC, CC), + (symbol /, CC, CCi), (symbol /, CC, QQ), (symbol /, CC, RR), + (symbol /, CC, RRi), (symbol /, CC, ZZ), + (symbol /, CCi, CC), + (symbol /, CCi, CCi), + (symbol /, CCi, QQ), + (symbol /, CCi, RR), + (symbol /, CCi, RRi), + (symbol /, CCi, ZZ), (symbol /, QQ, CC), + (symbol /, QQ, CCi), (symbol /, QQ, QQ), (symbol /, QQ, RR), + (symbol /, QQ, RRi), (symbol /, QQ, ZZ), (symbol /, RR, CC), + (symbol /, RR, CCi), (symbol /, RR, QQ), (symbol /, RR, RR), + (symbol /, RR, RRi), (symbol /, RR, ZZ), (symbol /, ZZ, CC), + (symbol /, ZZ, CCi), (symbol /, ZZ, QQ), (symbol /, ZZ, RR), + (symbol /, ZZ, RRi), (symbol /, ZZ, ZZ), - (symbol /, QQ, RRi), - (symbol /, RR, RRi), + (symbol /, RRi, CC), + (symbol /, RRi, CCi), (symbol /, RRi, QQ), (symbol /, RRi, RR), (symbol /, RRi, RRi), (symbol /, RRi, ZZ), - (symbol /, ZZ, RRi) }, Headline => "a binary operator, usually used for division", Usage => "x / y", @@ -66,11 +79,12 @@ document { 2./3 ///, HEADER3 "Intervals", - PARA { "If one of the inputs is an ", TO "RRi", ", the output is an interval containing all quotients of pairs in the inputs." }, + PARA { "If one of the inputs is an ", TO "RRi", " or a ", TO "CCi", ", the output is an interval containing all quotients of pairs in the inputs." }, EXAMPLE { "2/interval(1,3)", "interval(-1,2)/interval(1,3)", - "interval(1,2)/interval(1,2)" + "interval(1,2)/interval(1,2)", + "interval(1,2+3*ii)/(1+2*ii)" }, SeeAlso => { "//"} } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/equality.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/equality.m2 index d1ca246d315..0f6a20b9527 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/equality.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/equality.m2 @@ -120,11 +120,13 @@ document { "image matrix {{2,a},{0,5}} == R^2" }, HEADER3 "Intervals", - PARA { "If either side of the equality is an ", TO "RRi", ", the equality is an equality of sets." }, + PARA { "If either side of the equality is an ", TO "RRi", " or a ", TO "CCi", ", the equality is an equality of sets." }, EXAMPLE { "interval(1,3) == interval(1,3)", "interval(1/2) == 1/2", - "interval(1/3) == 1/3" + "interval(1/3) == 1/3", + "interval(1+2*ii,3+4*ii) == span(1+4*ii,3+2*ii)", + "interval(1/2+ii/3) == (1/2+ii/3)" }, PARA{ "It may happen that for certain types of objects, there is no method installed (yet) diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/minus.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/minus.m2 index 9f235ecdd72..5e17780fdb4 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/minus.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/minus.m2 @@ -26,33 +26,47 @@ document { symbol -, (symbol -, ZZ), (symbol -, ZZ, CC), + (symbol -, ZZ, CCi), (symbol -, ZZ, QQ), (symbol -, ZZ, RR), (symbol -, ZZ, RRi), (symbol -, ZZ, ZZ), (symbol -, QQ), (symbol -, QQ, CC), + (symbol -, QQ, CCi), (symbol -, QQ, QQ), (symbol -, QQ, RR), (symbol -, QQ, RRi), (symbol -, QQ, ZZ), (symbol -, RR), (symbol -, RR, CC), + (symbol -, RR, CCi), (symbol -, RR, QQ), (symbol -, RR, RR), (symbol -, RR, RRi), (symbol -, RR, ZZ), (symbol -, RRi), + (symbol -, RRi, CC), + (symbol -, RRi, CCi), (symbol -, RRi, QQ), (symbol -, RRi, RR), (symbol -, RRi, RRi), (symbol -, RRi, ZZ), (symbol -, CC), (symbol -, CC, CC), + (symbol -, CC, CCi), (symbol -, CC, InfiniteNumber), (symbol -, CC, QQ), (symbol -, CC, RR), + (symbol -, CC, RRi), (symbol -, CC, ZZ), + (symbol -, CCi), + (symbol -, CCi, CC), + (symbol -, CCi, CCi), + (symbol -, CCi, QQ), + (symbol -, CCi, RR), + (symbol -, CCi, RRi), + (symbol -, CCi, ZZ), (symbol -, Constant), (symbol -, Constant, Constant), (symbol -, Constant, InexactNumber), @@ -102,11 +116,12 @@ document { M-1, M-2 ///, HEADER3 "Intervals", - PARA { "If one of the inputs is an ", TO "RRi", ", the output is an interval containing all differences of pairs in the inputs." }, + PARA { "If one of the inputs is an ", TO "RRi", " or a ", TO "CCi", ", the output is an interval containing all differences of pairs in the inputs." }, EXAMPLE { "2-interval(1,3)", "interval(1,3)-interval(-1,2)", - "interval(-1,1)-interval(-1,1)" + "interval(-1,1)-interval(-1,1)", + "interval(1,2+3*ii)-(2+ii)" }, SeeAlso =>{ "difference", "minus"} } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/plus.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/plus.m2 index 388c18536d3..2a3e9669934 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/plus.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/plus.m2 @@ -17,33 +17,47 @@ document { symbol +, (symbol +, ZZ), (symbol +, ZZ, CC), + (symbol +, ZZ, CCi), (symbol +, ZZ, QQ), (symbol +, ZZ, RR), (symbol +, ZZ, RRi), (symbol +, ZZ, ZZ), (symbol +, QQ), (symbol +, QQ, CC), + (symbol +, QQ, CCi), (symbol +, QQ, QQ), (symbol +, QQ, RR), (symbol +, QQ, RRi), (symbol +, QQ, ZZ), (symbol +, RR), (symbol +, RR, CC), + (symbol +, RR, CCi), (symbol +, RR, QQ), (symbol +, RR, RR), (symbol +, RR, RRi), (symbol +, RR, ZZ), (symbol +, RRi), + (symbol +, RRi, CC), + (symbol +, RRi, CCi), (symbol +, RRi, QQ), (symbol +, RRi, RR), (symbol +, RRi, RRi), (symbol +, RRi, ZZ), (symbol +, CC), (symbol +, CC, CC), + (symbol +, CC, CCi), (symbol +, CC, InfiniteNumber), (symbol +, CC, QQ), (symbol +, CC, RR), + (symbol +, CC, RRi), (symbol +, CC, ZZ), + (symbol +, CCi), + (symbol +, CCi, CC), + (symbol +, CCi, CCi), + (symbol +, CCi, QQ), + (symbol +, CCi, RR), + (symbol +, CCi, RRi), + (symbol +, CCi, ZZ), (symbol +, Constant), (symbol +, Constant, Constant), (symbol +, Constant, InexactNumber), @@ -93,11 +107,12 @@ document { M+1, M+2 ///, HEADER3 "Intervals", - PARA { "If one of the addends is an ", TO "RRi", ", the output is an interval containing all sums of pairs in the addends." }, + PARA { "If one of the addends is an ", TO "RRi", " or a ", TO "CCi", ", the output is an interval containing all sums of pairs in the addends." }, EXAMPLE { "2+interval(1,3)", "interval(1,3)+interval(-1,2)", - "interval(-1,1)+interval(-1,1)" + "interval(-1,1)+interval(-1,1)", + "interval(1,1+2*ii)+interval(2+3*ii,3+5*ii)" }, SeeAlso =>{ "plus", "sum"} } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/shift.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/shift.m2 index 579faa8a083..b9a66caec34 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/shift.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/shift.m2 @@ -19,6 +19,8 @@ document { Key => symbol >>, Headline => "a binary operator, used for bit shifting or attaching optional inputs to functions", SeeAlso => { (symbol >>, OptionTable, Function) }, + Caveat => {"Due to its low precedence, parentheses must be used on the left hand side when installing methods for this operator, e.g., ", + CODE "(X >> Y) := f", "."}, Subnodes => { TO "right shift" } } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/tensor.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/tensor.m2 index 1297b6d5def..e966f1cdc30 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/tensor.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/tensor.m2 @@ -34,3 +34,13 @@ Node Example {1, 2} ** {10, 20, 30} /// + +document { + Key => symbol ⊠, + Headline => "a binary operator, usually used for exterior product", +} + +document { + Key => symbol ⧢, + Headline => "a binary operator, usually used for shuffle product", +} diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/times.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/times.m2 index a5a53a400a2..c976cc1bbd9 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/times.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/times.m2 @@ -56,23 +56,36 @@ document { (symbol *, Number,Constant), (symbol *, Number,Matrix), (symbol *, QQ,CC), + (symbol *, QQ,CCi), (symbol *, QQ,QQ), (symbol *, QQ,RR), (symbol *, QQ,ZZ), (symbol *, RR,CC), + (symbol *, RR,CCi), (symbol *, RR,QQ), (symbol *, RR,RR), (symbol *, RR,ZZ), (symbol *, ZZ,CC), + (symbol *, ZZ,CCi), (symbol *, ZZ,QQ), (symbol *, ZZ,RR), (symbol *, ZZ,ZZ), (symbol *, CC,CC), + (symbol *, CC,CCi), (symbol *, CC,QQ), (symbol *, CC,RR), + (symbol *, CC,RRi), (symbol *, CC,ZZ), + (symbol *, CCi, CC), + (symbol *, CCi, CCi), + (symbol *, CCi, QQ), + (symbol *, CCi, RR), + (symbol *, CCi, RRi), + (symbol *, CCi, ZZ), (symbol *, QQ, RRi), (symbol *, RR, RRi), + (symbol *, RRi, CC), + (symbol *, RRi, CCi), (symbol *, RRi, QQ), (symbol *, RRi, RR), (symbol *, RRi, RRi), @@ -137,11 +150,12 @@ document { "isHomogeneous N" }, HEADER3 "Intervals", - PARA { "If one of the factors is an ", TO "RRi", ", the output is an interval containing all products of pairs in the factors." }, + PARA { "If one of the factors is an ", TO "RRi", " or a ", TO "CCi", ", the output is an interval containing all products of pairs in the factors." }, EXAMPLE { "2*interval(1,3)", "interval(1,3)*interval(-1,2)", - "interval(-1,1)*interval(-1,1)" + "interval(-1,1)*interval(-1,1)", + "interval(1,2+2*ii)*interval(2+3*ii,4+3*ii)" }, SeeAlso =>{ "times", "product"} } @@ -190,3 +204,8 @@ document { }, SeeAlso => {(degree,Matrix),degrees} } + +document { + Key => symbol ·, + Headline => "a binary operator, usually used for dot product", +} diff --git a/M2/Macaulay2/packages/Macaulay2Doc/operators/underscore.m2 b/M2/Macaulay2/packages/Macaulay2Doc/operators/underscore.m2 index 6129d1d6c4e..c645e292458 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/operators/underscore.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/operators/underscore.m2 @@ -3,6 +3,8 @@ undocumented { (symbol _, EngineRing, ZZ), (symbol _, RR, EngineRing), (symbol _, RRi, EngineRing), + (symbol _, CC, EngineRing), + (symbol _, CCi, EngineRing), (symbol _, ZZ, Monoid), (symbol _, Vector, ZZ), (symbol _, Monoid, List), diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 index bbc68162df5..3fb8fe12fbb 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 @@ -50,6 +50,7 @@ Node BesselJ BesselY agm + polylog :Trigonometric functions sin asin @@ -152,34 +153,32 @@ document { Key => {CatalanConstant}, doc /// Key log1p - (log1p,CC) - (log1p,RR) - (log1p,RRi) + (log1p,InexactNumber) Headline logarithm of 1+x Usage log1p x log1p I Inputs - x:{CC,RR} - I:RRi + x:{RR,CC} + I:{RRi,CCi} Outputs :{RR,CC} the logarithm of @TT "1+x"@ - :{RRi} + :{RRi,CCi} an interval containing logarithm of 1 plus the points of @TT "I"@ Description Example log1p 1p100e-10 log(1 + 1p100e-10) + log1p interval(2,3) + log1p interval(1+2*ii,3+4*ii) /// doc /// Key expm1 - (expm1,CC) - (expm1,RR) - (expm1,RRi) + (expm1,InexactNumber) Headline exponential minus 1 Usage @@ -191,30 +190,38 @@ doc /// Outputs :{RR,CC} the quantity @TT "exp(x)-1"@ - :RRi + :{RRi,CCi} an interval containing the exponential of the points of @TT "I"@ minus one Description Example expm1 1p100e-10 exp(1p100e-10)-1 + expm1 interval(2,3) + expm1 interval(1+2*ii,3+4*ii) /// -document { Key => {eint,(eint, RR),(eint,CC),(eint,RRi)}, +document { Key => {eint,(eint, InexactNumber)}, Usage => "eint x", Headline => "exponential integral", Inputs => { "x" }, - Outputs => { RR => { "the exponential integral of ", TT "x" }}, + Outputs => { RR => { "the exponential integral of ", TT "x" }, + RRi => { "an interval containing the exponential integral of the points of ", TT "x" }, + CCi => { "a complex interval containing the exponential integral of the points of ", TT "x" } +}, EXAMPLE lines /// eint 2 ///, PARA {"See ", wikipedia "Exponential integral", "."} } -document { Key => {Digamma,(Digamma, RR),(Digamma,CC),(Digamma,RRi)}, +document { Key => {Digamma,(Digamma, InexactNumber)}, Usage => "Digamma x", Headline => "Digamma function", Inputs => { "x" }, - Outputs => { RR => { "the digamma function (logarithmic derivative of the gamma function) of ", TT "x" }}, + Outputs => { RR => { "the digamma function (logarithmic derivative of the gamma function) of ", TT "x" }, + RRi => { "an interval containing the digamma function (logarithmic derivative of the gamma function) of the points of ", TT "x" }, + CCi => { "a complex interval containing the digamma function (logarithmic derivative of the gamma function) of the points of ", TT "x" } +}, EXAMPLE lines /// Digamma 6 ///, @@ -222,11 +229,14 @@ document { Key => {Digamma,(Digamma, RR),(Digamma,CC),(Digamma,RRi)}, SeeAlso => {Gamma} } -document { Key => {zeta,(zeta, RR),(zeta,CC),(zeta,RRi)}, +document { Key => {zeta,(zeta, InexactNumber)}, Usage => "zeta x", Headline => "Riemann zeta function", Inputs => { "x" }, - Outputs => { RR => { "the zeta function of ", TT "x" }}, + Outputs => { RR => { "the zeta function of ", TT "x" }, + RRi => { "an interval containing the the zeta function of the points of ", TT "x" }, + CCi => { "a complex interval containing the the zeta function of the points of ", TT "x" } +}, EXAMPLE lines /// zeta 2 ///, @@ -235,12 +245,14 @@ document { Key => {zeta,(zeta, RR),(zeta,CC),(zeta,RRi)}, document { --- author(s): L. Gold, Dan Grayson - Key => {acos,(acos,RR),(acos,CC),(acos, RRi)}, + Key => {acos,(acos, InexactNumber)}, Headline => "arccosine", Usage => "acos x\nacos I", - Inputs => { "x", "I" => RRi }, + Inputs => { "x" => RR, "x" => CC, "I" => RRi, "I" => CCi }, Outputs => { Number => { "the arccosine (in radians) of ", TT "x"}, - RRi => { "an interval containing the arccosines of the points of ", TT "I" }}, + RRi => { "an interval containing the arccosines of the points of ", TT "I" }, + CCi => { "a complex interval containing the arccosines of the points of ", TT "I" } +}, EXAMPLE lines /// acos 0.5 ///, @@ -249,13 +261,14 @@ document { document { --- author(s): L. Gold, Dan Grayson - Key => {asin,(asin,RR),(asin,CC),(asin, RRi)}, + Key => {asin,(asin,InexactNumber)}, Headline => "arcsine", Usage => "asin x\nasin I", - Inputs => { "x", "I" => RRi }, + Inputs => { "x" => RR, "x" => CC, "I" => RRi, "I" => CCi }, Outputs => { Number => {"the arcsine (in radians) of ", TT "x"}, - RRi => { "an interval containing the arcsines of the points of ", TT "I" } + RRi => { "an interval containing the arcsines of the points of ", TT "I" }, + CCi => { "a complex interval containing the arcsines of the points of ", TT "I" } }, EXAMPLE { "asin 1" @@ -265,12 +278,14 @@ document { document { --- author(s): L. Gold - Key => {cosh, (cosh,RR),(cosh,CC),(cosh,RRi)}, + Key => {cosh, (cosh,InexactNumber)}, Headline => "compute the hyperbolic cosine", Usage => "cosh x\ncosh I", - Inputs => { "x", "I"=>RRi}, + Inputs => { "x"=>RR, "x"=>CC, "I"=>RRi, "I"=>CCi}, Outputs => { Number => { "the hyperbolic cosine of ", TT "x" }, - RRi => { "an interval containing the hyerbolic cosines of the points of ", TT "I" } }, + RRi => { "an interval containing the hyerbolic cosines of the points of ", TT "I" }, + CCi => { "a complex interval containing the hyerbolic cosines of the points of ", TT "I" } +}, EXAMPLE lines /// cosh .2 ///, @@ -301,12 +316,13 @@ document { PARA {"See ", wikipedia "Hyperbolic function", "."} } -document { Key => {sec,(sec,CC),(sec, RR),(sec, RRi)}, +document { Key => {sec,(sec,InexactNumber)}, Usage => "sec x\nsec I", Headline => "secant", - Inputs => { "x", "I" => RRi }, + Inputs => { "x" => RR, "x" => CC, "I" => RRi, "I" => CCi }, Outputs => { RR => { "the secant of ", TT "x" }, - RRi => { "an interval containing the secants of the points of ", TT "I" } + RRi => { "an interval containing the secants of the points of ", TT "I" }, + CCi => { "a complex interval containing the secants of the points of ", TT "I" } }, EXAMPLE lines /// sec(pi/3) @@ -314,12 +330,13 @@ document { Key => {sec,(sec,CC),(sec, RR),(sec, RRi)}, PARA {"See ", wikipedia "Trigonometric function", "."} } -document { Key => {csc,(csc,CC),(csc, RR),(csc,RRi)}, +document { Key => {csc,(csc,InexactNumber)}, Usage => "csc x\ncsc I", Headline => "cosecant", - Inputs => { "x","I"=>RRi }, + Inputs => { "x"=>RR, "x" => CC, "I"=>RRi, "I"=>CCi }, Outputs => { RR => { "the cosecant of ", TT "x" }, - RRi => { "an interval containing the cosecants of the points of ", TT "I" } + RRi => { "an interval containing the cosecants of the points of ", TT "I" }, + CCi => { "a complex interval containing the cosecants of the points of ", TT "I" } }, EXAMPLE lines /// csc(pi/3) @@ -327,12 +344,13 @@ document { Key => {csc,(csc,CC),(csc, RR),(csc,RRi)}, PARA {"See ", wikipedia "Trigonometric function", "."} } -document { Key => {cot,(cot, RR),(cot,CC),(cot,RRi)}, +document { Key => {cot,(cot, InexactNumber)}, Usage => "cot x\ncot I", Headline => "cotangent", - Inputs => { "x", "I"=>RRi }, + Inputs => { "x"=>RR, "x" => CC, "I"=>RRi, "I"=>CCi }, Outputs => { RR => { "the cotangent of ", TT "x" }, - RRi => { "an interval containing the cotangents of points of ", TT "I"} + RRi => { "an interval containing the cotangents of points of ", TT "I"}, + CCi => { "a complex interval containing the cotangents of points of ", TT "I"} }, EXAMPLE lines /// cot(pi/3) @@ -340,12 +358,13 @@ document { Key => {cot,(cot, RR),(cot,CC),(cot,RRi)}, PARA {"See ", wikipedia "Trigonometric function", "."} } -document { Key => {sech,(sech,CC),(sech, RR),(sech, RRi)}, +document { Key => {sech,(sech,InexactNumber)}, Usage => "sech x\nsech I", Headline => "hyperbolic secant", - Inputs => { "x", "I" => RRi }, + Inputs => { "x"=>RR, "x" => CC, "I"=>RRi, "I"=>CCi }, Outputs => { RR => { "the hyperbolic secant of ", TT "x" }, - RRi => { "an interval containing the hyerbolic secants of the points of ", TT "I" } + RRi => { "an interval containing the hyerbolic secants of the points of ", TT "I" }, + CCi => { "a complex interval containing the hyerbolic secants of the points of ", TT "I" } }, EXAMPLE lines /// sech(pi/3) @@ -353,12 +372,13 @@ document { Key => {sech,(sech,CC),(sech, RR),(sech, RRi)}, PARA {"See ", wikipedia "Hyperbolic function", "."} } -document { Key => {csch,(csch,CC),(csch, RR),(csch,RRi)}, +document { Key => {csch,(csch,InexactNumber)}, Usage => "csch x\ncsch I", Headline => "hyperbolic cosecant", - Inputs => { "x", "I"=>RRi }, + Inputs => { "x"=>RR, "x" => CC, "I"=>RRi, "I"=>CCi }, Outputs => { RR => { "the hyperbolic cosecant of ", TT "x" }, - RRi => { "an interval containing the hyperbolic cosecants of the points of ", TT "I" } + RRi => { "an interval containing the hyperbolic cosecants of the points of ", TT "I" }, + CCi => { "a complex interval containing the hyperbolic cosecants of the points of ", TT "I" } }, EXAMPLE lines /// csch(pi/3) @@ -366,12 +386,13 @@ document { Key => {csch,(csch,CC),(csch, RR),(csch,RRi)}, PARA {"See ", wikipedia "Hyperbolic function", "."} } -document { Key => {coth,(coth,CC),(coth, RR),(coth,RRi)}, +document { Key => {coth,(coth,InexactNumber)}, Usage => "coth x\ncoth I", Headline => "hyperbolic cotangent", - Inputs => { "x","I"=>RRi}, + Inputs => { "x"=>RR, "x" => CC, "I"=>RRi, "I"=>CCi }, Outputs => { RR => { "the hyperbolic cotangent of ", TT "x" }, - RRi => { "an interval containing the hyperbolic cotangents of the points of ", TT "I" } + RRi => { "an interval containing the hyperbolic cotangents of the points of ", TT "I" }, + CCi => { "a complex interval containing the hyperbolic cotangents of the points of ", TT "I" } }, EXAMPLE lines /// coth(pi/3) @@ -415,7 +436,9 @@ document { Key => {BesselY,(BesselY, ZZ, Number),(BesselY,Number,Number)}, SeeAlso => { BesselJ } } -document { Key => {agm, (agm, RR, RR), (agm,CC,CC), (agm,CC,RR), (agm,RR,CC)}, +document { Key => { + agm, + (agm,InexactNumber,InexactNumber)}, Usage => "agm(x,y)", Inputs => { "x" => "a number", "y" => "a number" }, Outputs => { {"the arithmetic-geometric mean of ", TT "x", " and ", TT "y"}}, @@ -427,7 +450,8 @@ document { Key => {agm, (agm, RR, RR), (agm,CC,CC), (agm,CC,RR), (agm,RR,CC)}, } -- TODO: find a better place for these -document { Key => {size2, (size2,CC), (size2,RR), (size2,ZZ), (size2,RRi)}, +document { Key => {size2, (size2,CC), (size2,RR), (size2,ZZ), (size2,RRi), + (size2,CCi)}, Usage => "size2 x", Headline => "number of binary digits to the left of the point", Inputs => {"x" => Number}, @@ -444,7 +468,7 @@ document { Key => {size2, (size2,CC), (size2,RR), (size2,ZZ), (size2,RRi)}, size2 (1/0.-1/0.) ///} -document { Key => {isReal,(isReal,CC),(isReal,QQ),(isReal,RR),(isReal,ZZ), +document { Key => {isReal,(isReal,CC),(isReal,CCi),(isReal,QQ),(isReal,RR),(isReal,ZZ), (isReal, RRi), (isReal, Constant), (isReal, InfiniteNumber)}, Usage => "isReal x", Headline => "whether a number is real", @@ -511,34 +535,57 @@ document { Key => {isANumber, (isANumber,Number)}, SeeAlso => {isFinite, isInfinite} } -document { - Key => {norm, - (norm, InfiniteNumber, Matrix),(norm, Matrix),(norm, RR, Matrix),(norm, InfiniteNumber, RingElement),(norm, MutableMatrix), - (norm, RingElement),(norm, InexactField, MutableMatrix),(norm, RR, MutableMatrix),(norm, RR, RingElement), - (norm,List),(norm,Vector),(norm,Number),(norm,RR,Number),(norm,InfiniteNumber,Number) - }, - Usage => "norm M\nnorm(p,M)", - Inputs => { - "M"=>{ofClass{MutableMatrix,Matrix,RingElement,Number,Vector,List}}, - "p"=>{ofClass{RR,InfiniteNumber}, ", specifying which norm to compute. Currently, only ", TT "p=infinity", " is accepted."} - }, - Outputs => {TEX{"the $L^p$-norm of ", TT "M", " computed to the minimum of the precisions of ", TT "M", " and of ", TT "p", - "."}}, - EXAMPLE lines /// - printingPrecision = 2 - R = RR_100 - M = 10*random(R^3,R^10) - norm M - norm_(numeric_20 infinity) M - norm {3/2,4,-5} - ///, - "The norm of a polynomial is the norm of the vector of its coefficients.", - EXAMPLE lines /// - RR[x] - (1+x)^5 - norm oo - /// - } +doc /// + Key + norm + (norm,List) + (norm,Matrix) + (norm,MutableMatrix) + (norm,Number) + (norm,Number,List) + (norm,Number,Matrix) + (norm,Number,MutableMatrix) + (norm,Number,Number) + (norm,Number,RingElement) + (norm,Number,Vector) + (norm,RingElement) + (norm,Vector) + Headline + l^p norm + Usage + norm M + norm(p, M) + Inputs + p:Number -- specifying which norm to compute + M:{List, Matrix, MutableMatrix, Number, RingElement, Vector} + Outputs + :Number + The $\ell^p$-norm of @VAR "M"@, computed to the minimum of the precisions + of @VAR "M"@ and @VAR "p"@. + Description + Text + By default, the $\ell^\infty$ norm giving the maximum absolute entry is returned. + Example + printingPrecision = 2 + R = RR_100 + M = 10*random(R^3,R^10) + norm M + norm_(numeric_20 infinity) M + norm {3/2,4,-5} + Text + For finite $p$, the $\ell^p$ norm + $$\|M\|_p = \left(\sum_{i,j} |M_{ij}|^p\right)^{1/p}$$ + is returned. For example, when $p = 2$, this is the Frobenius norm. + Example + M = matrix {{3, 4}, {12, 84}} + norm_2 M + Text + The norm of a polynomial is the norm of the vector of its coefficients. + Example + RR[x] + (1+x)^5 + norm oo +/// document { Key => {commonRing, (commonRing,List)}, Usage => "commonRing v", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_debugging.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_debugging.m2 index 36fd7ea4fdf..8d46c297d8c 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_debugging.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_debugging.m2 @@ -25,6 +25,7 @@ document { -- TO "currentDirectory", TO "pseudocode", TO "disassemble", + TO "parse", TO "frames", TO functionBody, TO dictionary, @@ -327,6 +328,72 @@ document { } } +doc /// + Key + Error + (NewFromMethod, Error, Thing) + Headline + error information + Description + Text + An instance of this class may be caught when an error occurs inside a + @TO symbol trap@ or @TO symbol try@ statement. The error message may be + recovered using @TO toString@ and the location using @TO locate@. + Example + (val, err) = trap 1/0 + toString err + locate err + Text + An @CODE "Error"@ object may be constructed from an error message. + Example + err = new Error from "foo" + Text + An error may be raised from an @CODE "Error"@ object using @TO error@. + Example + stopIfError = false + error err + Text + It is possible to create subclasses of @CODE "Error"@ for finer error + handling. + Example + MyError = new SelfInitializingType of Error + try error MyError "bar" except err do err + SeeAlso + symbol try + symbol trap + error +/// + +doc /// + Key + symbol trap + Headline + trap an error + Usage + trap c + Description + Text + The code @VAR "c"@ is evaluated and a sequence containing two elements + is returned. If the evaluation completes successfully, then the first + element is the value and the second element is null. + Example + trap 5 + Text + If an error occurs, then the first element is null and the second element + is an @TO Error@ object containing information about the error is + returned. + Example + trap 1/0 + Text + Note that this is a @TO Keyword@, not a method, and so it is not + necessary to enclose @VAR "c"@ in parentheses. + Example + trap 5 == "foo" + {trap error "bar", 1/2} + trap 1/0; 1/2 + SeeAlso + symbol try +/// document { Key => "recursionLimit", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_examples.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_examples.m2 index de22a60dfe5..b92cbef9104 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_examples.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_examples.m2 @@ -121,6 +121,7 @@ Node inversePermutation partitions random + shuffle Set rays cone diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 index f9ff13e50bf..2d03f7e832b 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 @@ -698,44 +698,83 @@ document { errors. Your error message will appear on the screen and execution will be stopped.", PARA{}, - "The function ", TO "try", " can be used to catch an error before - execution is stopped and to continue or to try something else.", + "The keywords ", TO "try", " and ", TO "trap", " can be used to catch an + error before execution is stopped and to continue or to try something + else.", Subnodes => { TO "error", TO "try", TO "throw", - } - } - -document { - Key => "try", - Headline => "catch an error", - Usage => "try x then y else z", - Inputs => { - "x" => "code", - "y" => "code", - "z" => "code" - }, - Consequences => { - {"the code ", TT "x", " is run; if no error or ", TO "alarm", " occurs, then the code ", TT "y", " is run; otherwise, the code ", TT "z", " is run"} + TO "trap", + TO "Error", }, - "The return value is the value returned by ", TT "y", " or ", TT "z", ", as the case may be.", - PARA{}, - "The clause '", TT "then y", "' may be omitted, in which case the return value is the value returned by ", TT "x", ", if there is no error or alarm.", - PARA{}, - "The clause '", TT "else z", "' may be omitted, - in which case the return value is the value returned by ", TT "y", ", - unless an error or alarm occurs, in which case ", TO "null", " is returned.", - PARA{}, - "The clauses '", TT "then y else z", "' may both be omitted, in which case the return value is the value returned by ", TT "x", ", unless an error or - alarm occurs, in which case ", TO "null", " is returned.", - PARA{}, - "The behavior of interrupts (other than alarms) is unaffected.", - EXAMPLE "apply(-3..3,i->try 1/i else infinity)", - Caveat => "We will change the behavior of this function soon so that it will be possible to catch errors of a particular type. Meanwhile, users are - recommended to use this function sparingly, if at all." + SeeAlso => {"stopIfError"} } +doc /// + Key + symbol try + symbol except + Headline + catch an error + Usage + try x then y else z + try x then y except err do z + Inputs + x: -- code + y: -- code + err:Symbol + z: -- code + Consequences + Item + the code @VAR "x"@ is run; if no error or @TO alarm@ occurs occurs, then + the code @VAR "y"@ is run; otherwise, the code @VAR "z"@ is run + Description + Text + The return value is the value returned by @VAR "y"@ or @VAR "z"@, as the + case may be. + Example + apply(-3..3, i -> try 1/i then 1/i else infinity) + Text + The clause @M2CODE "then y"@ may be omitted, in which case the return + value is the value returned by @VAR "x"@ if there is no error or + alarm. + Example + apply(-3..3, i -> try 1/i else infinity) + Text + The clause @M2CODE "else z"@ may be omitted, in which case the return + value is the value returned by @VAR "y"@, unless an error or alarm + occurs, in which case @TO null@ is returned. + Example + apply(-3..3, i -> try 1/i then 1/i) + Text + The clauses @M2CODE "then y else z"@ may both be omitted, in which case + the return value is the value returned by @VAR "x"@, unless an error or + alarm occurs, in which case @TO null@ is returned. + Example + apply(-3..3, i -> try 1/i) + Text + In the alternate form @M2CODE "try x then y except err do z"@, + @VAR "x"@ is first evaluated. If no error occurs, then the value of + @VAR "y"@ is returned, or the value of @VAR "x"@ if the @M2CODE "then y"@ + clause is omitted. If an error occurs, then the corresponding @TO Error@ + object is assigned to the symbol specified by @VAR "err"@. That symbol is + then available during the evaluation of @VAR "z"@, whose value is + returned. + Example + apply(-3..3, i -> try 1/i then 1/i except err do err) + apply(-3..3, i -> try 1/i except err do err) + oo#3 + Text + The behavior of interrupts (other than alarms) is unaffected. + Caveat + We will change the behavior of this function soon so that it will be + possible to catch errors of a particular type. Meanwhile, users are + recommended to use this function sparingly, if at all. + SeeAlso + symbol trap +/// + document { Key => "break", Headline => "break from a loop", @@ -1071,6 +1110,7 @@ document { TO symbol + , TO symbol - , TO symbol * , + TO symbol · , TO symbol / , TO symbol // , TO symbol % , @@ -1079,6 +1119,8 @@ document { TO symbol ++ , TO symbol ** , TO symbol ^** , + TO symbol ⊠ , + TO symbol ⧢ , TO symbol ~ , TO symbol (*) , TO symbol : , diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_lists.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_lists.m2 index 30bf72e7d2a..a09cb053d19 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_lists.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_lists.m2 @@ -791,23 +791,50 @@ document { SeeAlso => sequence } -document { - Key => MutableList, - Headline => "the class of all mutable lists", - PARA {"For an overview of lists and sequences, see ", TO "lists and sequences", "."}, - PARA{}, - "Normally the entries in a mutable list are not printed, to prevent - infinite loops in the printing routines. To print them out, use - ", TO "peek", ".", - EXAMPLE { - "s = new MutableList from {a,b,c};", - "s#2 = 1234;", - "s", - "peek s", - }, - SeeAlso => {"BasicList"}, - Subnodes => { TO Bag }, - } +doc /// + Key + MutableList + (NewFromMethod, MutableList, ZZ) + Headline + the class of all mutable lists + Description + Text + For an overview of lists and sequences, see @TO "lists and sequences"@. + + Normally the entries in a mutable list are not printed, to prevent + infinite loops in the printing routines. To print them out, use @TO peek@. + Example + s = new MutableList from {a,b,c}; + s#2 = 1234 + s + peek s + Text + To construct a mutable list with a given length and whose entries are all + @TO null@, provide an integer. + Example + s = new MutableList from 6; + peek s + Text + If a value is assigned at a position that is not yet present in a mutable list, + then it grows accordingly, and any intermediate positions are filled with + @TO null@. + Example + t = new MutableList from {}; + t#4 = e + peek t + Text + When working with mutable lists, be careful not to turn a linear algorithm into + a quadratic one by repeatedly appending to it. Compare the following: + Example + s = new MutableList + elapsedTime scan(1000, i -> s#i = i^2) -- quadratic, since we grow s at each step + t = new MutableList from 1000 + elapsedTime scan(1000, i -> t#i = i^2) -- linear + SeeAlso + BasicList + Subnodes + Bag +/// document { Key => { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_methods.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_methods.m2 index 9f2f62e43fa..83738a48aa3 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_methods.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_methods.m2 @@ -350,8 +350,8 @@ document { PARA{"Most users will use a different way of installing methods."}, PARA{ TT "installMethod(M,f)", " -- installs a function ", TT "f", " as a nullary method - under the name ", TT "M", ". This is a replacement for the syntax ", "M () := f", ", - which hasn't yet been made to work. As currently implemented, this is also the same + under the name ", TT "M", ". This is the same as ", "M() := f", " + if ", TT "M", " is a function. As currently implemented, this is also the same as ", TT "nullaryMethods#(1:M) = f", "." }, PARA{ @@ -584,6 +584,7 @@ document { Key => MethodFunctionWithOptions, undocumented (methodOptions, MethodFunctionWithOptions) undocumented (methodOptions, MethodFunction) undocumented (methodOptions, Symbol) +undocumented (methodOptions, List) document { Key => {(methodOptions, Function),(methodOptions, Command),(methodOptions, ScriptedFunctor),methodOptions}, Headline => "recover the options used when a method function was created", Usage => "methodOptions f", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_rings.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_rings.m2 index 1c185b872b2..a9bf7b2280c 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_rings.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_rings.m2 @@ -52,6 +52,7 @@ document { TO "RR", TO "RRi", TO "CC", + TO "CCi", }, "The names of some of these rings are double letters so the corresponding symbols with single letters are preserved for use as variables.", @@ -106,6 +107,7 @@ document { TO "RR", TO "RRi", TO "CC", + TO "CCi", } } @@ -449,7 +451,7 @@ document { results. That allows numbers of various precisions to be used without creating a new ring for each precision.", EXAMPLE {"class interval(3.1,3.5)", "ring interval(3.1,3.5)"}, - "The precision can be specified on input by specifying the precision of both input ", TO "RR", " numbers.", + "The precision can be specified on input by specifying the precision of both input ", TO "RR", " numbers. ", "Alternatively, the precision can be specified by including the option ", TT "Precision", ".", EXAMPLE {"interval(2.5p100,3.2p1000)","interval(2.5,3.2,Precision=>200)"}, "Intervals can also be created using ", TO (span,Sequence), " to create the smallest interval containing the inputs.", @@ -465,7 +467,9 @@ document { EXAMPLE {"exp(interval(2,4))","cos(interval(1,1.3))","sqrt(interval(2))"}, "Transcendental functions are available to high precision, with ", TO "numericInterval", ".", EXAMPLE {"numericInterval(100,pi)","numericInterval_200 EulerConstant"}, - SeeAlso => {toRRi, numericInterval, precision, interval, (span,Sequence), (span,List)}, + "The left and right endpoints of an interval can be accessed with ", TO "left", " and ", TO "right", ". Similarly, the midpoint and the length of an interval can be found with ", TO "midpoint", " and ", TO "diameter", ".", + EXAMPLE {"left interval(1,5)","right interval(1,5)","midpoint interval(1,5)","diameter interval(1,5)"}, + SeeAlso => {CCi, toRRi, numericInterval, precision, interval, (span,Sequence), (span,List)}, Subnodes => { TO toRRi, TO interval, @@ -478,9 +482,56 @@ document { TO (isMember, QQ, RRi), TO (isEmpty, RRi), TO (isSubset, RRi, RRi), - TO span, -- TODO: perhaps this should be shared - TO (span, List), - TO (span, Sequence), + TO span + }, + } + +undocumented {"CCi_*"} + +document { + Key => CCi, + Headline => "the class of all complex intervals", + "A complex interval is entered as a pair of real intervals to the ", TO "interval", " function. It is stored internally as a pair of arbitrary precision intervals using the ", TO "MPFI", " library.", + EXAMPLE "interval(interval(3.1415,3.1416), interval(2.7182,2.7183))", + "The precision is measured in bits, is visible in the ring displayed on + the second of each pair of output lines, and can be recovered using ", TO "precision", ".", + EXAMPLE "precision interval(interval(3.1415,3.1416), interval(2.7182,2.7183))", + "Complex intervals are objects in ", TO "class", " CCi which is an ", TO "InexactFieldFamily", ".", "For complex intervals, the functions ", TO "class", " and ", TO "ring", " yield different + results. That allows numbers of various precisions + to be used without creating a new ring for each precision.", + EXAMPLE {"class interval(interval(3.1,3.5),interval(1.1,1.2))"}, + "The precision can be specified on input by specifying the precision of both input ", TO "CC", " numbers. ", + "Alternatively, the precision can be specified by including the option ", TT "Precision", ".", + EXAMPLE {"interval(interval(2.5p1000,3.2p100),interval(2.3,3.1,Precision=>200))"}, + "Complex intervals can also be created using ", TO (span,Sequence), " to create the smallest complex axis-aligned rectangle containing the inputs.", + EXAMPLE {"span(2+3*ii,Precision=>100)","span(2,3*ii,interval(interval(-1.5,-0.5),interval(3,3.1)),73)"}, + "Operations using complex intervals are computed as sets so that the resulting intervals contain all possible outputs from pairs of points in input intervals.", + EXAMPLE {" (I, J, K) = (interval(interval(.5,.8),interval(.6,.9)), interval(interval(.54,.78),interval(.65,.89)), interval(interval(.45,.6),interval(.3,.78)));", + "I + J", + "I - J", + "I * K", + "I / K", + "I ^ 3", + "2 * I", + "(2+3*ii) * I"}, + "The real and imaginary parts of a complex interval can be accessed with ", TO "realPart", " and ", TO "imaginaryPart", ". These are real intervals of class ", TO "RRi", ". The ", TO "diameter", " of a complex interval is the (rounded) approximation to the length of its diagonal, while its ", TO "midpoint", " is the complex midpoint of its real and imaginary parts", + EXAMPLE {"realPart interval(1+2*ii,3+4*ii)","imaginaryPart interval(1+2*ii,3+4*ii)","diameter interval(1+2*ii,3+4*ii)","midpoint interval(1+2*ii,3+4*ii)"}, + SeeAlso => { + RRi, + toCCi, + precision, + interval, + midpoint, + diameter, + realPart, + imaginaryPart, + intersect, + isEmpty, + isSubset, + span, + }, + Subnodes => { + TO toCCi, }, } @@ -1188,49 +1239,71 @@ document { -- needs an example } } +doc /// + Key + "exterior algebras" + Headline + a polynomial ring with skew-commutative variables + Description + Text + An exterior algebra is a polynomial ring $R$ where multiplication of + the variables obeys the commutation relation $xy = (-1)^{\textrm{deg}(x) + \textrm{deg}(y)}yx$. One notable consequence of this is + that if $\textrm{deg}(x)$ is odd, then $x^2 = 0$. -document { - Key => "exterior algebras", - -- making one, making quotients, - -- using it. - -- modules are right-modules, example of multiplication. - "An exterior algebra is a polynomial ring where multiplication is - mildly non-commutative, in that, for every x and y in the ring, - y*x = (-1)^(deg(x) deg(y)) x*y, and that for every x of odd degree, - x*x = 0.", - "In Macaulay2, deg(x) is the degree of x, or the first degree of x, in case - a multi-graded ring is being used. The default degree for each variable is 1, so - in this case, y*x = -x*y, if x and y are variables in the ring.", - PARA{}, - "Create an exterior algebra with explicit generators by creating a polynomial - ring with the option ", TO "SkewCommutative", ".", - EXAMPLE { - "R = QQ[x,y,z, SkewCommutative => true]", - "y*x", - "(x+y+z)^2", - "basis R", - "basis(2,R)", - }, - EXAMPLE { - "S = QQ[a,b,r,s,t, SkewCommutative=>true, Degrees=>{2,2,1,1,1}];", - "r*a == a*r", - "a*a", - "f = a*r+b*s; f^2", - "basis(2,S)", - }, - "All modules over exterior algebras are right modules. This means that matrices - multiply from the opposite side:", - EXAMPLE { - "x*y", - "matrix{{x}} * matrix{{y}}" - }, - "You may compute Gröbner bases, syzygies, and form quotient rings of these skew - commutative rings.", - Subnodes => { - TO isSkewCommutative, - TO antipode, - }, - } + Here, $\textrm{deg}(x)$ is the degree of $x$ - or the first + degree of $x$ in case $R$ is multi-graded. By default, the + degree of each variable in a polynomial ring is 1, so in this case + we have the simple rule $xy = -yx$ for multiplying variables. + Text + Create an exterior algebra with explicit generators by creating a polynomial + ring with the option @TO "SkewCommutative"@. + Example + R = QQ[x,y,z, SkewCommutative => true] + y*x + (x+y+z)^2 + basis R + basis(2,R) + Text + You can declare that only a subset of the variables are skew-commutative. + Example + R = QQ[a..c, SkewCommutative => {b, c}] + a*b == b*a + a*c == c*a + b*c == -c*b + Text + The degree of the variables can be specified just as in the commutative case. + Example + R = QQ[a,b,r,s,t, SkewCommutative=>true, Degrees=>{2,2,1,1,1}] + r*a == a*r + a*a + f = a*r+b*s; f^2 + basis(2,R) + Text + As usual in Macaulay2, matrices are actually matrices over + $R^\textrm{op}$ and so matrix arithmetic over exterior algebras is + slightly different from what you see in the commutative case. + Example + R = QQ[a..d, SkewCommutative => true] + A = matrix {{a, b}, {1, 1}} + B = matrix {{c, 1}, {d, 1}} + A * B + Text + See @TO "right modules or left modules?"@ for more details. + Text + You may compute Gröbner bases, syzygies, and form quotient rings of these skew + commutative rings. Warning that quotienting by an ideal which is not a + 2-sided ideal will produce quotient ring where multiplication is not well + defined on coset representatives. + Example + R = QQ[a..d, SkewCommutative => true] + I = ideal {a*b + c} + promote(I_0 * d, R/I) + promote(I_0, R/I) * promote(d, R/I) + Subnodes + "isSkewCommutative" + "antipode" +/// document { Key => "symmetric algebras", diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 index 24d586b1128..04ecae20005 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_threads.m2 @@ -41,7 +41,7 @@ Node including cpu caches; it is like running Macaulay2 on a computer that is running other big programs at the same time. We can see this using @ TO "elapsedTime" @. Example - L = random toList (1..10000); + L = shuffle toList (1..10000); elapsedTime apply(1..100, n -> sort L); elapsedTime parallelApply(1..100, n -> sort L); Text @@ -147,6 +147,10 @@ Node result has the same class as @VAR "L"@. Normally the default strategy (@M2CODE "Strategy => null"@) is more efficient. + The order of the elements of the output is preserved. + Example + parallelApply(0..10, x -> x^2) + Text See @ TO "parallel programming with threads and tasks" @ for more information and an important warning about thread safety. Node diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_types.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_types.m2 index 62d88b6019d..332b949de72 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_types.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_types.m2 @@ -536,6 +536,7 @@ document { File, --Function, AtomicInt, + Mutex, Symbol, Pseudocode --FunctionBody diff --git a/M2/Macaulay2/packages/MacaulayPosets.m2 b/M2/Macaulay2/packages/MacaulayPosets.m2 new file mode 100644 index 00000000000..c23158d72fa --- /dev/null +++ b/M2/Macaulay2/packages/MacaulayPosets.m2 @@ -0,0 +1,1948 @@ +newPackage( + "MacaulayPosets", + Version => "1.0", + Date => "April 5, 2026", + Headline => "Macaulay posets", + Authors => { + { + Name => "Penelope Beall", + Email => "pbeall@ucdavis.edu", + HomePage=>"https://pbeall.github.io" + }, + { + Name => "Yu Olivier Li", + Email => "yol1@st-andrews.ac.uk" + } + }, + PackageExports => { + "Posets" + }, + PackageImports => { + "Visualize" + }, + Keywords => {"Combinatorial Commutative Algebra"} +) +export { + "PosetMap", + "isRankPreserving", + "getMons", + "getPoset", + "upperShadow", + "lowerShadow", + "macaulayOrders", + "isMacaulay", + "isAdditive", + "posetWedgeProduct", + "posetFiberProduct", + "posetClosedProduct", + "posetConnectedSum", + "ringFiberProduct", + "ringConnectedSum", + "MaxDegree", + "TikZ", + "Visual", + "AllOrders" +} + + +-------------------------------------------------- +--- Poset maps +-------------------------------------------------- + +PosetMap = new Type of HashTable; +PosetMap.synonym = "map of posets"; + +source PosetMap := Poset => (f) -> (f.source) +target PosetMap := Poset => (f) -> (f.target) + +map (Poset, Poset, HashTable) := PosetMap => opts -> (Q, P, f) -> ( + -- check that the domain of f is P + if not set keys f == set vertices P then error "the keys must be vertices of the source Poset"; + + -- check that the codomain of f is contained in Q + if not isSubset(values f, vertices Q) then error "the image must be contained in the codomain"; + + g := new PosetMap from join({ + symbol source => P, + symbol target => Q}, + pairs f + ); + -- check monotonicity + scan(coveringRelations P, c -> if not compare(Q, g(c#0), g(c#1)) then error "the resulting PosetMap would not be monotone"); + g +) + +map (Poset, Poset, List) := PosetMap => opts -> (Q, P, L) -> ( + -- Either everything in L a vertex of Q, + if isSubset(L, vertices Q) then ( + return map(Q, P, new HashTable from apply(#P_*, i -> P_*#i => L#i)); + ); + -- or everything in L is an Option and not everything in L is a vertex of Q + if all(L, i -> instance(i, Option)) then ( + return map(Q, P, new HashTable from L); + ); + error "list elements must all be vertices of the target or all be Options"; +) + +PosetMap Thing := Thing => (f, p) -> ( + if not f#?p then error "the element is not in the source of the map."; + f#p +) + +PosetMap == PosetMap := Boolean => (f, g) -> ( + if not ((source f) == (source g) and (target f) == (target g)) then return false; + all(vertices source f, i -> f(i) == g(i)) +) + +PosetMap * PosetMap := PosetMap => (f, g) -> ( + map(target f, source g, apply(vertices source g, p -> f(g(p)))) +) + +image (PosetMap, List) := List => (f, L) -> ( + unique apply(select(L, l -> isMember(l, vertices source f)), p -> f(p)) +) + +image PosetMap := List => (f) -> ( + image(f, vertices source f) +) + +isInjective PosetMap := Boolean => (f) -> ( + --use finiteness + #(image f) == #(vertices source f) +) + +isSurjective PosetMap := Boolean => (f) -> ( + isSubset(vertices target f, image f) +) + +isRankPreserving = method(); +isRankPreserving PosetMap := Boolean => (f) -> ( + if (not isRanked source f) or (not isRanked target f) then return false; + + targetRankFunction := rankFunction target f; + targetRank := hashTable apply(#(vertices target f), i -> (vertices target f)#i => targetRankFunction#i); + + sourceRankFunction := rankFunction source f; + sourceRank := hashTable apply(#(vertices source f), i -> (vertices source f)#i => sourceRankFunction#i); + + all(vertices source f, i -> sourceRank#i == targetRank#(f(i))) +) + +-------------------------------------------------- +--- Getting posets of polynomial rings with any homogeneous ideal +-------------------------------------------------- + +-- Suppose s is a monomial in a ring S. +-- This returns what degree s would be if S was replaced by newRing(S, Degrees=>toList((numgens S):1)) +theDegree = (s) -> ( + S := ring s; + if s == 0_S then return -infinity; + sum(unique gens S, g -> if g != 0_S then degree(g, s) else 0) +) + +isHomogeneousWrtVars = (I) -> ( + S := (ring I)/I; + isHomogeneous newRing(S, Degrees=>toList((numgens S):1)) +) + +-- First function is to get all the unique monomial representations in the quotient ring. +-- The function super basis is the cornerstone for this. +-- Everything else is just manipulating the data. +getMons = method(Options => { MaxDegree => 10 } ); +getMons (PolynomialRing, Ideal) := opts -> (R, I) -> ( + if not isField baseRing R then error "the base ring of the polynomial ring must be a field"; + if not isHomogeneousWrtVars I then error "the given ring must be homogeneous"; + try S := R/I else error "the first input needs to be a polynomial ring R, second input needs to be an ideal of R"; + + -- If the quotient ring has a finite number of elements (i.e. the super basis returns a finite number of monomials): + try flatten entries super basis S + else ( + -- If the quotient ring doesn't have a finite number of elements: + print "Ideal is not finite over the base (quotient ring has infinitely many monomials)"; + print concatenate("Monomials up to degree ", toString(opts.MaxDegree), " given"); + + G := gens S; + minEl := fold((a, b) -> a * b^0, 1, G); + -- N#i will be the list of degree i monomials + M := {minEl}; + M | flatten for i from 0 to (opts.MaxDegree - 1) list ( + -- Compute a basis for m*S_i (m = maximal ideal) + M = unique flatten apply(M, m -> select( apply(G, g -> m*g), a -> a != 0_S ) ) + ) + ) +) +getMons Ideal := opts -> (I) -> ( + R := ring I; + if not isPolynomialRing R then error "the given ideal must be an ideal of a polynomial ring"; + if not isField baseRing R then error "the base ring of the polynomial ring must be a field"; + getMons(R, I, MaxDegree => opts.MaxDegree) +) +getMons QuotientRing := opts -> (S) -> ( + R := ambient S; + if not isPolynomialRing R then error "the given quotient ring must be the quotient of a polynomial ring R and an ideal of R"; + if not isField baseRing R then error "the base ring of the polynomial ring must be a field"; + getMons(R, ideal S, MaxDegree => opts.MaxDegree) +) +getMons PolynomialRing := opts -> (R) -> ( + getMons(R, ideal(0_R), MaxDegree => opts.MaxDegree) +) + +getCoverRels = (mons, R, I) -> ( + S := R/I; + for monPair in subsets(mons, 2) list ( + -- For each monomial in the mons list multiply them with another monomial. + -- Then check if the monomial divides the result and the degree difference is one. + (mon, otherMon) := toSequence monPair; + if abs(theDegree mon - theDegree otherMon) != 1 then continue; + if otherMon % mon == 0 then monPair + else if mon % otherMon == 0 then reverse monPair + else continue + ) +) + +getPoset = method(TypicalValue => Poset, Options => {MaxDegree => 10}); +getPoset (PolynomialRing, Ideal) := opts -> (R, I) -> ( + mons := getMons(R, I, MaxDegree => opts.MaxDegree); + if instance(mons, String) then return mons; + poset(mons, getCoverRels(mons, R, I)) +) +getPoset Ideal := opts -> (I) -> ( -- Given an ideal as an input. + R := ring I; + if not isPolynomialRing R then error "the given ideal must be an ideal of a polynomial ring"; + if not isField baseRing R then error "the base ring of the polynomial ring must be a field"; + getPoset(R, I, MaxDegree => opts.MaxDegree) +) +getPoset QuotientRing := opts -> (S) -> ( -- Given a quotient ring as an input. + R := ambient S; + if not isPolynomialRing R then error "the given quotient ring must be the quotient of a polynomial ring R and an ideal of R"; + if not isField baseRing R then error "the base ring of the polynomial ring must be a field"; + getPoset(R, ideal S, MaxDegree => opts.MaxDegree) +) +getPoset PolynomialRing := opts -> (R) -> ( + getPoset(R, ideal(0_R), MaxDegree => opts.MaxDegree) +) + + +-------------------------------------------------- +--- Poset Operations +-------------------------------------------------- +posetWedgeProduct = method() +posetWedgeProduct List := Poset => posets -> ( + -- Check that the posets are actually posets with unique minimal elements. + for idxPoset in pairs posets do ( + (idx, P) := idxPoset; + if not instance(P, Poset) then ( + error concatenate("all elements in the list must be posets; element number ", toString(idx+1), " is not a Poset"); + ); + if #(minimalElements P) != 1 then ( + error "all posets must have a unique minimal element"; + ); + ); + + allPosetElements := for idxPoset in pairs posets list ( + (idx, P) := idxPoset; + + -- For the ith poset, we are creating a set with the same number of elements as the number of elements in the ground set. + minimalElement := first minimalElements P; + minimalElementIndex := position(P_*, b -> b === minimalElement); + + posetiGroundSet := toList((idx, 1) .. (idx, #P_*)); + -- Then replace the element of the same position in the new set. + posetiGroundSet = insert(minimalElementIndex, "min", posetiGroundSet); + drop(posetiGroundSet, {minimalElementIndex + 1, minimalElementIndex + 1}) + ); + + wedgeGroundSet := unique flatten allPosetElements; + wedgeCoverRelations := flatten for idxPoset in pairs posets list ( + (idx, P) := idxPoset; + for coverRelation in coveringRelations P list ( + -- Find the positions of the elements in their respective poset ground set. + firstPosition := position(P_*, x -> x === first coverRelation); + secondPosition := position(P_*, x -> x === last coverRelation); + {(allPosetElements#idx)_firstPosition, (allPosetElements#idx)_secondPosition} + ) + ); + poset(wedgeGroundSet, wedgeCoverRelations) +) +posetWedgeProduct (Poset, Poset) := Poset => (P, Q) -> (posetWedgeProduct {P, Q}) + + +posetFiberProduct = method() +posetFiberProduct List := Poset => posetMaps -> ( + numberPosetMaps := #posetMaps; + if numberPosetMaps == 0 then error "number of poset maps must be greater than 0"; + + for idxf in pairs posetMaps do ( + (idx, f) := idxf; + if not instance(f, PosetMap) then ( + error concatenate("all elements in the list must be poset maps; element number ", toString(idx+1), " is not a PosetMap"); + ); + if not isRankPreserving f then ( + error "all poset maps must be rank-preserving"; + ); + if not isInjective f then ( + error "all poset maps must be injective"; + ); + ); + fiberSource := source first posetMaps; + for f in posetMaps do ( + if not source f == fiberSource then error "all poset maps must have the same source"; + ); + + allPosetElements := apply(vertices fiberSource, b -> (numberPosetMaps, b)); + posetElementsAndCovers := for idxf in pairs posetMaps list ( + (idx, f) := idxf; + -- For the ith map, take its elements not in the image of the map and disjointify them from the other images + posetiGroundSet := apply(select((target f)_*, b -> not isMember(b, image f)), b -> (idx, b)); + + -- Also, turn the old cover relations into cover relations for the new poset + preimagei := hashTable for p in (source f)_* list f(p) => p; + posetCoverRelations := for coveringRelation in coveringRelations target f list ( + for j in (0,1) list ( + posetElement := coveringRelation#j; + if preimagei#?posetElement then (numberPosetMaps, preimagei#posetElement) else (idx, posetElement) + ) + ); + {posetiGroundSet, posetCoverRelations} + ); + allPosetElements = flatten append(allPosetElements, first \ posetElementsAndCovers); + allPosetCovers := last \ posetElementsAndCovers; + poset(flatten allPosetElements, unique flatten allPosetCovers) +) +posetFiberProduct (PosetMap, PosetMap) := Poset => (P, Q) -> (posetFiberProduct {P, Q}) + + +posetConnectedSum = method() +posetConnectedSum List := Poset => posetMaps -> ( + for idxf in pairs posetMaps do ( + (idx, f) := idxf; + if not instance(f, PosetMap) then ( + error concatenate("all elements in the list must be poset maps; element number ", toString(idx+1), " is not a PosetMap"); + ); + if not isRankPreserving f then ( + error "all poset maps must be rank-preserving and injective"; + ); + if not isInjective f then ( + error "all poset maps must be injective"; + ); + ); + + -- Take disjointified copies of the codomains, except add some glue according to the maps. + -- The desired poset is the non-disjoint union of these connected summands. + union for idxf in pairs posetMaps list ( + (idx, f) := idxf; + iso := isomorphism(target f, dual target f); + fInverse := hashTable apply(vertices source f, p -> f#p => p); + + newLabels := hashTable for p in vertices target f list ( + if isMember(p, image f) then ( + p => (0, fInverse#p) + ) else if isMember(iso#p, image f) then ( + p => (#posetMaps + 1, fInverse#(iso#p)) + ) else ( + p => (idx, p) + ) + ); + labelPoset(target f, newLabels) + ) +) +posetConnectedSum (PosetMap, PosetMap) := Poset => (P, Q) -> (posetConnectedSum {P, Q}) + + +posetClosedProduct = method() +posetClosedProduct List := Poset => posets -> ( + for idxPoset in pairs posets do ( + (idx, P) := idxPoset; + if not instance(P, Poset) then ( + error concatenate("all elements in the list must be posets; element number ", toString(idx+1), " is not a Poset"); + ); + ); + + posetRanks := for P in posets list ( + if (#(minimalElements P) != 1 or #(maximalElements P) != 1) then ( + error "all posets must have a unique minimal element and a unique maximal element"; + ); + #(rankPoset P) - 1 + ); + if #(unique posetRanks) > 1 then ( + print "the given posets do not have the same rank, therefore the resulting poset will not be ranked"; + ); + + allPosetElements := for idxPoset in pairs posets list ( + (idx, P) := idxPoset; + posetiGroundSet := toList((idx, 1) .. (idx, #P_*)); + + -- Replace the element in the minimum element index with a common minimal element m_l. + minimalElement := first minimalElements P; + minimalElementIndex := position(P_*, b -> b === minimalElement); + posetiGroundSet = insert(minimalElementIndex, "min", posetiGroundSet); + posetiGroundSet = drop(posetiGroundSet, {minimalElementIndex + 1, minimalElementIndex + 1}); + + -- Do the same as above but replace the element in the maximum element + -- index with a common maximal element "max". + maximalElement := first maximalElements P; + maximalElementIndex := position(P_*, b -> b === maximalElement); + posetiGroundSet = insert(maximalElementIndex, "max", posetiGroundSet); + drop(posetiGroundSet, {maximalElementIndex + 1, maximalElementIndex + 1}) + ); + + closedGroundSet := unique flatten allPosetElements; + closedCoverRelations := flatten for idxPoset in pairs posets list ( + (idx, P) := idxPoset; + for coverRelation in coveringRelations P list ( + firstPosition := position(P_*, x -> x === first coverRelation); + secondPosition := position(P_*, x -> x === last coverRelation); + {(allPosetElements#idx)_firstPosition, (allPosetElements#idx)_secondPosition} + ) + ); + poset(closedGroundSet, closedCoverRelations) +) +posetClosedProduct (Poset, Poset) := Poset => (P, Q) -> (posetClosedProduct {P, Q}) + + +-------------------------------------------------- +--- Ring Operations +-------------------------------------------------- +ringProductHelper := (I1, I2, connected) -> ( + -- I and J are ideals. Since the presentation of the fiber product and the + -- connected sum are nearly the same, the `connected` argument is a flag + -- determining which construction to use. + R1 := ring I1; + R2 := ring I2; + if not (isPolynomialRing R1 and isPolynomialRing R2) then + error "both quotient rings must be the quotient of a polynomial ring R and an ideal of R"; + if not (isField coefficientRing R1 and isField coefficientRing R2) then + error "both quotient rings must be quotients of a polynomial ring with base ring of a field"; + if not (coefficientRing R1 === coefficientRing R2) then + error "the coefficient field of both ambient rings of both quotient rings must be the same"; + if not (isHomogeneousWrtVars I1 and isHomogeneousWrtVars I2) then + error "both ideals of each quotient ring must be homogeneous"; + + try S := R1/I1 else error concatenate(toString(I1), " must be an ideal of ", toString(R1)); + try T := R2/I2 else error concatenate(toString(I2), " must be an ideal of ", toString(R2)); + + -- The ambient ring of the ring tensor product is the same as what we want + -- for the ring fiber product. + ringTensorProduct := S**T; + fiberRing := ambient ringTensorProduct; + fiberIdeal := ideal ringTensorProduct; + + -- Get the two maps into the tensor product. + -- We assume that the fiberRing gained from the tensor product will be in + -- the correct form with the variables of the 1st ring at the start, and + -- the variables of the 2nd ring at the end. + R1toTensor := map(fiberRing, R1, take(gens fiberRing, #(gens R1))); + R2toTensor := map(fiberRing, R2, take(gens fiberRing, -#(gens R2))); + + extraIdeal := ideal apply((gens R1)**(gens R2), (x,y) -> R1toTensor(x) * R2toTensor(y)); + fiberIdeal = fiberIdeal + extraIdeal; + if connected then ( + maxs := for I in {I1, I2} list ( + maxP := maximalElements getPoset I; + if #maxP != 1 then error "the monomial posets of the rings must have unique maximal elements"; + first maxP + ); + fiberIdeal = fiberIdeal + ideal(R1toTensor lift(first maxs, R1) - R2toTensor lift(last maxs, R2)); + ); + fiberRing / fiberIdeal +) +ringFiberProduct = method(Binary=>true) +ringFiberProduct (Ideal, Ideal) := Ideal => (I1, I2) -> ( + ideal ringProductHelper(I1, I2, false) +) +ringFiberProduct (QuotientRing, QuotientRing) := QuotientRing => (S, T) -> ( + ringProductHelper(ideal S, ideal T, false) +) + +ringConnectedSum = method(Binary=>true) +ringConnectedSum (Ideal, Ideal) := Ideal => (I1, I2) -> ( + ideal ringProductHelper(I1, I2, true) +) +ringConnectedSum (QuotientRing, QuotientRing) := QuotientRing => (S, T) -> ( + ringProductHelper(ideal S, ideal T, true) +) + +-------------------------------------------------- +--- Shadows +-------------------------------------------------- +shadowHelper = (P, S, p) -> ( + if not isSubset(S, P_*) then error "the given set is not a subset of the given poset"; + + -- For a cover relation x < y, we say x is the lower shadow and y is the upper shadow. + -- For each covering relation k={k#0,k#1} with k#0 in S, the element k#1 is + -- supposed to be in the upper shadow of S. + -- For each covering relation k={k#0,k#1} with k#1 in S, the element k#0 is + -- supposed to be in the lower shadow of S. + unique for k in coveringRelations P list if any(S, s -> s === k#p) then k#(p - 1) else continue +) + +lowerShadow = method() +lowerShadow (Poset, List) := List => (P, S) -> shadowHelper(P, S, 1) + +upperShadow = method() +upperShadow (Poset, List) := List => (P, S) -> shadowHelper(P, S, 0) + +-- This returns the initial segment of size s in level d of the ranked poset P +-- with respect to the order O. +-- O could be a list of elements of P, but it actually only needs to be a list +-- containing the initial segment. +initialSegment = method() +initialSegment (Poset, List, ZZ, ZZ) := List => (P, O, d, s) -> ( + if O != unique O then error "the given order must not have duplicate entries"; + if (s > 0 and #O == 0) then error "the order must contain the desired segment."; + + dLevel := (rankPoset P)#d; + if (s < 0 or s > #dLevel) then ( + error "the size of the segment must be inclusively between 0 and the size of the dth level"; + ); + + j := #O-1; + for i from 0 to s-1 list ( + while not isMember(O#j, dLevel) do ( + j = j-1; + if j < 0 then ( + error "the order must contain the desired segment"; + ); + ); + j = j-1; + O#(j+1) + ) +) + +finalSegment = method() +finalSegment (Poset, List, ZZ, ZZ) := List => (P, O, d, s) -> initialSegment(P, reverse O, d, s) + +-------------------------------------------------- +--- Macaulay orders +-------------------------------------------------- + +-- Suppose P is a ranked poset, and its subposet inclusively consisting of the +-- levels from 0 to d is Macaulay with respect to the total order O. +-- The integer d is supposed to be inclusively between -1 and the rank of P. +-- Then, this returns all extensions of O with respect to which P is Macaulay, +-- modulo relations between elements of different ranks. +-- But, if not returnAllOrders, then this instead returns only a list with at +-- most one order. +macaulayHelper := (P, O, d, returnAllOrders) -> ( + levels := rankPoset P; + if d + 1 >= #levels then ( + -- If d is the rank of P, then it is already Macaulay with respect to O. + return {O}; + ); + + -- This will be the subset of permutations(levels#(d+1)) consisting of all + -- total orderings o of level d+1 such that the upper shadow of an initial + -- segment in level d wrt O is an initial segment of level d+1 wrt o. + d1orders := {{}}; + if d == -1 then ( + -- For level 0, there is no lower layer putting constraints on d1orders. + d1orders = permutations(levels#(d+1)); + ) else ( + shadows := flatten append({{{}}}, for s from 1 to #(levels#d) list ( + -- Let shadows#s be the upper shadow of the initial segment of + -- size s in level d. + upperShadow(P, initialSegment(P, O, d, s)) + ) + ); + -- There might also be elements of levels#(d+1) which are not in the + -- upper shadow of levels#d + shadows = append(shadows, levels#(d+1)); + + for s from 1 to #(levels#d) + 1 do ( + -- Suppose d1orders is the list of possible orderings of shadows#(s-1). + -- Then, replace each ordering with its possible extensions to an + -- ordering of shadows#s. + d1orders = flatten for o in d1orders list ( + for p in permutations(shadows#s - set shadows#(s-1)) list ( + -- The initial segment of size s in level d should have an + -- upper shadow no larger than the upper shadow of any other + -- subset of size s in level d. + if any(subsets(levels#d, s), A -> #upperShadow(P, A) < #(shadows#s)) then ( + continue + ); + join(p, o) + ) + ); + ); + ); + + -- This will be the list of all orders with respect to which P is Macaulay, + -- modulo relations between different levels. Or, if not returnAllOrders, + -- this will have at most one order. + orders := {}; + flatten for o in d1orders when (returnAllOrders or orders == {}) list ( + orders = macaulayHelper(P, join(O,o), d+1, returnAllOrders); + if instance(orders, String) then return orders; + orders + ) +) + +macaulayOrders = method(TypicalValue => List, + Options => { + TikZ => false, + Visual=>false, + AllOrders => true + } + ) +-- Modulo relations between elements of different rank, this returns the list +-- of orders with respect to which P is Macaulay. But, if not returnAllOrders, +-- this returns an order with respect to which P is Macaulay. +macaulayOrders Poset := opts -> (P) -> ( + if not instance(opts.TikZ, Boolean) then error "the option TikZ must be a Boolean"; + if not instance(opts.AllOrders, Boolean) then error "the option AllOrders must be a Boolean"; + if not isRanked(P) then error "the poset must be ranked"; + + orders := macaulayHelper(P, {}, -1, opts.AllOrders); + + if opts.TikZ then ( + levels := rankPoset P; + for order in orders do ( -- Print a TikZ picture for each order. + TikzPicture := "\\begin{tikzpicture}\n"; + + -- draw vertices + for i from 0 to (#levels)-1 do ( + level := levels#i; + j := -1; + for k from 0 to (#level)-1 do ( + -- Find the next element of level wrt order. + j = j + 1; + while not isMember(order#j, level) do j = j + 1; + -- Insert a node for this element. + TikzPicture = concatenate(TikzPicture, "\tdraw \\node at (", toString(k - (#level-1)/2), ", ", toString(i), ") (", toString(j), ") {", tex order#j, "};\n"); + ); + ); + + -- Draw cover relations + elementToInt := hashTable apply(0 ..< #order, i -> order#i => i); -- As maps, the inverse of order + tikzPicture := concatenate for coverRelation in coveringRelations P list ( + "\n\t\\draw (", toString elementToInt#(first coverRelation), ") -- (", toString elementToInt#(last coverRelation), ");" + ); + print concatenate(tikzPicture, "\n\\end{tikzpicture}"); + ); + ); + + if instance(opts.Visual, String) or (instance(opts.Visual, Boolean) and opts.Visual) then ( + openPort if instance(opts.Visual, String) then opts.Visual else "8080"; + visualize P; + -- visualize P with its Macaulay orders + for order in orders do ( + elementToInt := hashTable apply(0 ..< #order, i -> order#i => i); -- As maps, the inverse of order + visualize poset(apply(P_*, p -> elementToInt#p), apply(coveringRelations P, c -> {elementToInt#(first c), elementToInt#(last c)})); + ); + closePort(); + ); + orders +) +macaulayOrders QuotientRing := opts -> (S) -> ( + R := ambient S; + I := ideal S; + + if not isPolynomialRing R then error "the ambient ring must be a polynomial ring"; + if not isField baseRing R then error "the base ring of the polynomial ring must be a field"; + if not isHomogeneousWrtVars I then error "the ring must be homogeneous"; + -- The ideal also should be level linearly independent + + macaulayOrders(getPoset(R,I), TikZ => opts.TikZ, Visual => opts.Visual, AllOrders => opts.AllOrders) +) +macaulayOrders Ideal := opts -> (I) -> ( + macaulayOrders((ring I)/I, TikZ => opts.TikZ, Visual => opts.Visual, AllOrders => opts.AllOrders) +) + +isMacaulay = method(TypicalValue => Boolean, + Options => { + TikZ => false, + Visual => false + } + ); +isMacaulay Poset := opts -> (P) -> ( + result := macaulayOrders(P, TikZ => opts.TikZ, + Visual => opts.Visual, + AllOrders => false); + #result != 0 +) +isMacaulay QuotientRing := opts -> (S) -> ( + result := macaulayOrders(S, TikZ => opts.TikZ, + Visual => opts.Visual, + AllOrders => false); + #result != 0 +) +isMacaulay Ideal := opts -> (I) -> ( + isMacaulay((ring I)/I, TikZ => opts.TikZ, + Visual => opts.Visual) +) + +-------------------------------------------------- +--- Additivity +-------------------------------------------------- + +isAdditive = method(TypicalValue => Boolean) +isAdditive Poset := Boolean => (P) -> ( + -- Check whether P is additive with respect to some Macaulay order. + orders := macaulayOrders P; + levels := rankPoset P; + for order in orders do ( + levelMin := -1; + levelMax := -1; + -- Check its additivity for all ranks. + if all(0 ..< #levels, r -> ( + levelMin = levelMax+1; + while (levelMax+1 <= #order) and (r+1 >= #levels or not isMember(order#(levelMax+1), levels#(r+1))) do ( + levelMax += 1; + ); + level := take(order, {levelMin, levelMax}); + + shadows := new MutableHashTable; + shadows#{} = upperShadow(P, {}); + -- Compute shadows of nonempty segments. + levelIntervals := subsets(0 ..< #level, 2) | apply(toList(0 ..< #level), i -> {i,i}); + for idxPair in levelIntervals do ( + -- idxPair *should* already be sorted. + segment := take(level, idxPair); + shadows#segment = upperShadow(P, segment); + ); + + -- Check additivity between levels. + all(levelIntervals, idxPair -> ( + (i, j) := toSequence idxPair; + initialNewShadow := shadows#(take(level, {#level-(j-i+1), #level-1})); + newShadow := shadows#(take(level, {i, j})) - set shadows#(take(level, {j+1, #level-1})); + finalNewShadow := shadows#(take(level, {0, j-i})) - set shadows#(take(level, {j-i+1, #level-1})); + #initialNewShadow >= #newShadow and #newShadow >= #finalNewShadow + ) + ) + ) + ) then return true; + ); + false +) + + + +-------------------------------------------------- +--- Documentation +-------------------------------------------------- + +beginDocumentation() + +doc /// + +Node + Key + MacaulayPosets + Headline + a package for working with Macaulay posets and Macaulay rings + Description + Text + This package contains a few methods for working with posets, rings, + and the Macaulay property. + + Two boolean methods for ranked posets are implemented. + Text + @UL{{TO "isAdditive"}, + {TO "isMacaulay", ", also applicable to rings"}}@ + Text + Four poset operations and two ring operations are implemented. + Text + @UL{{TO "posetWedgeProduct", ", analogous to ", TO "ringFiberProduct"}, + {TO "posetClosedProduct", ", analogous to ", TO "ringConnectedSum"}, + {TO "posetFiberProduct"}, + {TO "posetConnectedSum"}}@ + Text + To define the latter two poset operations, a new data type + @TO "PosetMap"@ was created. + + To obtain the monomial poset of a ring, one can use @TO "getPoset"@, + a generalization of @TO "standardMonomialPoset"@ from the + @TO2(Posets, "Posets package")@ to ideals which are not necessarily + monomial. + + This package was used in some of the work leading to the paper + @HREF{"https://arxiv.org/abs/2502.15166"}@, where many of these + operations and properties are discussed. +Node + Key + getMons + (getMons, PolynomialRing, Ideal) + (getMons, Ideal) + (getMons, QuotientRing) + (getMons, PolynomialRing) + Headline + calculates the monomials of a ring + Usage + getMons(R, I) + getMons I + getMons S + getMons R + Inputs + R:PolynomialRing + I:Ideal + a homogeneous ideal of a polynomial ring + S:QuotientRing + a quotient of a polynomial ring R and a homogeneous ideal I of R + MaxDegree=>Number + Outputs + :List + Description + Text + Given a polynomial ring R and a homogeneous ideal of R (an ideal + generated by homogeneous elements of R) called I, the function + getMons gets the monomials of the quotient ring R/I. + Example + R = QQ[x,y] + I = ideal(x^3, y^3) + getMons(R, I) + + J = ideal(x*y - y^2, x^4, x^3 * y, x^2 * y^2) + getMons(R / J) + Text + For quotient rings with infinitely many monomials, the optional + argument MaxDegree can be used to calculate monomials up to a + certain degree. If this is left blank, the default value used will + be 10. + Example + I = ideal(x^2 - y^2) + getMons(R/I, MaxDegree=>10) + Caveat + The optional argument MaxDegree is only meant to be used for quotient + rings where there are infinitely many monomials. For quotient rings with + finitely many monomials, MaxDegree will be ignored regardless of it's value. + SeeAlso + getPoset +Node + Key + getPoset + (getPoset, PolynomialRing, Ideal) + (getPoset, Ideal) + (getPoset, QuotientRing) + (getPoset, PolynomialRing) + Headline + calculates the monomial poset of a ring + Usage + getPoset(R, I) + getPoset I + getPoset S + getPoset R + Inputs + R:PolynomialRing + I:Ideal + a homogeneous ideal of a polynomial ring + S:QuotientRing + a quotient of a polynomial ring R and a homogeneous ideal I of R + MaxDegree=>Number + Outputs + :Poset + Description + Text + Given a polynomial ring R and a homogeneous ideal of R (an ideal + generated by homogeneous elements of R) called I, the monomial + poset of R/I is the collection of @TO2(getMons, "monomials of R/I")@ + equipped with the divisibility partial order. + Example + R = QQ[x,y] + I = ideal(x*y - y^2, x^4, x^3*y, x^2*y^2) + getPoset(R, I) + Text + For posets with infinitely many monomials, the optional argument + MaxDegree can be used to calculate monomials up to a certain degree + to use in the poset. If this is left blank, the default value used + will be 10. + Example + J = ideal(x^2 - y^2) + getPoset(R, J, MaxDegree => 15) + Caveat + Like the function getMons, the optional argument MaxDegree is only meant + to be used for quotient rings where there are infinitely many monomials. + For quotient rings with finitely many monomials, MaxDegree will be ignored + regardless of it's value. + SeeAlso + getMons +Node + Key + PosetMap + Headline + the class of all morphisms between posets + Description + Text + Poset maps are used to obtain @TO2(posetFiberProduct, "fiber products")@ + and @TO2(posetConnectedSum, "connected sums")@ of @TO2(Poset, "posets")@. + SeeAlso + posetFiberProduct + posetConnectedSum +Node + Key + (source, PosetMap) + Usage + source f + Inputs + f:PosetMap + Outputs + :Poset + SeeAlso + PosetMap + (target, PosetMap) +Node + Key + (target, PosetMap) + Usage + target f + Inputs + f:PosetMap + Outputs + :Poset + SeeAlso + PosetMap + (source, PosetMap) +Node + Key + (image, PosetMap, List) + (image, PosetMap) + Headline + the image of a poset map + Usage + image(f, L) + Inputs + f:PosetMap + L:List + a subset of @TT "vertices source f"@ + Outputs + :List + Description + Text + The image of the map $[3]\rightarrow[10]$ given by $i\mapsto 2i$ is + computed below. + Example + f = map(chain 10, chain 3, {1=>2, 2=>4, 3=>6}) + image f + Text + The image of ${1,2}$ under this map is computed below. + Example + image(f, {1,2}) +Node + Key + (isInjective, PosetMap) + Usage + isInjective f + Inputs + f:PosetMap + Outputs + :Boolean + SeeAlso + (isSurjective, PosetMap) + isRankPreserving +Node + Key + (isSurjective, PosetMap) + Usage + isSurjective f + Inputs + f:PosetMap + Outputs + :Boolean + SeeAlso + (isInjective, PosetMap) + isRankPreserving +Node + Key + isRankPreserving + (isRankPreserving, PosetMap) + Headline + whether a poset map is rank-preserving + Usage + isRankPreserving f + Inputs + f:PosetMap + Outputs + :Boolean + Description + Text + A @TO2(PosetMap, "poset map")@ $f: P\rightarrow Q$ between + @TO2(isRanked, "ranked")@ posets is rank-preserving if and only if, + for each $p \in P$, the rank of $p$ is equal to the rank of $f(p)$, + assuming both $P$ and $Q$ have a rank $0$ element. + Example + isRankPreserving map(chain 2, chain 1, {1}) + isRankPreserving map(chain 2, chain 1, {2}) + SeeAlso + (isInjective, PosetMap) + (isSurjective, PosetMap) +Node + Key + (symbol ==, PosetMap, PosetMap) + Headline + whether two poset maps are equal + Usage + P==Q + Inputs + P:PosetMap + Q:PosetMap + Outputs + :Boolean +Node + Key + (symbol SPACE, PosetMap, Thing) + Headline + applies a poset map to a poset element + Usage + P p + Inputs + P:PosetMap + p:Thing + Outputs + :Thing + Description + Example + f = map(product(chain 2, chain 2), chain 2, {{1,1},{1,2}}) + f 1 + f 2 +Node + Key + posetWedgeProduct + (posetWedgeProduct, List) + (posetWedgeProduct, Poset, Poset) + Headline + constructs the wedge product of several posets + Usage + posetWedgeProduct L + Inputs + L:List + a list of @TO2(Poset, "posets")@ + P:Poset + Q:Poset + Outputs + :Poset + Description + Text + Given a list L of posets all with unique minimal elements, the + function returns the wedge product of all the posets. The wedge + product is defined as follows: + Suppose that for $1\leq i\leq t$ we have posets $P_i$ with unique + least element $\ell_i$. Their $\bf{wedge product}$ is the set: + \[ P_1 \vee P_2 \vee \cdots \vee P_t = \left(\bigsqcup_{i=1}^t P_i \right)/ (\ell_1=\ell_2=\cdots =\ell_t), \] + (meaning that we take the disjoint union of the sets $P_i$ in which + we identify all the $\ell_i$ into one element) with the partial order + $a \leq b$ if and only if $a\leq b$ in $P_i$ for some $i$. + + This is the special case of @TO "posetFiberProduct"@ where the + domain of the map is a $1$-vertex poset. + Example + R = QQ[x,y] + I = ideal(x^3, y^2) + posetWedgeProduct {booleanLattice 3, chain 4, getPoset(R/I)} + SeeAlso + ringFiberProduct + posetFiberProduct + posetClosedProduct +Node + Key + posetFiberProduct + (posetFiberProduct, List) + (posetFiberProduct, PosetMap, PosetMap) + Headline + constructs the fiber product of several posets + Usage + posetFiberProduct L + posetFiberProduct(f, g) + Inputs + L:List + a list of rank-preserving injective @TO PosetMap@s. + f:PosetMap + g:PosetMap + Outputs + :Poset + Description + Text + In this construction, several posets are glued together along a + common subposet. Suppose $(A, \leq_A), (B, \leq_B), (C, \leq_C)$ + are posets and $\iota_A: C \rightarrow A$ and $\iota_B: C \rightarrow B$ + are rank-preserving, injective, and monotone. Then, we have a fiber + product \[A \times_C B = (A \setminus \iota_A(C)) \sqcup (B \setminus \iota_B(C)) \sqcup C\] + with $a \geq c$ iff $a \geq_A \iota_A(c)$ for $a \in A$ and $c \in C$, + and $b \geq c$ iff $b \geq_B \iota_A(c)$ for $b \in A$ and $c \in C$. + + This is a generalization of @TO "posetWedgeProduct"@. + Example + P = product(chain 2, chain 2) + Q = product(chain 2, chain 3) + f = map(Q, P, {{1,1}, {1,2}, {2,1}, {2,2}}) + g = map(Q, P, {{1,1}, {2,1}, {1,2}, {2,2}}) + + R = (ZZ/2)[x,y] + I = monomialIdeal(x^3, x^2 * y^2, y^3) + areIsomorphic(posetFiberProduct(f, g), getPoset(R/I)) + + chainWedgePoset = product(chain 2, adjoinMin posetWedgeProduct(chain 2, chain 2)) + areIsomorphic(posetFiberProduct(f, f), chainWedgePoset) + SeeAlso + posetWedgeProduct + posetConnectedSum +Node + Key + posetClosedProduct + (posetClosedProduct, List) + (posetClosedProduct, Poset, Poset) + Headline + constructs the closed product of several posets + Usage + posetClosedProduct L + posetClosedProduct(P, Q) + Inputs + L:List + a list of posets + P:Poset + Q:Poset + Outputs + :Poset + Description + Text + Given a list of posets that all have a unique minimal and maximal + element, the function returns the closed product of all the posets. + The closed product is defined as follows: + Suppose that for $1 \leq i \leq t$ we have posets $P_i$ with unique + least element $\ell_i$ and unique largest element $L_i$. Their + $\bf{closed product}$ is the set: + \[ P_1 \diamond P_2 \diamond \cdots \diamond P_t = \left( \bigsqcup_{i=1}^t P_i \right) / (\ell_1 = \ell_2 = \cdots = \ell_t, L_1 = L_2 = \cdots = L_t ), \] + (meaning that we take the disjoint union of the sets $P_i$ in which + we identify all the $\ell_i$ into one element and all the $L_i$ + elements into one element) with the partial order $a \leq b$ if and + only if $a \leq b$ in $P_i$ for some $i$. + + This is the special case of @TO "posetConnectedSum"@ where the + domain of the map is a $1$-vertex poset. + Example + R = QQ[x,y] + I = ideal(x^2, y^2) + posetClosedProduct(chain 3, getPoset(R/I)) + SeeAlso + ringConnectedSum + posetConnectedSum + posetWedgeProduct +Node + Key + posetConnectedSum + (posetConnectedSum, List) + (posetConnectedSum, PosetMap, PosetMap) + Headline + constructs the connected sum of several posets + Usage + posetConnectedSum L + posetConnectedSum(f, g) + Inputs + L:List + of rank-preserving injective @TO PosetMap@s. + f:PosetMap + g:PosetMap + Outputs + :Poset + Description + Text + In this construction, several posets are glued together at the top + and at the bottom. Suppose $A,B$ are self-dual posets, $C$ is a + poset, and $i_A: C \rightarrow A$ and $i_B: C \rightarrow B$ are + rank-preserving, injective @TO2(PosetMap, "poset maps")@. Let + $d_A: A \rightarrow A^{\operatorname{op}}$ and + $d_B: B \rightarrow B^{\operatorname{op}}$ be the dual isomorphisms. + The connected sum of $A$ and $B$ with respect to $i_A, i_B$ is the + poset obtained by taking the disjoint union of $A$ and $B$, + identifying the image of $i_A$ with the image of $i_B$, and + identifying the image of $d_A \circ i_A$ with the image of $d_B \circ i_B$. + This construction can also be generalized to connected sums of more + than two posets. + + This is a generalization of @TO "posetClosedProduct"@. + + Here is the connected sum of the chain $[5]$ with itself with + respect to the inclusion map $[2]\hookrightarrow[5]$ with + $1 \mapsto 1$ and $2 \mapsto 2$. + Example + i = map(chain 5, chain 2, {1=>1, 2=>2}) + A = posetConnectedSum(i, i) + areIsomorphic( A, adjoinMin adjoinMax product(chain 2, chain 2) ) + Text + Let $P = [2] \times [2]$, let $Q = [2] \times [5]$. There are two + different connected sums over $P$ of $Q$ with itself. The map + $f: P \rightarrow Q$ is the inclusion map with $(1,2) \mapsto (1,2)$ + and $(2,1) \mapsto (2,1)$. The map $g: P \rightarrow Q$ is the other + rank-preserving injection, with $(1,2) \mapsto (2,1)$ and $(2,1) \mapsto (1,2)$. + Example + P = product(chain 2, chain 2) + Q = product(chain 2, chain 5) + + f = map(Q, P, {{1,1}, {1,2}, {2,1}, {2,2}}) + g = map(Q, P, {{1,1}, {2,1}, {1,2}, {2,2}}) + + R = posetConnectedSum(f, g) + S = posetConnectedSum(f, f) + + areIsomorphic(R, S) + + areIsomorphic(S, product(chain 2, A)) +Node + Key + ringFiberProduct + (ringFiberProduct, Ideal, Ideal) + (ringFiberProduct, QuotientRing, QuotientRing) + (ringFiberProduct, List) + (ringFiberProduct, Sequence) + Headline + constructs the fiber product of several rings + Usage + ringFiberProduct(I, J) + ringFiberProduct(S, T) + Inputs + I:Ideal + a homogeneous ideal of a @TO PolynomialRing@. + J:Ideal + a homogeneous ideal of a @TO PolynomialRing@. + S:QuotientRing + a quotient of a polynomial ring by a homogeneous ideal + T:QuotientRing + a quotient of a polynomial ring by a homogeneous ideal + Outputs + :Ideal + :QuotientRing + Description + Text + Suppose we have rings $S_1 = R_1/I_1$ and $S_2 = R_2/I_2$ for some + homogeneous ideals $I_1$ of $R_1 = K[x_1, \dots, x_n]$ and $I_2$ of + $R_2 = K[y_1, \dots, y_m]$, where $K$ is a field. Their fiber product + over $K$ is the ring: + \[ S_1 \times_K S_2 = K[x_1, \dots, x_n, y_1, \dots, y_m] / (I_1 + I_2 + (x_i y_j : 1 \leq i \leq n, 1 \leq j \leq m )) \] + Example + R = QQ[x,y] + I = ideal(x^2, y^2) + + S = QQ[a,b] + J = ideal(a^4, b^4) + ringFiberProduct(R/I, S/J) + Example + R = (ZZ/7)[x,y,z] + I = ideal(x^2, y^3, z^4) + + S = (ZZ/7)[a,b,c] + J = ideal(a, b^3, c^3) + ringFiberProduct(R/I, S/J) + SeeAlso + posetWedgeProduct + ringConnectedSum +Node + Key + ringConnectedSum + (ringConnectedSum, Ideal, Ideal) + (ringConnectedSum, QuotientRing, QuotientRing) + (ringConnectedSum, List) + (ringConnectedSum, Sequence) + Headline + constructs the connected sum of several rings + Usage + ringConnectedSum(I, J, ...) + ringConnectedSum(S, T, ...) + Inputs + I:Ideal + a homogeneous ideal of a @TO PolynomialRing@. + J:Ideal + a homogeneous ideal of a @TO PolynomialRing@. + S:QuotientRing + a quotient of a polynomial ring by a homogeneous ideal + T:QuotientRing + a quotient of a polynomial ring by a homogeneous ideal + Outputs + :Ideal + :QuotientRing + Description + Text + Suppose we have Gorenstein rings $S_1 = R_1/I_1$ and $S_2 = R_2/I_2$ + for some homogeneous ideals $I_1$ of $R_1 = K[x_1, \dots, x_n]$ and + $I_2$ of $R_2 = K[y_1, \dots, y_m]$, where $K$ is a field. Let $m_1, m_2$ + be the maximal elements of the @TO2(getMons, "monomial posets")@ of + $S_1, S_2$, respectively. The connected sum of $S_1$ and $S_2$ is their + @TO2(ringFiberProduct, "fiber product")@ mod $m_1-m_2$. In symbols, + $S_1 \# S_2 = \frac{S_1 \times_K S_2}{(m_1-m_2)}$. + Example + R = (ZZ/2)[u,v,w] + I = ideal(u^4, v^2-w^2, w^2-v*u) + S = R/I + + T = newRing(S, Variables=>{x,y,z}) + U = ringConnectedSum(S, T) + ideal U + isMacaulay S + isMacaulay U + SeeAlso + posetClosedProduct + ringFiberProduct +Node + Key + (map, Poset, Poset, List) + (map, Poset, Poset, HashTable) + Headline + make a poset map + Usage + map(Q, P, L) + Inputs + P:Poset + the source poset + Q:Poset + the target poset + L:List + of length @TT "#(vertices P)"@ and whose $i$th entry specifies the image of the $i$th element of @TT "P"@, or a @TO "List"@ of @TO "Option"@s whose keys are @TT "vertices P"@ and whose values are in @TT "vertices Q"@, or a @TO "HashTable"@ whose keys are @TT "vertices P"@ and whose values are in @TT "vertices Q"@ + Outputs + :PosetMap + the poset map from @TT "P"@ to @TT "Q"@ specified by @TT "L"@. + Description + Text + A poset map is a monotone function $f: P\rightarrow Q$ from a poset + $P$ to a poset $Q$, so $f(p)\leq f(q)$ whenever $p\leq q$. + + Let $P = [2]$ and $Q = [3] \times [3]$. Here are the three ways to + obtain the map $\phi: P \rightarrow Q$ given by $\phi(1) = (1,1)$ and + $\phi(2) = (1,2)$. + Example + P = chain 2 + Q = product(chain 3, chain 3) + + f = map(Q, P, {{1,1}, {1,2}}) + g = map(Q, P, {1=>{1,1}, 2=>{1,2}}) + h = map(Q, P, hashTable {1=>{1,1}, 2=>{1,2}}) + + f == g + g == h + Text + Potentially, some of the vertices of the target may be @TO2(Option, "options")@. + When there is ambiguity, the list elements will be taken as vertices + of the target as in the first of the three ways above. + Example + P = poset({1}, {}) + Q = poset({1, 1=>1}, {{1, 1=>1}}) + f = map(Q, P, {1=>1}) + f 1 +Node + Key + (symbol *, PosetMap, PosetMap) + Headline + composition of poset maps + Usage + g * f + Inputs + f:PosetMap + g:PosetMap + Outputs + :PosetMap + the composition $g \circ f$ +Node + Key + upperShadow + (upperShadow, Poset, List) + Headline + the upper shadow of a subset of a poset + Usage + upperShadow(P,S) + Inputs + P:Poset + S:List + a subset of the ground set of the poset + Outputs + :List + Description + Text + If $S$ is a subset of a poset, then the upper shadow of $S$ is the + set of all elements of the poset which cover some element of $S$. + Example + upperShadow(divisorPoset 12, {2,3}) + + S = QQ[x,y]/(x^4, y^4) + x = S_0 + y = S_1 + + upperShadow(getPoset S, {x^3, x^2*y}) + SeeAlso + lowerShadow +Node + Key + lowerShadow + (lowerShadow, Poset, List) + Headline + the lower shadow of a subset of a poset + Usage + lowerShadow(P,S) + Inputs + P:Poset + S:List + a subset of the ground set of the poset + Outputs + :List + Description + Text + If $S$ is a subset of a poset, then the lower shadow of $S$ is the + set of all elements of the poset which cover some element of $S$. + Example + lowerShadow(divisorPoset 12, {12}) + + S = QQ[x,y]/(x^4, y^4) + x = S_0 + y = S_1 + + lowerShadow(getPoset S, {x^3, x^2*y}) + SeeAlso + upperShadow +Node + Key + macaulayOrders + (macaulayOrders, Poset) + (macaulayOrders, QuotientRing) + (macaulayOrders, Ideal) + Headline + finds all orders with respect to which the poset is Macaulay + Usage + macaulayOrders P + macaulayOrders S + macaulayOrders I + Inputs + P:Poset + S:QuotientRing + a homogeneous level linearly independent quotient of a polynomial ring over a field + I:Ideal + a homogeneous ideal of a polynomial ring such that the quotient is level linearly independent + TikZ=>Boolean + Visual=>Boolean + AllOrders=>Boolean + Outputs + :List + whose elements are lists representing orders with respect to which the poset is Macaulay + Description + Text + Given a poset with rank function $r$, this method returns all + Macaulay orders $<$ on the poset such that $r(p) < r(q)$ implies + $p < q$. + + Given a quotient $S$ of a polynomial ring, this method returns + Macaulay orders on the monomial poset of $S$. + + A total order $<$ on a poset $P$ is represented by a @TO "list"@. + It is the permutation of the of the ground set of $P$ that is + increasing with respect to $<$. + Example + macaulayOrders(product(chain 3, chain 3)) + Text + The option @TO "TikZ"@ can print a Hasse diagram of the poset for + each order $<$ such that the vertices in each level are ordered left + to right according to $<$. + Example + R = (ZZ/2)[x,y] + I = ideal(x^4, x^3-y^3, y^4) + macaulayOrders(R/I, TikZ=>true) + Caveat + Given a @TO "QuotientRing"@ or an @TO "Ideal"@, this method will not + verify level linear independence. + SeeAlso + isMacaulay +Node + Key + isMacaulay + (isMacaulay, Poset) + (isMacaulay, QuotientRing) + (isMacaulay, Ideal) + Headline + whether a poset or ring is Macaulay + Usage + isMacaulay P + isMacaulay S + isMacaulay I + Inputs + P:Poset + S:QuotientRing + a homogeneous level linearly independent quotient of a polynomial ring over a field + I:Ideal + a homogeneous ideal of a polynomial ring such that the quotient is level linearly independent + TikZ=>Boolean + Visual=>Boolean + Outputs + :Boolean + Description + Text + Suppose $P$ is a ranked poset and $<$ is a total order on the ground + set of $P$. Let $\operatorname{Seg}_d n$ denote the largest $n$ + elements of rank $d$ with respect to $<$. Suppose that for every + integer $d$ between $0$ and the rank of $P$, and for every subset $A$ + of the $d$th level of $P$, we have $\lvert\nabla_P\operatorname{Seg}_d\lvert A\rvert\rvert \leq \lvert\nabla_P(A)\rvert$ + and $\nabla_P\operatorname{Seg}_d\lvert A\rvert = \operatorname{Seg}_{d+1}\lvert\nabla_P(A)\rvert$. + Then, we say $P$ is Macaulay with respect to $<$. A Macaulay poset is + a poset for which there exists an order with respect to which it is + Macaulay. + + There is an analogous property for rings, not to be confused with + the Cohen-Macaulay property. + + Products of chains are Macaulay by the Clements-Lindstrom Theorem. + Example + isMacaulay booleanLattice 3 + + R = (ZZ/2)[x,y,z] + I = monomialIdeal(x^2, y^3, z^3) + isMacaulay(R/I) + Text + It is possible for a monomial poset to not be Macaulay. + Example + S = (ZZ/2)[w,x,y,z] + J = monomialIdeal(w^4, x^2, y^2, z^2, x*w, y*w, z*w) + isMacaulay(S/J) + Text + If the poset is Macaulay, the option @TO "TikZ"@ can provide a Hasse + diagram in which the vertices are horizontally ordered according to + a Macaulay order. + Example + R = (ZZ/2)[x,y] + I = monomialIdeal(x^2, y^3) + isMacaulay(R/I, TikZ=>true) + Caveat + Given a @TO "QuotientRing"@ or an @TO "Ideal"@, this method will not verify level linear independence. + SeeAlso + macaulayOrders + isAdditive + upperShadow + lowerShadow +Node + Key + isAdditive + (isAdditive, Poset) + Headline + whether a poset is additive + Usage + isAdditive P + Inputs + P:Poset + Outputs + :Boolean + Description + Text + See section 3.2 of @HREF "https://arxiv.org/pdf/2502.15166"@ for a + definition of additivity. + + Boolean lattices are additive. Therefore, the + @TO2(posetWedgeProduct, "wedge product")@ of a Boolean lattice with + itself is Macaulay. + Example + B = booleanLattice 3 + isAdditive B + isMacaulay posetWedgeProduct(B, B) + Text + Here is a non-additive poset. + Example + isAdditive poset({1,2,3,4,5,6}, {{1,2},{1,3},{2,4},{3,4},{3,5},{3,6}}) + SeeAlso + isMacaulay + upperShadow + lowerShadow +Node + Key + MaxDegree + [getMons,MaxDegree] + [getPoset,MaxDegree] + Headline + the maximum degree of a monomial to calculate + Usage + getMons(R, I, MaxDegree=>Number) + getPoset(R, I, MaxDegree=>Number) + Description + Text + For getMons, the function starts at the lowest degree 0 and starts + working out the monomials of 0, 1, 2, ... until the number given. + Example + R = QQ[x,y] + I = ideal(x^2 - y^2) + getMons(R, I, MaxDegree=>15) + SeeAlso + getMons + getPoset +Node + Key + TikZ + [macaulayOrders,TikZ] + [isMacaulay,TikZ] + Headline + whether to print TikZ code + Usage + macaulayOrders(P, TikZ=>Boolean) + isMacaulay(P, TikZ=>Boolean) + Description + Text + Suppose the poset is Macaulay with respect to a total order $<$. If + two vertices $p$ and $q$ have the same rank, then $ptrue"@. For @TO macaulayOrders@ with @TT "AllOrders=>true"@, + there is a Hasse diagram for each order. For @TO isMacaulay@ and for + @TO macaulayOrders@ with @TT "AllOrders=>false"@, there is at most + one Hasse diagram. + Example + R = QQ[x,y] + I = monomialIdeal(x^3, x^2*y^2, y^3) + macaulayOrders(standardMonomialPoset I, TikZ=>true) + SeeAlso + macaulayOrders + isMacaulay + Visual +Node + Key + Visual + [macaulayOrders,Visual] + [isMacaulay,Visual] + Headline + whether to visualize the Macaulay orders + Usage + macaulayOrders(P, Visual=>Thing) + isMacaulay(P, Visual=>Thing) + Description + Text + If this option is set to a @TO String@ or to the @TO Boolean@ + @TT "true"@, then the method will use @TO Visualize@ to create a + visualization of the poset, and an additional visualization for + each Macaulay order with the vertices labeled with integers + according to the order. If set to a @TO String@, that port will + be used. Otherwise, port "8080" will be used. + SeeAlso + macaulayOrders + isMacaulay + TikZ +Node + Key + AllOrders + [macaulayOrders,AllOrders] + Headline + whether to return all orders instead of at most one order + Usage + macaulayOrders(P, AllOrders=>Boolean) + Description + Text + With @TT "AllOrders=>false"@, the list returned by the method will + have at most one element. + Example + B = booleanLattice 3 + #(macaulayOrders B) + macaulayOrders(B, AllOrders=>false) + SeeAlso + macaulayOrders +/// + +-------------------------------------------------- +--- Tests +-------------------------------------------------- + +-- Poset maps +TEST /// + P = chain 1 + Q = chain 2 + f = map(Q, P, {1=>1}) + g = map(P, Q, {1=>1, 2=>1}) + assert(source f == P and source g == Q) + assert(target f == Q and target g == P) + assert(isInjective f and not isInjective g) + assert(isSurjective g and not isSurjective f) + assert(isRankPreserving f and not isRankPreserving g) + assert(image f == {1}) + assert(f 1 == 1 and g 1 == 1 and g 2 == 1) + assert(g*f*g == g) + assert(f != g) +/// + +-- isMacaulay +TEST /// + KK = ZZ/2; + + S = KK[x,y,z]/monomialIdeal(x^2,y^3,z^2,x*y,y*z,z*x) + assert isMacaulay S + -- assert not isMacaulay tensor(S, S) + + -- figure 5 in an upcoming revision to https://arxiv.org/abs/2502.15166 + P = chain 2; + Q = adjoinMin posetWedgeProduct(chain 2, chain 2); + assert isMacaulay P + assert isMacaulay Q + assert not isMacaulay product(P, Q) + + -- figure 6 in an upcoming revision to https://arxiv.org/abs/2502.15166 + R = KK[x]/monomialIdeal(x^2) + S = KK[y,z]/(y^2-z^2,y^3,z^3) + assert isMacaulay R + assert isMacaulay S + assert not isMacaulay tensor(R, S) + + -- figure 7 in an upcoming revision to https://arxiv.org/abs/2502.15166 + S = KK[y,z]/monomialIdeal(y^3,y^2*z,y*z^2,z^3) + T = KK[x]/monomialIdeal(x^2) + assert isMacaulay S + assert not isMacaulay tensor(S, T) +/// + +-- getPoset +TEST /// + R = QQ[x, y] + I = ideal(x^2, y^2) + S = R/I + x = S_0 + y = S_1 + + assert(set getMons(R, I) === set {1_S, x, y, x*y}) + + assert(#getMons(QQ[x, y], ideal(x^2 - y^2), MaxDegree => 15) == 31) + + x = R_0 + y = R_1 + + + computedP = getPoset(R, ideal(x*y - y^2, x^4, x^3*y, x^2*y^2)) + actualP = poset({1, x, x^2, x^3, y, y^2, y^3}, {{1, x}, {1, y}, {x, x^2}, {x, y^2}, {x^2, x^3}, {x^2, y^3}, {y, y^2}, {y^2, y^3}}) + assert(areIsomorphic(computedP, actualP)) +/// + +-- Tests for the poset operations +TEST /// + -- posetWedgeProduct + assert areIsomorphic(posetWedgeProduct(chain 2, chain 2), adjoinMin poset({1,2}, {})) + + -- posetClosedProduct + assert areIsomorphic(posetClosedProduct(chain 3, chain 3), product(chain 2, chain 2)) + + -- posetFiberProduct + P = product(chain 2, chain 2) + Q = product(chain 2, chain 3) + + f = map(Q, P, {{1,1}, {1,2}, {2,1}, {2,2}}) + g = map(Q, P, {{1,1}, {2,1}, {1,2}, {2,2}}) + + assert areIsomorphic(posetFiberProduct(f, g), getPoset(ZZ/2[x,y]/monomialIdeal(x^3,x^2*y^2,y^3))) + assert areIsomorphic(posetFiberProduct(f, f), product(chain 2, adjoinMin posetWedgeProduct(chain 2, chain 2))) + + -- posetConnectedSum + i = map(chain 5, chain 2, {1=>1, 2=>2}) + assert areIsomorphic(posetConnectedSum(i, i), adjoinMin adjoinMax product(chain 2, chain 2)) +/// + +-- Tests for the ring operations +TEST /// + -- ringFiberProduct + S = QQ[x,y] / ideal(x^2, y^2); + P = getPoset S; + assert(areIsomorphic(posetWedgeProduct(P, P), getPoset ringFiberProduct(S, S))) + + KK = ZZ/7 + S = KK[a,b,c] / ideal(a^3, b^3, c^3); + T = KK[x,y,z] / ideal(x^4, y^2, z); + assert(areIsomorphic(posetWedgeProduct(getPoset S, getPoset T), getPoset ringFiberProduct(S, T))) + + -- ringConnectedSum + KK = ZZ/2; + P = poset({0,1,2,3,4,5}, {{0,1},{0,2},{1,3},{2,4},{3,5},{4,5}}); + S = KK[x] / monomialIdeal(x^4); + connectedSumPoset = getPoset ringConnectedSum(S, S); + assert(areIsomorphic(connectedSumPoset, P)); + + R = KK[x,y]; + S = R / monomialIdeal(x^3, y^3); + use R; + T = R / monomialIdeal(x^2, y^4); + connectedSumPoset = getPoset ringConnectedSum(S, T); + closedProductPoset = posetClosedProduct(product(chain 3, chain 3), product(chain 2, chain 4)); + assert(areIsomorphic(connectedSumPoset, closedProductPoset)); +/// + +-- Some wedge and closed product tests. +TEST /// + R = QQ[x,y,z]; + I = ideal(x*y, x*z, y^2 - y*z, y^2 - z^2, x^3 - y^3) + P = getPoset(R/I); + assert(isMacaulay posetClosedProduct(P, P)); + assert(not isMacaulay posetWedgeProduct(P, P)) +/// + + +TEST /// + -- Theorem A in https://arxiv.org/abs/2502.15166 + P = getPoset(QQ[x, y]/ideal(x^2, y^2)); + assert (isAdditive P) + assert (isAdditive P == isMacaulay posetWedgeProduct(P, P) == isMacaulay(posetClosedProduct(P, P))) + + R = QQ[x,y] + I = ideal(x^4, y^4) + P = getPoset(R/I); + assert(isMacaulay posetClosedProduct {P, P, P}) + + -- Two assertions for Theorem B in https://arxiv.org/abs/2502.15166 + R = QQ[x,y,z] + I = ideal(x^2, y^2, z^2) + P = getPoset(R/I); + assert(not isMacaulay posetClosedProduct(P, chain 4)) + + R = QQ[x,y] + I = ideal(x^2, y^3) + assert(not isMacaulay posetClosedProduct(P, getPoset(R/I))) +/// + + +TEST /// + assert(null == try getPoset(QQ[x, y]/ideal(x^3 - y^2))) + + assert(isMacaulay posetWedgeProduct {chain 5, chain 4, chain 10}) + assert(isMacaulay posetClosedProduct {chain 5, chain 5, chain 5}) + + P27 = booleanLattice 2; + P27Top = adjoinMax(P27, "newMax"); + P27Bottom = adjoinMin(P27, "newMin"); + + assert(isMacaulay posetClosedProduct(P27Top, P27Bottom)) + assert(not isMacaulay posetWedgeProduct(P27Top, P27Bottom)) + + assert(isMacaulay posetWedgeProduct(chain 2, posetClosedProduct(chain 2, chain 2))) +/// + +TEST /// + S = QQ[x, y, z] / ideal(x^2, y^2, z^2) + T = QQ[w] / ideal(w^4) + assert(not isMacaulay ringFiberProduct(S, T)) + + S = QQ[x, y, z] / ideal(x^2, y^2, z^2) + T = QQ[v, w] / ideal(v^2, w^3) + assert(not isMacaulay ringFiberProduct(S, T)) + + S = ZZ/5[x, y]/ideal(x^3, y^3) + T = ZZ/5[z] / ideal(z^5) + assert(not isMacaulay ringFiberProduct(S, T)) +/// + +-- Upper shadows of levels of a Boolean lattice +TEST /// + B = booleanLattice 3 + levels = rankPoset B + assert(set levels#1 === set upperShadow(B, levels#0)) + assert(set levels#2 === set upperShadow(B, levels#1)) + assert(set levels#3 === set upperShadow(B, levels#2)) + assert(set {} === set upperShadow(B, levels#3)) +/// + +-- Lower shadows of levels of a Boolean lattice +TEST /// + B = booleanLattice 3 + levels = rankPoset B + assert(set {} === set lowerShadow(B, levels#0)) + assert(set levels#1 === set lowerShadow(B, levels#2)) + assert(set levels#2 === set lowerShadow(B, levels#3)) +/// + +-- isMacaulay +TEST /// + R = QQ[x,y,z,w] + I = monomialIdeal(w^3,x*w^2,x^3, y^3,z*y^2,z^3, w*y,w*z,x*y,x*z) + assert not isMacaulay(R/I) +/// + +TEST /// + -- figure 3 in https://arxiv.org/abs/2510.22843 + R = QQ[x,y] + I = ideal(x^6,x^3*y,y^4,x^2*y^3-x^5) + assert isMacaulay(R/I) +/// + +TEST /// + for i from 1 to 3 do ( + for j from i to 3 do ( + for k from j to 3 do ( + assert isMacaulay product(chain i, product(chain j, chain k)); + ); + ); + ); + + for i from 3 to 4 do assert isMacaulay booleanLattice i; +/// + +TEST /// + R = QQ[x,y,z] + for i from 1 to 4 do ( + for j from i to 4 do ( + for k from j to 4 do ( + I = monomialIdeal(x^i,y^j,z^k, x*y,y*z,z*x); + assert isMacaulay(R/I); + ); + ); + ); +/// + +-- macaulayOrders for a poset in which every Macaulay order has the same restrictions to each level +TEST /// + P = poset({0,1,2,3,4,5,6,7,8,9,10},{{0,1},{0,2},{1,3},{1,4},{2,5},{3,6},{3,7},{4,8},{5,9},{6,10}}); + + for macaulayOrder in macaulayOrders P do ( + for level in rankPoset P do ( + for i from 0 to (#level)-2 do ( + assert(level#i < level#(i+1)); + ); + ); + ); +/// + +-- macaulayOrders for a 3x3 box poset +TEST /// + R = QQ[x,y] + I = monomialIdeal(x^3,y^3) + S = R/I + P = standardMonomialPoset I + x = S_0 + y = S_1 + -- Suppose P is Macaulay wrt <. Then xfalse, product(chain i, product(chain j, chain k)))) == 1); + ); + ); + ); +/// + +TEST /// + assert isAdditive booleanLattice 3 + assert not isAdditive poset({1,2,3,4,5,6}, {{1,2},{1,3},{2,4},{3,4},{3,5},{3,6}}) +/// + +end \ No newline at end of file diff --git a/M2/Macaulay2/packages/MatchingFields.m2 b/M2/Macaulay2/packages/MatchingFields.m2 index 6d982886188..f693e84cf30 100644 --- a/M2/Macaulay2/packages/MatchingFields.m2 +++ b/M2/Macaulay2/packages/MatchingFields.m2 @@ -1,24 +1,25 @@ - -- Matching Fields package for Macaulay2 by Oliver Clarke newPackage( "MatchingFields", - Version => "1.2", - Date => "November 23, 2023", + Version => "1.3", + Date => "February 2, 2026", Authors => { - {Name => "Oliver Clarke", Email => "oliver.clarke@ed.ac.uk", HomePage => "https://www.oliverclarkemath.com/"} - }, + {Name => "Oliver Clarke", Email => "oliver.clarke@durham.ac.uk", HomePage => "https://www.oliverclarkemath.com/"} + }, Headline => "Toric degenerations of flag varieties via matching fields", Keywords => {"Flag Varieties"}, DebuggingMode => false, PackageExports => {"Polyhedra", "SubalgebraBases", "Matroids", "FourTiTwo", "Graphs"} - ) + ) -- ########### -- # Exports # -- ########### export { + "MatchingField", + "matchingField", "GrMatchingField", "grMatchingField", "FlMatchingField", @@ -62,39 +63,43 @@ export { -- # Main Code # -- ############# +-- Matching Field Base Type +MatchingField = new Type of HashTable; +-- Matching Fields MF have: +-- MF.tupleMaxValue (formally n) +-- MF.tupleSizeList (formally kList) +-- MF.tupleMaxSize (formally k) +-- MF.tuples +-- MF.cache -- Matching Field Types -GrMatchingField = new Type of HashTable; +GrMatchingField = new Type of MatchingField; -- Grassmannian Matching Fields MF have: --- MF.n --- MF.k --- MF.tuples = List of k-subets of {1 .. n} --- MF.cache +-- MF.tupleSizeList : singleton {k} +-- MF.tuples : List of k-subets of {1 .. n} in revlex order -FlMatchingField = new Type of HashTable; +FlMatchingField = new Type of MatchingField; -- Flag Matching Fields MF have: --- MF.kList --- MF.grMatchingFieldList --- MF.cache +-- MF.tuples : List of k-subsets in size order and in revlex order -- The cache table contains some of the following (or eventually computed): -- weightMatrix -- weightPluecker -- mfPolytopePoints --- mfPolytope +-- mfPolytope -- matchingFieldIdeal -- matchingFieldRingMap -- ringP -- ringX --- X +-- XVarMatrix -- mfSubring -- mfNOBody -- mfPlueckerIdeal protect symbol tuples -protect symbol n -protect symbol k -protect symbol kList +protect symbol tupleMaxValue +protect symbol tupleMaxSize +protect symbol tupleSizeList protect symbol grMatchingFieldList protect symbol weightMatrix @@ -102,9 +107,9 @@ protect symbol weightPluecker protect symbol mfPolytope protect symbol mfPolytopePoints -protect symbol ringP -- Polynomial ring in variables P_I, I in subsets(n, k) -protect symbol ringX -- Polynomial ring in variables x_(i,j), 1 <= i <= k, 1 <= j <= n -protect symbol X -- matrix of ringX variables +protect symbol ringP -- Polynomial ring in variables P_I, I in subsets(n, k) +protect symbol ringX -- Polynomial ring in variables x_(i,j), 1 <= i <= k, 1 <= j <= n +protect symbol XVarMatrix -- matrix of ringX variables protect symbol mfRingMap protect symbol plueckerRingMap protect symbol mfIdeal @@ -121,83 +126,118 @@ protect symbol computedAlgebraicMatroid --------------------------------------------- -- Matching Field constructor -grMatchingField = method( - TypicalValue => GrMatchingField +matchingField = method( + TypicalValue => MatchingField ) --- MF from weight matrix -grMatchingField(Matrix) := M -> ( - -- uses min convention - -- returns matching f ield for weight matrix along with the weight for the Plucker variables +-- Matching field from weight matrix: +-- M : weight matrix +-- S : list of subsets (subsets of column indices) +-- K : list of subset sizes (for displaying GrMatchingField and FlMatchingField) +matchingField(List, List, Matrix) := (K, S, M) -> ( + -- uses min convention + -- returns matching field for weight matrix along with the weight for the Plucker variables -- NB we assume the MF is well defined from the weight matrix -- i.e. the minimum was uniquely attained for each plucker form - Mk := numRows M; - Mn := numColumns M; + Mn := numcols M; + Mk := numrows M; local subsetOrder; local subsetWeight; local weight; L := {}; W := {}; - for I in subsets(Mn, Mk) do ( - subsetWeight = infinity; - for ordering in permutations(I) do ( - weight = sum for i from 0 to Mk-1 list M_(i, ordering_i); - if weight < subsetWeight then ( - subsetOrder = apply(ordering, i -> i + 1); - subsetWeight = weight; - ); - ); - L = append(L, subsetOrder); - W = append(W, subsetWeight); - ); - MF := new GrMatchingField from { - n => Mn, - k => Mk, - tuples => L, - cache => new CacheTable from { - weightMatrix => M, - weightPluecker => W - } - } + for I in S do ( + if #I > Mn or #I > Mk then ( + error("Subset too large for weight matrix"); + ); + subsetWeight = infinity; + for ordering in permutations(I) do ( + weight = sum for i from 0 to #I-1 list M_(i, ordering_i); + if weight < subsetWeight then ( + subsetOrder = apply(ordering, i -> i + 1); + subsetWeight = weight; + ); + ); + L = append(L, subsetOrder); + W = append(W, subsetWeight); + ); + new MatchingField from { + tupleMaxValue => Mn, + tupleMaxSize => Mk, + tupleSizeList => K, + tuples => L, + cache => new CacheTable from { + weightMatrix => M, + weightPluecker => W + } + } + ) + +-- MF from tuples +-- K : list of subset sizes +-- Ln : maximum value in tuple +-- L : list of tuples +matchingField(List, ZZ, List) := (K, Ln, L) -> ( + -- sort tuples: + -- 1) first compare the sizes + -- 2) tuples of the same size are sorted by revlex order + if #K == 0 then ( + error("List of subset sizes cannot be empty"); + ); + sortFunc := I -> {#I} | rsort I; + LSorted := sort(L, sortFunc); + new MatchingField from { + tupleMaxValue => Ln, + tupleSizeList => K, + tupleMaxSize => max K, + tuples => LSorted, + cache => new CacheTable from {} + } + ) + +--------------------------------------------- +-- Grassmannian Matching Field constructor +grMatchingField = method( + TypicalValue => GrMatchingField + ) + +-- MF from weight matrix +grMatchingField(Matrix) := M -> ( + Mn := numcols M; + Mk := numrows M; + new GrMatchingField from matchingField({Mk}, subsets(Mn, Mk), M) ) -- Matching Field from list of tuples grMatchingField(ZZ, ZZ, List) := (Lk , Ln, L) -> ( -- check user input: + if not allKSubsets(Lk, Ln, L) then ( + error("Unexpected tuples."); + ); + new GrMatchingField from matchingField({Lk}, Ln, L) + ) + +-- check if L is the set of tuples of a Grassmannian Matching Field +allKSubsets = method() +allKSubsets(ZZ, ZZ, List) := (Lk, Ln, L) -> ( sortedTuples := sort (sort \ L); usualSubsets := subsets(1 .. Ln, Lk); - if not (sortedTuples == sort usualSubsets) then ( - error("Unexpected tuples."); - ); - -- put the subsets in the correct order: - lookupPosition := new HashTable from for i from 0 to #usualSubsets - 1 list (usualSubsets_i => i); - tupleList := sort(L, x -> lookupPosition#(sort(x))); - new GrMatchingField from { - n => Ln, - k => Lk, - tuples => tupleList, - cache => new CacheTable from {} - } + sortedTuples == sort usualSubsets ) +-------------------------------------------------- -- Constructor for partial Flag Matching Fields flMatchingField = method( TypicalValue => FlMatchingField ) flMatchingField(List, Matrix) := (inputKList, inputWeightMatrix) -> ( if numRows inputWeightMatrix < max inputKList then ( - error("expected a matrix with at least " | toString max inputKList | " rows"); - ); + error("expected a matrix with at least " | toString max inputKList | " rows"); + ); + Mn := numcols inputWeightMatrix; + subsetList := flatten for Mk in inputKList list subsets(Mn, Mk); grMatchingFields := for Mk in inputKList list grMatchingField(inputWeightMatrix^(toList(0 .. Mk - 1))); - new FlMatchingField from { - n => numColumns inputWeightMatrix, - kList => inputKList, - grMatchingFieldList => grMatchingFields, - cache => new CacheTable from { - weightMatrix => inputWeightMatrix, - weightPluecker => flatten for grMF in grMatchingFields list grMF.cache.weightPluecker - } - } + new FlMatchingField from matchingField(sort inputKList, subsetList, inputWeightMatrix) ) flMatchingField(Matrix) := inputWeightMatrix -> ( @@ -205,114 +245,83 @@ flMatchingField(Matrix) := inputWeightMatrix -> ( ) -- Flag matching field from list of tuples --- tuple list should be a list of lists flMatchingField(List, ZZ, List) := (LkList, Ln, L) -> ( - grMatchingFields := for kIndex from 0 to #LkList - 1 list ( - grMatchingField(LkList_kIndex, Ln, L_kIndex) - ); - new FlMatchingField from { - n => Ln, - kList => LkList, - grMatchingFieldList => grMatchingFields, - cache => new CacheTable from {} - } + -- check user input: + for Lk in LkList do ( + if not allKSubsets(Lk, Ln, select(L, I -> #I == Lk)) then ( + error("Unexpected tuples."); + ); + ); + new FlMatchingField from matchingField(LkList, Ln, L) ) -net(GrMatchingField) := MF -> ( - "Grassmannian Matching Field for Gr(" | toString MF.k | ", " | toString MF.n | ")" +html GrMatchingField := +net GrMatchingField := MF -> ( + "Grassmannian Matching Field for Gr(" | toString MF.tupleMaxSize | ", " | toString MF.tupleMaxValue | ")" ) -net(FlMatchingField) := MF -> ( - s := toString MF.kList; - "Flag Matching Field for Fl(" | s_(1, #s - 2) | "; " | toString MF.n | ")" +html FlMatchingField := +net FlMatchingField := MF -> ( + s := toString MF.tupleSizeList; + "Flag Matching Field for Fl(" | s_(1, #s - 2) | "; " | toString MF.tupleMaxValue | ")" + ) + +html MatchingField := +net MatchingField := MF -> ( + s := toString MF.tupleSizeList; + "Matching Field: tuple sizes " | s_(1, #s - 2) | "; with values 1 .. " | toString MF.tupleMaxValue ) ----------------------- -- Setup weight vectors --- unexported +-- unexported -- Called by weight-getters setupWeights = method() -setupWeights(GrMatchingField) := MF -> ( +setupWeights MatchingField := MF -> ( if not MF.cache.?weightMatrix then ( - MF.cache.weightMatrix = computeWeightMatrix MF; - ); + MF.cache.weightMatrix = computeWeightMatrix MF; + ); if not MF.cache.?weightPluecker then ( - MF.cache.weightPluecker = for tuple in MF.tuples list sum(for i from 0 to MF.k - 1 list MF.cache.weightMatrix_(i, tuple_i - 1)); - ); - ) - -setupWeights(FlMatchingField) := MF -> ( - if not MF.cache.?weightMatrix then ( - MF.cache.weightMatrix = computeWeightMatrix MF; - ); - if not MF.cache.?weightPluecker then ( - MF.cache.weightPluecker = flatten for grMF in MF.grMatchingFieldList list ( - for tuple in grMF.tuples list sum( - for i from 0 to grMF.k - 1 list MF.cache.weightMatrix_(i, tuple_i - 1) - ) - ); - ); + MF.cache.weightPluecker = for tuple in MF.tuples list sum(for i from 0 to #tuple - 1 list MF.cache.weightMatrix_(i, tuple_i - 1)); + ); ) -- basic getters: getTuples = method() -getTuples(GrMatchingField) := MF -> ( +getTuples MatchingField := MF -> ( MF.tuples ) -getTuples(FlMatchingField) := MF -> ( - for grMF in MF.grMatchingFieldList list grMF.tuples - ) - - -getGrMatchingFields = method() -getGrMatchingFields(FlMatchingField) := MF -> ( - MF.grMatchingFieldList - ) - getWeightMatrix = method() -getWeightMatrix(GrMatchingField) := MF -> ( - if not MF.cache.?weightMatrix then ( - setupWeights MF; - ); - MF.cache.weightMatrix - ) - -getWeightMatrix(FlMatchingField) := MF -> ( +getWeightMatrix MatchingField := MF -> ( if not MF.cache.?weightMatrix then ( - setupWeights MF; - ); + setupWeights MF; + ); MF.cache.weightMatrix ) - getWeightPluecker = method() -getWeightPluecker(GrMatchingField) := MF -> ( +getWeightPluecker MatchingField := MF -> ( if not MF.cache.?weightPluecker then ( - setupWeights MF; - ); + setupWeights MF; + ); MF.cache.weightPluecker ) -getWeightPluecker(FlMatchingField) := MF -> ( - if not MF.cache.?weightPluecker then ( - setupWeights MF; - ); - MF.cache.weightPluecker +-- Grassmannian Matching Fields inside a partial flag Matching Field +getGrMatchingFields = method() +getGrMatchingFields FlMatchingField := MF -> ( + if not MF.cache.?grMatchingFieldList then ( + L := MF.tuples; + MF.cache.grMatchingFieldList = for Lk in MF.tupleSizeList list grMatchingField(Lk, MF.tupleMaxValue, select(L, I -> #I == Lk)); + ); + MF.cache.grMatchingFieldList ) - -- Comparison operators: (note that tuples are always listed in revlex order) -GrMatchingField == GrMatchingField := (MF1, MF2) -> ( - MF1.n == MF2.n and - MF1.k == MF2.k and - getTuples MF1 == getTuples MF2 - ) - -FlMatchingField == FlMatchingField := (MF1, MF2) -> ( - MF1.n == MF2.n and - MF1.kList == MF2.kList and +MatchingField == MatchingField := (MF1, MF2) -> ( + MF1.tupleMaxValue == MF2.tupleMaxValue and getTuples MF1 == getTuples MF2 ) @@ -321,8 +330,8 @@ FlMatchingField == FlMatchingField := (MF1, MF2) -> ( -- The vertices of the matching field polytope -- -- Note that the package Polyhedra will compute its own vertices --- for the matching field polytope. So the order of --- the columns is not guaranteed when calling 'vertices' on a Polyhedron +-- for the matching field polytope. So the order of +-- the columns is not guaranteed when calling 'vertices' on a Polyhedron -- so we use our own function since we know that all supplied points are vertices -- -- This function is for internal use only (unexported) @@ -330,31 +339,31 @@ FlMatchingField == FlMatchingField := (MF1, MF2) -> ( -- We make use of this matrix in 'matchingFieldIdeal' matchingFieldPolytopePoints = method( Options => { - ExtraZeroRows => 0 - } + ExtraZeroRows => 0 + } ) -matchingFieldPolytopePoints(GrMatchingField) := opts -> MF -> ( +matchingFieldPolytopePoints GrMatchingField := opts -> MF -> ( if opts.ExtraZeroRows == 0 and MF.cache.?mfPolytopePoints then ( - MF.cache.mfPolytopePoints - ) else ( - -- construct a matching field polytope P_L from - -- L a matching field for Gr(k, n) Grassmannian - points := {}; - for I in MF.tuples do ( - -- construct the point corresponding to I - point := (); - for i in I do ( - point = point | (i - 1 : 0) | (1 : (1)) | (MF.n - i : 0); - ); - point = point | (MF.n * opts.ExtraZeroRows : 0); - point = toList point; - points = append(points, point); - ); - points = transpose matrix points; - if opts.ExtraZeroRows == 0 then MF.cache.mfPolytopePoints = points; - points - ) + MF.cache.mfPolytopePoints + ) else ( + -- construct a matching field polytope P_L from + -- L a matching field for Gr(k, n) Grassmannian + points := {}; + for I in MF.tuples do ( + -- construct the point corresponding to I + point := (); + for i in I do ( + point = point | (i - 1 : 0) | (1 : (1)) | (MF.tupleMaxValue - i : 0); + ); + point = point | (MF.tupleMaxValue * opts.ExtraZeroRows : 0); + point = toList point; + points = append(points, point); + ); + points = transpose matrix points; + if opts.ExtraZeroRows == 0 then MF.cache.mfPolytopePoints = points; + points + ) ) @@ -364,34 +373,34 @@ matchingFieldPolytopePoints(GrMatchingField) := opts -> MF -> ( -- given by the convex hull of the exponent vectors of the monomial map -- -- Options: --- ExtraZeroRows: adds this many rows of 0's to each vertex (thought of as a k by n matrix) --- of the polytope (used for constructing flag polytopes) --- +-- ExtraZeroRows: adds this many rows of 0's to each vertex (thought of as a k by n matrix) +-- of the polytope (used for constructing flag polytopes) +-- matchingFieldPolytope = method( Options => { - ExtraZeroRows => 0 - } + ExtraZeroRows => 0 + } ) -matchingFieldPolytope(GrMatchingField) := opts -> MF -> ( +matchingFieldPolytope GrMatchingField := opts -> MF -> ( if opts.ExtraZeroRows == 0 and MF.cache.?mfPolytope then ( - MF.cache.mfPolytope - ) else ( - P := convexHull matchingFieldPolytopePoints(MF, opts); - if opts.ExtraZeroRows == 0 then MF.cache.mfPolytope = P; - P - ) + MF.cache.mfPolytope + ) else ( + P := convexHull matchingFieldPolytopePoints(MF, opts); + if opts.ExtraZeroRows == 0 then MF.cache.mfPolytope = P; + P + ) ) matchingFieldPolytope(FlMatchingField) := opts -> MF -> ( if opts.ExtraZeroRows == 0 and MF.cache.?mfPolytope then ( - MF.cache.mfPolytope - ) else ( - P := sum for grMF in MF.grMatchingFieldList list matchingFieldPolytope(grMF, - ExtraZeroRows => (max MF.kList - grMF.k + opts.ExtraZeroRows) - ); - if opts.ExtraZeroRows == 0 then MF.cache.mfPolytope = P; - P - ) + MF.cache.mfPolytope + ) else ( + P := sum for grMF in getGrMatchingFields MF list matchingFieldPolytope(grMF, + ExtraZeroRows => (MF.tupleMaxSize - grMF.tupleMaxSize + opts.ExtraZeroRows) + ); + if opts.ExtraZeroRows == 0 then MF.cache.mfPolytope = P; + P + ) ) -------------------------- @@ -409,7 +418,7 @@ diagonalMatchingField(List, ZZ) := (LkList, Ln) -> ( flMatchingField(LkList, M) ) --- full-flag variety +-- full-flag variety -- by convention this is a (n-1) by (n) matrix diagonalMatchingField(ZZ) := Ln -> ( M := matrix for i from 0 to Ln - 2 list for j from 0 to Ln - 1 list i*(Ln - j); @@ -426,399 +435,229 @@ diagonalMatchingField(ZZ) := Ln -> ( -- MonomialOrder => "default" or "none" setupMatchingFieldRings = method( Options => { - MonomialOrder => "default" - } + MonomialOrder => "default" + } ) -setupMatchingFieldRings(GrMatchingField) := opts -> MF -> ( - local monomialOrder; - if not MF.cache.?ringP then ( - p := symbol p; - variables := for s in subsets(toList(1 .. MF.n), MF.k) list p_(if #s == 1 then s_0 else toSequence s); - if opts.MonomialOrder == "default" then ( - monomialOrder = ( - maxVal := max (getWeightPluecker MF); - {Weights => for val in (getWeightPluecker MF) list maxVal - val} - ); - ) else if opts.MonomialOrder == "none" then ( - monomialOrder = GRevLex; - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - MF.cache.ringP = QQ[variables, MonomialOrder => monomialOrder]; - ); - if not MF.cache.?ringX then ( - x := symbol x; - if opts.MonomialOrder == "default" then ( - monomialOrder = ( - weights := for wRow in entries getWeightMatrix MF list ( - bigVal := toList(MF.n : (max wRow)); - bigVal - wRow - ); - {Weights => flatten weights} - ); - ) else if opts.MonomialOrder == "none" then ( - monomialOrder = GRevLex; - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - MF.cache.ringX = QQ[x_(1,1) .. x_(MF.k, MF.n), MonomialOrder => monomialOrder]; - ); - if not MF.cache.?X then ( - MF.cache.X = transpose genericMatrix(MF.cache.ringX, MF.n, MF.k); - ); - ) - -setupMatchingFieldRings(FlMatchingField) := opts -> MF -> ( + +setupMatchingFieldRings MatchingField := opts -> MF -> ( local monomialOrder; if not MF.cache.?ringP then ( - p := symbol p; - variables := flatten for Lk in MF.kList list for s in subsets(toList(1 .. MF.n), Lk) list p_(if #s == 1 then s_0 else toSequence s); - if opts.MonomialOrder == "default" then ( - monomialOrder = ( - bigVals := flatten ( - currentIndex := 0; - for grMF in MF.grMatchingFieldList list ( - numberEntries := binomial(grMF.n, grMF.k); - currentIndex = currentIndex + numberEntries; - toList(numberEntries : max ((getWeightPluecker MF)_{currentIndex - numberEntries .. currentIndex - 1})) - ) - ); - {Weights => (bigVals - (getWeightPluecker MF))} - ); - ) else if opts.MonomialOrder == "none" then ( - monomialOrder = GRevLex; - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - MF.cache.ringP = QQ[variables, MonomialOrder => monomialOrder]; - ); + p := getSymbol "p"; + variables := for tuple in MF.tuples list p_(if #tuple == 1 then tuple_0 else toSequence sort tuple); + if opts.MonomialOrder == "default" then ( + monomialOrder = ( + maxVal := max (getWeightPluecker MF); + {Weights => for val in (getWeightPluecker MF) list maxVal - val} + ); + ) else if opts.MonomialOrder == "none" then ( + monomialOrder = GRevLex; + ) else ( + error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); + ); + MF.cache.ringP = QQ[variables, MonomialOrder => monomialOrder]; + ); if not MF.cache.?ringX then ( - x := symbol x; - if opts.MonomialOrder == "default" then ( - monomialOrder = ( - weights := for wRow in entries getWeightMatrix MF list ( - bigVal := toList(MF.n : (max wRow)); - bigVal - wRow - ); - {Weights => flatten weights} - ); - ) else if opts.MonomialOrder == "non" then ( - monomialOrder = GRevLex; - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - MF.cache.ringX = QQ[x_(1,1) .. x_(max MF.kList, MF.n), MonomialOrder => monomialOrder]; - ); - if not MF.cache.?X then ( - MF.cache.X = transpose genericMatrix(MF.cache.ringX, MF.n, max MF.kList); - ); + x := getSymbol "x"; + if opts.MonomialOrder == "default" then ( + monomialOrder = ( + weights := for wRow in entries getWeightMatrix MF list ( + bigVal := toList(MF.tupleMaxValue : (max wRow)); + bigVal - wRow + ); + {Weights => flatten weights} + ); + ) else if opts.MonomialOrder == "none" then ( + monomialOrder = GRevLex; + ) else ( + error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); + ); + MF.cache.ringX = QQ[x_(1,1) .. x_(MF.tupleMaxSize, MF.tupleMaxValue), MonomialOrder => monomialOrder]; + ); + if not MF.cache.?XVarMatrix then ( + MF.cache.XVarMatrix = transpose genericMatrix(MF.cache.ringX, MF.tupleMaxValue, MF.tupleMaxSize); + ); ) - -- gets the sign of a tuple -- which is (-1) to power the number of descets modulo 2 -- 1 means even tuple, -1 means odd tuple tupleSign = method() tupleSign(List) := I -> ( if #I <= 1 then 1 else ( - (-1)^((sum for s in subsets(I, 2) list if s_0 > s_1 then 1 else 0) % 2) - ) + if even length select(subsets(I, 2), s -> s_0 > s_1) then 1 else -1 + ) ) -- matching field ring map: P_I -> x_(1,I_1) * x_(2, I_2) ... x_(k, I_k), for each tuple I matchingFieldRingMap = method( Options => { - MonomialOrder => "default" -- monomial order for the ambient rings (default or none) - } - ) -matchingFieldRingMap(GrMatchingField) := opts -> MF -> ( - setupMatchingFieldRings(opts, MF); - if not MF.cache.?mfRingMap then ( - R := MF.cache.ringP; - S := MF.cache.ringX; - MF.cache.mfRingMap = map(S, R, - for tuple in MF.tuples list tupleSign(tuple) * (product for i from 0 to MF.k - 1 list (MF.cache.X)_(i, tuple_i - 1)) - ); - ); - MF.cache.mfRingMap + MonomialOrder => "default" -- monomial order for the ambient rings (default or none) + } ) -matchingFieldRingMap(FlMatchingField) := opts -> MF -> ( +matchingFieldRingMap MatchingField := opts -> MF -> ( setupMatchingFieldRings(opts, MF); if not MF.cache.?mfRingMap then ( - R := MF.cache.ringP; - S := MF.cache.ringX; - MF.cache.mfRingMap = map(S, R, - flatten for grMF in MF.grMatchingFieldList list ( - for tuple in grMF.tuples list tupleSign(tuple) * (product for i from 0 to grMF.k - 1 list (MF.cache.X)_(i, tuple_i - 1)) - ) - ); - ); + R := MF.cache.ringP; + S := MF.cache.ringX; + MF.cache.mfRingMap = map(S, R, + for tuple in MF.tuples list tupleSign(tuple) * (product for i from 0 to #tuple - 1 list (MF.cache.XVarMatrix)_(i, tuple_i - 1)) + ); + ); MF.cache.mfRingMap - ) + ) -- matching field ideal -- compute using M2 or FourTiTwo methods matchingFieldIdeal = method( Options => { - Strategy => "4ti2", -- "FourTiTwo" or "M2" - MonomialOrder => "default" -- monomial order for ambient rings (default or none) - } - ) -matchingFieldIdeal(GrMatchingField) := opts -> MF -> ( - -- setting up MF rings is done by grMatchingFieldRingMap if necessary - if not MF.cache.?mfIdeal then ( - if opts.Strategy == "M2" then ( - MF.cache.mfIdeal = kernel matchingFieldRingMap(MF, MonomialOrder => opts.MonomialOrder); - ) - else if opts.Strategy == "4ti2" then ( - local gensMatrix; - setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); - V := matchingFieldPolytopePoints MF; - if opts.MonomialOrder == "default" then ( - gensMatrix = gens toricGroebner(V, MF.cache.ringP, Weights => getWeightPluecker MF); - ) else if opts.MonomialOrder == "none" then ( - gensMatrix = gens toricGroebner(V, MF.cache.ringP); - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - -- adjust the signs of the variables - signChange := map(MF.cache.ringP, MF.cache.ringP, matrix { - for i from 0 to #MF.tuples - 1 list (tupleSign (MF.tuples)_i)*(MF.cache.ringP)_i - }); - gensMatrix = signChange gensMatrix; - MF.cache.mfIdeal = ideal gensMatrix; - -- sometimes the Weights might not work (depends on 4ti2 version) see docs - forceGB gens MF.cache.mfIdeal; - ) - else ( - error("unknown Strategy: " | toString opts.Strategy | " for matchingFieldIdeal"); - ); - ); - MF.cache.mfIdeal + Strategy => "4ti2", -- "FourTiTwo" or "M2" + MonomialOrder => "default" -- monomial order for ambient rings (default or none) + } ) -matchingFieldIdeal(FlMatchingField) := opts -> MF -> ( +matchingFieldIdeal MatchingField := opts -> MF -> ( -- setting up MF rings is done by grMatchingFieldRingMap if necessary if not MF.cache.?mfIdeal then ( - if opts.Strategy == "M2" then ( - MF.cache.mfIdeal = kernel matchingFieldRingMap(MF, MonomialOrder => opts.MonomialOrder); - ) - else if opts.Strategy == "4ti2" then ( - local gensMatrix; - setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); - VList := for grMF in MF.grMatchingFieldList list ( - matchingFieldPolytopePoints(grMF, ExtraZeroRows => (max MF.kList - grMF.k)) - ); - V := fold(VList, (V1, V2) -> V1 | V2); - if opts.MonomialOrder == "default" then ( - gensMatrix = gens toricGroebner(V, MF.cache.ringP, Weights => getWeightPluecker MF); - ) else if opts.MonomialOrder == "none" then ( - gensMatrix = gens toricGroebner(V, MF.cache.ringP); - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - -- adjust the signs of the variables - signChange := map(MF.cache.ringP, MF.cache.ringP, matrix { - variableIndex := -1; - flatten for grMF in MF.grMatchingFieldList list ( - for i from 0 to #grMF.tuples - 1 list ( - variableIndex = variableIndex + 1; - (tupleSign (grMF.tuples)_i)*(MF.cache.ringP)_variableIndex - ) - ) - }); - gensMatrix = signChange gensMatrix; - MF.cache.mfIdeal = ideal gensMatrix; - -- sometimes the Weights might not work (depends on 4ti2 version) see docs - forceGB gens MF.cache.mfIdeal; - ) - else ( - error("unknown Strategy" | toString opts.Strategy | " for matchingFieldIdeal"); - ); - ); + if opts.Strategy == "M2" then ( + MF.cache.mfIdeal = kernel matchingFieldRingMap(MF, MonomialOrder => opts.MonomialOrder); + ) + else if opts.Strategy == "4ti2" then ( + local gensMatrix; + setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); + monomialList := first entries matrix matchingFieldRingMap MF; + V := transpose matrix flatten (monomialList / exponents); + if opts.MonomialOrder == "default" then ( + gensMatrix = gens toricGroebner(V, MF.cache.ringP, Weights => getWeightPluecker MF); + ) else if opts.MonomialOrder == "none" then ( + gensMatrix = gens toricGroebner(V, MF.cache.ringP); + ) else ( + error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); + ); + -- adjust the signs of the variables + signChange := map(MF.cache.ringP, MF.cache.ringP, matrix { + for i from 0 to #MF.tuples - 1 list (tupleSign (MF.tuples)_i)*(MF.cache.ringP)_i + }); + gensMatrix = signChange gensMatrix; + MF.cache.mfIdeal = ideal gensMatrix; + -- sometimes the Weights might not work (depends on 4ti2 version) see docs + forceGB gens MF.cache.mfIdeal; + ) + else ( + error("unknown Strategy: " | toString opts.Strategy | " for matchingFieldIdeal"); + ); + ); MF.cache.mfIdeal ) ---------------------------------------------------- --- projective matching field ideal (unexported) --- this is the ideal obtained by composing the --- matching field rings map and the segre embedding --- --- Note: the signs are not correct --- 4ti2 Strategy only --- assumes that the weight matrices of the grMatchingFields --- are sub weight matrices of the flMatchingField - -projectiveMatchingFieldIdeal = method( - Options => { - Strategy => "4ti2", -- "FourTiTwo" - MonomialOrder => "default" -- monomial order for ambient rings (default or none) - } - ) - -projectiveMatchingFieldIdeal(GrMatchingField) := opts -> MF -> ( - matchingFieldIdeal(opts, MF) - ) - -projectiveMatchingFieldIdeal(FlMatchingField) := opts -> MF -> ( - if not MF.cache.?projMFIdeal then ( - local monomialOrder; - if opts.MonomialOrder == "none" then ( - monomialOrder = GRevLex; - ) else if opts.MonomialOrder == "default" then ( - plueckerWeights := for grMF in MF.grMatchingFieldList list ( - plueckerWeight := getWeightPluecker grMF; - maxWeight := max plueckerWeight; - for weight in plueckerWeight list maxWeight - weight - ); - monomialOrder = (Weights => fold(plueckerWeights, (W1, W2) -> flatten for w1 in W1 list for w2 in W2 list w1+w2)); - ) else ( - error("Unknown option: MonomialOrder => " | toString opts.MonomialOrder); - ); - p := symbol p; - subsetList := for k in MF.kList list subsets(1 .. MF.n, k); - variableIndices := fold(subsetList, (S1, S2) -> flatten for s1 in S1 list for s2 in S2 list s1 | s2); - R := QQ[for variableIndex in variableIndices list p_(toSequence variableIndex), MonomialOrder => monomialOrder]; - if opts.Strategy == "4ti2" then ( - VList := for grMF in MF.grMatchingFieldList list ( - pointsMatrix := matchingFieldPolytopePoints(grMF, ExtraZeroRows => (max MF.kList - grMF.k)); - for columnIndex from 0 to numColumns pointsMatrix -1 list pointsMatrix_{columnIndex} - ); - VCols := fold(VList, (V1, V2) -> flatten for v1 in V1 list for v2 in V2 list v1+v2); - V := fold(VCols, (col1, col2) -> col1 | col2); - gensMatrix := gens toricGroebner(V, R); - MF.cache.projMFIdeal = ideal gensMatrix; - forceGB gens MF.cache.projMFIdeal; - ) else ( - error("Unknown option: Strategy => " | toString opts.Strategy); - ); - ); - MF.cache.projMFIdeal - ) - - -- Grassmannian ideal using the constructed pluecker variable ring -- Sets the weight of the polynomial ring to be the MF pluecker weight --- --- the matching field must be coherent for this shortcut to work -Grassmannian(GrMatchingField) := opts -> MF -> ( +-- +-- the matching field must be coherent for this shortcut to work +Grassmannian GrMatchingField := opts -> MF -> ( plueckerIdeal MF ) plueckerIdeal = method( Options => { - MonomialOrder => "default" -- default or none; see setupMatchingFieldRings(.., MonomialOrder => [option]) - } + MonomialOrder => "default" -- default or none; see setupMatchingFieldRings(.., MonomialOrder => [option]) + } ) -plueckerIdeal(GrMatchingField) := opts -> MF -> ( +plueckerIdeal GrMatchingField := opts -> MF -> ( if not MF.cache.?mfPlueckerIdeal then ( - setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); - R := MF.cache.ringP; - MF.cache.mfPlueckerIdeal = Grassmannian(MF.k - 1, MF.n - 1, R); - ); + setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); + R := MF.cache.ringP; + MF.cache.mfPlueckerIdeal = Grassmannian(MF.tupleMaxSize - 1, MF.tupleMaxValue - 1, R); + ); MF.cache.mfPlueckerIdeal ) -plueckerIdeal(FlMatchingField) := opts -> MF -> ( +plueckerIdeal FlMatchingField := opts -> MF -> ( if not MF.cache.?mfPlueckerIdeal then ( - setupMatchingFieldRings(opts, MF); - local i; - local variableFromSubset; - local generatorList; - i = 0; - variableFromSubset = new HashTable from flatten ( - varsMatrix := vars MF.cache.ringP; - for grMF in MF.grMatchingFieldList list ( - for s in subsets(toList(1 .. grMF.n), grMF.k) list ( - i = i + 1; - s => varsMatrix_(0, i-1) - ) - ) - ); - -------------------------------- - -- Grassmannian relations - -- - generatorList = flatten for grMF in MF.grMatchingFieldList list ( - if grMF.k >= 2 and grMF.n - grMF.k >= 2 then ( - flatten for I in subsets(1 .. grMF.n, grMF.k - 1) list ( - for J in subsets(1 .. grMF.n, grMF.k + 1) list ( - newGenerator := sum for jPosition from 0 to #J - 1 list ( - j := J_jPosition; - if not member(j, I) then ( - IIndex := sort(I | {j}); - JIndex := delete(j, J); - swapsToSortI := # for i in I list if i > j then i else continue; - pI := variableFromSubset#IIndex; - pJ := variableFromSubset#JIndex; - (-1)^(jPosition + swapsToSortI)*pI*pJ - ) else continue - ); - if not zero(newGenerator) then ( - newGenerator - ) else continue - ) - ) - ) else continue - ); - --------------------- - -- Incident Relations - -- - generatorList = generatorList | flatten for grMFs in subsets(MF.grMatchingFieldList, 2) list ( - grMF0 := grMFs_0; - grMF1 := grMFs_1; - flatten for I in subsets(1 .. grMF0.n, grMF0.k - 1) list ( - for J in subsets(1 .. grMF1.n, grMF1.k + 1) list ( - sum for jPosition from 0 to #J - 1 list ( - j := J_jPosition; - if not member(j, I) then ( - IIndex := sort(I | {j}); - JIndex := delete(j, J); - swapsToSortI := # for i in I list if i > j then i else continue; - pI := variableFromSubset#IIndex; - pJ := variableFromSubset#JIndex; - (-1)^(jPosition + swapsToSortI)*pI*pJ - ) else continue - ) - ) - ) - ); - MF.cache.mfPlueckerIdeal = ideal(generatorList); - ); + setupMatchingFieldRings(opts, MF); + local i; + local variableFromSubset; + local generatorList; + i = 0; + variableFromSubset = new HashTable from flatten ( + varsMatrix := vars MF.cache.ringP; + for grMF in getGrMatchingFields MF list ( + for s in subsets(toList(1 .. grMF.tupleMaxValue), grMF.tupleMaxSize) list ( + i = i + 1; + s => varsMatrix_(0, i-1) + ) + ) + ); + -------------------------------- + -- Grassmannian relations + -- + generatorList = flatten for grMF in getGrMatchingFields MF list ( + if grMF.tupleMaxSize >= 2 and grMF.tupleMaxValue - grMF.tupleMaxSize >= 2 then ( + flatten for I in subsets(1 .. grMF.tupleMaxValue, grMF.tupleMaxSize - 1) list ( + for J in subsets(1 .. grMF.tupleMaxValue, grMF.tupleMaxSize + 1) list ( + newGenerator := sum for jPosition from 0 to #J - 1 list ( + j := J_jPosition; + if not member(j, I) then ( + IIndex := sort(I | {j}); + JIndex := delete(j, J); + swapsToSortI := # for i in I list if i > j then i else continue; + pI := variableFromSubset#IIndex; + pJ := variableFromSubset#JIndex; + (-1)^(jPosition + swapsToSortI)*pI*pJ + ) else continue + ); + if not zero(newGenerator) then ( + newGenerator + ) else continue + ) + ) + ) else continue + ); + --------------------- + -- Incident Relations + -- + generatorList = generatorList | flatten for grMFs in subsets(getGrMatchingFields MF, 2) list ( + grMF0 := grMFs_0; + grMF1 := grMFs_1; + flatten for I in subsets(1 .. grMF0.tupleMaxValue, grMF0.tupleMaxSize - 1) list ( + for J in subsets(1 .. grMF1.tupleMaxValue, grMF1.tupleMaxSize + 1) list ( + sum for jPosition from 0 to #J - 1 list ( + j := J_jPosition; + if not member(j, I) then ( + IIndex := sort(I | {j}); + JIndex := delete(j, J); + swapsToSortI := # for i in I list if i > j then i else continue; + pI := variableFromSubset#IIndex; + pJ := variableFromSubset#JIndex; + (-1)^(jPosition + swapsToSortI)*pI*pJ + ) else continue + ) + ) + ) + ); + MF.cache.mfPlueckerIdeal = ideal(generatorList); + ); MF.cache.mfPlueckerIdeal ) ---------------- -- Pluecker map is the determinantal map associated to Grassmannian / Flag variety --- its kernel coincides with the pluecker ideal defined above +-- its kernel coincides with the pluecker ideal defined above plueckerMap = method( Options => { - MonomialOrder => "default" -- default or none; see setupMatchingFieldRings(.., MonomialOrder => [option]) - } - ) -plueckerMap(GrMatchingField) := opts -> MF -> ( - setupMatchingFieldRings(opts, MF); - if not MF.cache.?plueckerRingMap then ( - R := MF.cache.ringP; - S := MF.cache.ringX; - matX := MF.cache.X; - MF.cache.plueckerRingMap = map(S, R, for s in subsets(MF.n, MF.k) list det(matX_s)); - ); - MF.cache.plueckerRingMap + MonomialOrder => "default" -- default or none; see setupMatchingFieldRings(.., MonomialOrder => [option]) + } ) - -plueckerMap(FlMatchingField) := opts -> MF -> ( +plueckerMap MatchingField := opts -> MF -> ( setupMatchingFieldRings(opts, MF); if not MF.cache.?plueckerRingMap then ( - R := MF.cache.ringP; - S := MF.cache.ringX; - matX := MF.cache.X; - MF.cache.plueckerRingMap = map(S, R, flatten for grMF in MF.grMatchingFieldList list ( - for s in subsets(grMF.n, grMF.k) list det(matX_s^(toList(0 .. grMF.k - 1)))) - ); - ); + R := MF.cache.ringP; + S := MF.cache.ringX; + matX := MF.cache.XVarMatrix; + MF.cache.plueckerRingMap = map(S, R, for tuple in MF.tuples list det(matX_(apply(sort tuple, i -> i - 1))^(toList(0 .. #tuple - 1)))); + ); MF.cache.plueckerRingMap ) @@ -826,78 +665,80 @@ plueckerMap(FlMatchingField) := opts -> MF -> ( ---------------------------------- -- matching field from permutation -- Fix a permutation S, a 'generic' weight matrix M --- that induces the diagonal matching field +-- that induces the diagonal matching field -- Permute the 2nd row of M using S -- See the paper: Clarke-Mohammadi-Zaffalon 2022 -- matchingFieldFromPermutation = method( Options => { - RowNum => 2, -- which row to permute - UsePrimePowers => false, -- Take N (in the definition of the weight matrix) to be a prime number - ScalingCoefficient => 1, -- scale the permuted row by this coefficient, if > 2 then matrix may be non-generic unless prime power is true - PowerValue => 0 -- Value of N in weight matrix, if supplied 0 then choose N to be n or nextPrime n depending on above options - }) + RowNum => 2, -- which row to permute + UsePrimePowers => false, -- Take N (in the definition of the weight matrix) to be a prime number + ScalingCoefficient => 1, -- scale the permuted row by this coefficient, if > 2 then matrix may be non-generic unless prime power is true + PowerValue => 0 -- Value of N in weight matrix, if supplied 0 then choose N to be n or nextPrime n depending on above options + }) matchingFieldFromPermutation(ZZ, ZZ, List) := opts -> (Lk, Ln, S) -> ( if # S != Ln or # set S < Ln then ( - error("expected a permutation of " | toString Ln | " distinct values"); - ); + error("expected a permutation of " | toString Ln | " distinct values"); + ); if opts.ScalingCoefficient == 1 then ( - matchingFieldFromPermutationNoScaling(Lk, Ln, S, opts) - ) else ( - local N; - local M; - local W; - if opts.PowerValue > 0 then ( - N = opts.PowerValue; - ) else if opts.UsePrimePowers then ( - N = nextPrime Ln; - ) else ( - N = Ln - ); - if Lk == 1 then ( - M = matrix {toList {Ln : 0}}; - ) else ( - M = matrix {toList{Ln : 0}} || matrix for i from 1 to Lk - 1 list for j from 1 to Ln list ( - if i + 1 == opts.RowNum then ( - (S_(j - 1))*opts.ScalingCoefficient*N^(i - 1) - ) else ( - (Ln - j)*N^(i - 1) - ) - ); - ); - grMatchingField M - ) + matchingFieldFromPermutationNoScaling(Lk, Ln, S, opts) + ) else ( + local N; + local M; + local W; + if opts.PowerValue > 0 then ( + N = opts.PowerValue; + ) else if opts.UsePrimePowers then ( + N = nextPrime Ln; + ) else ( + N = Ln + ); + if Lk == 1 then ( + M = matrix {toList {Ln : 0}}; + ) else ( + M = matrix {toList{Ln : 0}} || matrix for i from 1 to Lk - 1 list for j from 1 to Ln list ( + if i + 1 == opts.RowNum then ( + (S_(j - 1))*opts.ScalingCoefficient*N^(i - 1) + ) else ( + (Ln - j)*N^(i - 1) + ) + ); + ); + grMatchingField M + ) ); -- The Flag matching field from permuting the second row matchingFieldFromPermutation(List, ZZ, List) := opts -> (LkList, Ln, S) -> ( if # S != Ln or # set S < Ln then ( - error("expected a permutation of " | toString Ln | " distinct values"); - ); + error("expected a permutation of " | toString Ln | " distinct values"); + ); sortedLkList := sort LkList; grMatchingFields := for Lk in sortedLkList list matchingFieldFromPermutation(Lk, Ln, S, opts); lastGrMatchingField := grMatchingFields_(#sortedLkList - 1); new FlMatchingField from { - n => Ln, - kList => sortedLkList, - grMatchingFieldList => grMatchingFields, - cache => new CacheTable from { - weightMatrix => lastGrMatchingField.cache.weightMatrix, - weightPluecker => flatten for grMF in grMatchingFields list grMF.cache.weightPluecker - } - } + tupleMaxValue => Ln, + tupleSizeList => sortedLkList, + tupleMaxSize => last sortedLkList, + tuples => flatten for grMF in grMatchingFields list grMF.tuples, + cache => new CacheTable from { + grMatchingFieldList => grMatchingFields, + weightMatrix => lastGrMatchingField.cache.weightMatrix, + weightPluecker => flatten for grMF in grMatchingFields list grMF.cache.weightPluecker + } + } ); --- matching field from permutation +-- matching field from permutations -- assume that there is no scaling coefficient so the tuples of the matching field can be written down quickly -- unexported method (used by matchingFieldFromPermutation) matchingFieldFromPermutationNoScaling = method( Options => { - RowNum => 2, -- which row to permute - UsePrimePowers => false, -- Take N (in the definition of the weight matrix) to be a prime number - ScalingCoefficient => 1, -- scale the permuted row by this coefficient, if > 2 then matrix may be non-generic unless prime power is true - PowerValue => 0 -- Value of N in weight matrix, if supplied 0 then choose N to be n or nextPrime n depending on above options - }) + RowNum => 2, -- which row to permute + UsePrimePowers => false, -- Take N (in the definition of the weight matrix) to be a prime number + ScalingCoefficient => 1, -- scale the permuted row by this coefficient, if > 2 then matrix may be non-generic unless prime power is true + PowerValue => 0 -- Value of N in weight matrix, if supplied 0 then choose N to be n or nextPrime n depending on above options + }) matchingFieldFromPermutationNoScaling(ZZ, ZZ, List) := opts -> (Lk, Ln, S) -> ( local IOrdered; local minIndex; @@ -906,57 +747,58 @@ matchingFieldFromPermutationNoScaling(ZZ, ZZ, List) := opts -> (Lk, Ln, S) -> ( local W; L := {}; for I in subsets(Ln, Lk) do ( - if opts.RowNum <= Lk then ( - -- find i in 0 .. rowNum-1 such that S_(I_i) is minimum - minIndex = 0; - for i from 1 to opts.RowNum - 1 do ( - if S_(I_i) < S_(I_minIndex) then ( - minIndex = i; - ); - ); - -- The elements I_0 .. I_(minIndex-1) are ordered in increasing order - IOrdered = for i from 0 to minIndex-1 list I_i + 1; - -- The next elements are I_(minIndex+1) .. I_(rowNum-1) - IOrdered = IOrdered | for i from minIndex+1 to opts.RowNum-1 list I_i + 1; - -- Then we get I_minIndex - IOrdered = append(IOrdered, I_minIndex + 1); - -- Then the rest of I in order - IOrdered = IOrdered | for i from opts.RowNum to Lk - 1 list I_i + 1; - L = append(L, IOrdered); - ) else ( - L = append(L, apply(I, i -> i+1)); - ); - ); + if opts.RowNum <= Lk then ( + -- find i in 0 .. rowNum-1 such that S_(I_i) is minimum + minIndex = 0; + for i from 1 to opts.RowNum - 1 do ( + if S_(I_i) < S_(I_minIndex) then ( + minIndex = i; + ); + ); + -- The elements I_0 .. I_(minIndex-1) are ordered in increasing order + IOrdered = for i from 0 to minIndex-1 list I_i + 1; + -- The next elements are I_(minIndex+1) .. I_(rowNum-1) + IOrdered = IOrdered | for i from minIndex+1 to opts.RowNum-1 list I_i + 1; + -- Then we get I_minIndex + IOrdered = append(IOrdered, I_minIndex + 1); + -- Then the rest of I in order + IOrdered = IOrdered | for i from opts.RowNum to Lk - 1 list I_i + 1; + L = append(L, IOrdered); + ) else ( + L = append(L, apply(I, i -> i+1)); + ); + ); if opts.PowerValue > 0 then ( - N = opts.PowerValue; - ) else if opts.UsePrimePowers then ( - N = nextPrime Ln; - ) else ( - N = Ln - ); + N = opts.PowerValue; + ) else if opts.UsePrimePowers then ( + N = nextPrime Ln; + ) else ( + N = Ln + ); if Lk == 1 then ( - M = matrix {toList {Ln : 0}}; - ) else ( - M = matrix {toList{Ln : 0}} || matrix for i from 1 to Lk - 1 list for j from 1 to Ln list ( - if i + 1 == opts.RowNum then ( - (S_(j - 1))*N^(i - 1) - ) else ( - (Ln - j)*N^(i - 1) - ) - ); - ); + M = matrix {toList {Ln : 0}}; + ) else ( + M = matrix {toList{Ln : 0}} || matrix for i from 1 to Lk - 1 list for j from 1 to Ln list ( + if i + 1 == opts.RowNum then ( + (S_(j - 1))*N^(i - 1) + ) else ( + (Ln - j)*N^(i - 1) + ) + ); + ); W = for I in L list ( - sum for i from 0 to Lk - 1 list M_(i, I_i - 1) - ); + sum for i from 0 to Lk - 1 list M_(i, I_i - 1) + ); new GrMatchingField from { - n => Ln, - k => Lk, - tuples => L, - cache => new CacheTable from { - weightMatrix => M, - weightPluecker => W - } - } + tupleMaxValue => Ln, + tupleMaxSize => Lk, + tupleSizeList => {Lk}, + tuples => L, + cache => new CacheTable from { + weightMatrix => M, + weightPluecker => W + } + } ); @@ -982,15 +824,7 @@ matchingFieldFromPermutationNoScaling(ZZ, ZZ, List) := opts -> (Lk, Ln, S) -> ( -- so we can forgo the while loop isToricDegeneration = method () -isToricDegeneration(GrMatchingField) := MF -> ( - matchingFieldIdealGens := gens matchingFieldIdeal MF; - maxDegree := max flatten flatten degrees matchingFieldIdealGens; - plueckerGB := gb(plueckerIdeal MF, Algorithm => Homogeneous, DegreeLimit => maxDegree); - inPluecker := forceGB leadTerm(1, gens plueckerGB); - zero(matchingFieldIdealGens % inPluecker) - ) - -isToricDegeneration(FlMatchingField) := MF -> ( +isToricDegeneration MatchingField := MF -> ( matchingFieldIdealGens := gens matchingFieldIdeal MF; maxDegree := max flatten flatten degrees matchingFieldIdealGens; plueckerGB := gb(plueckerIdeal MF, Algorithm => Homogeneous, DegreeLimit => maxDegree); @@ -1003,98 +837,86 @@ isToricDegeneration(FlMatchingField) := MF -> ( -- in older versions this method overloaded the method subring from the package SubalgebraBases -- the pluecker algebra inside inside a ring with term order -- given by the weightMatrix --- +-- -- MonomialOrder is the monomial order of the ambient rings; see setupMatchingFieldRings(.., MonomialOrder => [option]) plueckerAlgebra = method( Options => { - MonomialOrder => "default" -- default or none - } - ) -plueckerAlgebra(GrMatchingField) := opts -> MF -> ( - if not MF.cache.?mfSubring then ( - setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); - matX := MF.cache.X; - MF.cache.mfSubring = subring for s in subsets(MF.n, MF.k) list det(matX_s); - ); - MF.cache.mfSubring + MonomialOrder => "default" -- default or none + } ) - -plueckerAlgebra(FlMatchingField) := opts -> MF -> ( +plueckerAlgebra MatchingField := opts -> MF -> ( if not MF.cache.?mfSubring then ( - setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); - matX := MF.cache.X; - MF.cache.mfSubring = subring flatten for grMF in MF.grMatchingFieldList list ( - for s in subsets(grMF.n, grMF.k) list det(matX_s^(toList(0 .. grMF.k - 1))) - ); - ); + setupMatchingFieldRings(MF, MonomialOrder => opts.MonomialOrder); + matX := MF.cache.XVarMatrix; + MF.cache.mfSubring = subring for tuple in MF.tuples list det(matX_(apply(sort tuple, i -> i - 1))^(toList(0 .. #tuple - 1))); + ); MF.cache.mfSubring ) --------------------------------------------- -- Newton-Okounkov body for a matching field --- +-- NOBody = method() --- Note: it is okay to always set +-- Note: it is okay to always set -- sagbi( .. AutoSubduce => false .. ) -- -- Grassmannian matching fields -- the NO body has vertices that are directly read from the initial algebra (using Sagbi basis) -- So, the lead terms can be simply scaled and the NO body is the convex hull of exponent vectors -NOBody(GrMatchingField) := MF -> ( - if not MF.cache.?mfNOBody then ( - -- compute the initial algebra of the Pluecker algebra wrt the weight term order - initialAlgebraGens := first entries leadTerm subalgebraBasis(plueckerAlgebra MF, AutoSubduce => false); - generatorExponents := apply(initialAlgebraGens, f -> (exponents(f))_0); - NOBodyVertices := apply(generatorExponents, v -> ((MF.k) / sum(v))*v); -- normalize the vertices - MF.cache.mfNOBody = convexHull transpose matrix NOBodyVertices; - ); +NOBody GrMatchingField := MF -> ( + if not MF.cache.?mfNOBody then ( + -- compute the initial algebra of the Pluecker algebra wrt the weight term order + initialAlgebraGens := first entries leadTerm subalgebraBasis(plueckerAlgebra MF, AutoSubduce => false); + generatorExponents := apply(initialAlgebraGens, f -> (exponents(f))_0); + NOBodyVertices := apply(generatorExponents, v -> ((MF.tupleMaxSize) / sum(v))*v); -- normalize the vertices + MF.cache.mfNOBody = convexHull transpose matrix NOBodyVertices; + ); MF.cache.mfNOBody ) -- Flag matching fields -- each Pluecker form has a specific grading -- the sagbi generators are lifted by their grading --- and we compute the NO body by taking the slice of the cone +-- and we compute the NO body by taking the slice of the cone -- that corresponds to the grading (1 .. 1) -NOBody(FlMatchingField) := MF -> ( +NOBody FlMatchingField := MF -> ( if not MF.cache.?mfNOBody then ( - kmax := max MF.kList; - initialAlgebraGens := first entries leadTerm subalgebraBasis(plueckerAlgebra MF, AutoSubduce => false); - generatorExponents := matrix apply(initialAlgebraGens, f -> (exponents(f))_0); - gradingMap := matrix for gradingRow from 0 to #MF.kList -1 list( - flatten for kIndex from 0 to kmax-1 list ( - if MF.kList_gradingRow - 1 == kIndex then ( - toList(MF.n : 1) - ) else if MF.kList_gradingRow == kIndex then ( - toList(MF.n : -1) - ) else ( - toList(MF.n : 0) - ) - ) - ); - coneRays := (gradingMap * transpose generatorExponents) || (transpose generatorExponents); - polyCone := coneFromVData coneRays; - -- take the part of the cone with grading (1 .. 1) - slice := polyhedronFromHData ( - matrix {toList(#MF.kList + kmax * MF.n : 0)}, - matrix {{0}}, - id_(ZZ^(#MF.kList)) | matrix for row in MF.kList list for col from 1 to (MF.n * kmax) list 0, - transpose matrix {toList(#MF.kList : 1)} - ); - NOBodyAsIntersection := intersection(polyCone, slice); - -- simplify by removing the grading part - NOBodyVertices := (vertices NOBodyAsIntersection)^{#MF.kList .. #MF.kList + MF.n * kmax - 1}; - MF.cache.mfNOBody = convexHull NOBodyVertices; - ); + initialAlgebraGens := first entries leadTerm subalgebraBasis(plueckerAlgebra MF, AutoSubduce => false); + generatorExponents := matrix apply(initialAlgebraGens, f -> (exponents(f))_0); + gradingMap := matrix for gradingRow from 0 to #MF.tupleSizeList -1 list( + flatten for kIndex from 0 to MF.tupleMaxSize - 1 list ( + if MF.tupleSizeList_gradingRow - 1 == kIndex then ( + toList(MF.tupleMaxValue : 1) + ) else if MF.tupleSizeList_gradingRow == kIndex then ( + toList(MF.tupleMaxValue : -1) + ) else ( + toList(MF.tupleMaxValue : 0) + ) + ) + ); + coneRays := (gradingMap * transpose generatorExponents) || (transpose generatorExponents); + polyCone := coneFromVData coneRays; + -- take the part of the cone with grading (1 .. 1) + slice := polyhedronFromHData ( + matrix {toList(#MF.tupleSizeList + MF.tupleMaxSize * MF.tupleMaxValue : 0)}, + matrix {{0}}, + id_(ZZ^(#MF.tupleSizeList)) | matrix for row in MF.tupleSizeList list for col from 1 to (MF.tupleMaxValue * MF.tupleMaxSize) list 0, + transpose matrix {toList(#MF.tupleSizeList : 1)} + ); + NOBodyAsIntersection := intersection(polyCone, slice); + -- simplify by removing the grading part + NOBodyVertices := (vertices NOBodyAsIntersection)^{#MF.tupleSizeList .. #MF.tupleSizeList + MF.tupleMaxValue * MF.tupleMaxSize - 1}; + MF.cache.mfNOBody = convexHull NOBodyVertices; + ); MF.cache.mfNOBody ) ----------------------- -- Regular Subdivision of a set of points --- code is copied and modified from "Polyhedra" Package --- not exported +-- code is copied and modified from "Polyhedra" Package +-- not exported -- Why is code copied: -- >> See previous comment about the permutation of vertex names in Polyhedra package -- the same bug is present in the regular subdivision method @@ -1109,7 +931,6 @@ pointRegularSubdivision(Matrix, Matrix) := (points, weight) -> ( V := vertices P; apply (F, f -> V_(f#0)^(toList(0 .. (numRows points - 1)))) ) - --------------------------------- -- matroidal subdivision from matching field -- Take the Pluecker weight w of a matching field @@ -1125,10 +946,10 @@ matroidSubdivision(ZZ, ZZ, List) := (k, n, L) -> ( for piece in subdivisionPieces list for c from 0 to numColumns piece - 1 list vertexLookup#(piece_{c}) ) -matroidSubdivision(GrMatchingField) := MF -> ( +matroidSubdivision GrMatchingField := MF -> ( if not MF.cache.?computedMatroidSubdivision then ( - MF.cache.computedMatroidSubdivision = matroidSubdivision(MF.k, MF.n, getWeightPluecker MF); - ); + MF.cache.computedMatroidSubdivision = matroidSubdivision(MF.tupleMaxSize, MF.tupleMaxValue, getWeightPluecker MF); + ); MF.cache.computedMatroidSubdivision ) @@ -1138,61 +959,60 @@ matroidSubdivision(GrMatchingField) := MF -> ( -- the cone whose interior points are weight matrices that induce the given matching field weightMatrixCone = method( Options => { - ExtraZeroRows => 0 -- adds this many rows of 0 to each inequality, used for FlMatchingField cone - } + ExtraZeroRows => 0 -- adds this many rows of 0 to each inequality, used for FlMatchingField cone + } ) -weightMatrixCone(GrMatchingField) := opts -> MF -> ( +weightMatrixCone GrMatchingField := opts -> MF -> ( if opts.ExtraZeroRows == 0 and MF.cache.?computedWeightMatrixCone then ( - MF.cache.computedWeightMatrixCone - ) else ( - -- form the matrix of inequalities A: such that the cone is Ax >= 0 - local inequalities; - if MF.k > 1 then ( - inequalities = matrix ( - subsetList := subsets(1 .. MF.n, MF.k); - flatten for i from 0 to binomial(MF.n, MF.k) - 1 list ( - columnIndices := subsetList_i; - minimalTuple := MF.tuples_i; - for p in delete(minimalTuple, permutations columnIndices) list ( - -- the row vector the encodes: minimalTuple <= p - -- E.g. if the minimal tuple is {1,2,3} then - -- one of the inequalities is given by {1,2,3} <= {1,3,2} - -- which cancels down further since 1 is in the same place - for coord in (0,1) .. (MF.k - 1 + opts.ExtraZeroRows, MF.n) list ( - sum {if coord_0 < MF.k and p_(coord_0) == coord_1 then 1 else 0, - if coord_0 < MF.k and minimalTuple_(coord_0) == coord_1 then -1 else 0} - ) - ) - ) - ); - ) else ( - inequalities = matrix {toList((MF.k + opts.ExtraZeroRows) * MF.n : 0)}; - ); - C := coneFromHData(inequalities); - if opts.ExtraZeroRows == 0 then MF.cache.computedWeightMatrixCone = C; + MF.cache.computedWeightMatrixCone + ) else ( + -- form the matrix of inequalities A: such that the cone is Ax >= 0 + local inequalities; + if MF.tupleMaxSize > 1 then ( + inequalities = matrix ( + subsetList := subsets(1 .. MF.tupleMaxValue, MF.tupleMaxSize); + flatten for i from 0 to binomial(MF.tupleMaxValue, MF.tupleMaxSize) - 1 list ( + columnIndices := subsetList_i; + minimalTuple := MF.tuples_i; + for p in delete(minimalTuple, permutations columnIndices) list ( + -- the row vector the encodes: minimalTuple <= p + -- E.g. if the minimal tuple is {1,2,3} then + -- one of the inequalities is given by {1,2,3} <= {1,3,2} + -- which cancels down further since 1 is in the same place + for coord in (0,1) .. (MF.tupleMaxSize - 1 + opts.ExtraZeroRows, MF.tupleMaxValue) list ( + sum {if coord_0 < MF.tupleMaxSize and p_(coord_0) == coord_1 then 1 else 0, + if coord_0 < MF.tupleMaxSize and minimalTuple_(coord_0) == coord_1 then -1 else 0} + ) + ) + ) + ); + ) else ( + inequalities = matrix {toList((MF.tupleMaxSize + opts.ExtraZeroRows) * MF.tupleMaxValue : 0)}; + ); + C := coneFromHData(inequalities); + if opts.ExtraZeroRows == 0 then MF.cache.computedWeightMatrixCone = C; C - ) + ) ) -weightMatrixCone(FlMatchingField) := opts -> MF -> ( +weightMatrixCone FlMatchingField := opts -> MF -> ( if opts.ExtraZeroRows == 0 and MF.cache.?computedWeightMatrixCone then ( - MF.cache.computedWeightMatrixCone - ) else ( - kMax := max MF.kList; - weightMatrixConeList := for grMF in MF.grMatchingFieldList list ( - weightMatrixCone(grMF, ExtraZeroRows => (kMax - grMF.k + opts.ExtraZeroRows)) - ); - inequalityMatrix := facets (weightMatrixConeList_0); - hyperplanesMatrix := hyperplanes (weightMatrixConeList_0); - for i from 1 to #weightMatrixConeList - 1 do ( - inequalityMatrix = inequalityMatrix || facets (weightMatrixConeList_i); - hyperplanesMatrix = hyperplanesMatrix || hyperplanes (weightMatrixConeList_i); - ); - C := coneFromHData(inequalityMatrix, hyperplanesMatrix); - if opts.ExtraZeroRows == 0 then MF.cache. computedWeightMatrixCone = C; - C - ) + MF.cache.computedWeightMatrixCone + ) else ( + weightMatrixConeList := for grMF in getGrMatchingFields MF list ( + weightMatrixCone(grMF, ExtraZeroRows => (MF.tupleMaxSize - grMF.tupleMaxSize + opts.ExtraZeroRows)) + ); + inequalityMatrix := facets (weightMatrixConeList_0); + hyperplanesMatrix := hyperplanes (weightMatrixConeList_0); + for i from 1 to #weightMatrixConeList - 1 do ( + inequalityMatrix = inequalityMatrix || facets (weightMatrixConeList_i); + hyperplanesMatrix = hyperplanesMatrix || hyperplanes (weightMatrixConeList_i); + ); + C := coneFromHData(inequalityMatrix, hyperplanesMatrix); + if opts.ExtraZeroRows == 0 then MF.cache. computedWeightMatrixCone = C; + C + ) ) ---------------------------------------------------------- @@ -1200,17 +1020,11 @@ weightMatrixCone(FlMatchingField) := opts -> MF -> ( -- check if a matching field is induced by a weight matrix -- isCoherent = method() -isCoherent(GrMatchingField) := MF -> ( +isCoherent MatchingField := MF -> ( if MF.cache.?weightMatrix then true else ( - C := weightMatrixCone MF; - (dim C) == (MF.k * MF.n) -- coherent iff C is full-dimensional - ) - ) -isCoherent(FlMatchingField) := MF -> ( - if MF.cache.?weightMatrix then true else ( - C := weightMatrixCone MF; - (dim C) == ((max MF.kList)* MF.n) -- coherent iff C is full-dimensional - ) + C := weightMatrixCone MF; + (dim C) == (MF.tupleMaxSize * MF.tupleMaxValue) -- coherent iff C is full-dimensional + ) ) ------------------------ @@ -1218,26 +1032,15 @@ isCoherent(FlMatchingField) := MF -> ( -- finds a weight matrix that induces the matching field -- unexported (see getWeightMatrix) computeWeightMatrix = method() -computeWeightMatrix(GrMatchingField) := MF -> ( +computeWeightMatrix MatchingField := MF -> ( if not isCoherent MF then ( - error("expected a coherent matching field"); - ); + error("expected a coherent matching field: perhaps try 'MonomialOrder => \"none\"'"); + ); C := weightMatrixCone MF; CRays := rays C; -- construct an interior point of the cone weight := first entries transpose sum for c from 0 to numColumns CRays - 1 list CRays_{c}; - matrix for i from 0 to MF.k - 1 list weight_{i*MF.n .. (i+1)*MF.n - 1} - ) - -computeWeightMatrix(FlMatchingField) := MF -> ( - if not isCoherent MF then ( - error("expected a coherent matching field"); - ); - C := weightMatrixCone MF; - CRays := rays C; - -- construct an interior point of the cone - weight := first entries transpose sum for c from 0 to numColumns CRays - 1 list CRays_{c}; - matrix for i from 0 to (max MF.kList) - 1 list weight_{i*MF.n .. (i+1)*MF.n - 1} + matrix for i from 0 to MF.tupleMaxSize - 1 list weight_{i*MF.tupleMaxValue .. (i+1)*MF.tupleMaxValue - 1} ) -------------------------------------------- @@ -1246,30 +1049,30 @@ computeWeightMatrix(FlMatchingField) := MF -> ( -- assume the initial ideal is toric -- take the generators x^u - x^v of the initial ideal -- form a matrix M with rows {.. 1_u .. -1_v ..} --- kernel M is the linear span of the tropical cone +-- kernel M is the linear span of the tropical cone removeZeroRows = method() -removeZeroRows(Matrix) := inputMatrix -> ( +removeZeroRows Matrix := inputMatrix -> ( nonZeroRowIndices := for i from 0 to numRows inputMatrix - 1 list if not zero inputMatrix^{i} then i else continue; inputMatrix^nonZeroRowIndices ) linearSpanTropCone = method( Options => { - VerifyToricDegeneration => true - } + VerifyToricDegeneration => true + } ) -linearSpanTropCone(GrMatchingField) := opts -> MF -> ( +linearSpanTropCone GrMatchingField := opts -> MF -> ( if not MF.cache.?computedLinearSpanTropCone then ( - if opts.VerifyToricDegeneration and not isToricDegeneration MF then ( - error("expected GrMatchingField that gives a toric degeneration."); - ); - matchingFieldIdealExponents := exponents \ first entries gens matchingFieldIdeal MF; -- a list of pairs (since generators are binomials) - constraintMatrix := matrix apply(matchingFieldIdealExponents, exponentPair -> exponentPair_0 - exponentPair_1); - constraintMatrix = removeZeroRows reducedRowEchelonForm sub(constraintMatrix, QQ); - MF.cache.computedLinearSpanTropCone = ker constraintMatrix; - ); + if opts.VerifyToricDegeneration and not isToricDegeneration MF then ( + error("expected GrMatchingField that gives a toric degeneration."); + ); + matchingFieldIdealExponents := exponents \ first entries gens matchingFieldIdeal MF; -- a list of pairs (since generators are binomials) + constraintMatrix := matrix apply(matchingFieldIdealExponents, exponentPair -> exponentPair_0 - exponentPair_1); + constraintMatrix = removeZeroRows reducedRowEchelonForm sub(constraintMatrix, QQ); + MF.cache.computedLinearSpanTropCone = ker constraintMatrix; + ); MF.cache.computedLinearSpanTropCone ) @@ -1278,105 +1081,101 @@ linearSpanTropCone(GrMatchingField) := opts -> MF -> ( -- compute the algebraic matroid of the Matching field inside Grassmannian -- this gives bases for the algebraic matroid of the grassmannian implied by the matching field algebraicMatroid = method() -algebraicMatroid(GrMatchingField) := MF -> ( +algebraicMatroid GrMatchingField := MF -> ( if not MF.cache.?computedAlgebraicMatroid then ( - MF.cache.computedAlgebraicMatroid = matroid transpose gens linearSpanTropCone MF; - ); + MF.cache.computedAlgebraicMatroid = matroid transpose gens linearSpanTropCone MF; + ); MF.cache.computedAlgebraicMatroid ) --- write down the bases of the algebraic matroid as subsets +-- write down the bases of the algebraic matroid as subsets algebraicMatroidBases = method() -algebraicMatroidBases(GrMatchingField) := MF -> ( - SS := subsets(toList(1 .. MF.n), MF.k); +algebraicMatroidBases GrMatchingField := MF -> ( + SS := subsets(toList(1 .. MF.tupleMaxValue), MF.tupleMaxSize); for B in bases algebraicMatroid MF list (i -> SS_i) \ B ) --- write down the bases of the algebraic matroid as subsets +-- write down the bases of the algebraic matroid as subsets algebraicMatroidCircuits = method() -algebraicMatroidCircuits(GrMatchingField) := MF -> ( - SS := subsets(toList(1 .. MF.n), MF.k); +algebraicMatroidCircuits GrMatchingField := MF -> ( + SS := subsets(toList(1 .. MF.tupleMaxValue), MF.tupleMaxSize); for B in circuits algebraicMatroid MF list (i -> SS_i) \ B ) -- tope fields -- a tope field (for Gr(k,n)) is pair: --- (i) GrMatchingField +-- (i) GrMatchingField -- (ii) type T = {t_1 .. t_s} -- -- The notes in the code follow notation of Smith-Loho -- A matching field is a tope field of type {1 .. 1} -- the type is the 'right degree vector' --- for each tuple (i_1,1 .. i_1,t_1 .. i_s,t_s) of the GrMatchingField, --- we get one bipartite graph of the tope field where 1 in R is --- adjacent to i_1,1 .. i_1,t_1, etc. +-- for each tuple (i_1,1 .. i_1,t_1 .. i_s,t_s) of the GrMatchingField, +-- we get one bipartite graph of the tope field where 1 in R is +-- adjacent to i_1,1 .. i_1,t_1, etc. TopeField = new Type of HashTable topeField = method() -topeField(GrMatchingField) := MF -> ( +topeField GrMatchingField := MF -> ( new TopeField from { - "type" => toList(MF.k : 1), - "matchingField" => MF - } + "type" => toList(MF.tupleMaxSize : 1), + "matchingField" => MF + } ) topeField(GrMatchingField, List) := (MF, type) -> ( - assert(sum type == MF.k); + assert(sum type == MF.tupleMaxSize); new TopeField from { - "type" => type, - "matchingField" => MF - } + "type" => type, + "matchingField" => MF + } ) net TopeField := TF -> ( - ("Tope field: n = " | toString TF#"matchingField".n | " and type = " | toString TF#"type") + ("Tope field: n = " | toString TF#"matchingField".tupleMaxValue | " and type = " | toString TF#"type") ) -getTuples(TopeField) := TF -> ( +getTuples TopeField := TF -> ( getTuples TF#"matchingField" ) -- isLinkage --- tests if a tope field is Linkage by checking that for each k+1 subset \tau, --- the union of graphs M_\sigma where \sigma \subset \tau is a forest +-- tests if a tope field is Linkage by checking that for each k+1 subset \tau, +-- the union of graphs M_\sigma where \sigma \subset \tau is a forest -- isLinkage = method() -isLinkage(TopeField) := TF -> ( +isLinkage TopeField := TF -> ( result := true; MF := TF#"matchingField"; - subsetList := subsets(1 .. MF.n, MF.k); - subsetIndex := new HashTable from for j from 0 to binomial(MF.n, MF.k)-1 list subsetList_j => j; + subsetList := subsets(1 .. MF.tupleMaxValue, MF.tupleMaxSize); + subsetIndex := new HashTable from for j from 0 to binomial(MF.tupleMaxValue, MF.tupleMaxSize)-1 list subsetList_j => j; tuples := getTuples MF; - - for s in subsets(1 .. MF.n, MF.k + 1) do ( - -- take the union of all edges over the k-subsets of s + for s in subsets(1 .. MF.tupleMaxValue, MF.tupleMaxSize + 1) do ( + -- take the union of all edges over the k-subsets of s -- list the vertices in L adjacent to each j in R in the union -- L edges: 1 .. n - -- R edges: n+1 .. n+t where t = #TF#"type" - - edges := flatten flatten for s' in subsets(s, MF.k) list ( - tuple := tuples_(subsetIndex#s'); - tuplePosition := -1; - for typeIndex from 0 to #TF#"type"-1 list ( - t := TF#"type"_typeIndex; - for j from 1 to t list ( - tuplePosition = tuplePosition +1; - {tuple_tuplePosition, MF.n + typeIndex + 1} - ) - ) - ); - - G := graph edges; - if not isForest G then ( - result = false; - break - ); - ); - + -- R edges: n+1 .. n+t where t = #TF#"type" + edges := flatten flatten for s' in subsets(s, MF.tupleMaxSize) list ( + tuple := tuples_(subsetIndex#s'); + tuplePosition := -1; + for typeIndex from 0 to #TF#"type"-1 list ( + t := TF#"type"_typeIndex; + for j from 1 to t list ( + tuplePosition = tuplePosition +1; + {tuple_tuplePosition, MF.tupleMaxValue + typeIndex + 1} + ) + ) + ); + G := graph edges; + if not isForest G then ( + result = false; + break + ); + ); result ) -isLinkage(GrMatchingField) := MF -> ( +isLinkage GrMatchingField := MF -> ( isLinkage topeField MF ) @@ -1384,55 +1183,47 @@ isLinkage(GrMatchingField) := MF -> ( amalgamation = method() amalgamation(ZZ, TopeField) := (i, TF) -> ( assert(isLinkage TF); - assert(1 <= i and i <= TF#"matchingField".n); + assert(1 <= i and i <= TF#"matchingField".tupleMaxValue); MF := TF#"matchingField"; - subsetList := subsets(1 .. MF.n, MF.k); - subsetIndex := new HashTable from for j from 0 to binomial(MF.n, MF.k)-1 list subsetList_j => j; + subsetList := subsets(1 .. MF.tupleMaxValue, MF.tupleMaxSize); + subsetIndex := new HashTable from for j from 0 to binomial(MF.tupleMaxValue, MF.tupleMaxSize)-1 list subsetList_j => j; tuples := getTuples MF; - -- construct the new tuples of the matching field: - newTuples := for s in subsets(1 .. MF.n, MF.k + 1) list ( - -- take the union of all edges over the k-subsets of s + newTuples := for s in subsets(1 .. MF.tupleMaxValue, MF.tupleMaxSize + 1) list ( + -- take the union of all edges over the k-subsets of s -- list the vertices in L adjacent to each j in R in the union - edges := new MutableHashTable from for j from 0 to #TF#"type"-1 list ( - j => set {} - ); - - for s' in subsets(s, MF.k) do ( - tuple := tuples_(subsetIndex#s'); - tuplePosition := -1; - for typeIndex from 0 to #TF#"type"-1 do ( - t := TF#"type"_typeIndex; - edges#typeIndex = edges#typeIndex + set for j from 1 to t list ( - tuplePosition = tuplePosition +1; - tuple_tuplePosition - ); - ); - ); - - matchedL := edges#(i-1); - matchedR := set {i-1}; - - -- remove the edges of the union graph to get the amalgamation - while #matchedL < MF.k+1 do ( - for j from 0 to #TF#"type"-1 do ( - if not member(j, matchedR) then ( - edges#j = edges#j - matchedL; - if #edges#j == TF#"type"_j then ( - matchedR = matchedR + set {j}; - matchedL = matchedL + edges#j; - ); - ); - ); - ); - - fold((a,b) -> a | b, for j from 0 to #TF#"type"-1 list sort toList edges#j) - ); - - - newMF := grMatchingField(MF.k+1, MF.n, newTuples); - newType := for j from 1 to #TF#"type" list if i == j then TF#"type"_(j-1)+1 else TF#"type"_(j-1); - + edges := new MutableHashTable from for j from 0 to #TF#"type"-1 list ( + j => set {} + ); + for s' in subsets(s, MF.tupleMaxSize) do ( + tuple := tuples_(subsetIndex#s'); + tuplePosition := -1; + for typeIndex from 0 to #TF#"type"-1 do ( + t := TF#"type"_typeIndex; + edges#typeIndex = edges#typeIndex + set for j from 1 to t list ( + tuplePosition = tuplePosition +1; + tuple_tuplePosition + ); + ); + ); + matchedL := edges#(i-1); + matchedR := set {i-1}; + -- remove the edges of the union graph to get the amalgamation + while #matchedL < MF.tupleMaxSize + 1 do ( + for j from 0 to #TF#"type"-1 do ( + if not member(j, matchedR) then ( + edges#j = edges#j - matchedL; + if #edges#j == TF#"type"_j then ( + matchedR = matchedR + set {j}; + matchedL = matchedL + edges#j; + ); + ); + ); + ); + fold((a,b) -> a | b, for j from 0 to #TF#"type"-1 list sort toList edges#j) + ); + newMF := grMatchingField(MF.tupleMaxSize + 1, MF.tupleMaxValue, newTuples); + newType := for j from 1 to #TF#"type" list if i == j then TF#"type"_(j-1)+1 else TF#"type"_(j-1); topeField(newMF, newType) ) @@ -1455,142 +1246,144 @@ doc /// A package for working with matching fields for Grassmannians and partial flag varieties Description Text - A matching field $\Lambda$ for the Grassmannian Gr($k$, $n$), is a simple combinatorial object. - It may be thought of as a choice of initial term for each maximal minor of a generic $k \times n$ matrix - of variables. For example, take $k = 2$ and $n = 4$. Let $X = (x_{i,j})$ be a generic $2 \times 4$ matrix of variables. - Suppose that a matching field $\Lambda$ has tuples $\{12, 31, 14, 32, 24, 34\}$. This means that $\Lambda$ - distinguishes the term $x_{1,1} x_{2,2}$ from the maximal minors on columns $1$ and $2$ of $X$: $x_{1,1} x_{2,2} - x_{1,2} x_{2,1}$. - Similarly for the terms $x_{1,3} x_{2,1}$, $x_{1,1} x_{2,4}$, and so on. - - If the terms of all maximal minors distinguished by a matching field are their initial terms with respect to a fixed weight matrix, - then we say that the matching field is coherent. Each such weight matrix induces a weight vector on the Pluecker coordinates of the - Grassmannian. If the initial ideal of the Pluecker ideal of the Grassmannian with respect to this weight vector is a toric ideal, - i.e. a prime binomial ideal, then we say that the matching field gives rise to a toric degeneration of the Grassmannian. - By a result of Sturmfels (1996), a matching field gives rise to a toric degeneration if and only if the maximal minors of $X$ form - a subalgebra basis (or SAGBI basis) with respect to the order induced by the weight matrix. - - This concept naturally generalises to partial flag varieties under the Pluecker embedding. - - The MatchingFields package gives basic functions, to construct many of the well-studied examples of matching fields. - Given a matching field $L$, it is straight forward to check whether $L$ is coherent, what is a weight matrix that induces it, - and whether is gives rise to a toric degeneration. The package also produces polytopes associated to matching fields and Newton-Okounkov bodies. + A matching field $\Lambda$ for the Grassmannian Gr($k$, $n$), is a simple combinatorial object. + It may be thought of as a choice of initial term for each maximal minor of a generic $k \times n$ matrix + of variables. For example, take $k = 2$ and $n = 4$. Let $X = (x_{i,j})$ be a generic $2 \times 4$ matrix of variables. + Suppose that a matching field $\Lambda$ has tuples $\{12, 31, 14, 32, 24, 34\}$. This means that $\Lambda$ + distinguishes the term $x_{1,1} x_{2,2}$ from the maximal minors on columns $1$ and $2$ of $X$: $x_{1,1} x_{2,2} - x_{1,2} x_{2,1}$. + Similarly for the terms $x_{1,3} x_{2,1}$, $x_{1,1} x_{2,4}$, and so on. + + If the terms of all maximal minors distinguished by a matching field are their respective initial terms with respect to a fixed weight matrix, + then we say that the matching field is coherent. Each such weight matrix induces a weight vector on the Pluecker coordinates of the + Grassmannian. If the initial ideal of the Pluecker ideal of the Grassmannian with respect to this weight vector is a toric ideal, + i.e. a prime binomial ideal, then we say that the matching field gives rise to a toric degeneration of the Grassmannian. + By a result of Sturmfels (1996), a matching field gives rise to a toric degeneration if and only if the maximal minors of $X$ form + a subalgebra basis (or SAGBI basis, respectively Khovanskii basis) with respect to the order (respectively valuation) induced by the weight matrix. + + This concept naturally generalises to partial flag varieties under the Pluecker embedding. + + The MatchingFields package provides functions to construct many of the well-studied examples of matching fields. + Given a matching field $L$, the package provides functions to: check whether $L$ is coherent; find a weight matrix that induces it; + and test whether $L$ gives rise to a toric degeneration. The package also produces polytopes associated to matching fields and can compute + their Newton-Okounkov bodies. Example - L = grMatchingField(2, 4, {{1,2}, {3,1}, {1,4}, {3,2}, {2,4}, {3,4}}) - isCoherent L - getWeightMatrix L - isToricDegeneration L + L = grMatchingField(2, 4, {{1,2}, {3,1}, {1,4}, {3,2}, {2,4}, {3,4}}) + isCoherent L + getWeightMatrix L + isToricDegeneration L SeeAlso Subnodes - :Main objects - GrMatchingField - FlMatchingField - - :Constructing matching fields - grMatchingField - flMatchingField - diagonalMatchingField - matchingFieldFromPermutation - - :Basic properties and functions - getTuples - getGrMatchingFields - isCoherent - getWeightMatrix - getWeightPluecker - isToricDegeneration - (net, FlMatchingField) - (symbol ==, GrMatchingField, GrMatchingField) - (symbol ==, FlMatchingField, FlMatchingField) - - :Rings, ideals and maps - plueckerIdeal - matchingFieldIdeal - plueckerMap - matchingFieldRingMap - plueckerAlgebra - - :Convex bodies and polyhedra - matchingFieldPolytope - NOBody - weightMatrixCone - - :Dressians and matroids - algebraicMatroid - algebraicMatroidCircuits - algebraicMatroidBases - matroidSubdivision - linearSpanTropCone - - :Topes and tope fields - TopeField - topeField - (net, TopeField) - (getTuples, TopeField) - isLinkage - amalgamation + :Main objects + MatchingField + GrMatchingField + FlMatchingField + + :Constructing matching fields + matchingField + grMatchingField + flMatchingField + diagonalMatchingField + matchingFieldFromPermutation + + :Basic properties and functions + getTuples + getGrMatchingFields + isCoherent + getWeightMatrix + getWeightPluecker + isToricDegeneration + (net, FlMatchingField) + (symbol ==, MatchingField, MatchingField) + + :Rings, ideals and maps + plueckerIdeal + matchingFieldIdeal + plueckerMap + matchingFieldRingMap + plueckerAlgebra + + :Convex bodies and polyhedra + matchingFieldPolytope + NOBody + weightMatrixCone + + :Dressians and matroids + algebraicMatroid + algebraicMatroidCircuits + algebraicMatroidBases + matroidSubdivision + linearSpanTropCone + + :Topes and tope fields + TopeField + topeField + (net, TopeField) + (getTuples, TopeField) + isLinkage + amalgamation /// doc /// Key plueckerIdeal - (plueckerIdeal, FlMatchingField) - (plueckerIdeal, GrMatchingField) - (Grassmannian, GrMatchingField) - [plueckerIdeal, MonomialOrder] + (plueckerIdeal, FlMatchingField) + (plueckerIdeal, GrMatchingField) + (Grassmannian, GrMatchingField) + [plueckerIdeal, MonomialOrder] Headline The Pluecker ideal of a matching field Usage - I = plueckerIdeal Lgr - I = plueckerIdeal Lfl - I = Grassmannian Lgr + I = plueckerIdeal Lgr + I = plueckerIdeal Lfl + I = Grassmannian Lgr Inputs Lgr: GrMatchingField - Lfl: FlMatchingField - MonomialOrder => String - either "default" or "none" (supply "none" only if $L$ is not coherent) + Lfl: FlMatchingField + MonomialOrder => String + either "default" or "none" (supply "none" only if $L$ is not coherent) Outputs I: Ideal - The Pluecker ideal associated to the corresponding Grassmannian or partial flag variety - with the correct term order given by a weight that induced the matching field + The Pluecker ideal associated to the corresponding Grassmannian or partial flag variety + with the correct term order given by a weight that induced the matching field Description Text - The Pluecker ideal is the defining ideal of a partial flag variety embedded in a product of Grassmannians, where - each Grassmannian is embedded, by the Pluecker embedding, into a suitable projective space. - In the case of the Grassmannian Gr($k$, $n$), it is concretely given by kernel of the ring map - $K[P_I : I \subseteq [n],\ |I| = k] \rightarrow K[x_{i,j} : i \in [k], \ j \in [n]]$ where $P_I$ is mapped - to the $k \times k$ maximal minor of the matrix $(x_{i,j})$ whose columns are indexed by the set $I$. - It is well-known that this ideal has a Groebner basis consisting of homogeneous quadrics. - - The function @TO "plueckerIdeal"@ takes a matching field, either for the Grassmannian or a partial flag variety - and outputs the Pluecker ideal for that Grassmannian or partial flag variety. The ambient polynomial ring that - contains this ideal is constructed to have the term order induced by the matching field. - - Example - L = grMatchingField(2, 4, {{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}}) - I = plueckerIdeal L - (monoid ring I).Options.MonomialOrder - getWeightPluecker L - - Text - In the above example, the weights for the ambient ring are not the same as the Pluecker weights of the matching field. - This is because of the minimum-maximum convention problem. For compatibility with packages such as @TO "Tropical"@, we use - the minimum convention in @TO "MatchingFields"@ so the smallest weight with respect to the weight matrix that - induces the matching field is the initial term of a Pluecker form. - However, the monomial ordering given by @TO "Weights"@ uses the - maximum convention, so the ambient ring has weights that are based on the negative of the induced Pluecker Weight. - - Note that the given matching field must be coherent. If the matching field is not defined in terms of a weight - matrix, then the function will attempt to compute a weight matrix for the matching field. If the matching field is - not coherent then the function will produce an error. + The Pluecker ideal is the defining ideal of a partial flag variety embedded in a product of Grassmannians, where + each Grassmannian is embedded, by the Pluecker embedding, into a suitable projective space. + In the case of the Grassmannian Gr($k$, $n$), it is concretely given by kernel of the ring map + $K[P_I : I \subseteq [n],\ |I| = k] \rightarrow K[x_{i,j} : i \in [k], \ j \in [n]]$ where $P_I$ is mapped + to the $k \times k$ maximal minor of the matrix $(x_{i,j})$ whose columns are indexed by the set $I$. + It is well-known that this ideal has a Groebner basis consisting of homogeneous quadrics. + + The function @TO "plueckerIdeal"@ takes a matching field, either for the Grassmannian or a partial flag variety, + and outputs the Pluecker ideal for that Grassmannian or partial flag variety. The ambient polynomial ring that + contains this ideal is constructed to have the term order induced by the matching field. + Example - L = grMatchingField(2, 4, {{1,2}, {1,3}, {4,1}, {2,3}, {2,4}, {3,4}}) - isCoherent L - -- I = plueckerIdeal L -- "error: expected a coherent matching field" - Text - To construct the pluecker ideal for a non-coherent matching field, set the option MonomialOrder to "none". - The resulting ideal is constructed in a polynomial ring with the @TO "GRevLex"@ order. - Example - I = plueckerIdeal(L, MonomialOrder => "none") + L = grMatchingField(2, 4, {{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}}) + I = plueckerIdeal L + (monoid ring I).Options.MonomialOrder + getWeightPluecker L + + Text + In the above example, the weights for the ambient ring are not the same as the Pluecker weights of the matching field. + This is because of the minimum-maximum convention problem. For compatibility with packages such as @TO "Tropical"@, we use + the minimum convention in @TT "MatchingFields"@ so the smallest weight with respect to the weight matrix that + induces the matching field is the initial term of a Pluecker form. + However, the monomial ordering given by @TO "Weights"@ uses the + maximum convention, so the ambient ring has weights that are based on the negative of the induced Pluecker Weight. + + Note that the given matching field must be coherent. If the matching field is not defined in terms of a weight + matrix, then the function will attempt to compute a weight matrix for the matching field. If the matching field is + not coherent then the function will produce an error. + Example + L = grMatchingField(2, 4, {{1,2}, {1,3}, {4,1}, {2,3}, {2,4}, {3,4}}) + isCoherent L + -- I = plueckerIdeal L -- "error: expected a coherent matching field" + Text + To construct the pluecker ideal for a non-coherent matching field, set the option MonomialOrder to "none". + The resulting ideal is constructed in a polynomial ring with the @TO "GRevLex"@ order. + Example + I = plueckerIdeal(L, MonomialOrder => "none") SeeAlso matchingFieldIdeal Subnodes @@ -1600,37 +1393,37 @@ doc /// doc /// Key matroidSubdivision - (matroidSubdivision, GrMatchingField) + (matroidSubdivision, GrMatchingField) (matroidSubdivision, ZZ, ZZ, List) Headline The matroid subdivision induced by the Pluecker weight of a coherent matching field Usage listOfBases = matroidSubdivision L - listOfBases = matroidSubdivision(k, n, plueckerWeight) + listOfBases = matroidSubdivision(k, n, plueckerWeight) Inputs - L: GrMatchingField - k: ZZ - n: ZZ - plueckerWeight: List - the weight of the pluecker coordinates in revLex order using minimum convention + L: GrMatchingField + k: ZZ + n: ZZ + plueckerWeight: List + the weight of the pluecker coordinates in revLex order using minimum convention Outputs listOfBases: List - Each element is a list of the vertices of a maximal cell of the matroid subdivision of the hypersimplex induced by - the Pluecker weight of the matching field. + Each element is a list of the vertices of a maximal cell of the matroid subdivision of the hypersimplex induced by + the Pluecker weight of the matching field. Description Text - The hypersimplex $\Delta(k, n) \subseteq \RR^{n}$ is the convex hull of the characteristic vectors of all $k$-subsets - of $\{1, \dots, n\}$, and we label each vertex with with its corresponding subset. A regular subdivision of the vertices of $\Delta(k, n)$ - is said to be matroidal if, for each maximal cell of the subdivision, the subsets labelling its vertices form the set of bases of a matroid. - The well-known result is: a point lies in the Dressian Dr($k$, $n$), the tropical prevariety of all $3$-term Pluecker relation in Gr($k$, $n$), if and only if - it induces a matroidal subdivision of the hypersimplex. - Example - L = grMatchingField(2, 4, {{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}}) - netList matroidSubdivision L -- an octahedron sliced into 2 pieces - Text - Whenever the function @TO "matroidSubdivision"@ is supplied with a Grassmannian matching field, the cached weight that induces the matching field - is used for the computation of the matroid subdivision. Note that, if the function is supplied directly with the \textit{plueckerWeight}, then - the coordinates are ordered so that the corresponding sets are listed in reverse lexicographic order. + The hypersimplex $\Delta(k, n) \subseteq \RR^{n}$ is the convex hull of the characteristic vectors of all $k$-subsets + of $\{1, \dots, n\}$, and we label each vertex with with its corresponding subset. A regular subdivision of the vertices of $\Delta(k, n)$ + is said to be matroidal if, for each maximal cell of the subdivision, the subsets labelling its vertices form the set of bases of a matroid. + The well-known result is: a point lies in the Dressian Dr($k$, $n$), the tropical prevariety of all $3$-term Pluecker relation in Gr($k$, $n$), if and only if + it induces a matroidal subdivision of the hypersimplex. + Example + L = grMatchingField(2, 4, {{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}}) + netList matroidSubdivision L -- an octahedron sliced into 2 pieces + Text + Whenever the function @TO "matroidSubdivision"@ is supplied with a Grassmannian matching field, the cached weight that induces the matching field + is used for the computation of the matroid subdivision. Note that, if the function is supplied directly with the \textit{plueckerWeight}, then + the coordinates are ordered so that the corresponding sets are listed in reverse lexicographic order. SeeAlso Subnodes /// @@ -1638,313 +1431,318 @@ doc /// doc /// Key algebraicMatroid - (algebraicMatroid, GrMatchingField) + (algebraicMatroid, GrMatchingField) Headline The algebraic matroid of the tropical cone that induces the matroid Usage M = algebraicMatroid L Inputs - L: GrMatchingField + L: GrMatchingField Outputs M: "matroid" - The algebraic matroid of the cone in Trop Gr$(k,n)$ that induces the matching field. + The algebraic matroid of the cone in Trop Gr$(k,n)$ that induces the matching field. Description Text - Let $V \subseteq \CC^n$ be an affine variety. - The algebraic matroid of $V$ is a matroid whose independent sets $S \subseteq [n]$ - are the subsets such that the projection from $V$ to the coordinates indexed by $S$ - is a dominant morphism. Similarly, if $C \subseteq \RR^n$ is a polyhedral cone, then the algebraic matroid - of $C$ is the matroid whose independent sets $S \subseteq [n]$ are the subsets such that image of the - projection of $C$ onto the coordinates indexed by $S$ is full-dimensional. - - In the case of the affine cone of Grassmannian under the Pluecker embedding, - there are a few different ways to compute its algebraic matroid. One way is to use its tropicalization. - The algebraic matroid of the Grassmannian is equal to the matroid whose bases are the union of all bases of the - algebraic matroid for all maximal cones of Trop Gr($k$, $n$). - - For each coherent matching field, we compute its cone in the tropicalization of the Grassmannian. - We compute the algebraic matroid of this cone. To view the bases of this matroid in terms of the $k$-subsets of $[n]$, - use the function @TO "algebraicMatroidBases"@. Similarly, to view its circuits use @TO "algebraicMatroidCircuits"@ - - Example - L = grMatchingField(2, 4, {{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}}) - M = algebraicMatroid L - netList algebraicMatroidBases L + Let $V \subseteq \CC^n$ be an affine variety. + The algebraic matroid of $V$ is a matroid whose independent sets $S \subseteq [n]$ + are the subsets such that the projection from $V$ to the coordinates indexed by $S$ + is a dominant morphism. Similarly, if $C \subseteq \RR^n$ is a polyhedral cone, then the algebraic matroid + of $C$ is the matroid whose independent sets $S \subseteq [n]$ are the subsets such that image of the + projection of $C$ onto the coordinates indexed by $S$ is full-dimensional. + + In the case of the affine cone of the Grassmannian under the Pluecker embedding, + there are a few different ways to compute its algebraic matroid. One way is to use its tropicalization. + The algebraic matroid of the Grassmannian is equal to the matroid whose bases are the union of all bases of the + algebraic matroid for all maximal cones of Trop Gr($k$, $n$). + + For each coherent matching field, we compute its cone in the tropicalization of the Grassmannian. + We compute the algebraic matroid of this cone. To view the bases of this matroid in terms of the $k$-subsets of $[n]$, + use the function @TO "algebraicMatroidBases"@. Similarly, to view its circuits use @TO "algebraicMatroidCircuits"@ + + Example + L = grMatchingField(2, 4, {{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}}) + M = algebraicMatroid L + netList algebraicMatroidBases L SeeAlso algebraicMatroidBases - algebraicMatroidCircuits + algebraicMatroidCircuits Subnodes /// doc /// Key getGrMatchingFields - (getGrMatchingFields, FlMatchingField) + (getGrMatchingFields, FlMatchingField) Headline - The Grassmannian matching fields of a Flag matching field + The Grassmannian matching fields of a partial flag matching field Usage matchingFieldList = getGrMatchingFields L Inputs - L: FlMatchingField + L: FlMatchingField Outputs matchingFieldList: List - The Grassmannian matching fields contained in L. + The Grassmannian matching fields contained in L. Description Text - This function returns a list of the @TO "GrMatchingField"@s that are contained - within the given @TO "FlMatchingField"@. - Example - D = diagonalMatchingField({1,2,3}, 6); - getWeightMatrix D - netList getGrMatchingFields D - D2 = (getGrMatchingFields D)_1; - getWeightMatrix D2 - Text - The above example constructs the diagonal matching field for the partial - flag variety Fl(123; 6), which contains the data for - three distinct Grassmannian matching fields. Each @TO "GrMatchingField"@ - is a diagonal matching field, which are induced by a submatrix of the original - weight matrix that induces the flag matching field. + This function returns a list of the @TO "GrMatchingField"@s that are contained + within the given @TO "FlMatchingField"@. + Example + D = diagonalMatchingField({1,2,3}, 6); + getWeightMatrix D + netList getGrMatchingFields D + D2 = (getGrMatchingFields D)_1; + getWeightMatrix D2 + Text + The above example constructs the diagonal matching field for the partial + flag variety Fl(123; 6), which contains the data for + three distinct Grassmannian matching fields. Each @TO "GrMatchingField"@ + is a diagonal matching field, which are induced by a submatrix of the original + weight matrix that induces the flag matching field. SeeAlso - Subnodes - + FlMatchingField + GrMatchingField /// doc /// Key flMatchingField - (flMatchingField, List, Matrix) - (flMatchingField, List, ZZ, List) - (flMatchingField, Matrix) + (flMatchingField, List, Matrix) + (flMatchingField, List, ZZ, List) + (flMatchingField, Matrix) Headline Construct a matching field for a partial flag variety Usage L = flMatchingField(kList, weightMatrix) - L = flMatchingField(kList, n, tuples) - L = flMatchingField(weightMatrix) + L = flMatchingField(kList, n, tuples) + L = flMatchingField(weightMatrix) Inputs kList: List - positive integers; the sizes of the tuples of the flag matching field - n: ZZ - positive integer; the tuples have entries in 1 .. n - weightMatrix: Matrix - induces the flag matching field + positive integers; the sizes of the tuples of the flag matching field + n: ZZ + positive integer; the tuples have entries in 1 .. n + weightMatrix: Matrix + induces the flag matching field Outputs L: FlMatchingField Description Text - This function is the basic constructor for - matching fields for partial flag varieties, which we simply call - flag matching fields. The function outputs an instance of type @TO "FlMatchingField"@, - which represents the flag matching field and stores all data related and - computed about it. - - There are three basic ways to define a flag matching field. The first way is to - supply a weight matrix that induces the flag matching field. This produces a flag matching field - for the full flag variety. - Example - M = matrix {{0,0,0,0}, {4,2,3,1}, {10, 40, 30, 20}} - L1 = flMatchingField M - netList getTuples L1 - isToricDegeneration L1 - Text - In the above example, we construct the flag matching field for the full - flag variety induced by the given weight matrix. The tuples for the - flag matching field are listed by their size. Similarly to Grassmannian - matching fields: @TO "GrMatchingField"@, the function @TO "isToricDegeneration"@ - checks the equality of the @TO "matchingFieldIdeal"@ and the initial ideal - of the @TO "plueckerIdeal"@ with respect to the weight of the matching field. - - The second way to define a flag matching field - is to supply a weight matrix and specify the size of the sets - or, in other words, specify the dimensions of the vector spaces in the flags. - Example - L2 = flMatchingField({1,2}, M) - netList getTuples L2 - Text - The third way to define a flag matching field is by listing out its tuples. - Example - T = getTuples L1 - L3 = flMatchingField({1,3}, 4, {T_0, T_2}) - getTuples L3 - isCoherent L3 - getWeightMatrix L3 - Text - As shown in the example above, the first argument "kList" - specifies the size of the sets. - The third argument is a list whose i-th entry is a list of tuples - of size "kList_i". In this example, the size of the sets are 1 and 3, - which correspond to "T_0" and "T_2". - When a flag matching field is constructed in this way, it is not - guaranteed to be coherent, i.e., it may not be induced by a weight matrix. - Similarly to Grassmannian matching fields, the function @TO "isCoherent"@ - checks whether the matching field is coherent and the function @TO "getWeightMatrix"@ - returns a weight matrix that induces the matching field, if it exists. - If the matching field is not coherent, then these methods produce an error. - - A note of caution. Two different weight matrices may induce the same matching field - so the function @TO "getWeightMatrix"@ may return a weight matrix that is - different to what may be expected. However, if a matching field is defined - by a weight matrix, then that weight matrix will be returned. - + This function is the basic constructor for + matching fields for partial flag varieties, often called + flag matching fields. The function outputs an instance of type @TO "FlMatchingField"@, + which represents the flag matching field and stores all data related and + computed about it. + + There are three basic ways to define a flag matching field. The first way is to + supply a weight matrix that induces the flag matching field. This produces a flag matching field + for the full flag variety. + Example + M = matrix {{0,0,0,0}, {4,2,3,1}, {10, 40, 30, 20}} + L1 = flMatchingField M + netList getTuples L1 + isToricDegeneration L1 + Text + In the above example, we construct the flag matching field for the full + flag variety induced by the given weight matrix. The tuples for the + flag matching field are listed by their size. Similarly to Grassmannian + matching fields: @TO "GrMatchingField"@, the function @TO "isToricDegeneration"@ + checks the equality of the @TO "matchingFieldIdeal"@ and the initial ideal + of the @TO "plueckerIdeal"@ with respect to the weight of the matching field. + + The second way to define a flag matching field + is to supply a weight matrix and specify the size of the sets + or, in other words, specify the dimensions of the vector spaces in the flags. + Example + L2 = flMatchingField({1,2}, M) + netList getTuples L2 + Text + The third way to define a flag matching field is by listing out its tuples. + Example + L3 = flMatchingField({1,3}, 4, { + {1}, {2}, {3}, {4}, + {3, 2, 1}, {2, 4, 1}, {3, 4, 1}, {3, 2, 4} + }) + getTuples L3 + isCoherent L3 + getWeightMatrix L3 + Text + As shown in the example above, the first argument "kList" + specifies the size of the sets. + The third argument is a list of tuples of the matching field. + When a flag matching field is constructed in this way, it is not + guaranteed to be coherent, i.e., it may not be induced by a weight matrix. + Similarly to Grassmannian matching fields, the function @TO "isCoherent"@ + checks whether the matching field is coherent and the function @TO "getWeightMatrix"@ + returns a weight matrix that induces the matching field, if it exists. + If the matching field is not coherent, then these methods produce an error. + + A note of caution. Two different weight matrices may induce the same matching field. + So, the function @TO "getWeightMatrix"@ may return a unexpected weight matrix. + However, if a matching field is defined + by a weight matrix, then that weight matrix will be returned. + SeeAlso FlMatchingField GrMatchingField - grMatchingField - isToricDegeneration - plueckerIdeal - matchingFieldIdeal - isCoherent - getWeightMatrix + grMatchingField + isToricDegeneration + plueckerIdeal + matchingFieldIdeal + isCoherent + getWeightMatrix Subnodes - + /// doc /// Key matchingFieldIdeal - (matchingFieldIdeal, FlMatchingField) - (matchingFieldIdeal, GrMatchingField) - [matchingFieldIdeal, Strategy] - [matchingFieldIdeal, MonomialOrder] + (matchingFieldIdeal, MatchingField) + [matchingFieldIdeal, Strategy] + [matchingFieldIdeal, MonomialOrder] Headline The toric ideal of a matching field Usage I = matchingFieldIdeal L Inputs - L: {GrMatchingField, FlMatchingField} - Strategy => String - either "M2" or "4ti2" the strategy for computing the generators - MonomialOrder => String - either "default" or "none" (supply "none" only if $L$ is not coherent) + L: {MatchingField, GrMatchingField, FlMatchingField} + Strategy => String + either "M2" or "4ti2" the strategy for computing the generators + MonomialOrder => String + either "default" or "none" (supply "none" only if $L$ is not coherent) Outputs I: Ideal - toric ideal of the matching field + toric ideal of the matching field Description Text - A matching field $\Lambda$ for the Grassmannian Gr$(k,n)$ associates to each subset $J = \{j_1 < \dots < j_k\}$ - an ordering of that subset $\Lambda(J) = (j_{\sigma(1)}, \dots, j_{\sigma(k)})$ for some permutation $\sigma \in S_k$. - The monomial map associated to a matching field $\Lambda$ is defined as the map that sends each Pluecker - coordinate $p_J$ to the monomial sgn$(\sigma)x_{1, \Lambda(J)_1} x_{2, \Lambda(J)_2} \cdots x_{k, \Lambda(J)_k}$ - where sgn$(\sigma) \in \{+1, -1\}$ is the sign of the permutation. The matching field - ideal is the kernel of this monomial map. - Example - L = diagonalMatchingField(2, 4) - m = matchingFieldRingMap L - I = matchingFieldIdeal L - ker m === I - Text - The analogous setup holds for flag matching fields. A flag matching field can be thought of as a union of Grassmannian matching fields. - The inclusion of the Grassmannian matching field naturally extends to an inclusion of the corresponding ideals. - The flag matching field ideals are also generated by 'incident relations' that involve Pluecker coordinates from distinct pairs of - Grassmannians within the flag variety. - Example - L = diagonalMatchingField({1,2}, 4) - I = matchingFieldIdeal L - Text - The functions @TO "matchingFieldIdeal"@ and @TO "plueckerIdeal"@ both construct ideals that belong to the same - polynomial ring. Similarly, the ring maps constructed by the function @TO "plueckerMap"@ and @TO "matchingFieldRingMap"@ - have the same target ring. - Example - I' = plueckerIdeal L - ring I === ring I' - source plueckerMap L - source plueckerMap L === source matchingFieldRingMap L - target plueckerMap L - target plueckerMap L === target matchingFieldRingMap L - Text - If the matching field is not coherent, then the matching field ideal can be constructed by setting the option MonomialOrder to "none". - Doing this sets the monomial order of the target polynomial ring above to @TO "GRevLex"@. - Example - L = grMatchingField(2, 5, {{2,1}, {3,2}, {4,3}, {1,4}, {2,4}, {1,3}, {1,5}, {5,2}, {3,5}, {4,5}}) - isCoherent L - I = matchingFieldIdeal(L, MonomialOrder => "none") - (options ring I).MonomialOrder - Text - The option @TO "Strategy"@ determines how the matching field ideal is computed. The default uses the package @TO "FourTiTwo"@. This strategy works - by passing in the matrix of the toric ideal to @TO "toricGroebner"@ with the correct weight vector. In the case of Grassmannian matching fields, - the columns of the matrix of the toric ideal are exactly the vertices of the matching field polytope. For a flag matching field $\Lambda$, - the matrix of the toric ideal is the top-justified juxtaposition of such matrices for the Grassmannian matching fields contained in $\Lambda$. - On the other hand, the strategy "M2" simply uses the in-built function to compute the kernel of the map @TO "matchingFieldRingMap"@. + A matching field $\Lambda$ for the Grassmannian Gr$(k,n)$ associates to each subset $J = \{j_1 < \dots < j_k\}$ + an ordering of that subset $\Lambda(J) = (j_{\sigma(1)}, \dots, j_{\sigma(k)})$ for some permutation $\sigma \in S_k$. + The monomial map associated to a matching field $\Lambda$ is defined as the map that sends each Pluecker + coordinate $p_J$ to the monomial sgn$(\sigma)x_{1, \Lambda(J)_1} x_{2, \Lambda(J)_2} \cdots x_{k, \Lambda(J)_k}$ + where sgn$(\sigma) \in \{+1, -1\}$ is the sign of the permutation. The matching field + ideal is the kernel of this monomial map. + Example + L = diagonalMatchingField(2, 4) + m = matchingFieldRingMap L + p_(1,2) + m p_(1,2) + I = matchingFieldIdeal L + ker m === I + Text + The analogous setup holds for flag matching fields. A flag matching field can be thought of as a union of Grassmannian matching fields. + The inclusion of the Grassmannian matching field naturally extends to an inclusion of the corresponding ideals. + The flag matching field ideals are also generated by 'incident relations' that involve Pluecker coordinates from distinct pairs of + Grassmannians within the flag variety. + Example + L = diagonalMatchingField({1,2}, 4) + I = matchingFieldIdeal L + Text + The functions @TO "matchingFieldIdeal"@ and @TO "plueckerIdeal"@ both construct ideals that belong to the same + polynomial ring. Similarly, the ring maps constructed by the function @TO "plueckerMap"@ and @TO "matchingFieldRingMap"@ + have the same target ring. + Example + I' = plueckerIdeal L + ring I === ring I' + source plueckerMap L + source plueckerMap L === source matchingFieldRingMap L + target plueckerMap L + target plueckerMap L === target matchingFieldRingMap L + Text + If the matching field is not coherent, then the matching field ideal can be constructed by setting the option MonomialOrder to "none". + Doing this sets the monomial order of the target polynomial ring above to @TO "GRevLex"@. + Example + L = grMatchingField(2, 5, {{2,1}, {3,2}, {4,3}, {1,4}, {2,4}, {1,3}, {1,5}, {5,2}, {3,5}, {4,5}}) + isCoherent L + I = matchingFieldIdeal(L, MonomialOrder => "none") + (options ring I).MonomialOrder + Text + The option @TO "Strategy"@ determines how the matching field ideal is computed. The default uses the package @TO "FourTiTwo"@. This strategy works + by passing in the matrix of the toric ideal to @TO "toricGroebner"@ with the correct weight vector. In the case of Grassmannian matching fields, + the columns of the matrix of the toric ideal are exactly the vertices of the matching field polytope. For a flag matching field $\Lambda$, + the matrix of the toric ideal is the top-justified juxtaposition of such matrices for the Grassmannian matching fields contained in $\Lambda$. + On the other hand, the strategy "M2" simply uses the in-built function to compute the kernel of the map @TO "matchingFieldRingMap"@. Caveat For some versions of the package @TO "FourTiTwo"@, the strategy "4ti2" may not correctly take into account the weights. See the caveat in the - documentation of the function @TO "toricGroebner"@. If there are any problems, it may be more reliable to use the option "M2". + documentation of the function @TO "toricGroebner"@. If there are any problems, it may be more reliable to use the option "M2". SeeAlso - Subnodes + MatchingField + GrMatchingField + FlMatchingField /// doc /// Key matchingFieldPolytope - (matchingFieldPolytope, FlMatchingField) - (matchingFieldPolytope, GrMatchingField) - [matchingFieldPolytope, ExtraZeroRows] + (matchingFieldPolytope, FlMatchingField) + (matchingFieldPolytope, GrMatchingField) + [matchingFieldPolytope, ExtraZeroRows] Headline The polytope of a matching field Usage P = matchingFieldPolytope L Inputs L: {GrMatchingField, FlMatchingField} - ExtraZeroRows => ZZ - produces a matching field polytope embedded in a larger space - typically used for producing polytopes of flag matching field + ExtraZeroRows => ZZ + produces a matching field polytope embedded in a larger space + typically used for producing polytopes of flag matching field Outputs P: Polyhedron - polytope of the matching field + polytope of the matching field Description Text - Each matching field defines a projective toric variety whose defining - ideal is given by the kernel of the monomial map. See @TO "matchingFieldRingMap"@. - The coordinate ring of this toric variety is the Ehrhart ring of - the matching field polytope. Note that for flag matching fields, the - toric variety is embedded into a high-dimensional - projective space via the Segre embedding whose domain is a product of - Grassmannians. - - Given a matching field $\Lambda$ for the Grassmannian Gr$(k,n)$, the matching field - polytope $P(\Lambda)$ is simply the convex hull of the exponent - vectors of the image of Pluecker variables under the monomial map of - $\Lambda$. The polytope naturally lives in the space - $\RR^{k \times n}$. - Example - L2 = diagonalMatchingField(2, 4) - P2 = matchingFieldPolytope L2 - fVector P2 - vertices P2 - Text - The columns of the above matrix are the vertices of the matching field - polytope $P(\Lambda)$. Each column should be thought of as a $2 \times 4$ - matrix whose entries are listed row by row. - - A matching field $\Lambda$ for a partial flag variety Fl$(k_1, \dots, k_s; n)$ is a union of matching - fields $\Lambda = \bigcup \Lambda_i$ for some Grassmannians. The matching field polytope for a - partial flag variety is the - Minkowski sum $P(\Lambda) = \sum P(\Lambda_i)$ of Grassmannian matching field polytopes in $\Lambda$. - For this sum to make sense, each Grassmannian matching field polytope - must be put into the same space, which is taken to be $\RR^{k_{\max} \times n}$ - where $k_{\max} = \max\{k_i\}$ is the largest $k$ such that there is a Grassmannian matching field - for Gr$(k,n)$ contained in $\Lambda$. If $v \in \RR^{k_i \times n}$ is a vertex for a - Grassmannian matching field polytope, then we embed $v$ into $\RR^{k_{\max} \times n}$ - by joining a suitably sized matrix of zeros to $v$ from below. - - Embedding a Grassmannian matching field polytope into a higher dimensional space as - described is done by specifying the optional value @TO "ExtraZeroRows"@. - Example - L1 = diagonalMatchingField(1, 4) - P1 = matchingFieldPolytope(L1, ExtraZeroRows => 1) - vertices P1 - P12 = minkowskiSum(P1, P2) - vertices P12 - Text - The above example constructs the diagonal matching field polytope for - the partial flag variety Fl$(1, 2; 4)$ as a Minkowski sum. - The quick way to do this is as follows. - Example - L = diagonalMatchingField({1,2}, 4) - Q = matchingFieldPolytope L - Q == P12 + Each matching field defines a projective toric variety whose defining + ideal is given by the kernel of the monomial map. See @TO "matchingFieldRingMap"@. + The coordinate ring of this toric variety is the Ehrhart ring of + the matching field polytope. Note that for flag matching fields, the + toric variety is embedded into a high-dimensional + projective space via the Segre embedding whose domain is a product of + Grassmannians. + + Given a matching field $\Lambda$ for the Grassmannian Gr$(k,n)$, the matching field + polytope $P(\Lambda)$ is simply the convex hull of the exponent + vectors of the image of Pluecker variables under the monomial map of + $\Lambda$. The polytope naturally lives in the space + $\RR^{k \times n}$. + Example + L2 = diagonalMatchingField(2, 4) + P2 = matchingFieldPolytope L2 + fVector P2 + vertices P2 + Text + The columns of the above matrix are the vertices of the matching field + polytope $P(\Lambda)$. Each column should be thought of as a $2 \times 4$ + matrix whose entries are listed row by row. + + A matching field $\Lambda$ for a partial flag variety Fl$(k_1, \dots, k_s; n)$ is a union of matching + fields $\Lambda = \bigcup \Lambda_i$ for some Grassmannians. The matching field polytope for a + partial flag variety is the + Minkowski sum $P(\Lambda) = \sum P(\Lambda_i)$ of Grassmannian matching field polytopes in $\Lambda$. + For this sum to make sense, each Grassmannian matching field polytope + must be put into the same space, which is taken to be $\RR^{k_{\max} \times n}$ + where $k_{\max} = \max\{k_i\}$ is the largest $k$ such that there is a Grassmannian matching field + for Gr$(k,n)$ contained in $\Lambda$. If $v \in \RR^{k_i \times n}$ is a vertex for a + Grassmannian matching field polytope, then we embed $v$ into $\RR^{k_{\max} \times n}$ + by joining a suitably sized matrix of zeros to $v$ from below. + + Embedding a Grassmannian matching field polytope into a higher dimensional space as + described is done by specifying the optional value @TO "ExtraZeroRows"@. + Example + L1 = diagonalMatchingField(1, 4) + P1 = matchingFieldPolytope(L1, ExtraZeroRows => 1) + vertices P1 + P12 = minkowskiSum(P1, P2) + vertices P12 + Text + The above example constructs the diagonal matching field polytope for + the partial flag variety Fl$(1, 2; 4)$ as a Minkowski sum. + The quick way to do this is as follows. + Example + L = diagonalMatchingField({1,2}, 4) + Q = matchingFieldPolytope L + Q == P12 SeeAlso + GrMatchingField + FlMatchingField ExtraZeroRows Subnodes ExtraZeroRows @@ -1953,116 +1751,117 @@ doc /// doc /// Key plueckerMap - (plueckerMap, FlMatchingField) - (plueckerMap, GrMatchingField) - [plueckerMap, MonomialOrder] + (plueckerMap, MatchingField) + [plueckerMap, MonomialOrder] Headline The ring map of the Pluecker embedding Usage m = plueckerMap L Inputs - L: {GrMatchingField, FlMatchingField} - MonomialOrder => String - either "default" or "none" (supply "none" only if $L$ is not coherent) + L: {MatchingField, GrMatchingField, FlMatchingField} + MonomialOrder => String + either "default" or "none" (supply "none" only if $L$ is not coherent) Outputs m: RingMap - the ring map of the Pluecker embedding + the ring map of the Pluecker embedding Description Text The ring map for the Pluecker embedding of the Grassmannian - sends each Pluecker variable $P_J$, - where $J$ is a $k$-subset of $[n]$, to its corresponding maximal minor in a - generic $k \times n$ matrix of variables $X = (x_{i,j})$. - - The domain and codomain of this ring map are naturally equipped with term - orders derived from a weight matrix, which induces the matching field. - So, for this function, we require that the matching fields be coherent. + sends each Pluecker variable $P_J$, + where $J$ is a $k$-subset of $[n]$, to its corresponding maximal minor in a + generic $k \times n$ matrix of variables $X = (x_{i,j})$. + + The domain and codomain of this ring map are naturally equipped with term + orders derived from a weight matrix, which induces the matching field. + So, for this function, we require that the matching fields be coherent. If a weight matrix is not supplied, then one is automatically computed. - If the matching field is not coherent, then an error is thrown. - Example + If the matching field is not coherent, then an error is thrown. + Example L = grMatchingField(2, 4, {{1, 2}, {1, 3}, {3, 2}, {1, 4}, {4, 2}, {3, 4}}) - isCoherent L - getWeightMatrix L - plueckerMap L + isCoherent L + getWeightMatrix L + plueckerMap L describe target plueckerMap L - Text + Text For the above polynomial ring, the monomial order is given by a weight ordering. - Note that the weights are based on $-1 \times W$ where $W$ is the weight matrix - that induces $L$, displayed using the function @TO "getWeightMatrix"@. - The purpose of $-1$ is to transition between the minimum convention - of matching fields and the maximum convention of initial terms in @TO "Macaulay2"@. - - The ring map for the Pluecker embedding of a partial flag variety is - completely analogous. - Example + Note that the weights are based on $-1 \times W$ where $W$ is the weight matrix + that induces $L$, displayed using the function @TO "getWeightMatrix"@. + The purpose of $-1$ is to transition between the minimum convention + of matching fields and the maximum convention of initial terms in @TO "Macaulay2"@. + + The ring map for the Pluecker embedding of a partial flag variety is + completely analogous. + Example L = diagonalMatchingField({1,2}, 4) - getWeightMatrix L - m = plueckerMap L - describe source m - Text - The monomial order on the ring of Pluecker variables, - shown above, is also based on $-1 \times W$. More concretely, - the weight vector of a Pluecker variable $P_J$ is the weight of - the initial term of the image of the Pluecker variable $m(P_J) = \det(X_J)$ under the map. - - The monomial map associated to the matching field, see @TO "matchingFieldRingMap"@ - is the map that sends each - Pluecker variable $P_J \mapsto \rm{in}(\det(X_J))$ to the lead term of the - maximal minor $\det(X_J)$. - + getWeightMatrix L + m = plueckerMap L + describe source m + Text + The monomial order on the ring of Pluecker variables, + shown above, is also based on $-1 \times W$. More concretely, + the weight vector of a Pluecker variable $P_J$ is the weight of + the initial term of the image of the Pluecker variable $m(P_J) = \det(X_J)$ under the map. + + The monomial map associated to the matching field, see @TO "matchingFieldRingMap"@ + is the map that sends each + Pluecker variable $P_J \mapsto \rm{in}(\det(X_J))$ to the lead term of the + maximal minor $\det(X_J)$. SeeAlso + MatchingField + GrMatchingField + FlMatchingField Subnodes /// doc /// Key weightMatrixCone - (weightMatrixCone, FlMatchingField) - (weightMatrixCone, GrMatchingField) - [weightMatrixCone, ExtraZeroRows] + (weightMatrixCone, FlMatchingField) + (weightMatrixCone, GrMatchingField) + [weightMatrixCone, ExtraZeroRows] Headline The cone of weight matrices that induce the matching field Usage C = weightMatrixCone L Inputs L: {GrMatchingField, FlMatchingField} - ExtraZeroRows => ZZ - produces a cone embedded in a higher dimensional space - typically used for constructing weight matrix cones for flag matching fields + ExtraZeroRows => ZZ + produces a cone embedded in a higher dimensional space + typically used internally for constructing weight matrix cones for flag matching fields Outputs C: Cone - the cone of weight matrices that induce the matching field + the cone of weight matrices that induce the matching field Description Text Given a coherent matching field $\Lambda$, either for the Grassmannian or partial flag variety, - the set of weight matrices that induce $\Lambda$ naturally form a polyhedral cone. - The function @TO "weightMatrixCone"@ constructs this cone by writing down a collection of inequalities. - To illustrate this assume that $(1,2)$ is a tuple of $\Lambda$. A weight matrix $M = (m_{i,j})$ - induces a matching field with the tuple $(1,2)$ if and only if $m_{1,1} + m_{2,2} < m_{1,2} + m_{2,1}$. - Continuing in this way for all other tuples of $\Lambda$ produces the cone of weight matrices. - Note, the inequalities, like the one above, are strict. So, in general, only the interior points of - the cone give rise to generic weight matrices that induce the matching field. - Example - L = diagonalMatchingField(2, 4) - C = weightMatrixCone L - rays C - linealitySpace C - dim C - Text - In the above example, we can see that adding a vector from the lineality space - can be interpreted as adding a constant to each element in a specific row or column - of the weight matrix. - - For matching fields that are not originally defined by a weight matrix, the cone of weight matrices - allows us to test if the matching field is coherent. The matching field is coherent if and only if - the cone is full dimensional. This is the strategy implemented by the function @TO "isCoherent"@. + the set of weight matrices that induce $\Lambda$ naturally form a polyhedral cone. + The function @TO "weightMatrixCone"@ constructs this cone by writing down a collection of inequalities. + To illustrate this, assume that $(1,2)$ is a tuple of $\Lambda$. A weight matrix $M = (m_{i,j})$ + induces a matching field with the tuple $(1,2)$ if and only if $m_{1,1} + m_{2,2} < m_{1,2} + m_{2,1}$. + Continuing in this way for all other tuples of $\Lambda$ produces the cone of weight matrices. + Note, the inequalities, like the one above, are strict. So, in general, only the interior points of + the cone give rise to generic weight matrices that induce the matching field. Example - L = grMatchingField(2, 3, {{1, 2}, {2, 3}, {3, 1}}) - isCoherent L - dim weightMatrixCone L - Text - In the example above, the cone naturally lives in $\RR^6$ so it is not full dimensional. - Therefore, the matching field is not coherent. + L = diagonalMatchingField(2, 4) + C = weightMatrixCone L + rays C + linealitySpace C + dim C + Text + In the above example, we can see that adding a vector from the lineality space + can be interpreted as adding a constant to each element in a specific row or column + of the weight matrix. + + For matching fields that are not originally defined by a weight matrix, the cone of weight matrices + allows us to test if the matching field is coherent. The matching field is coherent if and only if + the cone is full dimensional. This is the strategy implemented by the function @TO "isCoherent"@. + Example + L = grMatchingField(2, 3, {{1, 2}, {2, 3}, {3, 1}}) + isCoherent L + dim weightMatrixCone L + Text + In the example above, the cone naturally lives in $\RR^6$ so it is not full dimensional. + Therefore, the matching field is not coherent. SeeAlso Subnodes /// @@ -2070,7 +1869,7 @@ doc /// doc /// Key algebraicMatroidBases - (algebraicMatroidBases, GrMatchingField) + (algebraicMatroidBases, GrMatchingField) Headline The bases of the algebraic matroid Usage @@ -2079,24 +1878,24 @@ doc /// L: GrMatchingField Outputs B: List - the bases of the algebraic matroid of the matching field as $k$-subsets + the bases of the algebraic matroid of the matching field as $k$-subsets Description Text Displays the bases of the algebraic matroid associated to the Grassmannian Gr$(k,n)$ matching field - in terms of the $k$-subsets of $[n]$. For more details about the matroid, see the function @TO "algebraicMatroid"@. - Example - L = diagonalMatchingField(2, 4) - netList algebraicMatroidBases L + in terms of the $k$-subsets of $[n]$. For more details about the matroid, see the function @TO "algebraicMatroid"@. + Example + L = diagonalMatchingField(2, 4) + netList algebraicMatroidBases L SeeAlso algebraicMatroid - algebraicMatroidCircuits + algebraicMatroidCircuits Subnodes /// doc /// Key algebraicMatroidCircuits - (algebraicMatroidCircuits, GrMatchingField) + (algebraicMatroidCircuits, GrMatchingField) Headline The bases of the algebraic matroid Usage @@ -2105,25 +1904,24 @@ doc /// L: GrMatchingField Outputs C: List - the circuits of the algebraic matroid of the matching field as $k$-subsets + the circuits of the algebraic matroid of the matching field as $k$-subsets Description Text Displays the circuits of the algebraic matroid associated to the Grassmannian Gr$(k,n)$ matching field - in terms of the $k$-subsets of $[n]$. For more details about the matroid, see the function @TO "algebraicMatroid"@. - Example - L = diagonalMatchingField(2, 5) - netList algebraicMatroidCircuits L + in terms of the $k$-subsets of $[n]$. For more details about the matroid, see the function @TO "algebraicMatroid"@. + Example + L = diagonalMatchingField(2, 5) + netList algebraicMatroidCircuits L SeeAlso algebraicMatroid - algebraicMatroidCircuits + algebraicMatroidCircuits Subnodes /// doc /// Key isToricDegeneration - (isToricDegeneration, GrMatchingField) - (isToricDegeneration, FlMatchingField) + (isToricDegeneration, MatchingField) Headline Does the matching field give rise to a toric degeneration Usage @@ -2132,102 +1930,103 @@ doc /// L: {GrMatchingField, FlMatchingField} Outputs result: Boolean - does the matching field give rise to a toric degeneration + does the matching field give rise to a toric degeneration Description Text A matching field is said to give rise to a toric degeneration (of the corresponding variety: Grassmannian - or partial flag variety) if the matching field ideal is equal to the initial ideal of the Pluecker ideal + or partial flag variety) if the matching field ideal is equal to the initial ideal of the Pluecker ideal with respect the weight order that induces the matching field. For further details on each of these ideals - see the functions @TO "matchingFieldIdeal"@ and @TO "plueckerIdeal"@. - Example - L = diagonalMatchingField(2, 4) - I = plueckerIdeal L - J = matchingFieldIdeal L - J == ideal leadTerm(1, I) - isToricDegeneration L - Text - In the above example, the last two tests are the same. - - If the matching field provided is not defined in terms of a - weight matrix then one is automatically computed for it. - If the matching field is not coherent then this will produce an error. + see the functions @TO "matchingFieldIdeal"@ and @TO "plueckerIdeal"@. + Example + L = diagonalMatchingField(2, 4) + I = plueckerIdeal L + J = matchingFieldIdeal L + J == ideal leadTerm(1, I) + isToricDegeneration L + Text + In the above example, the last two tests are the same. + + If the matching field provided is not defined in terms of a + weight matrix then one is automatically computed for it. + If the matching field is not coherent then this will produce an error. + + A note of caution. While this function takes any object of type @TO "MatchingField"@, the + function will produce an error if the supplied object is not of type @TO "GrMatchingField"@ or + @TO "FlMatchingField"@. SeeAlso matchingFieldIdeal - plueckerIdeal + plueckerIdeal Subnodes /// doc /// Key getTuples - (getTuples, FlMatchingField) - (getTuples, GrMatchingField) + (getTuples, MatchingField) Headline The tuples of a matching field Usage - tuples = getTuples L + tuples = getTuples L Inputs - L: {GrMatchingField, FlMatchingField} + L: {MatchingField, GrMatchingField, FlMatchingField} Outputs tuples: List - A list of subsets of $1, \dots, n$; the tuples of the matching field + A list of subsets of $1, \dots, n$; the tuples of the matching field Description Text A matching field $\Lambda$ for the Grassmannian Gr$(k, n)$ is a collection tuples $\Lambda(J)$ for each - $k$-subset $J \subseteq [n]$. The entries of the tuple form a permutation of $J$, so in some literature - $\Lambda(J)$ is taken to be the element of the symmetric group $\sigma \in S_k$ such that - $\Lambda(J) = (j_{\sigma(1)}, j_{\sigma(2), \dots, j_{\sigma(k)}})$ where $J = \{j_1 < j_2 < \dots < j_k\}$. - Example - L = diagonalMatchingField(2, 4) - getTuples L - Text - The tuples are stored such that their underlying sets are in RevLex order, which is the order - produced by the method @TO "subsets"@. - - For flag matching fields, the tuples are stored as a list of list of tuples for each Grassmannian - matching field contained within. - Example - L = diagonalMatchingField({1,2}, 4) - netList getTuples L + $k$-subset $J \subseteq [n]$. The entries of the tuple form a permutation of $J$, so in some literature + $\Lambda(J)$ is taken to be the element of the symmetric group $\sigma \in S_k$ such that + $\Lambda(J) = (j_{\sigma(1)}, j_{\sigma(2), \dots, j_{\sigma(k)}})$ where $J = \{j_1 < j_2 < \dots < j_k\}$. + Example + L = diagonalMatchingField(2, 4) + getTuples L + Text + The tuples are stored such that their underlying sets are in RevLex order, which is the order + produced by the method @TO "subsets"@. + + For flag matching fields, the tuples are stored as a list of list of tuples for each Grassmannian + matching field contained within. + Example + L = diagonalMatchingField({1,2}, 4) + netList getTuples L SeeAlso grMatchingField - flMatchingField - diagonalMatchingField + flMatchingField + diagonalMatchingField Subnodes /// doc /// Key isCoherent - (isCoherent, FlMatchingField) - (isCoherent, GrMatchingField) + (isCoherent, MatchingField) Headline Is the matching field coherent Usage result = isCoherent L Inputs - L: {GrMatchingField, FlMatchingField} + L: {MatchingField, GrMatchingField, FlMatchingField} Outputs result: Boolean - is the matching field coherent, i.e., induced by a weight matrix + is the matching field coherent, i.e., induced by a weight matrix Description Text We say that a matching field $\Lambda$ is coherent if it is induced by a weight matrix. - Note that we use the minimum convention for weight matrices however for polynomial rings, the - @TO "Weights"@ option for @TO "MonomialOrder"@ uses the maximum convention. - Example - L1 = grMatchingField(2, 4, {{1,2}, {1,3}, {2,3}, {1,4}, {2,4}, {4,3}}) - isCoherent L1 - getWeightMatrix L1 - L2 = grMatchingField(2, 3, {{1,2}, {2,3}, {3,1}}) - isCoherent L2 - Text - In the examples above, the matching fields are defined in terms of their tuples. To check whether the - matching fields are coherent, the weight matrix cone is constructed, see the function @TO "weightMatrixCone"@. - The matching field is coherent if and only if the weight matrix cone is full dimensional. - If the matching field happens to be coherent, then an interior point is used for any further - computations that require a weight matrix. - + Note that we use the minimum convention for weight matrices however for polynomial rings, the + @TO "Weights"@ option for @TO "MonomialOrder"@ uses the maximum convention. + Example + L1 = grMatchingField(2, 4, {{1,2}, {1,3}, {2,3}, {1,4}, {2,4}, {4,3}}) + isCoherent L1 + getWeightMatrix L1 + L2 = grMatchingField(2, 3, {{1,2}, {2,3}, {3,1}}) + isCoherent L2 + Text + In the examples above, the matching fields are defined in terms of their tuples. To check whether the + matching fields are coherent, the weight matrix cone is constructed, see the function @TO "weightMatrixCone"@. + The matching field is coherent if and only if the weight matrix cone is full dimensional. + If the matching field happens to be coherent, then an interior point is used for any further + computations that require a weight matrix. SeeAlso weightMatrixCone Subnodes @@ -2237,32 +2036,32 @@ doc /// Key RowNum Headline - the row of the diagonal weight matrix to permute + The row of the diagonal weight matrix to permute Usage Lgr = matchingFieldFromPermutation(k, n, S, RowNum => r) - Lfl = matchingFieldFromPermutation(kList, n, S, RowNum => r) + Lfl = matchingFieldFromPermutation(kList, n, S, RowNum => r) Inputs k: ZZ - kList: List - n: ZZ - S: List - a permutation of $1, \dots, n$ - r: ZZ - an integer at most $k$ or at most $\max(kList)$ + kList: List + n: ZZ + S: List + a permutation of $1, \dots, n$ + r: ZZ + an integer at most $k$ or at most $\max(kList)$ Outputs Lgr: GrMatchingField - Lfl: FlMatchingField + Lfl: FlMatchingField Description Text The option @TO "RowNum"@ chooses the row of the diagonal matching field weight matrix to permute. - By default the value is $2$. - Example - getWeightMatrix matchingFieldFromPermutation(3, 6, {4,5,6,1,2,3}, RowNum => 1) - getWeightMatrix matchingFieldFromPermutation(3, 6, {4,5,6,1,2,3}, RowNum => 2) - getWeightMatrix matchingFieldFromPermutation(3, 6, {4,5,6,1,2,3}, RowNum => 3) + By default the value is $2$. + Example + getWeightMatrix matchingFieldFromPermutation(3, 6, {4,5,6,1,2,3}, RowNum => 1) + getWeightMatrix matchingFieldFromPermutation(3, 6, {4,5,6,1,2,3}, RowNum => 2) + getWeightMatrix matchingFieldFromPermutation(3, 6, {4,5,6,1,2,3}, RowNum => 3) SeeAlso matchingFieldFromPermutation - diagonalMatchingField + diagonalMatchingField Subnodes /// @@ -2271,27 +2070,64 @@ doc /// Key ExtraZeroRows Headline - enlarging a matrix with zero rows + Enlarge a (weight) matrix with zero rows Description Text The option @TO "ExtraZeroRows"@ is used by the functions @TO "matchingFieldPolytope"@ and - @TO "weightMatrixCone"@. In each case, the option controls the ambient space of the polyhedron. - By default the value is zero. It is typically used internally for computing Minkowski sums of - polyhedra that would ordinarily belong to different ambient spaces. - Example - L = diagonalMatchingField(2, 4) - P = matchingFieldPolytope(L, ExtraZeroRows => 1) - vertices P - C = weightMatrixCone(L, ExtraZeroRows => 1) - rays C - Text - In the above examples, the polyhedral object typically live in the space $\RR^{2 \times 4}$. However, - by adding an additional row, the objects live in $\RR^{3 \times 4} \cong \RR^{12}$. - Reading the down the entries of columns corresponds to reading row-by-row the entries of the corresponding - matrix. + @TO "weightMatrixCone"@. In each case, the option controls the ambient space of the polyhedron. + By default the value is zero. It is typically used internally for computing Minkowski sums of + polyhedra that would ordinarily belong to different ambient spaces. + Example + L = diagonalMatchingField(2, 4) + P = matchingFieldPolytope(L, ExtraZeroRows => 1) + vertices P + C = weightMatrixCone(L, ExtraZeroRows => 1) + rays C + Text + In the above examples, the polyhedral objects typically live in the space $\RR^{2 \times 4}$. However, + by adding an extra row, the objects live in $\RR^{3 \times 4} \cong \RR^{12}$. + Reading the entries of columns from top-to-bottom corresponds to reading the entries of the corresponding + matrix row-by-row. SeeAlso matchingFieldPolytope - weightMatrixCone + weightMatrixCone + Subnodes +/// + + +doc /// + Key + MatchingField + Headline + The class of Matching Fields + Description + Text + A MatchingField is the common ancestor of Grassmannian Matching Fields @TO "GrMatchingField"@ + and partial flag Matching Fields @TO "FlMatchingField"@. A MatchingField is constructed with + the function @TO "matchingField"@. + + Two MatchingFields are equal if and only if their tuples are the same. + + {\bf Technical details.} + A MatchingField is derived from the class @TO "HashTable"@. Each + MatchingField has the following fields: + + @UL{ + {"tupleMaxSize of type ZZ"}, + {"tupleSizeList of type List"}, + {"tupleMaxValue of type ZZ"}, + {"tuples of type List, accessible with ", TO {"getTuples"}}, + {"cache"} + }@ + + Everything else, including: weight matrices; polynomial rings, maps and ideals; polyhedra + such as the weight matrix cone and matching field polytope, are stored inside the cache. + Note that the package does not export the keys, such as tupleMaxSize or tupleMaxValue. + If you wish to directly address the contents of the + GrMatchingField, then use "debug MatchingFields". + SeeAlso + FlMatchingField + GrMatchingField Subnodes /// @@ -2300,38 +2136,39 @@ doc /// Key GrMatchingField Headline - the class of Grassmannian matching fields + the class of Grassmannian matching fields Description Text - Common ways to define Grassmannian matching fields: - - @UL { - {TO {"grMatchingField"}, "-- defined in terms of tuples or a weight matrix"}, - {TO {"diagonalMatchingField"}, "-- the diagonal matching field"}, - {TO {"matchingFieldFromPermutation"}, "-- family of matching fields indexed by permutations"} - }@ - - Two Grassmannian matching fields are said to be equal if and only if their tuples are the same. - - {\bf Technical details.} - A Grassmannian matching field is derived from the class @TO "HashTable"@. All - Grassmannian matching fields have the following fields: - - @ UL { - {"k of type ZZ"}, - {"n of type ZZ"}, - {"tuples of type List, accessible with", TO {"getTuples"}}, - {"cache"} - }@ - - Everything else, including: weight matrices; polynomial rings, maps and ideals; polyhedra - such as the weight matrix cone and matching field polytope, are all stored inside the cache. - Note that the package does not export the keys, such as k or n. - If you wish to directly address the contents of the - GrMatchingField, then use "debug MatchingFields". + Common ways to define Grassmannian matching fields: + + @UL{ + {TO {"grMatchingField"}, "-- defined in terms of tuples or a weight matrix"}, + {TO {"diagonalMatchingField"}, "-- the diagonal matching field"}, + {TO {"matchingFieldFromPermutation"}, "-- family of matching fields indexed by permutations"} + }@ + + Two Grassmannian matching fields are said to be equal if and only if their tuples are the same. + + {\bf Technical details.} + A Grassmannian matching field is derived from the class @TO "MatchingField"@. All + Grassmannian matching fields have the following fields: + + @UL{ + {"tupleMaxSize of type ZZ"}, + {"tupleSizeList of type List"}, + {"tupleMaxValue of type ZZ"}, + {"tuples of type List, accessible with ", TO {"getTuples"}}, + {"cache"} + }@ + + Everything else, including: weight matrices; polynomial rings, maps and ideals; polyhedra + such as the weight matrix cone and matching field polytope, are all stored inside the cache. + Note that the package does not export the keys, such as tupleMaxSize or tupleMaxValue. + If you wish to directly address the contents of the + GrMatchingField, then use "debug MatchingFields". SeeAlso FlMatchingField - grMatchingField + grMatchingField Subnodes /// @@ -2340,37 +2177,37 @@ doc /// Key FlMatchingField Headline - the class of matching fields for partial flag varieties + The class of Matching Fields for partial flag varieties Description Text - Common ways to define flag matching fields: - - @ UL { - {TO {"flMatchingField"}, "-- defined in terms of tuples or a weight matrix"}, - {TO {"diagonalMatchingField"}, "-- the diagonal matching field"}, - {TO {"matchingFieldFromPermutation"}, "-- family of matching fields indexed by permutations"} - }@ - - {\bf Technical details.} - A flag matching field is derived from the class @TO "HashTable"@. All - flag matching fields have the following fields: - - @ UL { - {"kList of type ZZ"}, - {"n of type ZZ"}, - {"grMatchingFieldList of type List, the list of Grassmannian matching fields contained within, - accessible with the function", TO "getGrMatchingFields"}, - {"cache"} - }@ - - Everything else, including: weight matrices; polynomial rings, maps and ideals; polyhedra - such as the weight matrix cone and matching field polytope, are all stored inside the cache. - Note that the package does not export the keys, such as kList or n. - If you wish to directly address the contents of the - FlMatchingField, then use "debug MatchingFields". + Common ways to define flag matching fields: + + @UL{ + {TO {"flMatchingField"}, "-- defined in terms of tuples or a weight matrix"}, + {TO {"diagonalMatchingField"}, "-- the diagonal matching field"}, + {TO {"matchingFieldFromPermutation"}, "-- family of matching fields indexed by permutations"} + }@ + + {\bf Technical details.} + A flag matching field is derived from the class @TO "MatchingField"@. All + flag matching fields have the following fields: + + @UL{ + {"tupleSizeList of type List"}, + {"tupleMaxSize of type ZZ"}, + {"tupleMaxValue of type ZZ"}, + {"tuples of type List, accessible with ", TO {"getTuples"}}, + {"cache"} + }@ + + Everything else, including: weight matrices; polynomial rings, maps and ideals; polyhedra + such as the weight matrix cone and matching field polytope, are all stored inside the cache. + Note that the package does not export the keys, such as tupleSizeList or tupleMaxValue. + If you wish to directly address the contents of the + FlMatchingField, then use "debug MatchingFields". SeeAlso GrMatchingField - flMatchingField + flMatchingField Subnodes /// @@ -2378,105 +2215,109 @@ doc /// Key matchingFieldFromPermutation (matchingFieldFromPermutation, List, ZZ, List) - (matchingFieldFromPermutation, ZZ, ZZ, List) - [matchingFieldFromPermutation, UsePrimePowers] - [matchingFieldFromPermutation, PowerValue] - [matchingFieldFromPermutation, RowNum] - [matchingFieldFromPermutation, ScalingCoefficient] + (matchingFieldFromPermutation, ZZ, ZZ, List) + [matchingFieldFromPermutation, UsePrimePowers] + [matchingFieldFromPermutation, PowerValue] + [matchingFieldFromPermutation, RowNum] + [matchingFieldFromPermutation, ScalingCoefficient] Headline - matching field parametrised by permutations + Matching field parametrised by permutations Usage Lgr = matchingFieldFromPermutation(k, n, S) - Lfl = matchingFieldFromPermutation(kList, n, S) + Lfl = matchingFieldFromPermutation(tupleSizeList, n, S) Inputs k: ZZ - positive integer; the size of the tuples of the Grassmannian matching field - kList: List + positive integer; the size of the tuples of the Grassmannian matching field + tupleSizeList: List positive integers; the sizes of the tuples of the flag matching field - n: ZZ - positive integer; the tuples have entries in 1 .. n + n: ZZ + positive integer; the tuples have entries in 1 .. n + S: List + permutation of 1 .. n RowNum => ZZ - which row of the digonal weight matrix to permute - UsePrimePowers => Boolean - use multiples of prime power used for the entries of the diagonal weight matrix - PowerValue => ZZ - use multiples of powers this value in the diagonal weight matrix - ScalingCoefficient => ZZ - the value by which to scale the permuted row of the diagonal weight matrix + which row of the digonal weight matrix to permute + UsePrimePowers => Boolean + use multiples of prime power used for the entries of the diagonal weight matrix + PowerValue => ZZ + use multiples of powers this value in the diagonal weight matrix + ScalingCoefficient => ZZ + the value by which to scale the permuted row of the diagonal weight matrix Outputs - Lgr: GrMatchingField - Lfl: FlMatchingField + Lgr: GrMatchingField + Lfl: FlMatchingField Description Text - Let $M_0 \in \RR^{k \times n}$ be a matrix that induced the diagonal matching field. - Usually, we take this matrix to be $(m_{i,j})$ with $m_{i,j} = (n-j)n^(i-2)$ if $i > 1$ and $m_{i,j} = 0$ if $i = 1$. - Note that this matrix is different to the weight matrix of the matching field produced by the function @TO "diagonalMatchingField"@, - however the matching fields are the same. - Given a permutation $\sigma \in S_n$, the matching field associated to $\sigma$ has weight matrix - $M_\sigma$, which is the same as $M_0$ except in the second row, which is given by $\sigma(1), \sigma(2), \dots, \sigma(n)$. - If $\sigma = (n, n-1, \dots, 1) \in S_n$ is the permutation written in single-line notation, then the matching field induced by $M_\sigma$ is - the diagonal matching field. - Example - L0 = diagonalMatchingField(3, 6) - getWeightMatrix L0 - L1 = matchingFieldFromPermutation(3, 6, {6,5,4,3,2,1}) - getWeightMatrix L1 - L0 == L1 - L2 = matchingFieldFromPermutation(3, 6, {1,3,2,6,4,5}) - getWeightMatrix L2 - L3 = matchingFieldFromPermutation({1,2,3}, 6, {1,4,2,3,6,5}) - getWeightMatrix L3 - Text - The optional argument @TO "RowNum"@ is used to change which row of $M_0$ is permuted. - Example - L4 = matchingFieldFromPermutation(3, 6, {1,4,2,3,6,5}, RowNum => 3) - getWeightMatrix L4 - Text - The optional argument @TO "UsePrimePowers"@ is used to modify the original diagonal weight matrix $M_0$. If - the option is set to true then we use the matrix with entries $m_{i,j} = (n-j) p^(i-2)$ if $i>1$ and $m_{i,j} = 0$ - if $i = 1$, where $p \ge n$ is the smallest prime number greater than or equal to $n$. + Let $M_0 \in \RR^{k \times n}$ be a matrix that induces the diagonal matching field. + Usually, we take this matrix to be $(m_{i,j})$ with $m_{i,j} = (n-j)n^(i-2)$ if $i > 1$ and $m_{i,j} = 0$ if $i = 1$. + Note that this matrix is different to the weight matrix of the matching field produced by the function @TO "diagonalMatchingField"@, + however the matching fields are the same. + Given a permutation $\sigma \in S_n$, the matching field associated to $\sigma$ has weight matrix + $M_\sigma$, which is the same as $M_0$ except for the second row. + The second row of $M_\sigma$ is obtained from the second row of $M$ by permuting the entries according to $\sigma$. + Explicitly, they are given by $\sigma(1), \sigma(2), \dots, \sigma(n)$. + For instance, if $\sigma = (n, n-1, \dots, 1) \in S_n$ is the permutation written in single-line notation, then the matching field induced by $M_\sigma$ is + the diagonal matching field. Example - L5 = matchingFieldFromPermutation(3, 6, {1,3,2,4,6,5}, UsePrimePowers => true) - getWeightMatrix L5 - Text - The optional argument @TO "PowerValue"@ is used to give a specific value to $p$ in the diagonal weight matrix - as described above. If the value is not positive, then the argument is ignored. This option should be used carefully. - If the power value is set incorrectly (typically by setting a value that is too low) then the weight matrix may not be {\it generic}, - i.e., it does not define a matching field. However, in such a case, the function produces a matching field without error, - as shown in the example $L7$ below. Working with such matching fields may lead to unexpected behaviours. - Example - L6 = matchingFieldFromPermutation(3, 6, {1,3,2,4,6,5}, PowerValue => 10) - getWeightMatrix L6 - L7 = matchingFieldFromPermutation(3, 6, {5,4,3,2,1,0}, PowerValue => 1) - getWeightMatrix L7 - Text - Any positive integer supplied to the argument @TO "PowerValue"@ takes precedence over @TO "UsePrimePowers"@. - The optional argument @TO "ScalingCoefficient"@ is used to scale the entries of the row that is permuted by the permutation. - If @TO "UsePrimePowers"@ is set to true, and $p \ge n$ is the smallest prime number less than $n$, then the scaling coefficient - can be set to any $c \in \{1, 2, \dots, p-1\}$ and the resulting weight matrix is guaranteed to be generic. - Example - L8 = matchingFieldFromPermutation(3, 6, {6,1,5,2,3,4}, UsePrimePowers => true, ScalingCoefficient => 3) - getWeightMatrix L8 - isToricDegeneration L8 - Text - The above is an example of a {\it hexagonal matching field}, which does not give rise to a toric degeneration of - the Grassmannian Gr$(3, 6)$. + L0 = diagonalMatchingField(3, 6) + getWeightMatrix L0 + L1 = matchingFieldFromPermutation(3, 6, {6,5,4,3,2,1}) + getWeightMatrix L1 + L0 == L1 + L2 = matchingFieldFromPermutation(3, 6, {1,3,2,6,4,5}) + getWeightMatrix L2 + L3 = matchingFieldFromPermutation({1,2,3}, 6, {1,4,2,3,6,5}) + getWeightMatrix L3 + Text + The optional argument @TO "RowNum"@ is used to change which row of $M_0$ is permuted. + Example + L4 = matchingFieldFromPermutation(3, 6, {1,4,2,3,6,5}, RowNum => 3) + getWeightMatrix L4 + Text + The optional argument @TO "UsePrimePowers"@ is used to modify the original diagonal weight matrix $M_0$. If + the option is set to true then we use the matrix with entries $m_{i,j} = (n-j) p^(i-2)$ if $i>1$ and $m_{i,j} = 0$ + if $i = 1$, where $p \ge n$ is the smallest prime number greater than or equal to $n$. + Example + L5 = matchingFieldFromPermutation(3, 6, {1,3,2,4,6,5}, UsePrimePowers => true) + getWeightMatrix L5 + Text + The optional argument @TO "PowerValue"@ is used to give a specific value to $p$ in the diagonal weight matrix + as described above. If the value is not positive, then the argument is ignored. This option should be used carefully. + If the power value is set incorrectly (typically by setting a value that is too low) then the weight matrix may not be {\it generic}, + i.e., it does not define a matching field. However, in such a case, the function produces a matching field without error, + as shown in the example $L7$ below. Working with such matching fields may lead to unexpected behaviours. + Example + L6 = matchingFieldFromPermutation(3, 6, {1,3,2,4,6,5}, PowerValue => 10) + getWeightMatrix L6 + L7 = matchingFieldFromPermutation(3, 6, {5,4,3,2,1,0}, PowerValue => 1) + getWeightMatrix L7 + Text + Any positive integer supplied to the argument @TO "PowerValue"@ takes precedence over @TO "UsePrimePowers"@. + The optional argument @TO "ScalingCoefficient"@ is used to scale the entries of the row that is permuted by the permutation. + If @TO "UsePrimePowers"@ is set to true, and $p \ge n$ is the smallest prime number less than $n$, then the scaling coefficient + can be set to any $c \in \{1, 2, \dots, p-1\}$ and the resulting weight matrix is guaranteed to be generic. + Example + L8 = matchingFieldFromPermutation(3, 6, {6,1,5,2,3,4}, UsePrimePowers => true, ScalingCoefficient => 3) + getWeightMatrix L8 + isToricDegeneration L8 + Text + The above is an example of a {\it hexagonal matching field}, which does not give rise to a toric degeneration of + the Grassmannian Gr$(3, 6)$. SeeAlso RowNum - UsePrimePowers - PowerValue - ScalingCoefficient + UsePrimePowers + PowerValue + ScalingCoefficient Subnodes RowNum - UsePrimePowers - ScalingCoefficient + UsePrimePowers + ScalingCoefficient /// doc /// Key NOBody (NOBody, GrMatchingField) - (NOBody, FlMatchingField) + (NOBody, FlMatchingField) Headline Newton-Okounkov body of the matching field Usage @@ -2485,110 +2326,111 @@ doc /// L: {GrMatchingField, FlMatchingField} Outputs D: Polyhedron - Newton-Okounkov body of the matching field + Newton-Okounkov body of the matching field Description Text - The Pluecker algebra is generated by Pluecker forms given by top-justified - minors of a generic matrix. The Pluecker algebra can be constructed with - the function @TO "plueckerAlgebra"@, of the image of the Pluecker ring map that can be - accessed with the function @TO "plueckerMap"@. Note that the ambient ring - containing the Pluecker algebra has a weight-based term order that comes from - the matching field. We compute a subalgebra basis (SAGBI basis) using the - package @TO "SubalgebraBases"@ for the Pluecker algebra. - - The Newton-Okounkov body of the matching field is constructed from this subalgebra basis. - In the case of Grassmannian matching fields, the NO body is simply the convex - hull of the exponent vectors of the initial terms of the subalgebra basis. - If the matching field gives rise to a toric degeneration (see the function @TO "isToricDegeneration"@) - then the NO body coincides with the matching field polytope - (see @TO "matchingFieldPolytope"@) because the maximal minors form a subalgebra basis for the Pluecker algebra. - Example - L = diagonalMatchingField(2, 4) - P = matchingFieldPolytope L - vertices P - noBody = NOBody L - vertices noBody - P == noBody - Text - In the case of flag matching fields, the NO body is computed in a similar way. - First a subalgebra basis is computed for the Pluecker algebra. - However, to construct the NO body from the subalgebra basis, we need to take into account - the grading on the Pluecker forms. From the geometric perspective, we are simply using the - Segre embedding to view the flag variety as a subvariety of a suitably large projective space. - Example - L = diagonalMatchingField({1,2}, 4) - noBody = NOBody L - vertices noBody - noBody == matchingFieldPolytope L - Text - Note that the matching field polytope is equal to the NO body if and only if the matching field - gives rise to a toric degeneration. So, for a {\it hexagonal matching field} for Gr$(3,6)$, the - NO body has an additional vertex. We construct a hexagonal matching field using the function - @TO "matchingFieldFromPermutation"@ as follows. - Example - L = matchingFieldFromPermutation(3, 6, {6,1,5,2,3,4}, UsePrimePowers => true, ScalingCoefficient => 3) - isToricDegeneration L - vertices NOBody L + The Pluecker algebra is generated by Pluecker forms given by top-justified + minors of a generic matrix. The Pluecker algebra can be constructed with + the function @TO "plueckerAlgebra"@ and coincides with + the image of the Pluecker ring map (see the function @TO "plueckerMap"@). + Note that the ambient ring + containing the Pluecker algebra has a weight-based term order that comes from + the matching field. We compute a subalgebra basis (SAGBI basis) using the + package @TO "SubalgebraBases"@ for the Pluecker algebra. + + The Newton-Okounkov body of the matching field is constructed from this subalgebra basis. + In the case of Grassmannian matching fields, the NO body is simply the convex + hull of the exponent vectors of the initial terms of the subalgebra basis. + If the matching field gives rise to a toric degeneration (see the function @TO "isToricDegeneration"@) + then the NO body coincides with the matching field polytope + (see @TO "matchingFieldPolytope"@) because the maximal minors form a subalgebra basis for the Pluecker algebra. + Example + L = diagonalMatchingField(2, 4) + P = matchingFieldPolytope L + vertices P + noBody = NOBody L + vertices noBody + P == noBody + Text + In the case of flag matching fields, the NO body is computed in a similar way. + First a subalgebra basis is computed for the Pluecker algebra. + To construct the NO body from the subalgebra basis, we need to take into account + the grading on the Pluecker forms. From a geometric perspective, we are simply using the + Segre embedding to view the flag variety as a subvariety of a suitably large projective space. + Example + L = diagonalMatchingField({1,2}, 4) + noBody = NOBody L + vertices noBody + noBody == matchingFieldPolytope L + Text + Note that the matching field polytope is equal to the NO body if and only if the matching field + gives rise to a toric degeneration. So, for a {\it hexagonal matching field} for Gr$(3,6)$, the + NO body has an additional vertex. We construct a hexagonal matching field using the function + @TO "matchingFieldFromPermutation"@ as follows. + Example + L = matchingFieldFromPermutation(3, 6, {6,1,5,2,3,4}, UsePrimePowers => true, ScalingCoefficient => 3) + isToricDegeneration L + vertices NOBody L SeeAlso matchingFieldFromPermutation - isToricDegeneration - SubalgebraBases + isToricDegeneration + SubalgebraBases plueckerAlgebra - plueckerMap + plueckerMap Subnodes /// doc /// Key UsePrimePowers - PowerValue + PowerValue Headline - use a diagonal weight matrix with multiples of certain powers + Use a diagonal weight matrix with multiples of certain powers Usage Lgr = matchingFieldFromPermutation(k, n, S, UsePrimePowers => b, PowerValue => v) - Lfl = matchingFieldFromPermutation(kList, n, S, UsePrimePowers => b, PowerValue => v) + Lfl = matchingFieldFromPermutation(tupleSizeList, n, S, UsePrimePowers => b, PowerValue => v) Inputs k: ZZ - kList: List - n: ZZ - S: List - a permutation of $1, \dots, n$ - r: Boolean - set whether prime powers are to be used - v: ZZ - set the value of the powers to be used + tupleSizeList: List + n: ZZ + S: List + a permutation of $1, \dots, n$ + r: Boolean + set whether prime powers are to be used + v: ZZ + set the value of the powers to be used Outputs Lgr: GrMatchingField - Lfl: FlMatchingField + Lfl: FlMatchingField Description Text - The options @TO "UsePrimePowers"@ and @TO "PowerValue"@ are optional arguments for the function - @TO "matchingFieldFromPermutation"@, which constructs a matching field by permuting a row (usually the second row) - of a weight matrix that gives rise to the diagonal matching field. - + The options @TO "UsePrimePowers"@ and @TO "PowerValue"@ are optional arguments for the function + @TO "matchingFieldFromPermutation"@, which constructs a matching field by permuting a row (usually the second row) + of a weight matrix that gives rise to the diagonal matching field. + If the option @TO "UsePrimePowers"@ is set to true, then the underlying {\it diagonal} weight matrix is given by - $M_0 = (m_{i,j})$ with $m_{i,j} = (n-j) p^(i-2)$ if $i > 1$ and $m_{i,j} = 0$ if $i = 1$, where - $p \ge n$ is the smallest prime number greater than or equal to $n$. - Example - getWeightMatrix matchingFieldFromPermutation(3, 6, {1,2,3,4,5,6}, UsePrimePowers => false) - getWeightMatrix matchingFieldFromPermutation(3, 6, {1,2,3,4,5,6}, UsePrimePowers => true) - Text - To set the value of $p$ in the above matrix to a specific value, use the option @TO "PowerValue"@. - Example - getWeightMatrix matchingFieldFromPermutation(3, 6, {1,2,3,4,5,6}, PowerValue => 10) - Text - The option @TO "PowerValue"@ overrides the option @TO "UsePrimePowers"@. - - {\bf Warning.} Certain values for the option @TO "PowerValue"@ will produce weight matrices that are - not {\it generic}, i.e., the initial term of the corresponding Pluecker forms are not all monomials, - hence the weight matrix does not define a matching field. The function @TO "matchingFieldFromPermutation"@ will - produce a matching field, which may lead to unexpected behaviours. - - {\bf Precise details.} The value of $p$ is determined as follows. - The option @TO "PowerValue"@ is used if it is a positive value. If @TO "PowerValue"@ is not positive - then the function checks to see if the option @TO "UsePrimePowers"@ is set to true. If it is, then - the value of $p$ is set to be the small prime number greater than or equal to $n$. Otherwise, if - @TO "UsePrimePowers"@ is set to false, then $p$ is set to be $n$. + $M_0 = (m_{i,j})$ with $m_{i,j} = (n-j) p^(i-2)$ if $i > 1$ and $m_{i,j} = 0$ if $i = 1$, where + $p \ge n$ is the smallest prime number greater than or equal to $n$. + Example + getWeightMatrix matchingFieldFromPermutation(3, 6, {1,2,3,4,5,6}, UsePrimePowers => false) + getWeightMatrix matchingFieldFromPermutation(3, 6, {1,2,3,4,5,6}, UsePrimePowers => true) + Text + To set the value of $p$ in the above matrix to a specific value, use the option @TO "PowerValue"@. + Example + getWeightMatrix matchingFieldFromPermutation(3, 6, {1,2,3,4,5,6}, PowerValue => 10) + Text + The option @TO "PowerValue"@ overrides the option @TO "UsePrimePowers"@. + + {\bf Warning.} Certain values for the option @TO "PowerValue"@ will produce weight matrices that are + not {\it generic}, i.e., the initial term of the corresponding Pluecker forms are not all monomials, + hence the weight matrix does not define a matching field. The function @TO "matchingFieldFromPermutation"@ will + produce a matching field, which may lead to unexpected behaviours. + + {\bf Precise details.} The value of $p$ is determined as follows. + The option @TO "PowerValue"@ is used if it is a positive value. If @TO "PowerValue"@ is not positive + then the function checks to see if the option @TO "UsePrimePowers"@ is set to true. If it is, then + the value of $p$ is set to be the small prime number greater than or equal to $n$. Otherwise, if + @TO "UsePrimePowers"@ is set to false, then $p$ is set to be $n$. SeeAlso matchingFieldFromPermutation RowNum @@ -2599,39 +2441,39 @@ doc /// Key ScalingCoefficient Headline - scale the permuted row of the weight matrix + Scale the permuted row of the weight matrix Usage Lgr = matchingFieldFromPermutation(k, n, S, ScalingCoefficient => c) - Lfl = matchingFieldFromPermutation(kList, n, S, ScalingCoefficient => c) + Lfl = matchingFieldFromPermutation(tupleSizeList, n, S, ScalingCoefficient => c) Inputs k: ZZ - kList: List - n: ZZ - S: List - a permutation of $1, \dots, n$ - c: ZZ - scale the value of permuted row by $c$ + tupleSizeList: List + n: ZZ + S: List + a permutation of $1, \dots, n$ + c: ZZ + scale the value of permuted row by $c$ Outputs Lgr: GrMatchingField - Lfl: FlMatchingField + Lfl: FlMatchingField Description Text - The function @TO "matchingFieldFromPermutation"@ constructs a weight matrix - by permuting the row of a weight matrix that induces the diagonal matching field. - The option @TO "ScalingCoefficient"@ sets the scaling coefficient of the row - being permuted, which by default is $1$. - - Note that by setting the option @TO "UsePrimePowers"@ to true, it guarantees that the - weight matrix is {\it generic}, i.e., the matching field is well defined, as long as the scaling coefficient is less - than the prime power used. - Example - getWeightMatrix matchingFieldFromPermutation(3, 6, {1, 3, 2, 4, 6, 5}, UsePrimePowers => true, ScalingCoefficient => 1) - getWeightMatrix matchingFieldFromPermutation(3, 6, {1, 3, 2, 4, 6, 5}, UsePrimePowers => true, ScalingCoefficient => 2) - getWeightMatrix matchingFieldFromPermutation(3, 6, {1, 3, 2, 4, 6, 5}, UsePrimePowers => true, ScalingCoefficient => 3) + The function @TO "matchingFieldFromPermutation"@ constructs a weight matrix + by permuting the row of a weight matrix that induces the diagonal matching field. + The option @TO "ScalingCoefficient"@ sets the scaling coefficient of the row + being permuted, which by default is $1$. + + Note that by setting the option @TO "UsePrimePowers"@ to true, it guarantees that the + weight matrix is {\it generic}, i.e., the matching field is well defined, as long as the scaling coefficient is less + than the prime power used. + Example + getWeightMatrix matchingFieldFromPermutation(3, 6, {1, 3, 2, 4, 6, 5}, UsePrimePowers => true, ScalingCoefficient => 1) + getWeightMatrix matchingFieldFromPermutation(3, 6, {1, 3, 2, 4, 6, 5}, UsePrimePowers => true, ScalingCoefficient => 2) + getWeightMatrix matchingFieldFromPermutation(3, 6, {1, 3, 2, 4, 6, 5}, UsePrimePowers => true, ScalingCoefficient => 3) SeeAlso matchingFieldFromPermutation RowNum - UsePrimePowers + UsePrimePowers Subnodes /// @@ -2639,194 +2481,193 @@ doc /// Key diagonalMatchingField (diagonalMatchingField, ZZ, ZZ) - (diagonalMatchingField, List, ZZ) - (diagonalMatchingField, ZZ) + (diagonalMatchingField, List, ZZ) + (diagonalMatchingField, ZZ) Headline the diagonal matching field Usage Lgr = diagonalMatchingField(k, n) - Lfl = diagonalMatchingField(kList, n) - Lfl = diagonalMatchingField(n) + Lfl = diagonalMatchingField(tupleSizeList, n) + Lfl = diagonalMatchingField(n) Inputs k: ZZ - kList: List - n: ZZ + tupleSizeList: List + n: ZZ Outputs Lgr: GrMatchingField - diagonal Grassmannian matching field - Lfl: FlMatchingField - diagonal flag matching field + diagonal Grassmannian matching field + Lfl: FlMatchingField + diagonal flag matching field Description Text - The diagonal matching field is defined to be the matching field - whose tuples are all in ascending order. It is a coherent matching field - so it is induced by a weight matrix. - - The weight matrix used to construct the diagonal matching field us given by - $M = (m_{i,j})$ with $m_{i,j} = (i-1)(n-j+1)$. - Example - L = diagonalMatchingField(3, 6) - getWeightMatrix L - Text - The function @TO "diagonalMatchingField"@ can be used in three different ways. - If it is supplied two integers $(k,n)$ then it produces the diagonal matching field - for the Grassmannian, as shown in the above example. - If it is supplied a single integer $n$, then it produces the diagonal matching field - for the full flag variety. The matching fields of the full flag variety have tuples of - size $1, 2, \dots, n-1$. - The function can be made to produce diagonal matching fields for partial flag varieties - by supplying it a list $kList$ and integer $n$. The sizes of the tuples are the entries - of $kList$. - Example - L = diagonalMatchingField 4; - netList getTuples L - L = diagonalMatchingField({1, 2}, 5); - netList getTuples L - Text - Diagonal matching fields always give rise to toric degenerations - of Grassmannians and flag varieties. In the literature, - this toric degeneration is also known as Gelfand-Tsetlin - degeneration. The matching field polytopes for the diagonal matching field, - which can be constructed with the function @TO "matchingFieldPolytope"@, - are unimodularly equivalent to Gelfand-Tsetlin polytopes. + The diagonal matching field is defined to be the matching field + whose tuples are all in ascending order. It is a coherent matching field + so it is induced by a weight matrix. + + The weight matrix used to construct the diagonal matching field us given by + $M = (m_{i,j})$ with $m_{i,j} = (i-1)(n-j+1)$. + Example + L = diagonalMatchingField(3, 6) + getWeightMatrix L + Text + The function @TO "diagonalMatchingField"@ can be used in three different ways. + If it is supplied two integers $(k,n)$ then it produces the diagonal matching field + for the Grassmannian, as shown in the above example. + If it is supplied a single integer $n$, then it produces the diagonal matching field + for the full flag variety. The matching fields of the full flag variety have tuples of + size $1, 2, \dots, n-1$. + The function can be made to produce diagonal matching fields for partial flag varieties + by supplying it a list $tupleSizeList$ and integer $n$. The sizes of the tuples are the entries + of $tupleSizeList$. + Example + L = diagonalMatchingField 4; + netList getTuples L + L = diagonalMatchingField({1, 2}, 5); + netList getTuples L + Text + Diagonal matching fields always give rise to toric degenerations + of Grassmannians and flag varieties. In the literature, + this toric degeneration is also known as Gelfand-Tsetlin + degeneration. The matching field polytopes for the diagonal matching field, + which can be constructed with the function @TO "matchingFieldPolytope"@, + are unimodularly equivalent to Gelfand-Tsetlin polytopes. SeeAlso GrMatchingField - FlMatchingField - isToricDegeneration - matchingFieldPolytope + FlMatchingField + isToricDegeneration + matchingFieldPolytope Subnodes /// doc /// Key matchingFieldRingMap - (matchingFieldRingMap, FlMatchingField) - (matchingFieldRingMap, GrMatchingField) - [matchingFieldRingMap, MonomialOrder] + (matchingFieldRingMap, MatchingField) + [matchingFieldRingMap, MonomialOrder] Headline monomial map of the matching field Usage m = matchingFieldRingMap L Inputs - L: {GrMatchingField, FlMatchingField} - MonomialOrder => String - either "default" or "none" (supply "none" only when $L$ is not coherent) + L: {MatchingField, GrMatchingField, FlMatchingField} + MonomialOrder => String + either "default" or "none" (supply "none" only when $L$ is not coherent) Outputs m: RingMap - monomial ring map whose kernel is the matching field ideal + monomial ring map whose kernel is the matching field ideal Description Text - Each tuple $J = (j_1, j_2, \dots, j_k)$ of a matching field defines a monomial given by - $m(J) = c x_{1, j_1} x_{2, j_2} \dots x_{k, j_k}$ where the coefficient $c \in \{+1, -1\}$ is - the sign of the permutation that permutes $J$ into ascending order. Equivalently, - $c = (-1)^d$ where $d = |\{(a, b) \in [k]^2 : a < b, j_a > j_b \}|$ is the number of descents of - $J$. The monomial $m(J)$ is the lead term of the corresponding Pluecker form with respect to the - weight order given by the matching field. - Example - L = matchingFieldFromPermutation(2, 4, {2, 3, 4, 1}) - getTuples L - matchingFieldRingMap L - plueckerForms = matrix plueckerMap L - leadTerm plueckerForms - leadTerm plueckerForms == matrix matchingFieldRingMap L - Text - Note that the polynomial rings have weight-based term orders that depend on a weight matrix that - induces the matching field. So if the matching field supplied is not coherent then function gives an - error. To check that a matching field is coherent use the function @TO "isCoherent"@. + Each tuple $J = (j_1, j_2, \dots, j_k)$ of a matching field defines a monomial given by + $m(J) = c x_{1, j_1} x_{2, j_2} \dots x_{k, j_k}$ where the coefficient $c \in \{+1, -1\}$ is + the sign of the permutation that permutes $J$ into ascending order. Equivalently, + $c = (-1)^d$ where $d = |\{(a, b) \in [k]^2 : a < b, j_a > j_b \}|$ is the number of descents of + $J$. The monomial $m(J)$ is the lead term of the corresponding Pluecker form with respect to the + weight order given by the matching field. + Example + L = matchingFieldFromPermutation(2, 4, {2, 3, 4, 1}) + getTuples L + matchingFieldRingMap L + plueckerForms = matrix plueckerMap L + leadTerm plueckerForms + leadTerm plueckerForms == matrix matchingFieldRingMap L + Text + Note that the polynomial rings have weight-based term orders that depend on a weight matrix that + induces the matching field. If the matching field supplied is not coherent then the rings will + have the default GRevLex monomial order. If no weight matrix was supplied in the construction of + the matching field, then one will be computed, see @TO "getWeightMatrix"@. + To check that a matching field is coherent use the function @TO "isCoherent"@. SeeAlso getTuples - plueckerMap - isCoherent + plueckerMap + isCoherent Subnodes /// doc /// Key getWeightPluecker - (getWeightPluecker, FlMatchingField) - (getWeightPluecker, GrMatchingField) + (getWeightPluecker, MatchingField) Headline weight of the Pluecker variables induced by the weight matrix Usage W = getWeightPluecker L Inputs - L: {GrMatchingField, FlMatchingField} + L: {MatchingField, GrMatchingField, FlMatchingField} Outputs W: List - weights of the Pluecker variables induced by the matching field + weights of the Pluecker variables induced by the matching field Description Text - Suppose that a coherent matching field is induced by a $k \times n$ weight matrix $M$. - The Pluecker forms are minors of a generic matrix of variables. For example, for the Grassmannian - the Pluecker forms are the maximal minors. The weight matrix $M$ is {\it generic}, which is equivalent - to the property: the initial form of each Pluecker form with respect to $M$ is a monomial. - The weight of the initial term of each Pluecker form is the induced weight on the ring in the Pluecker - variables, which is given by the function @TO "getWeightPluecker"@. By convention, the Pluecker variables - are listed such that their subsets are in RevLex order, which is the order given by the function @TO "subsets"@. - - An equivalent formulation is: the Pluecker weight vector is the tuple of tropical determinants of $M$, also - known as the image of $M$ under the {\it tropical Stiefel map} (or its natural generalisation to partial - flag varieties). + Suppose that a coherent matching field is induced by a $k \times n$ weight matrix $M$. + The Pluecker forms are minors of a generic matrix of variables. For example, for the Grassmannian + the Pluecker forms are the maximal minors. The weight matrix $M$ is {\it generic}, which is equivalent + to the property: the initial form of each Pluecker form with respect to $M$ is a monomial. + The weight of the initial term of each Pluecker form is the induced weight on the ring in the Pluecker + variables, which is given by the function @TO "getWeightPluecker"@. By convention, the Pluecker variables + are listed such that their subsets are in RevLex order, which is the order given by the function @TO "subsets"@. + + An equivalent formulation is: the Pluecker weight vector is the tuple of tropical determinants of $M$, also + known as the image of $M$ under the {\it tropical Stiefel map} (or its natural generalisation to partial + flag varieties). + Example + L = diagonalMatchingField(2, 4) + getWeightMatrix L + getWeightPluecker L + Text + Note that the polynomial rings associated to a matching field have weight vectors based on the weight matrix + given by @TO "getWeightMatrix"@ and weight vector given by @TO "getWeightPluecker"@. The package @TO "MatchingFields"@ + uses a minimum convention but the initial terms of polynomials uses the maximum convention so the weight vectors may look + a little different. Example - L = diagonalMatchingField(2, 4) - getWeightMatrix L - getWeightPluecker L - Text - Note that the polynomial rings associated to a matching field have weight vectors based on the weight matrix - given by @TO "getWeightMatrix"@ and weight vector given by @TO "getWeightPluecker"@. The package @TO "MatchingFields"@ - uses a minimum convention but the initial terms of polynomials uses the maximum convention so the weight vectors may look - a little different. - Example - m = matchingFieldRingMap L - describe source m - describe target m + m = matchingFieldRingMap L + describe source m + describe target m SeeAlso getWeightMatrix - matchingFieldRingMap + matchingFieldRingMap Subnodes /// doc /// Key getWeightMatrix - (getWeightMatrix, FlMatchingField) - (getWeightMatrix, GrMatchingField) + (getWeightMatrix, MatchingField) Headline weight matrix that induces the matching field Usage M = getWeightMatrix L Inputs - L: {GrMatchingField, FlMatchingField} + L: {MatchingField, GrMatchingField, FlMatchingField} Outputs M: Matrix - weight matrix that induces the matching field + weight matrix that induces the matching field Description Text - If the supplied matching field is coherent, then this function returns a weight matrix that - induces the matching field. If the matching field was originally defined by a weight matrix then - that weight matrix is returned. Otherwise, a weight matrix is computed. - The weight matrix is computed by computing the weight matrix cone, which can be returned with the - function @TO "weightMatrixCone"@. If the supplied matching field is not coherent, then the function - gives an error. - Example - L = diagonalMatchingField(2, 4) - getWeightMatrix L - L = grMatchingField(2, 4, {{1,2}, {1,3}, {3,2}, {1,4}, {4,2}, {3,4}}) - isCoherent L - getWeightMatrix L - Text - The weight on the ring containing the Pluecker forms, i.e., minors of a generic matrix, is based on - the weight matrix returned by @TO "getWeightMatrix"@. Note that the package @TO "MatchingFields"@ uses - the minimum convention but polynomial ring weight vectors use the maximum convention so some - conversion is required. - Example - plueckerMap L - R = target plueckerMap L - describe R + If the supplied matching field is coherent, then this function returns a weight matrix that + induces the matching field. If the matching field was originally defined by a weight matrix then + that weight matrix is returned. Otherwise, a weight matrix is computed. + The weight matrix is computed by computing the weight matrix cone, which can be returned with the + function @TO "weightMatrixCone"@. If the supplied matching field is not coherent, then the function + gives an error. + Example + L = diagonalMatchingField(2, 4) + getWeightMatrix L + L = grMatchingField(2, 4, {{1,2}, {1,3}, {3,2}, {1,4}, {4,2}, {3,4}}) + isCoherent L + getWeightMatrix L + Text + The weight on the ring containing the Pluecker forms, i.e., minors of a generic matrix, is based on + the weight matrix returned by @TO "getWeightMatrix"@. Note that the package @TO "MatchingFields"@ uses + the minimum convention but polynomial ring weight vectors use the maximum convention so some + conversion is required. + Example + plueckerMap L + R = target plueckerMap L + describe R SeeAlso getWeightPluecker - plueckerMap - weightMatrixCone + plueckerMap + weightMatrixCone Subnodes /// @@ -2834,252 +2675,310 @@ doc /// Key linearSpanTropCone (linearSpanTropCone, GrMatchingField) - [linearSpanTropCone, VerifyToricDegeneration] - VerifyToricDegeneration + [linearSpanTropCone, VerifyToricDegeneration] + VerifyToricDegeneration Headline linear span of the tropical cone associated to the matching field Usage linSpace = linearSpanTropCone L Inputs L: GrMatchingField - VerifyToricDegeneration => Boolean - controls if @TO "isToricDegeneration"@ is run + VerifyToricDegeneration => Boolean + controls if @TO "isToricDegeneration"@ is run Outputs linSpace: Module - a free QQ-module, the linear span of the cone in the tropicalisation - corresponding to the matching field + a free QQ-module, the linear span of the cone in the tropicalisation + corresponding to the matching field Description Text - Suppose that $I$ is an ideal and $in_w(I)$ is a binomial initial ideal of $I$ with - resepct to a weight $w$. - Let $C_w$ be the cone in the Groebner fan of $I$ that contains $w$ in its relative interior. - The linear span of $C_w$ can be constructed from a generating set of $in_w(I)$. Each generator - $x^u - x^v$ gives a hyperplane defined by kernel of $(0 .. 0, 1_u, 0 .. 0, -1_v, 0 .. 0)$. - The intersection of these hyperplanes gives the linear span of the Groebner cone. - - The function @TO "linearSpanTropCone"@ checks if the supplied matching field gives rise to - a toric degeneration, which happens if and only if the initial ideal of - the Pluecker ideal is toric, i.e., the ideal is generated by binomials and is prime. - If it is already known that the matching field gives rise to a toric degeneration then - set the option @TO "VerifyToricDegeneration"@ to false to avoid repeating this check. - - The linear span is a realisation of the algebraic matroid associated to the matching field. - See the function @TO "algebraicMatroid"@. - Example - L = diagonalMatchingField(2, 4) - linearSpanTropCone L - algebraicMatroid L == matroid transpose gens linearSpanTropCone L + Suppose that $I$ is an ideal and $in_w(I)$ is a binomial initial ideal of $I$ with + resepct to a weight $w$. + Let $C_w$ be the cone in the Groebner fan of $I$ that contains $w$ in its relative interior. + The linear span of $C_w$ can be constructed from a generating set of $in_w(I)$. Each generator + $x^u - x^v$ gives a hyperplane defined by kernel of $(0 .. 0, 1_u, 0 .. 0, -1_v, 0 .. 0)$. + The intersection of these hyperplanes gives the linear span of the Groebner cone. + + The function @TO "linearSpanTropCone"@ checks if the supplied matching field gives rise to + a toric degeneration, which happens if and only if the initial ideal of + the Pluecker ideal is toric, i.e., the ideal is generated by binomials and is prime. + If it is already known that the matching field gives rise to a toric degeneration then + set the option @TO "VerifyToricDegeneration"@ to false to avoid repeating this check. + + The linear span is a realisation of the algebraic matroid associated to the matching field. + See the function @TO "algebraicMatroid"@. + Example + L = diagonalMatchingField(2, 4) + linearSpanTropCone L + algebraicMatroid L == matroid transpose gens linearSpanTropCone L SeeAlso algebraicMatroid - isToricDegeneration + isToricDegeneration Subnodes /// doc /// Key - grMatchingField - (grMatchingField, Matrix) - (grMatchingField, ZZ, ZZ, List) + matchingField + (matchingField, List, List, Matrix) + (matchingField, List, ZZ, List) Headline - Construct a matching field for the Grassmannian variety + Construct a matching field Usage - L = grMatchingField(weightMatrix) - L = grMatchingField(k, n, tuples) + L = matchingField(tupleSizeList, tupleIndices, weightMatrix) + L = matchingField(tupleSizeList, n, tupleList) Inputs - k: ZZ - positive integer; the size of the tuples of the matching field - n: ZZ - positive integer; the tuples have entries in 1 .. n - weightMatrix: Matrix - induces the matching field + n: ZZ + positive integer; the tuples have entries in 1 .. n + tupleSizeList: List + positive integers; the sizes of the tuples of the matching field + tupleIndices : List + list of subsets; the indices of columns of matrix that form tuples (subsets of 0 .. n-1) + tupleList : List + list of subsets; the tuples of the matching fields (subsets of 1 .. n) + weightMatrix: Matrix + induces the matching field Outputs - L: GrMatchingField + L: MatchingField Description Text - This function is the basic constructor for Grassmannian - matching fields. The function outputs an instance of type @TO "GrMatchingField"@, - which represents the matching field and stores all data related and - computed about it. - - There are two basic ways to define a Grassmannian matching field. The first way is to - supply a weight matrix that induces the matching field. This produces a coherent matching field - and is well-defined if the matrix is {\it generic}. - Example - M = matrix {{0,0,0,0,0,0}, {1,6,2,5,3,4}, {60,50,10,20,40,30}} - L1 = grMatchingField M - getTuples L1 - isToricDegeneration L1 - Text - In the above example, we construct the Grassmannian matching field - induced by the given weight matrix. The tuples for the - matching field are listed in RevLex order. The function @TO "isToricDegeneration"@ - checks the equality of the @TO "matchingFieldIdeal"@ and the initial ideal - of the @TO "plueckerIdeal"@ with respect to the weight inducing the matching field. - - The second way to define a Grassmannian matching field - is to list out its tuples. - Example - T = {{1,4}, {2,4}, {3,4}, {3,1}, {3,2}, {1,2}} - L2 = grMatchingField(2, 4, T) - getTuples L2 - isCoherent L2 - getWeightMatrix L2 - Text - As shown in the example above, the first argument "k" - specifies the size of the tuples. - The third argument is a list of the tuples. - Note that the tuples can be supplied in any order. - If the list of tuples is not correct, i.e. if some are missing or duplicated then - the function raises an error. - When a Grassmannian matching field is constructed in this way, it is not - guaranteed to be coherent, i.e., it may not be induced by a weight matrix. - The function @TO "isCoherent"@ - checks whether the matching field is coherent and the function @TO "getWeightMatrix"@ - returns a weight matrix that induces the matching field, if it exists. - If the matching field is not coherent, then these methods produce an error. - - A note of caution. Two different weight matrices may induce the same matching field - so the function @TO "getWeightMatrix"@ may return a weight matrix that is - different to what may be expected. However, if a matching field is defined - by a weight matrix, then that weight matrix will be returned. + This function is the basic constructor for a matching field. + The function outputs an instance of type @TO "MatchingField"@, + which represents the matching field and stores all data related and + computed about it. + + It is recommended for users to use the functions @TO "grMatchingField"@ to construct + matching fields for the Grassmannian and @TO "flMatchingField"@ to construct + matching fields for partial flag varieties. These functions produce objects of type + @TO "GrMatchingField"@ and @TO "FlMatchingField"@ respectively, which have clear + uses in this package. + + There are two basic ways to define a matching field. The first way is to + supply a weight matrix that induces the matching field. This produces a coherent matching field + and is well-defined if the weight matrix is {\it generic}. + Example + M = matrix {{0,0,0,0}, {1,3,2,4}} + L1 = matchingField({1, 2}, {{0}, {1}, {0, 1}, {0, 2}, {0, 3}}, M) + getTuples L1 + Text + In the above example, we construct a matching field + induced by the given weight matrix. The tuples for the + matching field are listed in RevLex order with subsets in increasing size. + + The second way to define a matching field + is to list out its tuples. + Example + T = {{1,4}, {2,4}, {3,4}, {3,1}, {3,2}, {1,2}} + L2 = matchingField({2}, 4, T) + getTuples L2 + Text + As shown in the example above, the first argument "{2}" + specifies the sizes of the tuples of the matching field. + The second argument "4" + specifies the maximum value of elements in the tuples. + The third argument is the list of tuples of the matching field. + Note that the tuples can be supplied in any order. + + It is recommended to use Grassmannian matching fields or + partial flag matching fields for computing toric degenerations + as these objects have access to the functions @TO "isToricDegeneration"@ + and @TO "plueckerIdeal"@. SeeAlso + MatchingField GrMatchingField + grMatchingField FlMatchingField - flMatchingField - isToricDegeneration - plueckerIdeal - matchingFieldIdeal - isCoherent - getWeightMatrix + flMatchingField Subnodes - /// doc /// Key - (symbol ==, FlMatchingField, FlMatchingField) + grMatchingField + (grMatchingField, Matrix) + (grMatchingField, ZZ, ZZ, List) Headline - equality of flag matching fields + Construct a matching field for the Grassmannian variety Usage - result = L1 == L2 + L = grMatchingField(weightMatrix) + L = grMatchingField(k, n, tuples) Inputs - L1: FlMatchingField - L2: FlMatchingField + k: ZZ + positive integer; the size of the tuples of the matching field + n: ZZ + positive integer; the tuples have entries in 1 .. n + weightMatrix: Matrix + induces the matching field Outputs - result: Boolean - are the flag matching fields equal + L: GrMatchingField Description Text - Two matching fields are said to be equal if their tuples are equal. - In the case of flag matching fields, the $kList$s must be equal. - Example - L1 = diagonalMatchingField({1,2}, 4) - getWeightMatrix L1 - getTuples L1 - L2 = flMatchingField({1,2}, matrix {{0,0,0,0}, {8,4,2,1}}) - getWeightMatrix L2 - getTuples L2 - L1 == L2 - L3 = flMatchingField({1,2}, 4, {{{1}, {4}, {3}, {2}}, {{3,4},{2,4},{1,4},{2,3},{1,3},{1,2}}}) - L3 == L1 + This function is the basic constructor for Grassmannian + matching fields. The function outputs an instance of type @TO "GrMatchingField"@, + which represents the matching field and stores all data related and + computed about it. + + There are two basic ways to define a Grassmannian matching field. The first way is to + supply a weight matrix that induces the matching field. This produces a coherent matching field + and is well-defined if the matrix is {\it generic}. + Example + M = matrix {{0,0,0,0,0,0}, {1,6,2,5,3,4}, {60,50,10,20,40,30}} + L1 = grMatchingField M + getTuples L1 + isToricDegeneration L1 + Text + In the above example, we construct the Grassmannian matching field + induced by the given weight matrix. The tuples for the + matching field are listed in RevLex order. The function @TO "isToricDegeneration"@ + checks the equality of the @TO "matchingFieldIdeal"@ and the initial ideal + of the @TO "plueckerIdeal"@ with respect to the weight inducing the matching field. + + The second way to define a Grassmannian matching field + is to list out its tuples. + Example + T = {{1,4}, {2,4}, {3,4}, {3,1}, {3,2}, {1,2}} + L2 = grMatchingField(2, 4, T) + getTuples L2 + isCoherent L2 + getWeightMatrix L2 + Text + As shown in the example above, the first argument "k" + specifies the size of the tuples. + The third argument is a list of the tuples. + Note that the tuples can be supplied in any order. + If the list of tuples is not correct, i.e. if some are missing or duplicated then + the function raises an error. + When a Grassmannian matching field is constructed in this way, it is not + guaranteed to be coherent, i.e., it may not be induced by a weight matrix. + The function @TO "isCoherent"@ + checks whether the matching field is coherent and the function @TO "getWeightMatrix"@ + returns a weight matrix that induces the matching field, if it exists. + If the matching field is not coherent, then these methods produce an error. + + A note of caution. Two different weight matrices may induce the same matching field + so the function @TO "getWeightMatrix"@ may return a weight matrix that is + different to what may be expected. However, if a matching field is defined + by a weight matrix, then that weight matrix will be returned. SeeAlso GrMatchingField FlMatchingField - getTuples - getWeightMatrix + flMatchingField + isToricDegeneration + plueckerIdeal + matchingFieldIdeal + isCoherent + getWeightMatrix Subnodes - /// doc /// Key - (symbol ==, GrMatchingField, GrMatchingField) + (symbol ==, MatchingField, MatchingField) Headline - equality of Grassmannian matching fields + equality of matching fields Usage result = L1 == L2 Inputs - L1: GrMatchingField - L2: GrMatchingField + L1: MatchingField + L2: MatchingField Outputs result: Boolean - are the matching fields equal + are the matching fields equal Description Text - Two matching fields are said to be equal if their tuples are equal. - Example - L1 = diagonalMatchingField(2, 4) - getWeightMatrix L1 - getTuples L1 - L2 = grMatchingField matrix {{0,0,0,0}, {8,4,2,1}} - getWeightMatrix L2 - getTuples L2 - L1 == L2 - L3 = grMatchingField(2, 4, {{3,4},{2,4},{1,4},{2,3},{1,3},{1,2}}) - L3 == L1 + Two matching fields are said to be equal if their tuples are equal. + Example + L1 = diagonalMatchingField(2, 4) + getWeightMatrix L1 + getTuples L1 + L2 = grMatchingField matrix {{0,0,0,0}, {8,4,2,1}} + getWeightMatrix L2 + getTuples L2 + L1 == L2 + L3 = grMatchingField(2, 4, {{3,4},{2,4},{1,4},{2,3},{1,3},{1,2}}) + L3 == L1 + Text + In the case of flag matching fields, the $tupleSizeList$s must be equal. + Example + L1 = diagonalMatchingField({1,2}, 4) + getWeightMatrix L1 + getTuples L1 + L2 = flMatchingField({1,2}, matrix {{0,0,0,0}, {8,4,2,1}}) + getWeightMatrix L2 + getTuples L2 + L1 == L2 + L3 = flMatchingField({1,2}, 4, { + {1}, {4}, {3}, {2}, + {3,4}, {2,4}, {1,4}, {2,3}, {1,3}, {1,2} + }) + L3 == L1 SeeAlso GrMatchingField FlMatchingField - getTuples - getWeightMatrix - Subnodes + getTuples + getWeightMatrix + Subnodes /// doc /// Key (net, GrMatchingField) - (net, FlMatchingField) + (net, FlMatchingField) + (net, MatchingField) + (html, GrMatchingField) + (html, FlMatchingField) + (html, MatchingField) Headline display a matching field Usage net L Inputs - L: {GrMatchingField, FlMatchingField} + L: {MatchingField, GrMatchingField, FlMatchingField} Description Text - The @TO "net"@ of a matching field displays $k$ or $kList$ and $n$ for that matching field. - See @TO "GrMatchingField"@ and @TO "FlMatchingField"@. + The @TO "net"@ of a matching field displays size of the tuples of the matching field + and the maximum value in a tuple for that matching field. + See @TO "MatchingField"@, @TO "GrMatchingField"@, and @TO "FlMatchingField"@. SeeAlso + MatchingField GrMatchingField FlMatchingField - Subnodes + Subnodes /// doc /// Key plueckerAlgebra - (plueckerAlgebra, GrMatchingField) - (plueckerAlgebra, FlMatchingField) - [plueckerAlgebra, MonomialOrder] + (plueckerAlgebra, MatchingField) + [plueckerAlgebra, MonomialOrder] Headline Pluecker algebra of a (partial) flag variety Usage S = plueckerAlgebra L Inputs - L: {GrMatchingField, FlMatchingField} - MonomialOrder => String - either "default" or "none" (supply "none" only if $L$ is not coherent) + L: {MatchingField, GrMatchingField, FlMatchingField} + MonomialOrder => String + either "default" or "none" (supply "none" only if $L$ is not coherent) Outputs S: Subring - generated by minors of a generic matrix of variables + generated by minors of a generic matrix of variables Description Text - The Pluecker algebra for the Grassmannian is generated by the maximal minors of a - generic $k \times n$ matrix of variables. Similarly, for a partial flag variety, the - Pluecker algebra is generated by a collection of top-justified minors of a generic matrix - of variables. - - A matching field specifies a weight order on the ambient ring containing the Pluecker algebra. - Example - L = diagonalMatchingField(2, 4) - S = plueckerAlgebra L - transpose gens S + The Pluecker algebra for the Grassmannian is generated by the maximal minors of a + generic $k \times n$ matrix of variables. Similarly, for a partial flag variety, the + Pluecker algebra is generated by a collection of top-justified minors of a generic matrix + of variables. + + A matching field specifies a weight order on the ambient ring containing the Pluecker algebra. + Example + L = diagonalMatchingField(2, 4) + S = plueckerAlgebra L + transpose gens S SeeAlso GrMatchingField FlMatchingField - plueckerMap - Subring - Subnodes + plueckerMap + Subring + Subnodes /// doc /// @@ -3089,27 +2988,27 @@ doc /// A tope field structure on a matching field Description Text - Tope fields were introduced in the study of tropical oriented matroids - and have been used generalise and study matching fields. In this package we follow the conventions - of tope fields given by Smith and Loho, i.e., the type of a tope field contains positive entries. - - The combinatorial data of a tope field is given by a matching field for $Gr(k,n)$ together with a type: $(t_1, \dots, t_s)$ - where $t_1 + \dots + t_s = k$ and each $t_i$ is a positive integer. The bipartite graphs of the tope field are encoded in the - tuples of the matching field as follows. Let $(i_{1,1}, i_{1,2}, \dots i_{1,t_1}, i_{2,1}, \dots, i_{s, t_s})$ be a tuple of the - matching field, the bipartite graph on vertices $L := [n]$ and $R := [s]$ has edges $\{i_{j, t}, j\}$ where $j \in [s]$ and $t \in [t_j]$. - - For example, if $(1,3,2)$ is a tuple of a matching field for $Gr(3,4)$ of a tope field of type $(2,1)$, then corresponding bipartite graph on - vertices $L = [4]$ and $R = [2]$ has edges: $E = \{11, 31, 22 \}$. - - The TopeField type in this package is a HashTable that stores the matching field and type. A tope field can be defined from a matching field - using the constructor @TO "topeField"@. New tope fields can be constructed from old using the function @TO "amalgamation"@. Note that - amalgamation is only defined for linkage tope field, see @TO "isLinkage"@. + Tope fields were introduced in the study of tropical oriented matroids + and have been used generalise and study matching fields. In this package we follow the conventions + of tope fields given by Smith and Loho, i.e., the type of a tope field contains positive entries. + + The combinatorial data of a tope field is given by a matching field for $Gr(k,n)$ together with a type: $(t_1, \dots, t_s)$ + where $t_1 + \dots + t_s = k$ and each $t_i$ is a positive integer. The bipartite graphs of the tope field are encoded in the + tuples of the matching field as follows. Let $(i_{1,1}, i_{1,2}, \dots i_{1,t_1}, i_{2,1}, \dots, i_{s, t_s})$ be a tuple of the + matching field, the bipartite graph on vertices $L := [n]$ and $R := [s]$ has edges $\{i_{j, t}, j\}$ where $j \in [s]$ and $t \in [t_j]$. + + For example, if $(1,3,2)$ is a tuple of a matching field for $Gr(3,4)$ of a tope field of type $(2,1)$, then corresponding bipartite graph on + vertices $L = [4]$ and $R = [2]$ has edges: $E = \{11, 31, 22 \}$. + + The TopeField type in this package is a HashTable that stores the matching field and type. A tope field can be defined from a matching field + using the constructor @TO "topeField"@. New tope fields can be constructed from old using the function @TO "amalgamation"@. Note that + amalgamation is only defined for linkage tope field, see @TO "isLinkage"@. SeeAlso - GrMatchingField - grMatchingField - topeField - amalgamation - isLinkage + GrMatchingField + grMatchingField + topeField + amalgamation + isLinkage Subnodes /// @@ -3117,33 +3016,33 @@ doc /// doc /// Key topeField - (topeField, GrMatchingField) - (topeField, GrMatchingField, List) + (topeField, GrMatchingField) + (topeField, GrMatchingField, List) Headline Constructor of a tope field Usage TF = topeField MF - TF = topeField(MF, T) + TF = topeField(MF, T) Inputs MF: GrMatchingField - matching field containing the tuples of the tope field - T: List - the type of the tope field + matching field containing the tuples of the tope field + T: List + the type of the tope field Outputs TF: TopeField Description Text - The standard constructor of a tope field. If the constructor is supplied with a matching field and no type, then - the type is automatically set to $1,1, \dots, 1$. - Example - MF = diagonalMatchingField(3,6); - TF = topeField MF - TF' = topeField(MF, {2,1}) + The standard constructor of a tope field. If the constructor is supplied with a matching field and no type, then + the type is automatically set to $1,1, \dots, 1$. + Example + MF = diagonalMatchingField(3,6); + TF = topeField MF + TF' = topeField(MF, {2,1}) SeeAlso - GrMatchingField - grMatchingField - TopeField - amalgamation + GrMatchingField + grMatchingField + TopeField + amalgamation Subnodes /// @@ -3152,33 +3051,32 @@ doc /// doc /// Key isLinkage - (isLinkage, GrMatchingField) - (isLinkage, TopeField) + (isLinkage, GrMatchingField) + (isLinkage, TopeField) Headline Test if a tope field is linkage Usage result = isLinkage MF - result = isLinkage TF + result = isLinkage TF Inputs MF: GrMatchingField - TF: TopeField + TF: TopeField Outputs result: Boolean Description Text - Consider a tope field given by a collection of bipartite graphs. - The tope field is said to be linkage if for each $k+1$-subset $S$ of $[n]$, the union of the edges of the bipartite graphs - $G$ where the non-isolated left-vertices of $G$ are contained in $S$, is a forest. - - Note that all coherent matching fields are linkage. - Example - L = diagonalMatchingField(2,4); - isLinkage L + Consider a tope field given by a collection of bipartite graphs. + The tope field is said to be linkage if for each $k+1$-subset $S$ of $[n]$, the union of the edges of the bipartite graphs + $G$ where the non-isolated left-vertices of $G$ are contained in $S$, is a forest. + Note that all coherent matching fields are linkage. + Example + L = diagonalMatchingField(2,4); + isLinkage L SeeAlso - GrMatchingField - grMatchingField - TopeField - amalgamation + GrMatchingField + grMatchingField + TopeField + amalgamation Subnodes /// @@ -3187,36 +3085,36 @@ doc /// doc /// Key amalgamation - (amalgamation, ZZ, GrMatchingField) - (amalgamation, ZZ, TopeField) + (amalgamation, ZZ, GrMatchingField) + (amalgamation, ZZ, TopeField) Headline The $i$th amalgamation of a tope field Usage result = amalgamation(i, MF) - result = amalgamation(i, TF) + result = amalgamation(i, TF) Inputs i: ZZ - the $i$th amalgamation + the $i$th amalgamation MF: GrMatchingField - TF: TopeField + TF: TopeField Outputs result: TopeField Description Text - Computes the $i$th amalgamation of a tope field. Note that the tope field must be linkage for amalgamation to be - well-defined. - Example - L = matchingFieldFromPermutation(3,6,{4,5,6,1,2,3}); - getTuples L - T = topeField L - T1 = amalgamation(1, T) - getTuples T1 + Computes the $i$th amalgamation of a tope field. Note that the tope field must be linkage for amalgamation to be + well-defined. + Example + L = matchingFieldFromPermutation(3,6,{4,5,6,1,2,3}); + getTuples L + T = topeField L + T1 = amalgamation(1, T) + getTuples T1 SeeAlso - GrMatchingField - grMatchingField - TopeField - topeField - isLinkage + GrMatchingField + grMatchingField + TopeField + topeField + isLinkage Subnodes /// @@ -3232,11 +3130,11 @@ doc /// TF: TopeField Description Text - The @TO "net"@ of a tope field displays $n$ and the type of the tope field. - See @TO "TopeField"@. + The @TO "net"@ of a tope field displays $n$ and the type of the tope field. + See @TO "TopeField"@. SeeAlso TopeField - Subnodes + Subnodes /// doc /// @@ -3250,17 +3148,17 @@ doc /// TF: TopeField Outputs tuples: List - list of tuples of the matching field of the tope field + list of tuples of the matching field of the tope field Description Text - Lists the tuples of the matching field of the tope field. - Example - L = diagonalMatchingField(3, 6); - T = topeField L - getTuples T + Lists the tuples of the matching field of the tope field. + Example + L = diagonalMatchingField(3, 6); + T = topeField L + getTuples T SeeAlso TopeField - Subnodes + Subnodes /// -- ######### @@ -3270,7 +3168,7 @@ doc /// -- MF from weight matrix: tuples, pluecker weight, toric degen TEST /// L = grMatchingField matrix { - {0,0,0,0}, + {0,0,0,0}, {1,3,2,4}}; tupleList = {{2,1},{3,1},{2,3},{4,1},{4,2},{4,3}}; assert(getTuples L == tupleList); @@ -3307,7 +3205,7 @@ S = plueckerAlgebra L; assert(isSAGBI S); /// --- non diag pluekcer algebra +-- non diag pluecker algebra TEST /// L = matchingFieldFromPermutation(2, 4, {2,3,1,4}); S = plueckerAlgebra L; @@ -3363,4 +3261,10 @@ m = map(ring I, ring I', vars ring I); assert(m I' == I); /// +-- construct a FlMatchingField from permutation +TEST /// +L = matchingFieldFromPermutation({2,3}, 6, {2,4,3,6,5,1}); +assert(#getTuples L == 35); +/// + end -- diff --git a/M2/Macaulay2/packages/MatrixFactorizations.m2 b/M2/Macaulay2/packages/MatrixFactorizations.m2 index f33bf1759ca..326873be77c 100644 --- a/M2/Macaulay2/packages/MatrixFactorizations.m2 +++ b/M2/Macaulay2/packages/MatrixFactorizations.m2 @@ -20,12 +20,9 @@ newPackage( Headline => "computing with matrix factorizations of different lengths", Keywords => {"Commutative Algebra", "Homological Algebra"}, PackageExports => { - "Complexes", - --"CompleteIntersectionResolutions", - --"TensorComplexes" + "Complexes" }, PackageImports => { - "Complexes", "CompleteIntersectionResolutions", "PushForward", "TensorComplexes" diff --git a/M2/Macaulay2/packages/MatrixFactorizations/MF_functions.m2 b/M2/Macaulay2/packages/MatrixFactorizations/MF_functions.m2 index a418e6d97a6..982b66d9220 100644 --- a/M2/Macaulay2/packages/MatrixFactorizations/MF_functions.m2 +++ b/M2/Macaulay2/packages/MatrixFactorizations/MF_functions.m2 @@ -76,8 +76,9 @@ isdFactorization(ZZdFactorization) := (Boolean, RingElement) => X -> ( tailMF = method() tailMF(Module) := ZZdFactorization => M -> ( F := res(M, LengthLimit=>dim ring M + 1); - C := chainComplex sub(F.dd_(dim ring M + 1), ambient ring M); - h := (nullhomotopy extend(C, C, ((ring M).relations)_(0,0)*id_(C_0)))_0; + C := complex sub(F.dd_(dim ring M + 1), ambient ring M); + f := extend(C, C, ((ring M).relations)_(0,0)*id_(C_0)); + h := (nullHomotopy(f, FreeToExact => true))_0; ZZdfactorization{sub(F.dd_(dim ring M + 1), ambient ring M), h} ) diff --git a/M2/Macaulay2/packages/MatrixFactorizations/MatrixFactorizationsDOC.m2 b/M2/Macaulay2/packages/MatrixFactorizations/MatrixFactorizationsDOC.m2 index 443590c2c1b..bc00898f114 100644 --- a/M2/Macaulay2/packages/MatrixFactorizations/MatrixFactorizationsDOC.m2 +++ b/M2/Macaulay2/packages/MatrixFactorizations/MatrixFactorizationsDOC.m2 @@ -5194,7 +5194,7 @@ doc /// Text The function tailMF takes a module $M$ over a hypersurface ring $R = S/(f)$ and generates a ZZdFactorization. This involves computing a resolution of $M$ over the hypersurface $R$, taking a high truncation - of this resolution, lifting it to the ambient ring $S$, then finding a nullhomotopy for multiplication by $f$. + of this resolution, lifting it to the ambient ring $S$, then finding a null homotopy for multiplication by $f$. Example S = ZZ/101[a,b,c]; R = S/(a^3+b^3+c^3); @@ -5870,7 +5870,7 @@ doc /// :ZZdFactorizationMap Description Text - This method constructs the injectice cover of a d-fold matrix factorization. The output + This method constructs the injective cover of a d-fold matrix factorization. The output is a ZZdFactorizationMap, which is the canonical injection from the original factorization into the injective cover. Example @@ -6218,7 +6218,7 @@ doc /// mooreMF(p) Inputs p: ZZ - The characteristing of the underlying field (p = 0 for rational coefficients) + The characteristic of the underlying field (p = 0 for rational coefficients) Outputs : ZZdFactorization The Moore matrix factorization diff --git a/M2/Macaulay2/packages/MatrixSchubert/ASM_Lists.m2 b/M2/Macaulay2/packages/MatrixSchubert/ASM_Lists.m2 index edb6b6d7a61..91fad0d62c5 100644 --- a/M2/Macaulay2/packages/MatrixSchubert/ASM_Lists.m2 +++ b/M2/Macaulay2/packages/MatrixSchubert/ASM_Lists.m2 @@ -49,7 +49,7 @@ cohenMacaulayASMsList ZZ := List => (n) -> ( ------------------------------ nonCohenMacaulayASMsList = method() nonCohenMacaulayASMsList ZZ := List => (n) -> ( - if (n < 1 or n > 6) then error("expected an integery between 1 and 6"); + if (n < 1 or n > 6) then error("expected an integer between 1 and 6"); if (n <= 3) then return {}; filename := concatenate( MatrixSchubert#"source directory", diff --git a/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructions.m2 b/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructions.m2 index 208d8c8594b..41a39654b2e 100644 --- a/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructions.m2 +++ b/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructions.m2 @@ -54,7 +54,7 @@ indexOfVariable RingElement := ZZ => (elem) -> ( --pads an ASM with a block identity matrix to view the ASM in a larger polynomial ring --useful for adding and intersecting ASM ideals of differing sizes --INPUT: an ASM, number of rows/columns to add ---OUTPUT: a new ASM from the old one with the same fulton generators, but forming a larger matrix +--OUTPUT: a new ASM from the old one with the same Fulton generators, but forming a larger matrix --TODO: add docs and tests ----------------------------------- padASM = method() @@ -358,7 +358,7 @@ schubertDeterminantalIdeal List := o -> w -> ( ---------------------------------------- --INPUT: a list w corresponding to a permutation in 1-line notation ---OUTPUT: list of fulton generators for schubert determinantal ideal w +--OUTPUT: list of Fulton generators for Schubert determinantal ideal w --------------------------------------- fultonGens = method( Options => { diff --git a/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructionsDOC.m2 b/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructionsDOC.m2 index 120e1e5adad..3a255071cc4 100644 --- a/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructionsDOC.m2 +++ b/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertConstructionsDOC.m2 @@ -1123,7 +1123,7 @@ doc /// :Boolean Description Text - Given a list of permutations in 1-line notation, check whether the union of their matrix schubert varieties is an ASM variety. This function uses entrywiseMaxRankTable to construct the rank table that is the entrywise maximum of the rank tables of the input permutations. It then constructs an ASM $A$ from that rank table and uses permSetOfASM to check if the permutation set of $A$ is equal to the input list of permutations. + Given a list of permutations in 1-line notation, check whether the union of their matrix Schubert varieties is an ASM variety. This function uses entrywiseMaxRankTable to construct the rank table that is the entrywise maximum of the rank tables of the input permutations. It then constructs an ASM $A$ from that rank table and uses permSetOfASM to check if the permutation set of $A$ is equal to the input list of permutations. If the union of the matrix Schubert varieties of the input list of permutations is an ASM variety, it must be the ASM variety considered by this algorithm. Example diff --git a/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertInvariants.m2 b/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertInvariants.m2 index 64c86aa24a3..f76a1a88406 100644 --- a/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertInvariants.m2 +++ b/M2/Macaulay2/packages/MatrixSchubert/MatrixSchubertInvariants.m2 @@ -1,6 +1,6 @@ ---------------------- --INPUT: matrixSchubertRegADI, takes a permutation in 1-line notation ---OUTPUT: returns the Castelnuovo-Mumford reguarity of the matrix +--OUTPUT: returns the Castelnuovo-Mumford regularity of the matrix -- Schubert variety by computing the regularity of the antidiagonal initial ideal ------------------------------------------ -- matrixSchubertRegADI = method() diff --git a/M2/Macaulay2/packages/MatrixSchubert/permutationMethodsDOC.m2 b/M2/Macaulay2/packages/MatrixSchubert/permutationMethodsDOC.m2 index 00dad7b75ec..ad3ffcffc82 100644 --- a/M2/Macaulay2/packages/MatrixSchubert/permutationMethodsDOC.m2 +++ b/M2/Macaulay2/packages/MatrixSchubert/permutationMethodsDOC.m2 @@ -444,7 +444,7 @@ doc /// algorithm "PipeDream" also available Description Text - Given a permutation in 1-line notation, finds its Grothenieck polynomial. Two algorithms are impliemented: DividedDifference (which is the default) and PipeDream. + Given a permutation in 1-line notation, finds its Grothendieck polynomial. Two algorithms are implemented: DividedDifference (which is the default) and PipeDream. Example w = {2,1,4,3} @@ -468,7 +468,7 @@ doc /// algorithm "Transition" also available Description Text - Given a permutation in 1-line notation, finds its (single) Schubert polynomial. Two algorithms are impliemented: DividedDifference (which is the default) and Transition + Given a permutation in 1-line notation, finds its (single) Schubert polynomial. Two algorithms are implemented: DividedDifference (which is the default) and Transition (which makes use of the transition equations for Schubert polynomials). Example diff --git a/M2/Macaulay2/packages/Matroids.m2 b/M2/Macaulay2/packages/Matroids.m2 index da01008725c..57878fc22f9 100644 --- a/M2/Macaulay2/packages/Matroids.m2 +++ b/M2/Macaulay2/packages/Matroids.m2 @@ -8,7 +8,7 @@ newPackage("Matroids", Headline => "computations with matroids", Keywords => {"Matroids"}, HomePage => "https://github.com/jchen419/Matroids-M2", - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, PackageExports => {"Graphs", "Posets"}, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry", @@ -747,7 +747,7 @@ relabel (Matroid, List) := Matroid => (M, perm) -> ( ) relabel Matroid := Matroid => M -> ( E := toList(0..<#M_*); - relabel(M, (transpose{E, random E})/toSequence//hashTable) + relabel(M, (transpose{E, shuffle E})/toSequence//hashTable) ) -- Recursively finds all permutations inducing a bijection on circuits (note: permutations(10) is already slow on a typical machine) diff --git a/M2/Macaulay2/packages/MinimalPrimes.m2 b/M2/Macaulay2/packages/MinimalPrimes.m2 index 2adfdd2a0a8..b2455ca098f 100644 --- a/M2/Macaulay2/packages/MinimalPrimes.m2 +++ b/M2/Macaulay2/packages/MinimalPrimes.m2 @@ -220,8 +220,6 @@ minprimesHelper = (I, key, opts) -> ( if I == 1 then return {}; J := first flattenRing I; if J == 0 then return {I}; - -- TODO: make presentation work for ZZ, then move this line - if ring I === ZZ then return ideal \ first \ toList factor (trim I)_0; S := ring presentation ring J; strategy := opts.Strategy; @@ -311,10 +309,22 @@ algorithms#(minimalPrimes, Ideal) = new MutableHashTable from { minI := dual radical monomialIdeal I; -- TODO: make sure (monomialIdeal, MonomialIdeal) isn't forgetful cast \ if minI == 1 then { 0_R } else support \ minI_*), + + ZZ => ( + isZZ := R -> ( + R === ZZ or + -- ZZ[] (or ZZ[][], ZZ[][][], etc.) + instance(R, PolynomialRing) and numgens R == 0 and + isZZ coefficientRing R); + (opts, I) -> ( + R := ring I; + if isZZ R or instance(R, QuotientRing) and isZZ baseRing R then ( + n := gcd append(apply(I_*, a -> a^ZZ), char R); + apply(first \ toList factor n, p -> ideal p_R)))), } -- Installing hooks for (minimalPrimes, Ideal) -scan({"Legacy", "NoBirational", "Birational", Hybrid, Monomial}, strategy -> +scan({"Legacy", "NoBirational", "Birational", Hybrid, Monomial, ZZ}, strategy -> addHook(key := (minimalPrimes, Ideal), algorithms#key#strategy, Strategy => strategy)) -------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/MinimalPrimes/tests.m2 b/M2/Macaulay2/packages/MinimalPrimes/tests.m2 index 953d0f15b8c..e523ea64777 100644 --- a/M2/Macaulay2/packages/MinimalPrimes/tests.m2 +++ b/M2/Macaulay2/packages/MinimalPrimes/tests.m2 @@ -1884,6 +1884,31 @@ TOODAMNSLOW /// minimalPrimes I -- doesn't seem to finish. But in fact crashed in <= M2 1.24.05, (see previous test). /// + +TEST /// +-- issue #3505 +R = ZZ/5 +assert(minimalPrimes ideal 0_R == {ideal 0_R}) +assert(minimalPrimes ideal 1_R == {}) +assert(minimalPrimes ideal 2_R == {}) +assert(minimalPrimes ideal 3_R == {}) +assert(minimalPrimes ideal 4_R == {}) +R = ZZ/6 +assert(minimalPrimes ideal 0_R == {ideal 2_R, ideal 3_R}) +assert(minimalPrimes ideal 1_R == {}) +assert(minimalPrimes ideal 2_R == {ideal 2_R}) +assert(minimalPrimes ideal 3_R == {ideal 3_R}) +assert(minimalPrimes ideal 4_R == {ideal 2_R}) +assert(minimalPrimes ideal 5_R == {}) +R = ZZ[][][][]/6 +assert(minimalPrimes ideal 0_R == {ideal 2_R, ideal 3_R}) +assert(minimalPrimes ideal 1_R == {}) +assert(minimalPrimes ideal 2_R == {ideal 2_R}) +assert(minimalPrimes ideal 3_R == {ideal 3_R}) +assert(minimalPrimes ideal 4_R == {ideal 2_R}) +assert(minimalPrimes ideal 5_R == {}) +/// + end-- -- UHOH problem with finite fields diff --git a/M2/Macaulay2/packages/ModuleDeformations.m2 b/M2/Macaulay2/packages/ModuleDeformations.m2 index ef357bb1f31..91f89ca5993 100644 --- a/M2/Macaulay2/packages/ModuleDeformations.m2 +++ b/M2/Macaulay2/packages/ModuleDeformations.m2 @@ -11,7 +11,7 @@ newPackage( Email => "hovinen@math.uni-hannover.de"}}, Headline => "versal deformations of maximal Cohen-Macaulay modules", Keywords => {"Deformation Theory"}, - PackageImports => { "OldChainComplexes", "Truncations" }, + PackageImports => { "Complexes", "Truncations" }, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry: Macaulay2", "journal URI" => "https://msp.org/jsag/", @@ -257,7 +257,7 @@ deformMCMModule(Module,RingMap) := o -> (M0,phi) -> ( -- Extract the other matrix of the matrix factorization Ml := coker substitute (presentation M0, prune ring M0); - C := chainComplex lift (presentation Ml, ambient ring Ml); + C := complex lift (presentation Ml, ambient ring Ml); f := (mingens ideal ring Ml)_0_0; B0 := substitute ((nullhomotopy (f * id_C))_0, ring M0); diff --git a/M2/Macaulay2/packages/MonomialIntegerPrograms.m2 b/M2/Macaulay2/packages/MonomialIntegerPrograms.m2 index d2eab20c1ae..e18a2f8ea98 100644 --- a/M2/Macaulay2/packages/MonomialIntegerPrograms.m2 +++ b/M2/Macaulay2/packages/MonomialIntegerPrograms.m2 @@ -29,7 +29,7 @@ newPackage ( }, AuxiliaryFiles => true, CacheExampleOutput => true, - PackageImports => {"OldChainComplexes", "LexIdeals","MinimalPrimes"}, + PackageImports => {"Complexes","LexIdeals","MinimalPrimes"}, OptionalComponentsPresent => scipPresent := run "type scip >/dev/null 2>&1" === 0, DebuggingMode => false, Keywords => {"Commutative Algebra"} diff --git a/M2/Macaulay2/packages/Msolve.m2 b/M2/Macaulay2/packages/Msolve.m2 index 92fa35879f1..2c41462c3cb 100644 --- a/M2/Macaulay2/packages/Msolve.m2 +++ b/M2/Macaulay2/packages/Msolve.m2 @@ -37,7 +37,7 @@ importFrom_Elimination { "eliminationRing" } --------------------------------------------------------------------------- msolveMinimumVersion = "0.7.0" -msolveProgram = findProgram("msolve", "msolve --help", +msolveProgram = findProgram("msolve", "msolve -h", MinimumVersion => (msolveMinimumVersion, "msolve -V"), RaiseError => false, Verbose => debugLevel > 0) @@ -69,7 +69,7 @@ toMsolveRing = I -> ( kk := ultimate(coefficientRing, S0); if not instance(S1, PolynomialRing) or instance(kk, GaloisField) or not isField kk or char kk > 2^31 or precision kk < infinity - then error "msolve: expected an ideal in a polynomial ring over QQ or ZZ/p with chacteristic less than 2^31"; + then error "msolve: expected an ideal in a polynomial ring over QQ or ZZ/p with characteristic less than 2^31"; -- resets the variables to p_0... S := newRing(S1, Variables => numgens S1); S, kk, substitute(I, vars S)) diff --git a/M2/Macaulay2/packages/MultiGradedRationalMap.m2 b/M2/Macaulay2/packages/MultiGradedRationalMap.m2 index 6d10aeacebd..d727456db9f 100644 --- a/M2/Macaulay2/packages/MultiGradedRationalMap.m2 +++ b/M2/Macaulay2/packages/MultiGradedRationalMap.m2 @@ -9,7 +9,7 @@ newPackage( Date => "2018", DebuggingMode => false, Configuration => {}, - PackageImports => {"OldChainComplexes", "ReesAlgebra"} + PackageImports => {"Complexes", "ReesAlgebra"} ) export { diff --git a/M2/Macaulay2/packages/MultiprojectiveVarieties.m2 b/M2/Macaulay2/packages/MultiprojectiveVarieties.m2 index fa634e8161c..a01e180d114 100644 --- a/M2/Macaulay2/packages/MultiprojectiveVarieties.m2 +++ b/M2/Macaulay2/packages/MultiprojectiveVarieties.m2 @@ -1000,7 +1000,7 @@ line (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := (X,p) -> ( if dim V >= 2 then return linearSpan {p,point V}; L := select(decompose V,l -> dim l == 1 and degree l == 1); if # L == 0 then error("failed to find line in "|toString(? X)); - first random L + randomElement L ); line (EmbeddedProjectiveVariety,ZZ) := (X,n) -> ( L := try line(X,point X) else null; diff --git a/M2/Macaulay2/packages/NCAlgebra.m2 b/M2/Macaulay2/packages/NCAlgebra.m2 index 394246286bf..9652834deb9 100644 --- a/M2/Macaulay2/packages/NCAlgebra.m2 +++ b/M2/Macaulay2/packages/NCAlgebra.m2 @@ -15,7 +15,7 @@ newPackage("NCAlgebra", Keywords => {"Noncommutative Algebra", "Interfaces"}, AuxiliaryFiles => true, CacheExampleOutput => true, - PackageExports =>{"OldChainComplexes", "IntegralClosure"}, + PackageExports =>{"Complexes", "IntegralClosure"}, OptionalComponentsPresent => bergmanPresent := run "type bergman >/dev/null 2>&1" === 0 ) diff --git a/M2/Macaulay2/packages/NCAlgebra/NCAlgebraDoc.m2 b/M2/Macaulay2/packages/NCAlgebra/NCAlgebraDoc.m2 index 685bcc77a7b..5bad9c055ad 100644 --- a/M2/Macaulay2/packages/NCAlgebra/NCAlgebraDoc.m2 +++ b/M2/Macaulay2/packages/NCAlgebra/NCAlgebraDoc.m2 @@ -4359,7 +4359,8 @@ doc /// g I /// -doc /// +"doc" -- MES: can't run this. Is this a new node, example doesn't exist? +/// Key (resolution, NCMatrix) NCChainComplex diff --git a/M2/Macaulay2/packages/Nauty.m2 b/M2/Macaulay2/packages/Nauty.m2 index 13d43135a70..738c0dcb8d9 100644 --- a/M2/Macaulay2/packages/Nauty.m2 +++ b/M2/Macaulay2/packages/Nauty.m2 @@ -247,7 +247,8 @@ generateRandomGraphs (ZZ, ZZ, ZZ) := List => opts -> (n, num, p) -> ( rndSeed := if instance(opts.RandomSeed, ZZ) then " -S" | toString(opts.RandomSeed % 2^30) else ""; callNauty("genrang -qg -P" | toString p | " " | toString n | " " | toString num | rndSeed, {}) ) -generateRandomGraphs (ZZ, ZZ, QQ) := List => opts -> (n, num, p) -> ( +generateRandomGraphs (ZZ, ZZ, QQ) := +generateRandomGraphs (ZZ, ZZ, RR) := List => opts -> (n, num, p) -> ( if n < 1 then error("generateRandomGraphs: nauty does not like graphs with non-positive numbers of vertices."); if num < 1 then return {}; if p <= 0 or p > 1 then error("generateRandomGraphs: Probability must be between 0 and 1."); @@ -257,7 +258,6 @@ generateRandomGraphs (ZZ, ZZ, QQ) := List => opts -> (n, num, p) -> ( q := round(100000000 * p) / 100000000; callNauty("genrang -qg -P" | toString q | " " | toString n | " " | toString num | rndSeed, {}) ) -generateRandomGraphs (ZZ, ZZ, RR) := List => opts -> (n, num, p) -> generateRandomGraphs(n, num, promote(p, QQ), opts) generateRandomGraphs (ZZ, ZZ) := List => opts -> (n, num) -> ( if n < 1 then error("generateRandomGraphs: nauty does not like graphs with non-positive numbers of vertices."); if not instance(opts.RandomSeed, Nothing) and not instance(opts.RandomSeed, ZZ) then error("generateRandomGraphs: Option [RandomSeed] is not a valid type, i.e., ZZ or Nothing."); diff --git a/M2/Macaulay2/packages/NautyGraphs.m2 b/M2/Macaulay2/packages/NautyGraphs.m2 index 03687f0dc81..c0c5cd02d73 100644 --- a/M2/Macaulay2/packages/NautyGraphs.m2 +++ b/M2/Macaulay2/packages/NautyGraphs.m2 @@ -233,7 +233,9 @@ generateRandomGraphs (ZZ, ZZ, ZZ) := List => opts -> (n, num, p) -> ( rndSeed := if instance(opts.RandomSeed, ZZ) then " -S" | toString(opts.RandomSeed % 2^30) else ""; callNauty("genrang -qg -P" | toString p | " " | toString n | " " | toString num | rndSeed, {}) ) -generateRandomGraphs (ZZ, ZZ, QQ) := List => opts -> (n, num, p) -> ( + +generateRandomGraphs (ZZ, ZZ, QQ) := +generateRandomGraphs (ZZ, ZZ, RR) := List => opts -> (n, num, p) -> ( if n < 1 then error("generateRandomGraphs: nauty does not like graphs with non-positive numbers of vertices."); if num < 1 then return {}; if p <= 0 or p > 1 then error("generateRandomGraphs: Probability must be between 0 and 1."); @@ -243,7 +245,6 @@ generateRandomGraphs (ZZ, ZZ, QQ) := List => opts -> (n, num, p) -> ( q := round(100000000 * p) / 100000000; callNauty("genrang -qg -P" | toString q | " " | toString n | " " | toString num | rndSeed, {}) ) -generateRandomGraphs (ZZ, ZZ, RR) := List => opts -> (n, num, p) -> generateRandomGraphs(n, num, promote(p, QQ), opts) generateRandomGraphs (ZZ, ZZ) := List => opts -> (n, num) -> ( if n < 1 then error("generateRandomGraphs: nauty does not like graphs with non-positive numbers of vertices."); if not instance(opts.RandomSeed, Nothing) and not instance(opts.RandomSeed, ZZ) then error("generateRandomGraphs: Option [RandomSeed] is not a valid type, i.e., ZZ or Nothing."); diff --git a/M2/Macaulay2/packages/NoetherianOperators.m2 b/M2/Macaulay2/packages/NoetherianOperators.m2 index c22b02c6122..a2011a0e7ba 100644 --- a/M2/Macaulay2/packages/NoetherianOperators.m2 +++ b/M2/Macaulay2/packages/NoetherianOperators.m2 @@ -313,15 +313,15 @@ socles MonomialIdeal := I -> mingens((I : ideal gens ring I)/I) socles Matrix := M -> socles monomialIdeal M sCorners = socles -hilbertFunction DualSpace := L -> ( +hilbertFunction DualSpace := opts -> L -> ( if not L.Space.Reduced then L = reduceSpace L; tally(flatten entries gens L / first @@ degree) ) -hilbertFunction(List,DualSpace) := (LL,L) -> ( +hilbertFunction(List,DualSpace) := opts -> (LL,L) -> ( h := hilbertFunction L; apply(LL, d->(if h#?d then h#d else 0)) ) -hilbertFunction(ZZ,DualSpace) := (d,L) -> first hilbertFunction({d},L) +hilbertFunction(ZZ,DualSpace) := opts -> (d,L) -> first hilbertFunction({d},L) localHilbertRegularity = method(TypicalValue => ZZ, Options=>{Tolerance => null}) localHilbertRegularity(AbstractPoint, Ideal) := o -> (p,I) -> localHilbertRegularity(p,gens I,o) diff --git a/M2/Macaulay2/packages/NonminimalComplexes.m2 b/M2/Macaulay2/packages/NonminimalComplexes.m2 deleted file mode 100644 index 267826829b3..00000000000 --- a/M2/Macaulay2/packages/NonminimalComplexes.m2 +++ /dev/null @@ -1,1238 +0,0 @@ -/// -restart -uninstallPackage"NonminimalComplexes" -installPackage"NonminimalComplexes" -loadPackage("NonminimalComplexes", Reload => true) -check "NonminimalComplexes" -viewHelp "NonminimalComplexes" -/// -newPackage( - "NonminimalComplexes", - Version => "0.2", - Date => "April 25, 2018", - Authors => {{Name => "Frank-Olaf Schreyer", - Email => "schreyer@math.uni-sb.de", - HomePage => "http://www.math.uni-sb.de/ag/schreyer"}, - {Name => "Mike Stillman", - Email => "mike@math.cornell.edu", - HomePage => "http://www.math.cornell.edu/~mike"} - }, - Keywords => {"Homological Algebra", "Commutative Algebra"}, - PackageExports => { "SVDComplexes", "OldChainComplexes" }, - Headline => "non-minimal strands of a non-minimal resolution of a homogeneous module" - ) - -export { - -- These functions should be moved. Where to? - "constantStrand", -- documented, tests - -- "constantStrands", -- documented, tests - -- "getNonminimalRes", -- internal function here... - -- "degreeZeroMatrix", -- internal function? - -- "minimizeBetti", -- internal function? - --"SVDBetti", -- used in commented out code in RandomComplexes. - "SparseMatrix", - "newSparseMatrix", - "sparseMatrix", - "Entries", - "RowNums", - "ColumnNums" -} - -debug Core - --* --- The following should be where? -newChainComplexMap = method() -newChainComplexMap(ChainComplex, ChainComplex, HashTable) := (tar,src,maps) -> ( - f := new ChainComplexMap; - f.cache = new CacheTable; - f.source = src; - f.target = tar; - f.degree = 0; - goodspots := select(spots src, i -> src_i != 0); - scan(goodspots, i -> f#i = if maps#?i then maps#i else map(tar_i, src_i, 0)); - f - ) -*- - -SparseMatrix = new Type of HashTable - -numRows SparseMatrix := S -> # S.RowNums -numColumns SparseMatrix := S -> # S.ColumnNums - -net SparseMatrix := (S) -> ( - netList{ - {"", netList toList{toList(0..numColumns S - 1), S.ColumnNums}}, - { - netList for r from 0 to numRows S - 1 list {r, S.RowNums#r}, - netList S.Entries - }} - ) - -newSparseMatrix = method() -newSparseMatrix(List, List, List) := SparseMatrix => (mat, rownums, colnums) -> ( - new SparseMatrix from { - Entries => mat, - RowNums => rownums, - ColumnNums => colnums - } - ) -sparseMatrix = method() -sparseMatrix Matrix := SparseMatrix => (M) -> ( - e := entries M; - mat := for e1 in e list (pos := positions(e1, x -> x != 0); {pos, e1_pos}); - rownums := for r in mat list #r#0; - colnums := new MutableList from (numColumns M : 0); - for r from 0 to numRows M - 1 do ( - for c in mat#r#0 do colnums#c = colnums#c + 1; - ); - newSparseMatrix(mat, rownums, toList colnums) - ) - -removeRows = method() -removeRows(List,SparseMatrix) := (r,S) -> ( - -- chores: - -- 1. remove entry r from RowNums, Entries. - -- 2. for each column in Entries#r#0: - removeThese := set r; - keep := sort toList(set toList(0..numRows S - 1) - removeThese); - colnums := new MutableList from S.ColumnNums; - for r1 in r do ( - for c in S.Entries#r#0 do colnums#c = colnums#c - 1; - ); - newSparseMatrix(S.Entries_keep, S.RowNums_keep, toList colnums) - ) - -removeColumn = method() -removeColumn(ZZ,SparseMatrix) := (c,S) -> ( - -- remove c from ColNums - -- loop over all rows r - -- if c is in S.Entries#r#0 - -- remove c from this list, and corresponding coeff. - -- decrement rownums#r. - ) -removeZeroOneRows = method() -removeZeroOneRows SparseMatrix := (S) -> ( - -- returns (#zero rows, #rows with 1 element) - -- removes columns in such rows too. - p := positions(toList S.RowNums, x -> x > 0); - ) -/// - restart - debug needsPackage "NonminimalComplexes" - kk := ZZ/101 - M = mutableMatrix(kk, 10, 10); - fillMatrix(M, Density=>.2) - rank M - S = sparseMatrix matrix M - - S = removeRows(positions(S.RowNums, x -> x == 0), S) - for r in positions(S.RowNums, x -> x == 1) list - M -/// ------------------------------------------------ --- Code for nonminimal resolutions over QQ ---- ------------------------------------------------ -isMadeFromFastNonminimal = (C) -> C.?Resolution and C.Resolution.?RawComputation -fastNonminimalComputation = (C) -> if C.?Resolution and C.Resolution.?RawComputation then C.Resolution.RawComputation else null - -constantStrand = method() --- The following was removed in M2 version 1.23. Its use is based on undocumented and now removed features from M2. --- constantStrand(ChainComplex, Ring, ZZ) := (C, kk, deg) -> ( --- -- assumption: we are resolving an ideal, or at least all gens occur in degree >= 0. --- comp := fastNonminimalComputation C; --- if comp === null then error "currently expect chain complex to have been constructed with res(...,FastNonminimal=>true)"; --- len := length C; --- reg := regularity C; --- chainComplex for lev from 1 to len list ( --- matrix map(kk, rawResolutionGetMutableMatrix2B(comp, raw kk, deg,lev)) --- ) --- ) - -constantStrand(ChainComplex, ZZ) := (C, deg) -> ( - kk := coefficientRing ring C; - if kk === QQ then error "the base field must be a finite prime field currently"; - comp := fastNonminimalComputation C; - if comp === null then error "currently expect chain complex to have been constructed with res(...,FastNonminimal=>true)"; - -- assumption: we are resolving an ideal, or at least all gens occur in degree >= 0. - len := length C; - reg := regularity C; - chainComplex for lev from 1 to len list ( - matrix map(kk, rawResolutionGetMutableMatrix2B(comp, raw kk, deg,lev)) - ) - ) - --- constantStrands = method() --- constantStrands(ChainComplex, Ring) := (C, kk) -> ( --- if kk =!= coefficientRing ring C then error "use of constantStrands for a ring other than the coefficient ring is no longer supported"; --- -- base ring of C should be QQ --- -- if coefficientRing ring C =!= QQ then error "ring of the complex must be a polynomial ring over QQ"; --- -- assumption: we are resolving an ideal, or at least all gens occur in degree >= 0. --- len := length C; --- reg := regularity C; --- hashTable for deg from 0 to len+reg list ( --- D := constantStrand(C,deg); --- if D == 0 then continue else deg => D --- ) --- ) --- constantStrands ChainComplex := (C) -> constantStrands(C, coefficientRing ring C) - -getNonminimalRes = method() -getNonminimalRes(ChainComplex, Ring) := (C, R) -> ( - -- if C was created using FastNonminimal=>true, then returns the nonminimal complex. - -- if ring C is not QQ, this should be exactly C (with C.dd set). - -- if ring C is QQ, then R must be either RR_53 (monoid ring C), or (ZZ/p)(monoid ring C), where p is the prime used to - -- construct the resolution (later, there might be several such primes, and also we can - -- query and get them. But not yet.) - rawC := C.Resolution.RawComputation; - result := new MutableList; - for i from 0 to length C - 1 do ( - result#i = matrix map(R, rawResolutionGetMutableMatrixB(rawC, raw R, i+1)); - if i > 0 then result#i = map(source result#(i-1),,result#i); - ); - chainComplex toList result - ) - -"TEST" -/// --- TODO for constantStrand, constantStrands: --- a. make it work for complexes constructed in different manners, not just for FastNonminimal --- b. allow a single multi-degree - -- constantStrand, constantStrands - -- these are from nonminimal free resolutions over QQ --* - restart - needsPackage "NonminimalComplexes" -*- - - R = QQ[a..e] - I = ideal(a^3, b^3, c^3, d^3, e^3, (a+b+c+d+e)^3) - C = res(ideal gens gb I, Strategy=>4.1) - C = res(ideal gens gb I, Strategy=>5.1) - betti C - constantStrand(C, RR_53, 4) - constantStrand(C, RR_53, 5) - constantStrand(C, RR_53, 10) - - constantStrands(C, RR_53) - constantStrands(C, RR_1000) - constantStrands(C, RR_300) - kk1 = ZZ/32003 - kk2 = ZZ/1073741909 - constantStrands(C, kk1) - constantStrands(C, kk2) - constantStrands(C, ZZ) - - R1 = RR_53 (monoid R) - R2 = RR_1000 (monoid R) - R3 = kk1 (monoid R) - R4 = kk2 (monoid R) - betti'ans = new BettiTally from {(0,{0},0) => 1, (1,{3},3) => 6, (1,{4},4) => 1, (1,{5},5) => 3, (1,{6},6) => 6, - (2,{4},4) => 1, (2,{5},5) => 3, (2,{6},6) => 22, (2,{7},7) => 29, (2,{8},8) => 9, (3,{6},6) => 1, (3,{7},7) - => 14, (3,{8},8) => 52, (3,{9},9) => 45, (3,{10},10) => 4, (4,{8},8) => 4, (4,{9},9) => 35, (4,{10},10) => - 52, (4,{11},11) => 14, (4,{12},12) => 4, (5,{10},10) => 9, (5,{11},11) => 29, (5,{12},12) => 10, (5,{13},13) - => 3, (5,{14},14) => 1, (6,{12},12) => 6, (6,{13},13) => 3, (6,{14},14) => 1} - -* - assert(betti'ans ==betti (C1 = getNonminimalRes(C, R1))) - assert(betti'ans == betti (C2 = getNonminimalRes(C, R2))) - assert(betti'ans == betti (C3 = getNonminimalRes(C, R3))) - assert(betti'ans == betti (C4 = getNonminimalRes(C, R4))) - assert(C1.dd^2 == 0) - assert(C2.dd^2 == 0) - assert(C3.dd^2 == 0) - assert(C4.dd^2 == 0) - *- -/// - -"TEST" -/// --* - restart - needsPackage "NonminimalComplexes" -*- - R = ZZ/32003[a..e] - I = ideal(a^3, b^3, c^3, d^3, e^3, (a+b+c+d+e)^3) - C = res(ideal gens gb I, Strategy=>4.1) - C1 = getNonminimalRes(C, R) - assert(C == C1) -/// - -degreeZeroMatrix = method() -degreeZeroMatrix(ChainComplex, ZZ, ZZ) := (C, slanteddeg, level) -> ( - if ring C === QQ then error "need to provide a target coefficient ring, QQ is not allowed"; - kk := coefficientRing ring C; - rawC := C.Resolution.RawComputation; - matrix map(coefficientRing ring C, rawResolutionGetMatrix2(rawC, level, slanteddeg+level)) - ) - -degreeZeroMatrix(ChainComplex, Ring, ZZ, ZZ) := (C, kk, slanteddeg, level) -> ( - if kk =!= QQ then degreeZeroMatrix(C,slanteddeg, level) - else ( - rawC := C.Resolution.RawComputation; - matrix map(kk, rawResolutionGetMutableMatrix2B(rawC, raw kk, slanteddeg+level,level)) - ) - ) - --- given a mutable Betti table, find the spots (deg,lev) where there are degree 0 maps. -degzero = (B) -> ( - degsB := select(keys B, (lev,deglist,deg) -> B#?(lev-1,deglist,deg)); - degsB = degsB/(x -> (x#0, x#2-x#0)); - degsB = degsB/reverse//sort -- (deg,lev) pairs. - ) - -minimizeBetti = method() -minimizeBetti(ChainComplex, Ring) := (C, kk) -> ( - B := betti C; - mB := new MutableHashTable from B; - rk := if kk =!= RR_53 then rank else numericRank; - for x in degzero B do ( - (sdeg,lev) := x; - m := degreeZeroMatrix(C, kk, sdeg, lev); - r := rk m; - << "doing " << (sdeg, lev) << " rank[" << numRows m << "," << numColumns m << "] = " << r << endl; - mB#(lev,{lev+sdeg},lev+sdeg) = mB#(lev,{lev+sdeg},lev+sdeg) - r; - mB#(lev-1,{lev+sdeg},lev+sdeg) = mB#(lev-1,{lev+sdeg},lev+sdeg) - r; - if debugLevel > 2 then << "new betti = " << betti mB << endl; - ); - new BettiTally from mB - ) - -toBetti = method() -toBetti(ZZ, HashTable) := (deg, H) -> ( - new BettiTally from for k in keys H list (k, {deg}, deg) => H#k - ) - --- How to handle this here?? --- SVDBetti = method() --- SVDBetti ChainComplex := (C) -> ( --- if coefficientRing ring C =!= QQ then error "expected FastNonminimal resolution over QQ"; --- Ls := constantStrands(C,RR_53); --- H := hashTable for i in keys Ls list i => SVDHomology Ls#i; --- H2 := hashTable for i in keys H list i => last H#i; --- -- << "singular values: " << H2 << endl; --- sum for i in keys H list toBetti(i, first H#i) --- ) - -beginDocumentation() - -doc /// - Key - NonminimalComplexes - Headline - support for computing homology, ranks and SVD complexes, from a chain complex over the real numbers - Description - Text - Some functionality here should be moved elsewhere. - Caveat - This package will be removed soon, with {\tt constantStrand} - functionality moved elsewhere. The undocumented features the - rest of the package relied on have been removed in M2 version - 1.23. -/// - -doc /// - Key - constantStrand - (constantStrand, ChainComplex, ZZ) - Headline - a constant strand of a chain complex - Usage - Cd = constantStrand(C, deg) - Inputs - C:ChainComplex - a chain complex created using (the unsupported) {\tt res(I, Strategy=>4.1)} - deg:ZZ - the degree of the strand of the complex - Outputs - Cd:ChainComplex - a chain complex over {\tt kk}, consisting of the submatrices of {\tt C} of degree {\tt deg} - Description - Text - Warning! This function is very rough currently. It works if one - uses it in the intended manner, as in the example below. But it - should be much more general, handling other rings with grace, - and also it should handle arbitrary (graded) chain complexes. - Example - R = ZZ/101[a..d] - I = ideal(a^3, b^3, c^3, d^3, (a+3*b+7*c-4*d)^3) - C = res(I, Strategy=>4) - betti C - CR = constantStrand(C, 5) - CR.dd_2 - Caveat - This function should be defined for any graded chain complex, not just ones created - using {\tt res(I, Strategy=>4)}. -/// - -TEST /// --- Test of computing non-minimal resolutions, modules --* - restart -*- - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d] - m = map(R^2,,{{a,b^2},{c,d^2}}) - - m = map(R^{0,1},,{{a,b^2},{c^2,d^3}}) - M = coker m - res(M, FastNonminimal=>true) -- non-compatible monomial order... - - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d, MonomialOrder=>{4,Position=>Up}] - m = map(R^{0,1},,{{a,b^2},{c^2,d^3}}) - M = coker m - res(M, FastNonminimal=>true) -- non-compatible monomial order... - - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d, MonomialOrder=>{4,Position=>Down}] - m = map(R^{0,1},,{{a,b^2},{c^2,d^3}}) - M = coker m - res(M, FastNonminimal=>true) -- non-compatible monomial order... - - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d, MonomialOrder=>{Position=>Down,4}] - m = map(R^{0,1},,{{a,b^2},{c^2,d^3}}) - M = coker m - res(M, FastNonminimal=>true) -- WORKS!! - - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d, MonomialOrder=>{Position=>Up,4}] - m = map(R^{0,1},,{{a,b^2},{c^2,d^3}}) - M = coker m - res(M, FastNonminimal=>true) -- non-compatible monomial order... - - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d, MonomialOrder=>{Position=>Down,4}] - m = map(R^{1,0},,{{c^2,d^3},{a,b^2}}) - M = coker m - res(M, FastNonminimal=>true) -- doesn't work - - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d, MonomialOrder=>{Position=>Up,4}] - m = map(R^{1,0},,{{c^2,d^3},{a,b^2}}) - M = coker m - res(M, FastNonminimal=>true) -- works! - -/// - -TEST /// --- Test of computing non-minimal resolutions - -- XXX --* - restart -*- - debug OldChainComplexes -- for the key resolutionNonminimal - - kk = ZZ/32003 - R = kk[a..d] - - hasFastNonminimal = method() - hasFastNonminimal Module := Boolean => M -> M.cache.?resolutionNonminimal - hasFastNonminimal Ideal := Boolean => I -> hasFastNonminimal comodule I - - nonminimalResolutionComputation = method() - nonminimalResolutionComputation Module := RawComputation => (M) -> M.cache.resolutionNonminimal.Resolution.RawComputation - nonminimalResolutionComputation Ideal := RawComputation => (I) -> nonminimalResolutionComputation comodule I - - I = ideal"ab-cd,a3+c3,a2c+b2c" - M = comodule I - C = res(M, Strategy=>4) - assert hasFastNonminimal M - assert hasFastNonminimal I - - D = nonminimalResolutionComputation I - getfree = (I,i) -> new Module from (ring I,rawResolutionGetFree(nonminimalResolutionComputation I,i)) - getfree(I,0) - getfree(I,1) - getmat = (I,i) -> ( - D := nonminimalResolutionComputation I; - src := getfree(I,i); - tar := getfree(I,i-1); - map(tar, src, rawResolutionGetMatrix(D,i)) - ) - getmat(I,1) - getmat(I,2) - - I = ideal"ab-cd,a3+c3,a2c+b2c" - C = res(I, Strategy=>4) - assert hasFastNonminimal I - - I = ideal"ab-cd,a3+c3,a2c+b2c" - C = res(I, FastNonminimal=>true, Strategy=>4) - assert hasFastNonminimal I - - I = ideal"ab-cd,a3+c3,a2c+b2c" - C = res(I, FastNonminimal=>true) - assert hasFastNonminimal I - - I = ideal"ab-cd,a3+c3,a2c+b2c" - C = res I - assert not hasFastNonminimal I - - M.cache.resolutionNonminimal.Resolution.RawComputation -/// - - -end-- - --* -/// - -- test for computing ranks of matrices concurrently - restart - allowableThreads = 10 - kk = ZZ/101 - sizes = for i from 1 to 5 list (1000*i, 1000*i) - mats = for i from 0 to 30 list ( - fillMatrix mutableMatrix(kk,sizes#(i % 5)#0, sizes#(i % 5)#1) - ); - fcn = (i) -> () -> ( - << "[" << i << "]" << endl; - t := elapsedTiming rank mats#i; - << "time for rank #" << i << " = " << t#0 << endl; - t#1 - ) - donetask = createTask(()->(<< "all computations are done!" << endl; "done!")) - tsks = for i from 0 to 30 list createTask (fcn i) - for i from 0 to 30 do addDependencyTask(donetask, tsks#i) - elapsedTime(schedule donetask; tsks/schedule; while not isReady donetask do sleep 1;) - tsks/taskResult - elapsedTime for i from 0 to 30 list (fcn i)() - - m1 = mutableMatrix(kk,5000,5000); fillMatrix m1; - m2 = mutableMatrix(kk,4000,5000); fillMatrix m2; - m3 = mutableMatrix(kk,3000,3000); fillMatrix m3; - - -- our goal: do these simultaneously - f1 = () -> (<< "[f1]" << endl; t := elapsedTiming rank m1; << "time for rank m1: " << t#0 << endl; t#1) - f2 = () -> (<< "[f2]" << endl; t := elapsedTiming rank m2; << "time for rank m2: " << t#0 << endl; t#1) - f3 = () -> (<< "[f3]" << endl; t := elapsedTiming rank m3; << "time for rank m3: " << t#0 << endl; t#1) - t4 = createTask(()->(<< "all computations are done!" << endl; "done!")) - t1 = createTask f1 - t2 = createTask f2 - t3 = createTask f3 - - addDependencyTask(t4, t1) - addDependencyTask(t4, t2) - addDependencyTask(t4, t3) - elapsedTime({t1,t2,t3,t4}/schedule; while not isReady t4 do sleep 1) - taskResult t4 - {t1,t2,t3}/taskResult - schedule t4 - schedule t1 -schedule t2 -schedule t3 -schedule t4 -/// --* - -Cs2 = (constantStrands(C, RR_1000))#8 - kk1 = ZZ/32003 - kk2 = ZZ/1073741909 - Cp1 = (constantStrands(C, kk1))#8 - Cp2 =(constantStrands(C, kk2))#8 - CR.dd_4, CR2.dd_4 - Cp1.dd_4, Cp2.dd_4 - netList {{CR.dd_4, CR2.dd_4}, {Cp1.dd_4, Cp2.dd_4}} - netList{(clean(1e-14,CR)).dd_4,(clean(1e-299,CR2)).dd_4} - netList {(clean(1e-14,CR)).dd_4} == netList{(clean(1e-299,CR2)).dd_4} - *- - -restart -uninstallPackage "NonminimalComplexes" -restart - -installPackage "NonminimalComplexes" -viewHelp "NonminimalComplexes" -restart -check "NonminimalComplexes" -restart -needsPackage "NonminimalComplexes" - -/// -needsPackage "RandomComplexes" -needsPackage "SVDComplexes" -needsPackage "AGRExamples" -R=QQ[a..h] -Rp=(ZZ/32003)(monoid R) -Rp1=(ZZ/1073741891)(monoid R) -R0=(RR_53)(monoid R) -deg=4 -nextra=10 -setRandomSeed "1" -F=sum(gens R,x->x^deg)+sum(nextra,i->(random(1,R))^deg); -elapsedTime I=ideal fromDual matrix{{F}}; -elapsedTime C=res(I,FastNonminimal =>true); -C0 = getNonminimalRes(C, R0); -betti C -elapsedTime minimalBetti sub(I,Rp) -elapsedTime SVDBetti C - - 0 1 2 3 4 5 6 7 8 -o14 = total: 1 28 105 288 420 288 104 30 4 - 0: 1 . . . . . . . . - 1: . 18 42 . . . . . . - 2: . 10 63 288 420 288 63 9 1 - 3: . . . . . . 41 19 2 - 4: . . . . . . . 2 1 -=> does not gives correct values for any choice off the cut off value. -the value here is 1.5e1 - -debug Core -rawResolutionGetMutableMatrixB(C.Resolution.RawComputation, raw R0, 3); - - -rawResolutionGetMutableMatrix2B(C.Resolution.RawComputation, raw(ZZ/32003), 3,2) -rawResolutionGetMutableMatrix2B(C.Resolution.RawComputation, raw(RR_53), 3,2) -rawResolutionGetMutableMatrix2B(C.Resolution.RawComputation, raw(ZZ), 9,7) -rawResolutionGetMutableMatrix2B(C.Resolution.RawComputation, raw(RR_53), 9,7) -for i from 1 to 5 list for j from 1 to 5 list - rawResolutionGetMutableMatrix2B(C.Resolution.RawComputation, raw(RR_53), i,j) - -Ls = constantStrands(C, RR_53) -Ls1 = constantStrands(C, RR_1000) -m1 = Ls#9 .dd_6; -m2 = Ls1#9 .dd_6; -elapsedTime SVDHomology Ls#9 -elapsedTime SVDHomology(Ls1#9**RR_53,Ls#9) -elapsedTime SVDHomology (Ls1#9**RR_53,Strategy=>Laplacian,Threshold=>1e-2) -first SVD m1, first SVD m2 -m1 = Ls#9 .dd_7; -m2 = Ls1#9 .dd_7; -clean(1e-7, m1-m2) -ratios = (L) -> prepend(L#0, for i from 0 to #L-2 list L#i/L#(i+1)) -- L is a sorted list of singular values -ratios first o20 -ratios first SVD Ls#11 .dd_8 -ratios first SVD Ls#10 .dd_7 -ratios first SVD Ls#10 .dd_8 -ratios first SVD Ls#9 .dd_6 -(a1,a2,a3) = SVDComplex Ls#9; -first SVD Ls#9 .dd_6 -first SVD Ls#9 .dd_7 -a3 -a2 -target a1 -(source a1).dd_8 -(target a1).dd_8 -a1_8 -Ls#10 .dd_8 - -laps = laplacians Ls#9; -laps = laplacians Ls#3; -#laps -evs = laps/(m -> rsort eigenvalues(m, Hermitian=>true)) - -laps1 = laplacians Ls1#9; -evs1 = laps1/(m -> rsort eigenvalues(m, Hermitian=>true)) - - -(M1, M2) = uniquify(evs_0, evs_1, 1e-3); -(M2', M3) = uniquify(M2, evs_2, 1e-3); -#M1 -#M2' -#M3 - -(M1, M2) = uniquify(evs1_0, evs1_1, 1e-3); -(M2', M3) = uniquify(M2, evs1_2, 1e-3); - - -(M1, M2) = (VerticalList M1, VerticalList M2) -(VerticalList M2', VerticalList M3) -(M1',M2')=uniquify(M1,M2,1e-1) -(#M1', #M2') -sings = (first SVD laps_0, first SVD laps_1, first SVD laps_2) - -rsort join(evs_0, evs_2), rsort evs_1 -sings = (eigenvalues( laps_0, first SVD laps_1, first SVD laps_2) - -rsort join(sings_0, sings_1) -R32009=(ZZ/32009)(monoid R) -minimalBetti sub(I, R32009) - - -/// - -"TEST" -/// - -- warning: this currently requires test code on res2017 branch. - -- XXXX -restart - needsPackage "SVDComplexes" - needsPackage "AGRExamples" - R = QQ[a..d] - F = randomForm(3, R) - I = ideal fromDual matrix{{F}} - C = res(I, FastNonminimal=>true) - - C.dd -- want this to currently give an error, or make a ring out of this type... - - Rp = (ZZ/32003)(monoid R) - R0 = (RR_53) (monoid R) - Ls = constantStrands(C,RR_53) - L = Ls#3 - Lp = laplacians L - Lp/eigenvalues - Lp/SVD/first - - Cp = getNonminimalRes(C, Rp) - C0 = getNonminimalRes(C, R0) - Cp.dd^2 - C0.dd^2 - -- lcm of lead term entries: 8902598454 - -- want to solve x = y/8902598454^2, where y is an integer, and we know x to double precision - -- and we know x mod 32003. - -- example: - cf = leadCoefficient ((C0.dd_2)_(9,8)) - -- .293215710985088 - leadCoefficient ((Cp.dd_2)_(9,8)) - -- -10338 - -- what is y? (x mod p) = (y mod p)/(lcm mod p)^2 - kk = coefficientRing Rp - (-10338_kk) / (8902598454_kk)^2 - -- -391... - (-391 + 32003*k) / 8902598454^2 == .293215710985088 - (cf * 8902598454^2 + 391)/32003.0 - y = 726156310379351 - (y+0.0)/8902598454^2 - oo * 1_kk -/// - -"TEST" -/// - -- warning: this currently requires test code on res2017 branch. -restart - -- YYYYY - needsPackage "RandomComplexes" - needsPackage "SVDComplexes" - needsPackage "AGRExamples" - R = QQ[a..f] - deg = 6 - nextra = 10 - nextra = 20 - nextra = 30 - --F = randomForm(deg, R) - setRandomSeed "1000" - F = sum(gens R, x -> x^deg) + sum(nextra, i -> (randomForm(1,R))^deg); -elapsedTime I = ideal fromDual matrix{{F}}; - C = res(I, FastNonminimal=>true) - - Rp = (ZZ/32003)(monoid R) - betti res substitute(I,Rp) - R0 = (RR_53) (monoid R) - minimalBetti sub(I, Rp) - SVDBetti C - - betti C - Ls = constantStrands(C,RR_53) --- Lp = constantStrands(C,ZZ/32003) - D = Ls#8 -Ls --- (F, hs, minsing) = - U=SVDComplex D; - (hs, minsing) = SVDHomology D; - hs, minsing - numericRank D.dd_4 - -maximalEntry D - - elapsedTime first SVDComplex D - elapsedTime SVDHomology( D,Strategy=>Laplacian) - elapsedTime SVDComplex Ls_5; - last oo - - hashTable for k in keys Ls list (k => betti Ls#k) - sumBetti = method() - sumBetti HashTable := H -> ( - for k in keys H list (betti H#k)(-k) - ) - - elapsedTime hashTable for i in keys Ls list i => SVDComplex Ls#i; - - elapsedTime hashTable for i in keys Ls list i => toBetti(i, first SVDHomology Ls#i); - - - for i from 0 to #Ls-1 list - max flatten checkSVDComplex(Ls_i, SVDComplex Ls_i) - - hashTable for i from 0 to #Ls-1 list - i => last SVDComplex Ls_i - - ------ end of example above - - debug Core - kk = ZZp(32003, Strategy=>"Flint") - Rp = kk(monoid R) - R0 = (RR_53) (monoid R) - Cp = getNonminimalRes(C,Rp) - C0 = getNonminimalRes(C,R0) - - minimizeBetti(C, kk) - minimizeBetti(C, RR_53) - - Ip = sub(I,Rp); - minimalBetti Ip - - Lps = constantStrands(C,kk) - netList oo - L = Ls_3 - Lp = laplacians L; - --Lp/eigenvalues - - SVDComplex L - - -- compute using projection method the SVD of the complex L - L.dd_2 - (sigma, U1, V1t) = SVD mutableMatrix L.dd_2 - sigma - - betti U1 - betti V1t - M = mutableMatrix L.dd_2 - sigma1 = mutableMatrix diagonalMatrix matrix sigma - sigma1 = flatten entries sigma - sigmaplus = mutableMatrix(RR_53, 75, 5) - for i from 0 to 4 do sigmaplus_(i,i) = 1/sigma1#i - sigmaplus - Mplus = (transpose V1t) * sigmaplus * (transpose U1) - pkerM = submatrix(V1t, 5..74,); - M2 = pkerM * mutableMatrix(L.dd_3); - (sigma2,U2,V2t) = SVD M2 - sigma2 = flatten entries sigma2 - nonzerosing = position(0..#sigma2-2, i -> (sigma2#(i+1)/sigma2#i < 1.0e-10)) - pkerM2 = submatrix(V2t, nonzerosing+1 .. numRows V2t-1,) - sigma2_{0..49} - sigma2_50 - M3 = pkerM2 * mutableMatrix(L.dd_4) ; - (sigma3,U3,V3t) = SVD M3 - sigma3 = flatten entries sigma3 - nonzerosing3 = position(0..#sigma3-2, i -> (sigma3#(i+1)/sigma3#i < 1.0e-10)) - sigma3#-1 / sigma3#-2 < 1.0e-10 - - evs = Lp/SVD/first - loc = 2 - vals = sort join(for a in evs#loc list (a,loc), for a in evs#(loc+1) list (a,loc+1)) - for i from 0 to #vals-2 list ( - if vals_i_1 != vals_(i+1)_1 then ( - abs(vals_i_0 - vals_(i+1)_0) / (vals_i_0 + vals_(i+1)_0), vals_i, vals_(i+1) - ) - else null - ) - errs = select(oo, x -> x =!= null) - netList oo - select(errs, x -> x#0 < .1) -- 66 - select(errs, x -> x#0 < .01) -- 50 - select(errs, x -> x#0 < .001) -- 47 - Cp = getNonminimalRes(C, Rp) - C0 = getNonminimalRes(C, R0) - Cp.dd^2 - C0.dd^2 -- TODO: make it so we can "clean" the results here. -/// - -"TEST" -/// -restart - needsPackage "SVDComplexes" - needsPackage "AGRExamples" - I = getAGR(6,9,50,0); - R = ring I - elapsedTime C = res(I, FastNonminimal=>true) - - betti C - elapsedTime SVDBetti C - - Rp = (ZZ/32003)(monoid R) - Ip = ideal sub(gens I, Rp); - elapsedTime minimalBetti Ip - elapsedTime Cp = res(Ip, FastNonminimal=>true) -/// - -TEST /// -restart - -- ZZZZ - needsPackage "SVDComplexes" - needsPackage "AGRExamples" - - I = value get "agr-6-7-37-0.m2"; - makeAGR(6,7,50,0) - - I = getAGR(6,7,50,0); --* - R = QQ[a..h] - deg = 6 - nextra = 30 - F = sum(gens R, x -> x^deg) + sum(nextra, i -> (randomForm(1,R))^deg); - elapsedTime I = ideal fromDual matrix{{F}}; -*- - - elapsedTime C = res(I, FastNonminimal=>true) - betti C - elapsedTime SVDBetti C - - Rp = (ZZ/32003)(monoid R) - Ip = ideal sub(gens I, Rp); - elapsedTime minimalBetti Ip - - D = constantStrand(C, RR_53, 7) - SVDComplex D; - E = target first oo - for i from 2 to 5 list sort flatten entries compress flatten E.dd_i - Ls = constantStrands(C, RR_53) -/// - -TEST /// -restart - needsPackage "SVDComplexes" - needsPackage "AGRExamples" - - elapsedTime makeAGR(7,7,100,32003) - I = getAGR(7,7,100,32003); - - elapsedTime minimalBetti I - -/// - -TEST /// - -- warning: this currently requires test code on res2017 branch. - -- XXXX -restart - needsPackage "SVDComplexes" - R = QQ[a..g] - deg = 6 - nextra = 10 - nextra = 30 - --F = randomForm(deg, R) - F = sum(gens R, x -> x^deg) + sum(nextra, i -> (randomForm(1,R))^deg); - elapsedTime I = ideal fromDual matrix{{F}}; - elapsedTime C = res(I, FastNonminimal=>true) - - kk = ZZ/32003 - Rp = kk(monoid R) - Ip = sub(I,Rp); - elapsedTime minimalBetti Ip - R0 = (RR_53) (monoid R) - - Ls = constantStrands(C,RR_53) - netList oo - Lps = constantStrands(C,kk) - debug Core - kkflint = ZZp(32003, Strategy=>"Ffpack") - Lps = constantStrands(C,kkflint) - Lp = Lps_5 - L = Ls_5 - for i from 3 to 6 list elapsedTime first SVD L.dd_i - for i from 3 to 6 list rank mutableMatrix Lp.dd_i - Lp = laplacians L; - --Lp/eigenvalues - evs = Lp/SVD/first - loc = 2 - vals = sort join(for a in evs#loc list (a,loc), for a in evs#(loc+1) list (a,loc+1)) - for i from 0 to #vals-2 list ( - if vals_i_1 != vals_(i+1)_1 then ( - abs(vals_i_0 - vals_(i+1)_0) / (vals_i_0 + vals_(i+1)_0), vals_i, vals_(i+1) - ) - else null - ) - errs = select(oo, x -> x =!= null) - netList oo - select(errs, x -> x#0 < .1) -- 66 - select(errs, x -> x#0 < .01) -- 50 - select(errs, x -> x#0 < .001) -- 47 - Cp = getNonminimalRes(C, Rp) - C0 = getNonminimalRes(C, R0) - Cp.dd^2 - C0.dd^2 -- TODO: make it so we can "clean" the results here. -/// - - -TEST /// - -- warning: this currently requires test code on res2017 branch. - -- XXXX -restart - needsPackage "SVDComplexes" - needsPackage "AGRExamples" - deg = 6 - nv = 7 - nextra = binomial(nv + 1, 2) - nv - 10 - R = QQ[vars(0..nv-1)] - - - --F = randomForm(deg, R) - F = sum(gens R, x -> x^deg) + sum(nextra, i -> (randomForm(1,R))^deg); - elapsedTime I = ideal fromDual matrix{{F}}; - elapsedTime C = res(I, FastNonminimal=>true) - - kk = ZZ/32003 - Rp = kk(monoid R) - Ip = sub(I,Rp); - elapsedTime Cp = res(Ip, FastNonminimal=>true) - elapsedTime minimalBetti Ip - R0 = (RR_53) (monoid R) - SVDBetti C - - Ls = constantStrands(C,RR_53) - mats = flatten for L in Ls list ( - kf := keys L.dd; - nonzeros := select(kf, k -> instance(k,ZZ) and L.dd_k != 0); - nonzeros/(i -> L.dd_i) - ); - elapsedTime(mats/(m -> first SVD m)) - netList oo - Lps = constantStrands(C,kk) - debug Core - kkflint = ZZp(32003, Strategy=>"Ffpack") - Lps = constantStrands(C,kkflint) - Lp = Lps_5 - L = Ls_5 - for i from 3 to 6 list rank mutableMatrix Lp.dd_i - Lp = laplacians L; - --Lp/eigenvalues - evs = Lp/SVD/first - loc = 2 - vals = sort join(for a in evs#loc list (a,loc), for a in evs#(loc+1) list (a,loc+1)) - for i from 0 to #vals-2 list ( - if vals_i_1 != vals_(i+1)_1 then ( - abs(vals_i_0 - vals_(i+1)_0) / (vals_i_0 + vals_(i+1)_0), vals_i, vals_(i+1) - ) - else null - ) - errs = select(oo, x -> x =!= null) - netList oo - select(errs, x -> x#0 < .1) -- 66 - select(errs, x -> x#0 < .01) -- 50 - select(errs, x -> x#0 < .001) -- 47 - Cp = getNonminimalRes(C, Rp) - C0 = getNonminimalRes(C, R0) - Cp.dd^2 - C0.dd^2 -- TODO: make it so we can "clean" the results here. -/// - - -doc /// -Key - SVDComplexes -Headline -Description - Text - Example -Caveat -SeeAlso -/// - -doc /// -Key -Headline -Usage -Inputs -Outputs -Consequences -Description - Text - Example - Code - Pre -Caveat -SeeAlso -/// - -TEST /// --- test code and assertions here --- may have as many TEST sections as needed -/// - - - -/// - Key - constantStrands - (constantStrands, ChainComplex, Ring) - Headline - all constant strands of a chain complex - Usage - Cs = constantStrands(C, kk) - Inputs - C:ChainComplex - A chain complex created using {\tt res(I, Strategy=>4.1)} - kk:Ring - if the coefficient ring of the ring of C is QQ, then this should be either: - RR_{53}, RR_{1000}, ZZ/1073741891, or ZZ/1073741909. - Outputs - Cs:List - the list of chain complex over {\tt kk}, which for each degree degree {\tt deg}, consisting of the submatrices of {\tt C} of degree {\tt deg} - Description - Text - Warning! This function is very rough currently. It works if one uses it in the intended manner, - as in the example below. But it should be much more general, handling other rings with grace, - and also it should handle arbitrary (graded) chain complexes. - Example - R = QQ[a..d] - I = ideal(a^3, b^3, c^3, d^3, (a+3*b+7*c-4*d)^3) - C = res(ideal gens gb I, Strategy=>4.1) - betti C - Cs = constantStrands(C, RR_53) - CR=Cs#8 - SVDBetti C, betti C - Caveat - This function should be defined for any graded chain complex, not just ones created - using {\tt res(I, Strategy=>4.1)}. Currently, it is used to extract information - from the not yet implemented ring QQhybrid, whose elements, coming from QQ, are stored as real number - approximations (as doubles, and as 1000 bit floating numbers), together with its remainders under a couple of primes, - together with information about how many multiplications were performed to obtain this number. - SeeAlso - constantStrand -/// - -/// - Key - SVDBetti - (SVDBetti, ChainComplex) - Headline - the Betti table computed with SVD methods - Usage - SVDBetti C - Inputs - C:ChainComplex - A chain complex created using {\tt res(I, Strategy=>4.1)} - if the coefficient ring of the ring of C is QQ, then this should be either: - RR_{53}, RR_{1000}, ZZ/1073741891, or ZZ/1073741909. - Outputs - :BettiTally - the betti table of the minimal resolution using SVD of complexes and the numerical data - Description - Text - Warning! This function is very rough currently. It works if one uses it in the intended manner, - as in the example below. But it should be much more general, handling other rings with grace, - and also it should handle arbitrary (graded) chain complexes. - Example - R = QQ[a..d] - I = ideal(a^3, b^3, c^3, d^3, (a+3*b+7*c-4*d)^3) - C = res(ideal gens gb I, Strategy=>4.1) - SVDBetti C, betti C - Rp=ZZ/32003[gens R] - betti res sub(I,Rp) - Caveat - This function should be defined for any graded chain complex, not just ones created - using {\tt res(I, Strategy=>4.1)}. Currently, it is used to extract information - from the not yet implemented ring QQhybrid, whose elements, coming from QQ, are stored as real number - approximations (as doubles, and as 1000 bit floating numbers), together with its remainders under a couple of primes, - together with information about how many multiplications were performed to obtain this number. - SeeAlso - constantStrands -/// - - -"TEST" - /// - R = QQ[a..d] - I = ideal(a^3, b^3, c^3, d^3, (a+3*b+7*c-4*d)^3) - C = res(ideal gens gb I,Strategy=>4.1) - betti C - betti'deg8 = new BettiTally from {(3,{},0) => 13, (4,{},0) => 4} - CR = constantStrand(C, RR_53, 8) - CR2 = constantStrand(C, RR_1000, 8) -- crash - - kk1 = ZZ/32003 - kk2 = ZZ/1073741909 - Cp1 = constantStrand(C, kk1, 8) - Cp2 = constantStrand(C, kk2, 8) - - assert(betti'deg8 == betti CR) - assert(betti'deg8 == betti CR2) - assert(betti'deg8 == betti Cp1) - assert(betti'deg8 == betti Cp2) - - (CR.dd_4, CR2.dd_4, Cp1.dd_4, Cp2.dd_4) - (clean(1e-14,CR)).dd_4 - (clean(1e-299,CR2)).dd_4 -/// - - - - - -"TEST" -/// - kk = QQ - R = kk[a..d] - I = ideal(a^3, b^3, c^3, d^3, (a+3*b+7*c-4*d)^3) - C = res(ideal gens gb I, Strategy=>4.1) - betti C - constantStrand(C, RR_53, 8) -/// - -"TEST" -/// - kk = QQ - R = kk[a..d] - I = ideal(a^3, b^3, c^3, d^3, (a+3*b+7*c-4*d)^3) - gbI = flatten entries gens gb I - gbI = for g in gbI list (1/(leadCoefficient g)) * g - gbI = matrix{gbI} - - R1 = RR_53[a..d] - gbI1 = sub(gbI, R1) - forceGB (gbI1) - I1 = ideal gbI1 - gbTrace=3 - gens gb I1 - --C = res(I1, Strategy=>5) -- refuses to try it - C = res(ideal gens gb I, Strategy=>5.1) - betti C - constantStrand(C, RR_53, 8) -/// - -/// --- Test of computing non-minimal resolutions - -- XXX - -- Try a non homogeneous ideal: - restart - debug Core -- for the key resolutionNonminimal - kk = ZZ/32003 - R = kk[a..d] - - hasFastNonminimal = method() - hasFastNonminimal Module := M -> M.cache.?resolutionNonminimal - hasFastNonminimal Ideal := I -> hasFastNonminimal comodule I - - I = ideal"ab-1,c2-c-a" - M = comodule I - C = res(I, Strategy=>5) -- currently gives an error: cannot use res(...,FastNonminimal=>true) with inhomogeneous input - assert hasFastNonminimal M - assert hasFastNonminimal I - - R = kk[a..d,DegreeRank=>4] - degree a - I = ideal(a^2, a*b, b^2) - C = res(I, Strategy=>4) -- currently gives an error: expected singly graded with positive degrees for the variables - - - - C = res I - C.dd - peek C.Resolution - debug Core - C.Resolution.RawComputation - - J = ideal"ab-cd,a3+c3,a2c+b2c" - CJ = res(J, FastNonminimal=>true) - CJ.dd - peek CJ.Resolution - debug Core - CJ.Resolution.RawComputation - - -- where are these stashed? - MI = comodule I - MI.cache.resolution === C - - MJ = comodule J - MJ.cache.resolution === CJ - -gbTrace=3 - minimalBetti J - minimalBetti I -/// diff --git a/M2/Macaulay2/packages/NormalToricVarieties/ToricVarieties.m2 b/M2/Macaulay2/packages/NormalToricVarieties/ToricVarieties.m2 index 6822e747512..325f8997621 100644 --- a/M2/Macaulay2/packages/NormalToricVarieties/ToricVarieties.m2 +++ b/M2/Macaulay2/packages/NormalToricVarieties/ToricVarieties.m2 @@ -673,8 +673,10 @@ makePrimitive List := List => w -> ( apply(w, i -> i // g) ); -toricBlowup = method () -toricBlowup (List, NormalToricVariety, List) := NormalToricVariety => (s, X, v) -> ( +toricBlowup = method(Options => { WeilToClass => null }) +toricBlowup(List, NormalToricVariety) := NormalToricVariety => opts -> (s, X) -> ( + toricBlowup(s, X, makePrimitive sum ((rays X)_s), opts)) +toricBlowup(List, NormalToricVariety, List) := NormalToricVariety => opts -> (s, X, v) -> ( coneList := max X; starIndex := positions (coneList, t -> all (s, i -> member (i,t))); star := coneList_starIndex; @@ -693,7 +695,10 @@ toricBlowup (List, NormalToricVariety, List) := NormalToricVariety => (s, X, v) if member (s#0,t) then continue else sort (t | s) ); - Z := normalToricVariety (rays X, coneList | coneList', CoefficientRing => X.cache.CoefficientRing, Variable => X.cache.Variable); + Z := normalToricVariety(rays X, coneList | coneList', + CoefficientRing => X.cache.CoefficientRing, + Variable => X.cache.Variable, + WeilToClass => opts.WeilToClass); Z.cache.toricBlowup = X; return Z ); @@ -701,16 +706,14 @@ toricBlowup (List, NormalToricVariety, List) := NormalToricVariety => (s, X, v) if all (s, i -> member (i,t)) then continue else t | {n} ); - Z = normalToricVariety (rays X | {v}, coneList | coneList', CoefficientRing => X.cache.CoefficientRing, Variable => X.cache.Variable); + Z = normalToricVariety(rays X | {v}, coneList | coneList', + CoefficientRing => X.cache.CoefficientRing, + Variable => X.cache.Variable, + WeilToClass => opts.WeilToClass); Z.cache.toricBlowup = X; Z ); -toricBlowup (List, NormalToricVariety) := NormalToricVariety => (s,X) -> ( - v := makePrimitive sum ((rays X)_s); - toricBlowup (s,X,v) - ); - makeSmooth = method( TypicalValue => NormalToricVariety, Options => {Strategy => 0} diff --git a/M2/Macaulay2/packages/NormalToricVarieties/ToricVarietiesDocumentation.m2 b/M2/Macaulay2/packages/NormalToricVarieties/ToricVarietiesDocumentation.m2 index 95edcf0a220..44cf097143e 100644 --- a/M2/Macaulay2/packages/NormalToricVarieties/ToricVarietiesDocumentation.m2 +++ b/M2/Macaulay2/packages/NormalToricVarieties/ToricVarietiesDocumentation.m2 @@ -2276,7 +2276,7 @@ doc /// # (set rays Y' - set rays Y) Caveat A singular normal toric variety almost never has a unique minimal - resolution. This method returns only of one of the many minimal + resolution. This method returns only one of the many minimal resolutions. SeeAlso "resolving singularities" diff --git a/M2/Macaulay2/packages/Normaliz.m2 b/M2/Macaulay2/packages/Normaliz.m2 index 5a2b35502f7..011d8a2c861 100644 --- a/M2/Macaulay2/packages/Normaliz.m2 +++ b/M2/Macaulay2/packages/Normaliz.m2 @@ -212,7 +212,7 @@ doWriteNmzData List := matrices -> ( for i from 0 to numRows sgr - 1 do ( s := ""; for j from 0 to numColumns sgr - 1 - do s = s | sgr_(i,j) | " "; + do s = s | toString(sgr_(i,j)) | " "; outf << s << endl; ); -- Until version 3.9.4, input type normal_toric_ideal was called lattice_ideal @@ -1077,7 +1077,7 @@ document { PARA{},"This function creates an input file for ", TT "Normaliz", " containing one or several matrices, whose rows are considered according to the type:", UL { "integral closure, normalization: generators of a rational cone", - "polytope: lattice points spanning a polytope", + "polytope: lattice points or rational points spanning a polytope", "rees_algebra: exponent vectors of monomials generating an ideal", "inequalities, equations, congruences: constraints defining the cone to be computed", "inhom_inequalities, inhom_equations, inhom_congruences: inhomogeneous constraints defining the cone to be computed", @@ -1287,6 +1287,11 @@ TEST /// {1, 0, 2, 2, 1, 0, 0, 2, 1}}) ) rmNmzFiles(); ///, +TEST /// +-- check that normaliz works with rational polytopes +V = matrix "1/2,0;-1/2,0;0,1;0,-1"; +C = normaliz(V, "polytope"); +///, } document{ diff --git a/M2/Macaulay2/packages/NumericalAlgebraicGeometry/doc.m2 b/M2/Macaulay2/packages/NumericalAlgebraicGeometry/doc.m2 index cea8b7d9914..d27834ec804 100644 --- a/M2/Macaulay2/packages/NumericalAlgebraicGeometry/doc.m2 +++ b/M2/Macaulay2/packages/NumericalAlgebraicGeometry/doc.m2 @@ -767,7 +767,7 @@ F2 = F1.Deflation#r2 P2 = newton(F2,P2') isFullNumericalRank evaluate(jacobian F2,P2) P = point {take(coordinates P2, F.NumberOfVariables)} -assert(residual(F,P) < 1e-50) +residual(F,P) ///, Caveat => {"Needs more documentation!!!"}, SeeAlso=>{PolySystem,newton} diff --git a/M2/Macaulay2/packages/NumericalAlgebraicGeometry/track.m2 b/M2/Macaulay2/packages/NumericalAlgebraicGeometry/track.m2 index 8c09f62328c..dacee2c1440 100644 --- a/M2/Macaulay2/packages/NumericalAlgebraicGeometry/track.m2 +++ b/M2/Macaulay2/packages/NumericalAlgebraicGeometry/track.m2 @@ -142,7 +142,7 @@ track (PolySystem,PolySystem,List) := List => o -> (S,T,solsS) -> ( t := symbol t; Rt := K(monoid[gens R, t]); t = last gens Rt; - (nS,nT) := if shouldNormalize -- make Bomboeri-Weyl norm of the systems equal 1 + (nS,nT) := if shouldNormalize -- make Bombieri-Weyl norm of the systems equal 1 then (XXXapply(S, f->f/sqrt(S.NumberOfPolys * BombieriWeylNormSquared f)), XXXapply(T, f->f/sqrt(T.NumberOfPolys * BombieriWeylNormSquared f))) else (S,T); diff --git a/M2/Macaulay2/packages/NumericalSchubertCalculus/examples/_set__Verbose__Level.out b/M2/Macaulay2/packages/NumericalSchubertCalculus/examples/_set__Verbose__Level.out index 8a20f5ae9eb..afb0b89c5c5 100644 --- a/M2/Macaulay2/packages/NumericalSchubertCalculus/examples/_set__Verbose__Level.out +++ b/M2/Macaulay2/packages/NumericalSchubertCalculus/examples/_set__Verbose__Level.out @@ -1,4 +1,4 @@ --- -*- M2-comint -*- hash: 317641762447771653 +-- -*- M2-comint -*- hash: 11766660079572042236 i1 : SchPblm = randomSchubertProblemInstance ({{1},{1},{1},{1}},2,4) @@ -52,88 +52,88 @@ o3 : List i4 : assert all(S,s->checkIncidenceSolution(s,SchPblm)) -i5 : setVerboseLevel 1; +i5 : setVerboseLevel 1; i6 : S = solveSchubertProblem(SchPblm,2,4) -- playCheckers --- cpu time = .0154201 +-- cpu time = .0109853 -- making a recursive call to resolveNode -- playCheckers --- cpu time = .0120184 +-- cpu time = .00400091 -- making a recursive call to resolveNode -- playCheckers --- cpu time = 0 +-- cpu time = .00100066 resolveNode reached node of no remaining conditions --- time to make equations: .163919 +-- time to make equations: .00499959 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0126281 sec. for [{0, 1, 2, 3}, {0, infinity, 2, infinity}] --- time of performing one checker move: .188607 --- time of performing one checker move: .00292733 --- time of performing one checker move: .00401444 --- time to make equations: .0116789 + -- trackHomotopy time = .00570243 sec. for [{0, 1, 2, 3}, {0, infinity, 2, infinity}] +-- time of performing one checker move: .0159995 +-- time of performing one checker move: .000999811 +-- time of performing one checker move: .000999681 +-- time to make equations: .00499988 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0396773 sec. for [{1, 2, 3, 0}, {1, infinity, infinity, 2}] --- time of performing one checker move: .0387082 --- time to make equations: .0112116 + -- trackHomotopy time = .0295184 sec. for [{1, 2, 3, 0}, {1, infinity, infinity, 2}] +-- time of performing one checker move: .196997 +-- time to make equations: .00500053 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0147794 sec. for [{1, 3, 2, 0}, {1, infinity, infinity, 2}] --- time of performing one checker move: .177902 --- time to make equations: .0158854 + -- trackHomotopy time = .00602776 sec. for [{1, 3, 2, 0}, {1, infinity, infinity, 2}] +-- time of performing one checker move: .0160007 +-- time to make equations: .00600012 Setup time: 0 Computing time:0 - -- trackHomotopy time = .015358 sec. for [{2, 3, 1, 0}, {2, infinity, infinity, 1}] --- time of performing one checker move: .0397252 --- time to make equations: .0278454 + -- trackHomotopy time = .00595454 sec. for [{2, 3, 1, 0}, {2, infinity, infinity, 1}] +-- time of performing one checker move: .0159997 +-- time to make equations: .170913 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0173996 sec. for [{0, 1, 2, 3}, {infinity, 1, 2, infinity}] --- time of performing one checker move: .194614 --- time to make equations: .0254151 + -- trackHomotopy time = .00692619 sec. for [{0, 1, 2, 3}, {infinity, 1, 2, infinity}] +-- time of performing one checker move: .183914 +-- time to make equations: .0119998 Setup time: 0 Computing time:0 - -- trackHomotopy time = .100483 sec. for [{0, 1, 3, 2}, {infinity, 1, infinity, 2}] --- time of performing one checker move: .203755 --- time of performing one checker move: .00400148 --- time of performing one checker move: .00393395 --- time to make equations: .0239996 + -- trackHomotopy time = .00723693 sec. for [{0, 1, 3, 2}, {infinity, 1, infinity, 2}] +-- time of performing one checker move: .182283 +-- time of performing one checker move: .00099989 +-- time of performing one checker move: .000999771 +-- time to make equations: .0109995 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0188128 sec. for [{1, 3, 2, 0}, {infinity, 3, infinity, 1}] --- time of performing one checker move: .0558564 + -- trackHomotopy time = .00692467 sec. for [{1, 3, 2, 0}, {infinity, 3, infinity, 1}] +-- time of performing one checker move: .0230001 -- making a recursive call to resolveNode -- playCheckers --- cpu time = .0079901 +-- cpu time = .00498282 -- making a recursive call to resolveNode -- playCheckers --- cpu time = 0 +-- cpu time = .000999721 resolveNode reached node of no remaining conditions --- time to make equations: .0118377 +-- time to make equations: .167022 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0176744 sec. for [{0, 1, 2, 3}, {0, infinity, 2, infinity}] --- time of performing one checker move: .0408146 --- time of performing one checker move: .142637 --- time to make equations: .0120001 + -- trackHomotopy time = .00594482 sec. for [{0, 1, 2, 3}, {0, infinity, 2, infinity}] +-- time of performing one checker move: .178023 +-- time of performing one checker move: .000999351 +-- time to make equations: .00499974 Setup time: 0 Computing time:0 - -- trackHomotopy time = .0144319 sec. for [{0, 2, 3, 1}, {0, infinity, infinity, 2}] --- time of performing one checker move: .0357794 --- time of performing one checker move: .00400301 --- time of performing one checker move: 0 --- time of performing one checker move: 0 --- time of performing one checker move: .00470073 --- time of performing one checker move: .0031443 --- time of performing one checker move: .00400018 --- time of performing one checker move: .158215 --- time to make equations: .0237389 + -- trackHomotopy time = .00596614 sec. for [{0, 2, 3, 1}, {0, infinity, infinity, 2}] +-- time of performing one checker move: .0160006 +-- time of performing one checker move: .00199911 +-- time of performing one checker move: .00100014 +-- time of performing one checker move: .0010001 +-- time of performing one checker move: .00200083 +-- time of performing one checker move: .000999761 +-- time of performing one checker move: .155392 +-- time of performing one checker move: .00200002 +-- time to make equations: .0109989 Setup time: 0 Computing time:0 - -- trackHomotopy time = .024264 sec. for [{1, 3, 2, 0}, {1, infinity, infinity, 3}] --- time of performing one checker move: .0592573 --- time of performing one checker move: .00625287 + -- trackHomotopy time = .00714963 sec. for [{1, 3, 2, 0}, {1, infinity, infinity, 3}] +-- time of performing one checker move: .0239992 +-- time of performing one checker move: .00300012 o6 = {| -1.65573-.600637ii .0201935+.0437095ii |, | -.154703+.175591ii | -1.23037-1.66989ii -.0308057-.00120618ii | | -.801221-.0354303ii diff --git a/M2/Macaulay2/packages/NumericalSemigroups.m2 b/M2/Macaulay2/packages/NumericalSemigroups.m2 index 15c27c0499b..3ac3e68cf10 100644 --- a/M2/Macaulay2/packages/NumericalSemigroups.m2 +++ b/M2/Macaulay2/packages/NumericalSemigroups.m2 @@ -1922,7 +1922,7 @@ doc /// Key (mingens, List) Headline - Find a mininmal set of semigroup generators + Find a minimal set of semigroup generators Usage L' = mingens L Inputs @@ -3167,7 +3167,7 @@ Description The function coneEquations(m, "Inhomogeneous" => true) returns the same matrix as in the homogeneous case, with one more row, where the last row represents the - constant terms of this inquality: + constant terms of this inequality: Example eq=coneEquations(3, "Inhomogeneous" => true) coneEquations({3,4,5}, "Inhomogeneous" => true) diff --git a/M2/Macaulay2/packages/OldChainComplexes/betti.m2 b/M2/Macaulay2/packages/OldChainComplexes/betti.m2 index c0f86760902..3cdd841d343 100644 --- a/M2/Macaulay2/packages/OldChainComplexes/betti.m2 +++ b/M2/Macaulay2/packages/OldChainComplexes/betti.m2 @@ -80,7 +80,7 @@ minimalBetti Module := BettiTally => opts -> M -> ( if not useFastNonminimal then return betti resolution(M, DegreeLimit => degreelimit, LengthLimit => lengthlimit); -- At this point, we think we are good to use the faster algorithm. - -- First, we need to comppute the non-minimal resolution to one further step. + -- First, we need to compute the non-minimal resolution to one further step. if instance(opts.LengthLimit, ZZ) then lengthlimit = lengthlimit + 1; C = resolution(M, StopBeforeComputation => true, FastNonminimal => true, ParallelizeByDegree => opts.ParallelizeByDegree, diff --git a/M2/Macaulay2/packages/OldChainComplexes/docs/resolution-doc.m2 b/M2/Macaulay2/packages/OldChainComplexes/docs/resolution-doc.m2 index a419d9c17c2..4b38078d898 100644 --- a/M2/Macaulay2/packages/OldChainComplexes/docs/resolution-doc.m2 +++ b/M2/Macaulay2/packages/OldChainComplexes/docs/resolution-doc.m2 @@ -58,14 +58,13 @@ doc /// If the input module is not graded, or is multi-graded, this function still works. However, @TO "minimalBetti"@ does not work in these cases. In the inhomogeneous case, the returned free resolution is often highly non minimal. Of course, there is no notion of minimal - resolution in this case, but one can use @TO "PruneComplex::pruneComplex"@ to clean up the - returned complex. + resolution in this case, but one can use @TO "Complexes :: pruneComplex"@ to clean up the + returned complex, however one must now use the Complexes package for that. Example R = ZZ/101[a..f] I = ideal"a3-ab-c2,abc-d2-1, b3-b2-b" C = res(I, FastNonminimal => true) - needsPackage "PruneComplex" - pruneComplex C + -- pruneComplex complex C Text If one has a specific Gröbner basis on which one wants to base the Schreyer resolution, use @TT "Strategy => 5"@. This will not check that the input forms a Gröbner basis, but @@ -93,7 +92,7 @@ doc /// betti [betti,Minimize] resolution - "PruneComplex::pruneComplex" + "Complexes::pruneComplex" /// -- Known bug: if I is inhomogeneous, in a poly ring with a non-degree monomial order, diff --git a/M2/Macaulay2/packages/Oscillators/Documentation.m2 b/M2/Macaulay2/packages/Oscillators/Documentation.m2 index 7f988934b35..b5f58bb35b5 100644 --- a/M2/Macaulay2/packages/Oscillators/Documentation.m2 +++ b/M2/Macaulay2/packages/Oscillators/Documentation.m2 @@ -250,7 +250,7 @@ doc /// #oo Text The angles of these solutions (in degrees, not radians, and the 3 refers to the - numbner of oscillators). + number of oscillators). Example netList getAngles(3, findRealSolutions I, Radians=>false) SeeAlso diff --git a/M2/Macaulay2/packages/PHCpack/PHCpackDoc.m2 b/M2/Macaulay2/packages/PHCpack/PHCpackDoc.m2 index 4a5a3f64411..fde6a936cd2 100644 --- a/M2/Macaulay2/packages/PHCpack/PHCpackDoc.m2 +++ b/M2/Macaulay2/packages/PHCpack/PHCpackDoc.m2 @@ -466,6 +466,7 @@ doc /// Key isWitnessSetMember (isWitnessSetMember,WitnessSet,AbstractPoint) + [isWitnessSetMember,Verbose] Headline tests whether a point belongs to a solution set Usage @@ -474,6 +475,9 @@ doc /// W:WitnessSet positive dimensional, properly embedded with slack variables p:AbstractPoint + Verbose=>Boolean + whether additional output is wanted, including the + input and solution file names used by {\tt phc}. Outputs :Boolean true if p is a member of the solution set of W, @@ -495,29 +499,6 @@ doc /// V = numericalIrreducibleDecomposition (system); W = first V#4; isWitnessSetMember(W, point{{0,0,0,0,0,0}}) - -///; - --- options for isWitnessSetMember - -doc /// - Key - [isWitnessSetMember,Verbose] - Headline - option to specify whether additional output is wanted - Usage - isWitnessSetMember(...,Verbose=>Boolean) - Description - Text - Use {\tt Verbose=>true} for additional output, which includes the - input and solution file names used by {\tt phc}. - - Example - R = CC[x11,x22,x21,x12,x23,x13]; - system = {x11*x22-x21*x12,x12*x23-x22*x13}; - V = numericalIrreducibleDecomposition (system); - W = first V#4; - isWitnessSetMember(W, point{{0,0,0,0,0,0}}) ///; ----------------- diff --git a/M2/Macaulay2/packages/PackageCitations.m2 b/M2/Macaulay2/packages/PackageCitations.m2 index e1d2d73d233..9e7118359b1 100644 --- a/M2/Macaulay2/packages/PackageCitations.m2 +++ b/M2/Macaulay2/packages/PackageCitations.m2 @@ -154,13 +154,13 @@ iCite Package := P -> ( certificationInfo := if P#Options#Certification =!= null then hashTable P#Options#Certification else null; -- bibtex author content if not P#Options#?Authors - then print concatenate ("Warning: The \"", T, "\" package provides insufficient citation data: author.") + then printerr("Warning: The ", format T, " package provides insufficient citation data: author.") else packageAuthorsWithContributors := apply(P#Options#Authors, a -> a#0#1); packageAuthors := select (packageAuthorsWithContributors, a -> not member (":", characters a)); bibPackageAuthors := wrapTexStrings (demark (" and ", packageAuthors)); if packageAuthors === {} - then print concatenate ("Warning: The \"", T, "\" package provides insufficient citation data: author.") + then printerr("Warning: The ", format T, " package provides insufficient citation data: author.") else null; -- set up bibtex string for certified packages bibtexCert := if certificationInfo === null then null else @@ -191,7 +191,7 @@ iCite Package := P -> ( then concatenate("\"", toString (P#Options#HomePage), "\"") else "\\url{https://github.com/Macaulay2/M2/tree/stable/M2/Macaulay2/packages}" else if P#Options#HomePage === null - then (print concatenate ("Warning: The \"", T, "\" package provides insufficient citation data: howpublished.")) + then printerr("Warning: the ", format T, " package provides insufficient citation data: howpublished.") else concatenate("\\url{" ,toString (P#Options#HomePage), "}"); bibtexString := concatenate ( @@ -310,6 +310,10 @@ doc /// user is urged to check for correct spelling and grammar. Example cite "Bruns" + Text + To override the automatically generated citation, package authors + may provide a @TO "Macaulay2Doc::Citation"@ entry in the main + documentation node for a package. SeeAlso PackageCitations /// diff --git a/M2/Macaulay2/packages/Padic.m2 b/M2/Macaulay2/packages/Padic.m2 new file mode 100644 index 00000000000..22f9c1601aa --- /dev/null +++ b/M2/Macaulay2/packages/Padic.m2 @@ -0,0 +1,1213 @@ +newPackage("Padic", + Headline => "p-adic numbers", + Version => "0.1", + Date => "April 28, 2026", + Authors => {{ + Name => "Doug Torrance", + Email => "dtorrance@piedmont.edu", + HomePage => "https://webwork.piedmont.edu/~dtorrance"}}, + Keywords => {"Algebraic Number Theory"}, + PackageExports => {"Valuations"}, + PackageImports => {"ForeignFunctions"}) + +endpkg = msg -> ( + document {Key => Padic, + Headline => (options currentPackage).Headline, + "Warning: Padic was loaded without key components."}; + printerr("warning: ", msg, "; ending"); + end) + +if not ForeignFunctions#"private dictionary"#?"foreignFunction" +then endpkg "foreign function interface is not available" + +flint = try openSharedLibrary "flint" else endpkg "flint is not available" + +export { + -- methods + "prime", + "pVal", + "teichmullerLift", + "unit", + + -- classes + "PadicNumber", + "PadicFieldFamily", + } + +-- unexported symbols +protect context + +--------------------- +-- FLINT interface -- +--------------------- + +-- fmpz (flint integer type) +fmpzInit = foreignFunction(flint, "fmpz_init", void, voidstar) +fmpzClear = foreignFunction(flint, "fmpz_clear", void, voidstar) +fmpzSetMpz = foreignFunction(flint, "fmpz_set_mpz", void, {voidstar, mpzT}) +fmpzGetMpz = foreignFunction(flint, "fmpz_get_mpz", void, {mpzT, voidstar}) + +toFmpz = x -> ( + -- typedef slong fmpz; + -- typedef fmpz fmpz_t[1]; + y := getMemory size long; + fmpzInit y; + registerFinalizer(y, fmpzClear); + fmpzSetMpz(y, x); + y) + +fromFmpz = x -> ( + y := mpzT 0; + fmpzGetMpz(y, x); + value y) + +-- fmpq (flint rational type) +fmpqInit = foreignFunction(flint, "fmpq_init", void, voidstar) +fmpqClear = foreignFunction(flint, "fmpq_clear", void, voidstar) +fmpqSetFmpzFrac = foreignFunction(flint, "fmpq_set_fmpz_frac", void, + {voidstar, voidstar, voidstar}) +fmpqGetMpzFrac = foreignFunction(flint, "fmpq_get_mpz_frac", void, + {mpzT, mpzT, voidstar}) + +toFmpq = x -> ( + -- typedef struct { + -- fmpz num; + -- fmpz den; + -- } fmpq; + y := getMemory(2 * size long); + fmpqInit y; + registerFinalizer(y, fmpqClear); + fmpqSetFmpzFrac(y, toFmpz numerator x, toFmpz denominator x); + y) + +fromFmpq = x -> ( + (a, b) := (mpzT 0, mpzT 1); + fmpqGetMpzFrac(a, b, x); + value a / value b) + +-- padic_ctx_t +padicCtxInit = foreignFunction(flint, "padic_ctx_init", void, + {voidstar, voidstar, long, long, int}) +padicCtxClear = foreignFunction(flint, "padic_ctx_clear", void, voidstar) + +newPadicContext = memoize((p, N) -> ( + -- typedef struct { + -- fmpz_t p; + -- double pinv; + -- fmpz *pow; + -- slong min; + -- slong max; + -- enum padic_print_mode mode; + -- } padic_ctx_struct; + ctx := getMemory(4 * size long + size double + size int); + m := max(0, N - 10); + M := max(0, N + 10); + padicCtxInit(ctx, toFmpz p, m, M, 1 -* PADIC_SERIES *-); + registerFinalizer(ctx, padicCtxClear); + ctx)) + +-- padic_t +padicInit2 = foreignFunction(flint, "padic_init2", void, {voidstar, long}) +padicClear = foreignFunction(flint, "padic_clear", void, voidstar) +padicUnit = foreignFunction(flint, "padic_unit", voidstar, voidstar) +padicGetVal = foreignFunction(flint, "padic_get_val", long, voidstar) +padicGetPrec = foreignFunction(flint, "padic_get_prec", long, voidstar) +padicSetFmpq = foreignFunction(flint, "padic_set_fmpq", void, + {voidstar, voidstar, voidstar}) +padicGetFmpz = foreignFunction(flint, "padic_get_fmpz", void, + {voidstar, voidstar, voidstar}) +padicGetFmpq = foreignFunction(flint, "padic_get_fmpq", void, + {voidstar, voidstar, voidstar}) + +newPadic = N -> ( + -- typedef struct { + -- fmpz u; + -- slong v; + -- slong N; + -- } padic_struct; + y := getMemory(3 * size long); + padicInit2(y, N); + registerFinalizer(y, padicClear); + y) + +padicGetStr = foreignFunction(flint, "padic_get_str", charstar, + {charstar, voidstar, voidstar}) + +padicAdd = foreignFunction(flint, "padic_add", void, + {voidstar, voidstar, voidstar, voidstar}) + +padicSub = foreignFunction(flint, "padic_sub", void, + {voidstar, voidstar, voidstar, voidstar}) + +padicMul = foreignFunction(flint, "padic_mul", void, + {voidstar, voidstar, voidstar, voidstar}) + +padicDiv = foreignFunction(flint, "padic_div", void, + {voidstar, voidstar, voidstar, voidstar}) + +padicShift = foreignFunction(flint, "padic_shift", void, + {voidstar, voidstar, long, voidstar}) + +padicNeg = foreignFunction(flint, "padic_neg", void, + {voidstar, voidstar, voidstar}) + +padicInv = foreignFunction(flint, "padic_inv", void, + {voidstar, voidstar, voidstar}) + +padicSqrt = foreignFunction(flint, "padic_sqrt", int, + {voidstar, voidstar, voidstar}) + +padicPowSi = foreignFunction(flint, "padic_pow_si", void, + {voidstar, voidstar, long, voidstar}) + +padicExp = foreignFunction(flint, "padic_exp", int, + {voidstar, voidstar, voidstar}) + +padicLog = foreignFunction(flint, "padic_log", int, + {voidstar, voidstar, voidstar}) + +padicTeichmuller = foreignFunction(flint, "padic_teichmuller", int, + {voidstar, voidstar, voidstar}) + +padicEqual = foreignFunction(flint, "padic_equal", int, {voidstar, voidstar}) + +padicIsZero = foreignFunction(flint, "padic_is_zero", int, voidstar) + +-------------------- +-- p-adic numbers -- +-------------------- + +PadicFieldFamily = new Type of RingFamily +PadicFieldFamily.synonym = "p-adic field family" + +expression PadicFieldFamily := kk -> Subscript(QQ, prime kk) +net PadicFieldFamily := net @@ expression +toString PadicFieldFamily := toString @@ expression + +PadicNumber = new Type of Number +PadicNumber.synonym = "p-adic number" + +precision PadicNumber := x -> value padicGetPrec x.number + +unit = method() +unit PadicNumber := x -> fromFmpz padicUnit x.number + +padicValuation PadicFieldFamily := +valuation PadicFieldFamily := kk -> kk.valuation + +pVal = method() +pVal PadicNumber := x -> (valuation class x) x + +prime = method() +prime PadicFieldFamily := kk -> kk.prime +prime PadicNumber := x -> prime class x + +numdigits := x -> floor log(10, x) + 1 +toString PadicNumber := x -> ( + (N, v, p) := (precision x, pVal x, prime x); + if v == infinity then v = 0; + -- from src/padic/get_str.c + n := (N - v) * (2 * numdigits p + numdigits max(abs v, abs N) + 5) + 1; + value padicGetStr(concatenate(n:"\0"), x.number, x.context)) + +PadicNumber.AfterPrint = lookup(AfterPrint, InexactNumber) + +peek'(ZZ, PadicNumber) := lookup(peek', ZZ, HashTable) + +describe PadicNumber := x -> describe(unit x * Power(prime x, pVal x)) + +knownPadicFields = new MutableHashTable + +-- want to use QQ_p, but Ring_ZZ already exists, so overwrite it +oldRingSubZZ = lookup(symbol _, Ring, ZZ) +Ring _ ZZ := (R, p) -> ( + if R =!= QQ then oldRingSubZZ(R, p) + else ( + if not isPrime p then error "expected a prime number"; + QQp := knownPadicFields#p ??= ( + new PadicFieldFamily of PadicNumber + from hashTable {symbol prime => p}); + QQp.valuation = valuation( + x -> ( + if x == 0 then infinity + else value padicGetVal (QQp x).number), + QQp, ZZ); + QQp)) + +new PadicNumber from (voidstar, voidstar) := (T, ctx, num) -> ( + new T from hashTable { + symbol context => ctx, + symbol number => num}) +new PadicNumber from (ZZ, Number) := (T, N, x) -> ( + try x _= QQ else x ^= QQ; -- promote/lift to QQ if needed + ctx := newPadicContext(prime T, N); + y := newPadic N; + padicSetFmpq(y, toFmpq x, ctx); + new T from (ctx, y)) +new PadicNumber from Number := (T, x) -> new T from (20, x) +new PadicNumber from Constant := (T, x) -> new T from numeric x +new PadicNumber from (ZZ, Constant) := (T, N, x) -> new T from (N, numeric x) +new PadicNumber from PadicNumber := (T, x) -> ( + if prime T == prime x then x + else T(precision x, x^QQ)) + +PadicFieldFamily Thing := (T, x) -> new T from x + +---------------- +-- operations -- +---------------- + +combinePadics = (x, y) -> ( + p := prime x; + if p != prime y then error "expected elements of the same field"; + N := min(precision x, precision y); + ctx := newPadicContext(p, N); + val := newPadic N; + QQ_p(ctx, val)) + +PadicNumber + PadicNumber := (x, y) -> ( + z := combinePadics(x, y); + padicAdd(z.number, x.number, y.number, z.context); + z) +PadicNumber + Number := (x, y) -> x + QQ_(prime x) y +Number + PadicNumber := (x, y) -> QQ_(prime y) x + y + +PadicNumber - PadicNumber := (x, y) -> ( + z := combinePadics(x, y); + padicSub(z.number, x.number, y.number, z.context); + z) +PadicNumber - Number := (x, y) -> x - QQ_(prime x) y +Number - PadicNumber := (x, y) -> QQ_(prime y) x - y + +PadicNumber * PadicNumber := (x, y) -> ( + z := combinePadics(x, y); + padicMul(z.number, x.number, y.number, z.context); + z) +PadicNumber * Number := (x, y) -> x * QQ_(prime x) y +Number * PadicNumber := (x, y) -> QQ_(prime y) x * y + +PadicNumber / PadicNumber := (x, y) -> ( + if y == 0 then error "division by zero"; + z := combinePadics(x, y); + padicDiv(z.number, x.number, y.number, z.context); + z) +PadicNumber / Number := (x, y) -> x / QQ_(prime x) y +Number / PadicNumber := (x, y) -> QQ_(prime y) x / y + +PadicNumber << ZZ := (x, y) -> ( + z := newPadic precision x; + padicShift(z, x.number, y, x.context); + QQ_(prime x)(x.context, z)) + +(PadicNumber >> ZZ) := (x, y) -> x << -y + +-PadicNumber := x -> ( + y := newPadic precision x; + padicNeg(y, x.number, x.context); + QQ_(prime x)(x.context, y)) + ++PadicNumber := identity + +abs PadicNumber := x -> (prime x)^(-pVal x) + +inverse PadicNumber := x -> ( + if x == 0 then error "division by zero"; + y := newPadic precision x; + padicInv(y, x.number, x.context); + QQ_(prime x)(x.context, y)) + +sqrt PadicNumber := x -> ( + y := newPadic precision x; + r := value padicSqrt(y, x.number, x.context); + if r == 1 then QQ_(prime x)(x.context, y) + else error("not a ", prime x, "-adic square")) + +PadicNumber^ZZ := (x, y) -> ( + z := newPadic precision x; + padicPowSi(z, x.number, y, x.context); + QQ_(prime x)(x.context, z)) + +exp PadicNumber := x -> ( + y := newPadic precision x; + r := value padicExp(y, x.number, x.context); + if r == 1 then QQ_(prime x)(x.context, y) + else error(prime x, "-adic exponential function does not converge")) + +log PadicNumber := x -> ( + y := newPadic precision x; + r := value padicLog(y, x.number, x.context); + if r == 1 then QQ_(prime x)(x.context, y) + else error(prime x, "-adic logarithm function does not converge")) + +teichmullerLift = method() +teichmullerLift PadicNumber := x -> ( + if pVal x < 0 then error("expected a ", prime x, "-adic integer"); + y := newPadic precision x; + padicTeichmuller(y, x.number, x.context); + QQ_(prime x)(x.context, y)) + +lift(PadicNumber, ZZ) := o -> (x, kk) -> ( + if pVal x < 0 then error("expected a ", prime x, "-adic integer"); + y := toFmpz 0; + padicGetFmpz(y, x.number, x.context); + fromFmpz y) + +lift(PadicNumber, QQ) := o -> (x, kk) -> ( + y := toFmpq(0/1); + padicGetFmpq(y, x.number, x.context); + fromFmpq y) + +lift(PadicNumber, PadicNumber) := o -> (x, kk) -> ( + if prime x == prime kk then x + else error("can't lift from ", toString class x, " to ", toString kk)) + +Number^PadicFieldFamily := (x, kk) -> lift(x, kk) + +promote(ZZ, PadicNumber) := +promote(QQ, PadicNumber) := (x, kk) -> kk x +promote(PadicNumber, PadicNumber) := (x, kk) -> ( + if prime x == prime kk then x + else error("can't promote from ", toString class x, " to ", toString kk)) + +Number_PadicFieldFamily := (x, kk) -> promote(x, kk) + +numeric PadicNumber := x -> numeric x^QQ +numeric(ZZ, PadicNumber) := (prec, x) -> numeric(prec, x^QQ) +interval PadicNumber := o -> x -> interval(x^QQ, o) +interval(PadicNumber, PadicNumber) := o -> (x, y) -> interval(x^QQ, y^QQ, o) +interval(PadicNumber, Number) := o -> (x, y) -> interval(x^QQ, y, o) +interval(Number, PadicNumber) := o -> (x, y) -> interval(x, y^QQ, o) + +PadicNumber == PadicNumber := (x, y) -> ( + prime x == prime y and value padicEqual(x.number, y.number) == 1 + or + -- if primes don't agree, then just compare in QQ + x^QQ == y^QQ) + +PadicNumber == Number := (x, y) -> ( + if y == 0 then value padicIsZero x.number == 1 + else x^QQ == y) +Number == PadicNumber := (x, y) -> y == x + +beginDocumentation() + +doc /// + Key + Padic + Headline + p-adic numbers + Description + Text + The field of $p$-adic numbers $\QQ_p$ consists of all formal + Laurent series + $$\sum_{n=\nu}^\infty a_n p^n = a_\nu p^\nu + a_{\nu+1} p^{\nu+1} + \cdots,$$ + where $\nu \in \ZZ$ and $a_n \in \{0, \ldots, p - 1\}$, together with the + usual operations of addition and multiplication in base $p$, with carrying + that may continue indefinitely. Equivalently, $\QQ_p$ is the completion + of $\QQ$ with respect to the $p$-adic absolute value $|x|_p = + p^{-\nu_p(x)}$, just as $\RR$ is the completion of $\QQ$ under the usual + absolute value. + + Elements of $\QQ_p$ are created by applying @ofClass PadicFieldFamily@ to a + rational number. They are stored and displayed as truncated series, with a + default @TO2((precision, PadicNumber), "precision")@ of 20 base-$p$ digits. + Example + x = QQ_7(12/7) + Text + This package is implemented using the + @TO "ForeignFunctions::ForeignFunctions"@ package to call $p$-adic + arithmetic routines from the @HREF("https://flintlib.org/", "FLINT")@ + C library. + + See the paper @arXiv("2604.16799", + "Implementing p-adic numbers in Macaulay2 using its foreign function interface and FLINT")@ + for more information. + Citation + @misc{torrance2026implementingpadicnumbersmacaulay2, + title={Implementing p-adic numbers in Macaulay2 using its foreign function interface and FLINT}, + author={Douglas A. Torrance}, + year={2026}, + eprint={2604.16799}, + archivePrefix={arXiv}, + primaryClass={math.AG}, + url={https://arxiv.org/abs/2604.16799}, + } + Subnodes + PadicFieldFamily + PadicNumber +/// + +doc /// + Key + PadicFieldFamily + Headline + class for p-adic fields + Description + Text + For any prime $p$, the $p$-adic field $\QQ_p$ is represented in Macaulay2 + by the object @CODE "QQ_p"@. + Example + QQ_7 + class QQ_7 + Text + Elements of $\QQ_p$ are created by applying the field to a number. + The default @TO2((precision, PadicNumber), "precision")@ is 20 base-$p$ + digits; an explicit precision $N$ may be supplied as a first argument. + Example + QQ_7 3 + QQ_7(30, 3) + Subnodes + prime +/// + +undocumented { + (expression, PadicFieldFamily), + (net, PadicFieldFamily), + (toString, PadicFieldFamily), + (toString, PadicNumber), + (peek', ZZ, PadicNumber), + (describe, PadicNumber)} + +doc /// + Key + PadicNumber + Headline + base class for p-adic numbers + Description + Text + Every $p$-adic number is an instance of the class @CODE "QQ_p"@ for + the appropriate value of @VAR "p"@. However, each of of these classes + is a subclass of @CODE "PadicNumber"@. + Example + x = QQ_7 5 + ancestors class x + Text + To install methods that work for all $p$-adic numbers, regardless of the + specific field, install methods for this class. + Example + foo = method() + foo PadicNumber := x -> x + 2 + foo x + Subnodes + (precision, PadicNumber) + unit + pVal + (symbol ==, PadicNumber, PadicNumber) + (symbol +, PadicNumber, PadicNumber) + (symbol *, PadicNumber, PadicNumber) + (abs, PadicNumber) + (symbol <<, PadicNumber, ZZ) + (symbol ^, PadicNumber, ZZ) + (exp, PadicNumber) + teichmullerLift + (lift, PadicNumber, ZZ) + (promote, ZZ, PadicNumber) + (numeric, PadicNumber) + (interval, PadicNumber) +/// + +doc /// + Key + (precision, PadicNumber) + Headline + precision of a p-adic number + Usage + precision x + Inputs + x:PadicNumber + Outputs + :ZZ + Description + Text + Every $p$-adic number is stored with a maximum number of base $p$ digits. + This is its precision. + Example + x = QQ_7 (-1) + precision x + y = QQ_7(30, -2) + precision y + Text + When performing binary operations on $p$-adic numbers, the result has the + smallest of the two precisions. + Example + x + y + precision oo +/// + +doc /// + Key + unit + (unit, PadicNumber) + Headline + unit part of a p-adic number + Usage + unit x + Inputs + x:PadicNumber + Outputs + :ZZ + Description + Text + Every $x\in\QQ_p$ can be decomposed into a product $x=up^\nu$, where + $u\in\ZZ_p^\times$, i.e., it is a unit in the ring of $p$-adic integers. + This function returns $u$ as an integer. + Example + x = QQ_7 (1/49) + unit x + Text + Note that in general, $u$ has infinitely many $p$-adic digits. Therefore, + it is truncated based on the @TO2((precision, PadicNumber), "precision")@ + of $x$. + Example + x = QQ_7(-1/49) + unit x +/// + +doc /// + Key + (symbol +, PadicNumber, PadicNumber) + (symbol +, PadicNumber, Number) + (symbol +, Number, PadicNumber) + (symbol +, PadicNumber) + Headline + add p-adic numbers + Usage + x + y + Inputs + x:PadicNumber + y:PadicNumber + Outputs + :PadicNumber -- the sum of x and y + Description + Text + Add two $p$-adic numbers. + Example + QQ_7 3 + QQ_7 11 + Text + If one of the arguments is an ordinary number, it is first promoted to + the appropriate $p$-adic field. + Example + QQ_7 3 + 4 + 5 + QQ_7 6 + Text + When adding two $p$-adic numbers, the result has the smaller of the two + @TO2((precision, PadicNumber), "precision")@ values. + Example + QQ_7(10, 3) + QQ_7(20, 4) + Text + The unary @CODE "+"@ operator is the identity. + Example + +QQ_7 3 + Subnodes + (symbol -, PadicNumber, PadicNumber) +/// + +doc /// + Key + (symbol -, PadicNumber, PadicNumber) + (symbol -, PadicNumber, Number) + (symbol -, Number, PadicNumber) + (symbol -, PadicNumber) + Headline + subtract or negate p-adic numbers + Usage + x - y + -x + Inputs + x:PadicNumber + y:PadicNumber + Outputs + :PadicNumber -- the difference of x and y, or the negation of x + Description + Text + Subtract two $p$-adic numbers. + Example + QQ_7 11 - QQ_7 3 + Text + If one of the arguments is an ordinary number, it is first promoted to + the appropriate $p$-adic field. + Example + QQ_7 11 - 3 + 11 - QQ_7 3 + Text + The unary @CODE "-"@ operator negates a $p$-adic number. + Example + -QQ_7 3 + SeeAlso + (symbol +, PadicNumber, PadicNumber) +/// + +doc /// + Key + (symbol *, PadicNumber, PadicNumber) + (symbol *, PadicNumber, Number) + (symbol *, Number, PadicNumber) + Headline + multiply p-adic numbers + Usage + x * y + Inputs + x:PadicNumber + y:PadicNumber + Outputs + :PadicNumber -- the product of x and y + Description + Text + Multiply two $p$-adic numbers. + Example + QQ_7 3 * QQ_7 5 + Text + If one of the arguments is an ordinary number, it is first promoted to + the appropriate $p$-adic field. + Example + QQ_7 3 * 5 + 4 * QQ_7 3 + SeeAlso + (symbol +, PadicNumber, PadicNumber) + Subnodes + (symbol /, PadicNumber, PadicNumber) + (inverse, PadicNumber) +/// + +doc /// + Key + (symbol /, PadicNumber, PadicNumber) + (symbol /, PadicNumber, Number) + (symbol /, Number, PadicNumber) + Headline + divide p-adic numbers + Usage + x / y + Inputs + x:PadicNumber + y:PadicNumber + Outputs + :PadicNumber -- the quotient of x and y + Description + Text + Divide two $p$-adic numbers. + Example + QQ_2 3 / QQ_2 2 + Text + If one of the arguments is an ordinary number, it is first promoted to + the appropriate $p$-adic field. + Example + QQ_7 6 / 2 + 6 / QQ_7 2 + Caveat + An error is raised if @VAR "y"@ is zero. + SeeAlso + (inverse, PadicNumber) +/// + +doc /// + Key + (inverse, PadicNumber) + Headline + multiplicative inverse of a p-adic number + Usage + inverse x + Inputs + x:PadicNumber + Outputs + :PadicNumber -- the multiplicative inverse of x + Description + Text + Returns the multiplicative inverse of a $p$-adic number. + Example + inverse QQ_7 3 + QQ_7 3 * inverse QQ_7 3 + Caveat + An error is raised if @VAR "x"@ is zero. + SeeAlso + (symbol /, PadicNumber, PadicNumber) +/// + +doc /// + Key + (abs, PadicNumber) + Headline + p-adic absolute value + Usage + abs x + Inputs + x:PadicNumber + Outputs + :QQ + Description + Text + Returns the $p$-adic absolute value $|x|_p = p^{-\nu_p(x)}$. + Example + abs QQ_7 49 + abs QQ_7(1/7) + abs QQ_7 0 + SeeAlso + pVal +/// + +doc /// + Key + (symbol <<, PadicNumber, ZZ) + Headline + multiply a p-adic number by a power of p + Usage + x << n + Inputs + x:PadicNumber + n:ZZ + Outputs + :PadicNumber -- x times p^n + Description + Text + Returns $x \cdot p^n$, shifting the $p$-adic expansion left by $n$ places. + A negative shift divides by $p^{|n|}$. + Example + QQ_7 3 << 2 + QQ_7 3 << -1 + Subnodes + (symbol >>, PadicNumber, ZZ) +/// + +doc /// + Key + (symbol >>, PadicNumber, ZZ) + Headline + divide a p-adic number by a power of p + Usage + x >> n + Inputs + x:PadicNumber + n:ZZ + Outputs + :PadicNumber -- x divided by p^n + Description + Text + Returns $x / p^n = x \cdot p^{-n}$, shifting the $p$-adic expansion right + by $n$ places. A negative shift multiplies by $p^{|n|}$. + Example + QQ_7 3 >> 2 + QQ_7 3 >> -1 + SeeAlso + (symbol <<, PadicNumber, ZZ) +/// + +doc /// + Key + (symbol ^, PadicNumber, ZZ) + Headline + raise a p-adic number to an integer power + Usage + x^n + Inputs + x:PadicNumber + n:ZZ + Outputs + :PadicNumber -- x raised to the n-th power + Description + Text + Raise a $p$-adic number to an integer power. + Example + (QQ_7 3)^2 + (QQ_7 3)^(-1) + Subnodes + (sqrt, PadicNumber) +/// + +doc /// + Key + (sqrt, PadicNumber) + Headline + square root of a p-adic number + Usage + sqrt x + Inputs + x:PadicNumber + Outputs + :PadicNumber -- a square root of x + Description + Text + Returns a square root of a $p$-adic number. + Example + sqrt QQ_7 2 + oo^2 + Caveat + An error is raised if @VAR "x"@ is not a $p$-adic square. + SeeAlso + (symbol ^, PadicNumber, ZZ) +/// + +doc /// + Key + (exp, PadicNumber) + Headline + p-adic exponential function + Usage + exp x + Inputs + x:PadicNumber + Outputs + :PadicNumber -- the p-adic exponential of x + Description + Text + Returns the $p$-adic exponential of $x$, defined by the power series + $\exp_p x = \sum_{n=0}^\infty x^n / n!$. The series converges when + $|x|_p < p^{-1/(p-1)}$. + Example + exp QQ_7 7 + log oo + Caveat + An error is raised if the series does not converge. + Subnodes + (log, PadicNumber) +/// + +doc /// + Key + (log, PadicNumber) + Headline + p-adic logarithm function + Usage + log x + Inputs + x:PadicNumber + Outputs + :PadicNumber -- the p-adic logarithm of x + Description + Text + Returns the $p$-adic logarithm of $x$, defined by the power series + $\log_p x = \sum_{n=1}^\infty (-1)^{n-1}(x-1)^n/n$. The series converges + when $|x - 1|_p < 1$. + Example + log exp QQ_7 7 + Caveat + An error is raised if the series does not converge. + SeeAlso + (exp, PadicNumber) +/// + +doc /// + Key + pVal + (pVal, PadicNumber) + Headline + p-adic valuation of a p-adic number + Usage + pVal x + Inputs + x:PadicNumber + Outputs + :ZZ + or @TO InfiniteNumber@ if @VAR "x"@ is zero + Description + Text + Returns the $p$-adic valuation $\nu_p(x)$, i.e., the exponent $\nu$ in + the factorization $x = u p^\nu$ where $u$ is a $p$-adic unit. + Example + pVal QQ_7 49 + pVal QQ_7(1/7) + pVal QQ_7 0 + SeeAlso + unit + (abs, PadicNumber) +/// + +doc /// + Key + prime + (prime, PadicFieldFamily) + (prime, PadicNumber) + Headline + prime of a p-adic field or number + Usage + prime kk + prime x + Inputs + kk:PadicFieldFamily + x:PadicNumber + Outputs + :ZZ + Description + Text + Returns the prime $p$ of a $p$-adic field or number. + Example + prime QQ_7 + prime QQ_7 3 +/// + +doc /// + Key + (symbol ==, PadicNumber, PadicNumber) + (symbol ==, PadicNumber, Number) + (symbol ==, Number, PadicNumber) + Headline + equality of p-adic numbers + Usage + x == y + Inputs + x:PadicNumber + y:PadicNumber + Outputs + :Boolean + Description + Text + Test equality of two $p$-adic numbers. Numbers in the same field are + compared directly. + Example + QQ_7 3 == QQ_7 3 + QQ_7 3 == QQ_7 4 + Text + If the two numbers lie in different $p$-adic fields, or if one argument is + an ordinary number, equality is tested by comparing their lifts to @TO QQ@. + Example + QQ_7 3 == QQ_5 3 + QQ_7 3 == 3 + 3 == QQ_7 3 +/// + +doc /// + Key + teichmullerLift + (teichmullerLift, PadicNumber) + Headline + Teichmüller lift of a p-adic integer + Usage + teichmullerLift x + Inputs + x:PadicNumber + Outputs + :PadicNumber + Description + Text + Returns the Teichmüller lift of $x$, which is the unique root of unity + $t \in \ZZ_p^\times$ satisfying $t \equiv x \pmod{p}$ and $t^p = t$. + Example + t = teichmullerLift QQ_7 3 + t^7 == t + Caveat + An error is raised if $\nu_p(x) < 0$, i.e., if $x$ is not a $p$-adic integer. +/// + +doc /// + Key + (lift, PadicNumber, ZZ) + (lift, PadicNumber, QQ) + (lift, PadicNumber, PadicNumber) + (symbol ^, Number, PadicFieldFamily) + [lift, Verify] + Headline + lift a p-adic number to another ring + Usage + lift(x, R) + x^R + Inputs + x:PadicNumber + R:{ZZ, QQ, PadicNumber} + Outputs + :Number -- an element of @VAR "R"@ + Description + Text + Lift a $p$-adic number to an other ring. This is only well-defined when + @VAR "R"@ to @TO ZZ@, @TO QQ@, or the $p$-adic field containing @VAR "x"@. + Example + x = QQ_7 49 + lift(x, ZZ) + lift(x, QQ) + lift(x, QQ_7) + Text + As usual, the @TO symbol ^@ operator is a shorthand for this operation. + Example + x^ZZ + x^QQ + x^(QQ_7) + Caveat + Lifting to @TO ZZ@ raises an error if the $p$-adic valuation of @VAR "x"@ + is negative. + SeeAlso + (promote, ZZ, PadicNumber) +/// + +doc /// + Key + (promote, ZZ, PadicNumber) + (promote, QQ, PadicNumber) + (promote, PadicNumber, PadicNumber) + (symbol _, Number, PadicFieldFamily) + Headline + promote a number to a p-adic field + Usage + promote(x, kk) + x_kk + Inputs + x:{ZZ, QQ, PadicNumber} + kk:PadicNumber + Outputs + :PadicNumber + Description + Text + Promote an integer, rational, or $p$-adic number to the given $p$-adic + field. The subscript operator @TO symbol _@ may also be used. + Example + promote(3, QQ_7) + promote(3/2, QQ_7) + 3_(QQ_7) + (3/2)_(QQ_7) + SeeAlso + (lift, PadicNumber, ZZ) +/// + +doc /// + Key + (numeric, ZZ, PadicNumber) + (numeric, PadicNumber) + Headline + convert a p-adic number to a real number + Usage + numeric(prec, x) + numeric x + Inputs + prec:ZZ -- number of bits of precision + x:PadicNumber + Outputs + :RR + Description + Text + Converts a $p$-adic number to a floating-point real number by first + lifting to @TO QQ@. + Example + numeric QQ_7 5 + numeric(100, QQ_7 5) + SeeAlso + (interval, PadicNumber) + (lift, PadicNumber, ZZ) +/// + +doc /// + Key + (interval, PadicNumber, PadicNumber) + (interval, PadicNumber, Number) + (interval, Number, PadicNumber) + (interval, PadicNumber) + [interval, Precision] + Headline + convert p-adic numbers to a real interval + Usage + interval(x, y) + interval x + Inputs + x:{PadicNumber, Number} + y:{PadicNumber, Number} + Outputs + :RRi + Description + Text + Converts a $p$-adic number to a real interval by first lifting to @TO QQ@. + When two arguments are given, returns the interval with those endpoints. + Example + interval QQ_7 5 + interval(QQ_7 5, QQ_7 6) + interval(QQ_7 5, 6) + interval(5, QQ_7 6) + SeeAlso + (numeric, PadicNumber) +/// + +TEST /// +assert Equation(net QQ_7, "QQ\n 7"^0) +assert Equation(toString QQ_7, "QQ_7") +assert Equation(toString QQ_7(12/7), "5*7^-1 + 1") +/// + +TEST /// +assert Equation(pVal QQ_7 49, 2) +assert Equation(pVal QQ_7(1/7), -1) +assert Equation(pVal QQ_7 3, 0) +assert Equation(pVal QQ_7 0, infinity) +assert Equation(unit QQ_7 49, 1) +assert Equation(unit QQ_7(12/7), 12) +assert Equation(precision QQ_7 49, 20) +assert Equation(precision QQ_7(30, 49), 30) +assert Equation(prime QQ_7, 7) +assert Equation(prime QQ_7 3, 7) +assert Equation(abs QQ_7 49, 1/49) +assert Equation(abs QQ_7(1/7), 7) +assert Equation(abs QQ_7 3, 1) +/// + +TEST /// +assert Equation(QQ_7 3 + QQ_7 2, QQ_7 5) +assert Equation(QQ_7 3 + 4, QQ_7 7) +assert Equation(4 + QQ_7 3, QQ_7 7) +assert Equation(QQ_7 3 - QQ_7 2, QQ_7 1) +assert Equation(QQ_7 5 - 3, QQ_7 2) +assert Equation(3 - QQ_7 5, QQ_7(-2)) +assert Equation(QQ_7 3 * QQ_7 2, QQ_7 6) +assert Equation(QQ_7 3 * 4, QQ_7 12) +assert Equation(4 * QQ_7 3, QQ_7 12) +assert Equation(QQ_2 3 / QQ_2 2, QQ_2(3/2)) +assert Equation(QQ_7 6 / 3, QQ_7 2) +assert Equation(6 / QQ_7 3, QQ_7 2) +assert Equation(QQ_7 3 << 2, QQ_7(3 * 49)) +assert Equation(QQ_7 3 >> 2, QQ_7(3/49)) +assert Equation(-QQ_7 3 + QQ_7 3, 0) +assert Equation(+QQ_7 3, QQ_7 3) +assert Equation(QQ_7 3 * inverse QQ_7 3, QQ_7 1) +assert Equation((QQ_7 3)^(-1), inverse QQ_7 3) +assert Equation(sqrt QQ_7 4, QQ_7 2) +assert Equation((sqrt QQ_7 2)^2, QQ_7 2) +assert Equation((QQ_7 3)^2, QQ_7 9) +assert Equation(precision(QQ_7(10, 3) + QQ_7(20, 4)), 10) +assert Equation(log exp QQ_7 7, QQ_7 7) +t = teichmullerLift QQ_7 3 +assert Equation(t^7 - t, 0) +/// + +TEST /// +assert Equation(QQ_2 5, QQ_2 5) +assert not (QQ_2 5 == QQ_2 6) +assert Equation(QQ_2 5, QQ_3 5) +assert not (QQ_2 5 == QQ_3 6) +assert Equation(QQ_2 5, 5) +assert Equation(5, QQ_2 5) +-- since === doesn't work in QQ_p :( +is = (x, y) -> assert(class x === class y and x == y) +is(promote(5, QQ_2), QQ_2 5) +is(promote(5/2, QQ_2), QQ_2(5/2)) +is(5_(QQ_2), QQ_2 5) +assert BinaryOperation(symbol ===, (QQ_2 5)^ZZ, 5) +assert BinaryOperation(symbol ===, (QQ_2 5)^QQ, 5/1) +is(lift(QQ_2 5, QQ_2), QQ_2 5) +assert BinaryOperation(symbol ===, numeric QQ_2 5, 5.0) +assert BinaryOperation(symbol ===, numeric(100, QQ_2 5), 5p100) +assert BinaryOperation(symbol ===, interval QQ_2 5, interval 5) +assert BinaryOperation(symbol ===, interval(QQ_2 5, Precision => 100), interval 5p100) +assert BinaryOperation(symbol ===, interval(QQ_2 5, QQ_2 6), interval(5, 6)) +assert BinaryOperation(symbol ===, interval(QQ_2 5, 6), interval(5, 6)) +assert BinaryOperation(symbol ===, interval(5, QQ_2 6), interval(5, 6)) +/// + +TEST /// +-- error cases +checkError = (f, msg) -> ( + (ret, err) := trap f(); + assert Equation(msg, toString err)) +checkError(() -> QQ_4, "expected a prime number") +checkError(() -> QQ_7 1 / QQ_7 0, "division by zero") +checkError(() -> inverse QQ_7 0, "division by zero") +checkError(() -> sqrt QQ_7 3, "not a 7-adic square") +checkError(() -> exp QQ_7 3, "7-adic exponential function does not converge") +checkError(() -> log QQ_7 3, "7-adic logarithm function does not converge") +checkError(() -> teichmullerLift QQ_7(1/7), "expected a 7-adic integer") +checkError(() -> lift(QQ_7(1/7), ZZ), "expected a 7-adic integer") +/// + +TEST /// +-- hensel's lemma example (cube root of 2 in QQ_5) +newton = x -> x - (x^3 - 2)/(3*x^2) +x = QQ_5 3 +while x != (x = newton x) do null +assert Equation(x^3, 2) +/// + +end + +loadPackage("Padic", FileName => "~/src/macaulay2/macaulay2-padic/Padic.m2", Reload => true) diff --git a/M2/Macaulay2/packages/Parametrization.m2 b/M2/Macaulay2/packages/Parametrization.m2 index 390ac91e948..72065a516ea 100644 --- a/M2/Macaulay2/packages/Parametrization.m2 +++ b/M2/Macaulay2/packages/Parametrization.m2 @@ -12,7 +12,7 @@ newPackage( CacheExampleOutput => true, AuxiliaryFiles => true, PackageExports => {"AdjointIdeal"}, - PackageImports => {"OldChainComplexes", "MapleInterface"} + PackageImports => {"Complexes", "MapleInterface"} ) -- For information see documentation key "Parametrization" below. diff --git a/M2/Macaulay2/packages/Parsing.m2 b/M2/Macaulay2/packages/Parsing.m2 index 0035444a22e..89737e443ea 100644 --- a/M2/Macaulay2/packages/Parsing.m2 +++ b/M2/Macaulay2/packages/Parsing.m2 @@ -499,7 +499,57 @@ document { Key => optP, ///, SeeAlso => {constParser, charAnalyzer, (symbol :, Parser, Analyzer)} } - + +TEST /// +debug needsPackage "Classic" +parser = (p,s) -> (p : charAnalyzer) s +assert( parser(symbolP, "x") === x ) +assert( parser(symbolP, "y") === y ) +assert( parser(NNParser, "234") === 234 ) +assert( parser(QQParser, "234/131") === 234/131 ) +assert( parser(QQParser, "-234/131") === -234/131 ) +assert( parser(optP constParser "-", "") === nil ) +assert( parser(andP(optP constParser "-",optP constParser "+"), "") === (nil,nil) ) +assert( parser(andP(optP constParser "-",optP constParser "+"), "+") === (nil,"+") ) +assert( parser(andP(optP constParser "-",optP constParser "+"), "-") === ("-",nil) ) +assert( parser(andP(optP constParser "-",optP constParser "+"), "-+") === ("-","+") ) +assert( (try parser(andP(optP constParser "-",optP constParser "+"), "+-") else oops) === oops ) +assert( parser(ZZParser, "234") === 234 ) +assert( parser(ZZParser, "-234") === -234 ) +assert( parser(ZZParser, "+234") === 234 ) +assert( parser(orP(NNParser,constParser "ab"), "234") === 234 ) +assert( parser(orP(NNParser,constParser "ab"), "ab") === "ab" ) +assert( (try parser(orP(NNParser,constParser "ab"), "a") else oops) === oops ) +assert( (try parser(orP(NNParser,constParser "ab"), "abc") else oops) === oops ) +assert( (try parser(andP(NNParser,constParser "ab"), "234a") else oops) === oops ) +assert( (try parser(andP(NNParser,constParser "ab"), "234b") else oops) === oops ) +assert( parser(andP(NNParser,constParser "ab"), "234ab") === (234,"ab") ) +assert( (try parser(andP(NNParser,constParser "ab"), "234abc") else oops) === oops ) +assert( parser(* andP(constParser ",", NNParser), ",33,4") === ((",",33),(",",4)) ) +R = QQ[a..t,x_0 .. x_9, y_(0,0) .. y_(3,3)] +assert( parser(ringVariableP, "t") === t ) +assert( parser(ringVariableP, "x[3]") === x_3 ) +assert( parser(ringVariableP, "y[2,3]") === y_(2,3) ) +assert( parser(powerP, "t12") === t^12 ) +assert( parser(powerP, "x[3]12") === x_3^12 ) +assert( parser(powerP, "y[2,3]12") === y_(2,3)^12 ) +assert( parser(parenExprP, "(y[2,3]12)") === y_(2,3)^12 ) +assert( parser(monomialP, "3x[2]y[2,3]3") === 3*x_2*y_(2,3)^3 ) +assert( parser(monomialP, "x[2]y[2,3]3") === x_2*y_(2,3)^3 ) +assert( parser(monomialP, "3(x[2]y[2,3])3") === 3*x_2^3*y_(2,3)^3 ) +assert( parser(monomialP, "3(-2x[2]y[2,3])3") === -24*x_2^3*y_(2,3)^3 ) +assert( parser(polyP, "3x[2]y[2,3]3") === 3*x_2*y_(2,3)^3 ) +assert( parser(polyP, "x[2]y[2,3]3") === x_2*y_(2,3)^3 ) +assert( parser(polyP, "3(x[2]y[2,3])3") === 3*x_2^3*y_(2,3)^3 ) +assert( parser(polyP, "3(-2x[2]y[2,3]+3t)3") === -24*x_2^3*y_(2,3)^3+108*t*x_2^2*y_(2,3)^2-162*t^2*x_2*y_(2,3)+81*t^3 ) +assert( parser(polyP, "(1-2/3a)5") === -32/243*a^5+80/81*a^4-80/27*a^3+40/9*a^2-10/3*a+1 ) +assert( parser(arrayPolyP, "a") === {{a}} ) +assert( parser(arrayPolyP, "(a+1)5,b,c;d,e,f2-g") === {{a^5+5*a^4+10*a^3+10*a^2+5*a+1, b, c}, {d, e, f^2-g}} ) +i=3 +assert( ideal "a,b,c" === ideal(a,b,c) ) +assert( matrix "a,b,c;d,e,f" === matrix( {{a, b, c}, {d, e, f}}) ) +assert( matrix "a,b,c;d,x[i]5,(f-1)4" === matrix( {{a, b, c}, {d, x_3^5, f^4-4*f^3+6*f^2-4*f+1}}) ) +/// -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/packages PACKAGES=Parsing pre-install" diff --git a/M2/Macaulay2/packages/PathSignatures.m2 b/M2/Macaulay2/packages/PathSignatures.m2 index c9c765aeb50..d45b684965a 100644 --- a/M2/Macaulay2/packages/PathSignatures.m2 +++ b/M2/Macaulay2/packages/PathSignatures.m2 @@ -21,7 +21,6 @@ export { "tensorParametrization", "wordAlgebra", "sgnVolTensor", - "shuffle", "halfshuffle", "wordFormat", "wordString", diff --git a/M2/Macaulay2/packages/PathSignatures/algebra.m2 b/M2/Macaulay2/packages/PathSignatures/algebra.m2 index 094426a6c9d..015f1ebb631 100644 --- a/M2/Macaulay2/packages/PathSignatures/algebra.m2 +++ b/M2/Macaulay2/packages/PathSignatures/algebra.m2 @@ -102,7 +102,6 @@ shuffleHelper(NCRingElement, NCRingElement) := (f,g) -> ( -- Exposed versions of shuffle -shuffle = method(); shuffle (NCRingElement, NCRingElement) := (a, b) -> shuffleHelper(a,b); NCRingElement ** NCRingElement := shuffle diff --git a/M2/Macaulay2/packages/PathSignatures/documentation.m2 b/M2/Macaulay2/packages/PathSignatures/documentation.m2 index c8aa18e1d32..4eecbac1ef2 100644 --- a/M2/Macaulay2/packages/PathSignatures/documentation.m2 +++ b/M2/Macaulay2/packages/PathSignatures/documentation.m2 @@ -452,20 +452,18 @@ Node @LABEL("[1]","id" => "ref1")@ @HREF {"https://doi.org/10.1007/s13366-020-00493-9","Signatures of paths transformed by polynomial maps (doi.org/10.1007/s13366-020-00493-9)"} @ Node Key - shuffle + (shuffle, NCRingElement, NCRingElement) (symbol **, NCRingElement, NCRingElement) (symbol ⧢, NCRingElement, NCRingElement) - (shuffle, NCRingElement, NCRingElement) Headline shuffle product of two words Inputs w1: NCRingElement w2: NCRingElement - R: NCPolynomialRing --The non-commutative polynomial where the operation ought to be carried out Outputs v: NCRingElement --The shuffle product of w1 and w2 Usage - v = shuffle(w1, w2, R) + v = shuffle(w1, w2) Description Text We start with the mathematical definition of this operation, based on @HREF("#ref1","[1]")@. diff --git a/M2/Macaulay2/packages/Permutations/Code/main.m2 b/M2/Macaulay2/packages/Permutations/Code/main.m2 index f48e7590756..023436160d9 100644 --- a/M2/Macaulay2/packages/Permutations/Code/main.m2 +++ b/M2/Macaulay2/packages/Permutations/Code/main.m2 @@ -239,7 +239,7 @@ length Permutation := ZZ => w -> (#(inversions w)) -- Random permutations ------------------------------------ randomPermutation = method() -randomPermutation ZZ := Permutation => (n) -> (permutation random toList(1..n)) +randomPermutation ZZ := Permutation => (n) -> (permutation shuffle toList(1..n)) ------------------------------------ @@ -316,4 +316,4 @@ symmetricGroupPoset = method() symmetricGroupPoset (ZZ, Function) := Poset => (n, comparisonFunction) -> ( Sn := apply(permutations n, p -> permutation to1Index p); poset(Sn, comparisonFunction) -) \ No newline at end of file +) diff --git a/M2/Macaulay2/packages/Permutations/Tests/mainTests.m2 b/M2/Macaulay2/packages/Permutations/Tests/mainTests.m2 index 1ac2e5b6b66..25edd74f782 100644 --- a/M2/Macaulay2/packages/Permutations/Tests/mainTests.m2 +++ b/M2/Macaulay2/packages/Permutations/Tests/mainTests.m2 @@ -7,11 +7,11 @@ TEST /// -- valid permutations should be nonempty lists consisting of only all numbers 1..n assert(isWellDefined permutation {1}) assert(isWellDefined permutation toList (1..8)) - assert(isWellDefined permutation random toList (1..8)) + assert(isWellDefined permutation shuffle toList (1..8)) assert(not isWellDefined permutation {}) assert(not isWellDefined permutation {0}) assert(not isWellDefined permutation toList (0..8)) - assert(not isWellDefined permutation random toList (0..8)) + assert(not isWellDefined permutation shuffle toList (0..8)) assert(not isWellDefined permutation {1,1,2}) /// diff --git a/M2/Macaulay2/packages/Permutations/Tests/operationsTests.m2 b/M2/Macaulay2/packages/Permutations/Tests/operationsTests.m2 index 6b2aaf3d950..2524db53050 100644 --- a/M2/Macaulay2/packages/Permutations/Tests/operationsTests.m2 +++ b/M2/Macaulay2/packages/Permutations/Tests/operationsTests.m2 @@ -128,8 +128,8 @@ TEST /// assert(p * toList(1 .. #p) == pList) assert(p * toList(1 .. #p+2) == toList extendedP) assert(p * {5,4,3,2,1,10,9,8,7,6} == {6,7,8,9,10,1,2,3,4,5}) - assert(p * (5,4,3,2,1,10,9,8,7,6) == {6,7,8,9,10,1,2,3,4,5}) - assert(p * [5,4,3,2,1,10,9,8,7,6] == {6,7,8,9,10,1,2,3,4,5}) + assert(p * (5,4,3,2,1,10,9,8,7,6) == (6,7,8,9,10,1,2,3,4,5)) + assert(p * [5,4,3,2,1,10,9,8,7,6] == [6,7,8,9,10,1,2,3,4,5]) assert(p * (matrix p) == id_(ZZ^#p)) assert(p * (matrix permutation {6,7,8,9,10,1,2,3,4,5}) == matrix {{0,0,0,0,1,0,0,0,0,0}, @@ -217,10 +217,10 @@ TEST /// ------- -- Misc ------- - p = permutation random toList (1..10) + p = permutation shuffle toList (1..10) assert((inverse p)*p == trimmedIdentity) assert(p*(inverse p) == trimmedIdentity) assert(ord p == ord inverse p) assert(cycleType p == cycleType inverse p) assert(sign p == sign inverse p) -/// \ No newline at end of file +/// diff --git a/M2/Macaulay2/packages/PhylogeneticTrees.m2 b/M2/Macaulay2/packages/PhylogeneticTrees.m2 index 061e9664c03..f2ad26756e9 100644 --- a/M2/Macaulay2/packages/PhylogeneticTrees.m2 +++ b/M2/Macaulay2/packages/PhylogeneticTrees.m2 @@ -244,7 +244,7 @@ phyloToricLinears(LeafTree,Model) := opts -> (T,M) -> ( if #p < 2 then continue; for j to #p-2 list sub(S_(p#j)-S_(p#(j+1)),S) ); - if not opts.Random then gensList else randomElement gensList + if not opts.Random then gensList else randomElement' gensList ) @@ -254,10 +254,10 @@ phyloToricQuads(ZZ,List,Model) := opts -> (n,E,M) -> phyloToricQuads(leafTree(n, phyloToricQuads(LeafTree,Model) := opts -> (T,M) -> ( S := if opts.QRing =!= null then opts.QRing else qRing(T,M); quadTemplates := apply(#(group M), g->({{g,g},{g,g}},{{g,g},{g,g}},{0,3,2,1})); - if opts.Random then quadTemplates = randomElement quadTemplates; + if opts.Random then quadTemplates = randomElement' quadTemplates; newl := symbol newl; intEdges := internalEdges T; - if opts.Random then intEdges = randomElement intEdges; + if opts.Random then intEdges = randomElement' intEdges; gensList := flatten for e in intEdges list ( P := edgeCut(T,e,newl); fillTemplates(T,M,S,P,quadTemplates,newl,opts.Random) @@ -274,7 +274,7 @@ phyloToricClaw(LeafTree,Model) := opts -> (T,M) -> ( newl := symbol newl; clawHash := new MutableHashTable; --store claw invariants to avoid recomputing intVerts := (internalEdges T)|{set{l}}; - if opts.Random then intVerts = randomElement intVerts; + if opts.Random then intVerts = randomElement' intVerts; gensList := flatten for e in intVerts list ( P := vertexCut(T,e,l,newl); if not clawHash#?(#P,M) then clawHash#(#P,M) = clawInvariants(#P,M); @@ -295,7 +295,7 @@ phyloToricRandom(LeafTree,Model) := opts -> (T,M) -> ( if #gensList > 0 then first gensList else phyloToricRandom(T,M,opts) ) -randomElement = L -> ( +randomElement' = L -> ( if #L == 0 then return {}; n := random(#L); {L#n} @@ -321,7 +321,7 @@ fillTemplates = (T,M,S,P,temps,newl,rand) -> ( fbinom1 := flatten binom#1; --same for second monomial PFCLists := apply(#fbinom0, j->(PFCs#(j%n))#(G#(fbinom0#j))); --for each entry of fbinom0(itself a list), the list of all coloring extensions PFCi := (#(binom#2):0)..(toSequence apply(PFCLists,l->(#l-1))); --sequence of list indices for all combinations of coloring extensions - if rand then PFCi = toSequence randomElement PFCi; + if rand then PFCi = toSequence randomElement' PFCi; newGens := for iList in PFCi list ( CList0 := apply(#iList, j->(PFCLists#j)#(iList#j)); --a list of color extensions for first monomial CList1 := apply(binom#2, k->CList0#k); --permutations of the color extensions for second monomial diff --git a/M2/Macaulay2/packages/PieriMaps.m2 b/M2/Macaulay2/packages/PieriMaps.m2 index 267e46efaf8..30b6b7dad73 100644 --- a/M2/Macaulay2/packages/PieriMaps.m2 +++ b/M2/Macaulay2/packages/PieriMaps.m2 @@ -10,8 +10,8 @@ newPackage( "PieriMaps", - Version => "1.0", - Date => "July 3, 2009", + Version => "2.0", + Date => "May 1, 2026", Certification => { "journal name" => "The Journal of Software for Algebra and Geometry: Macaulay2", "journal URI" => "https://msp.org/jsag/", @@ -29,13 +29,89 @@ newPackage( Name => "Steven V Sam", Email => "ssam@math.mit.edu", HomePage => "http://math.mit.edu/~ssam/" + }, + { + Name => "Keller VandeBogert", + Email => "keller.v@uky.edu", + HomePage => "https://sites.nd.edu/kellerv/" }}, - Headline => "maps between representations of the general linear group based on the Pieri formulas", + Headline => "Pieri inclusions and projections between Schur modules with multiple basis conventions", Keywords => {"Representation Theory"}, - DebuggingMode => false + DebuggingMode => false, + AuxiliaryFiles => true ) -export {"straighten", "standardTableaux", "pieri", "pureFree", "schurRank"} +-- SchurFunctors is needed for the convention-aware features (Filling / +-- WeylFilling bases, column-form Pieri, etc.) but is loaded lazily because +-- PieriMaps and SchurFunctors both export several symbols (`straighten`, +-- `standardTableaux`, `isStandard`) and we want PieriMaps's versions to take +-- precedence on the row-form side. Convention features call ensureSchurFn +-- to lazily load SchurFunctors and resolve its symbols via its private +-- dictionary. + +schurFnLoaded = false +sfStraightenPM = null +sfStandardTabPM = null +sfFillingType = null +sfWeylFillingType = null +sfWeylFn = null + +-- PieriMaps' Filling/Weyl convention features depend on SchurFunctors v >= 1.1 +-- (which exports `straighten Filling`, `straighten HashTable`, and the +-- `WeylFilling` / `weyl` symbols). The system-installed version in some M2 +-- distributions is older and only registers `straighten (Filling, Module)`. +-- We load lazily and detect the incomplete state at first use so the user +-- gets a clear, actionable error rather than a cryptic "no method for +-- straighten" later in the call stack. +ensureSchurFn = () -> ( + if schurFnLoaded then return; + needsPackage "SchurFunctors"; + sfPkg := value getGlobalSymbol "SchurFunctors"; + d := sfPkg#"private dictionary"; + sfStraightenPM = value (d#"straighten"); + if #methods sfStraightenPM < 2 then error( + "PieriMaps' Filling and Weyl conventions require an updated " | + "SchurFunctors that exports `straighten Filling` directly. The " | + "currently loaded SchurFunctors only has the (Filling, Module) " | + "signature. Install the newer SchurFunctors and load it BEFORE " | + "PieriMaps via `loadPackage(\"SchurFunctors\", FileName => \"\")`." + ); + sfStandardTabPM = value (d#"standardTableaux"); + sfFillingType = value (d#"Filling"); + -- WeylFilling and `weyl` are present only in the updated SchurFunctors; + -- they are optional (Weyl convention requires them). + if d#?"WeylFilling" then sfWeylFillingType = value (d#"WeylFilling"); + if d#?"weyl" then sfWeylFn = value (d#"weyl"); + schurFnLoaded = true; + ) + +export { + -- Original PieriMaps (unchanged signatures unless noted): + "straighten", "standardTableaux", "pieri", "pureFree", "schurRank", + -- LR maps (added in this overhaul): + "lrTableaux", "lrMap", "applyLR", "displayLRImage", + -- Convention-aware overhaul (Convention => "Row" | "Filling" | "Weyl" + -- option on pieri / lrMap / applyLR / pureFree). These are also exposed + -- as standalone functions for explicit basis access: + "Convention", + "pmToFilling", "fillingToPM", "pmToWeyl", "weylToPM", + "weylToFilling", "fillingToWeyl", + "pmToFillingMatrix", "fillingToPMMatrix", + -- Native column-form (SkewCommutative-target) Pieri: + "pieriColumn", + -- Convention compatibility checker: + "verifyWellDefined", + -- Dual / projection-direction maps (GL-equivariant): + "dualPieri", "dualLR", "applyDualPieri", "applyDualLR", + -- Inclusion-direction point evaluators: + "applyPieri", "applyPieriColumn", + -- Column-form (wedge-target) projection direction: + "dualPieriColumn", "applyDualPieriColumn", + -- General GL-equivariance check for matrices between Schur reps: + "verifyEquivariant", "Direction", + -- Pretty-print a matrix as a basis-labeled symbolic map: + "symbolicForm" + } -------------------------------- -- subroutines (not exported) -- @@ -68,7 +144,7 @@ isIncreasing = L -> ( -- If T not standard (weakly increasing rows, increasing columns), return -- first violating entry (starting from bottom to top, left to right); -- otherwise return null -isStandard = T -> ( +isStandardPM = T -> ( i := #T-2; while i >= 0 do ( a := T#i; @@ -90,21 +166,20 @@ isStandard = T -> ( -- entries (ignoring that some entries might be equal). The output is given in -- the form a hash table where the keys are the resulting tableau and the -- values are -1 -shuffle = (T, col, row1, row2) -> ( +shuffle' = (T, col, row1, row2) -> ( len1 := #(T#row1); len2 := #(T#row2); truncatedrow1 := (T#row1)_{0..col-2}; -- grab row1 entries truncatedrow2 := (T#row2)_{col..len2-1}; -- grab row2 entries L := join((T#row1)_{col-1..len1-1}, (T#row2)_{0..col-1}); P := permutations L; - output := {}; P = apply(P, i-> (for j from 0 to #T-1 list ( if j == row1 then sort join(truncatedrow1, i_{0..len1-col}) else if j == row2 then sort join(i_{len1-col+1..#i-1}, truncatedrow2) else T#j))); coeff := 0; for i in P do if i == T then coeff = coeff + 1; - for i in P do if i != T then output = append(output, (i, -1 / coeff)); + output := for i in P list if i != T then (i, -1 / coeff) else continue; return hashTable(plus, output); ) @@ -116,17 +191,15 @@ shuffle = (T, col, row1, row2) -> ( -- table which contains tableaux T_i as keys and their values c_i which -- represent coefficients: T = c_1T_1 + ... + c_nT_n towardStandard = T -> ( - x := isStandard T; + x := isStandardPM T; if x === null then return new HashTable from {T=>1}; - H := new MutableHashTable from shuffle(T, x#1+1, x#0, x#0+1); + H := new MutableHashTable from shuffle'(T, x#1+1, x#0, x#0+1); if H #? T then ( coeff := -(H#T) + 1; remove(H,T); - prehash := {}; - for i in keys H do - prehash = append(prehash, (i, H#i / coeff)); + prehash := for i in keys H list (i, H#i / coeff); return hashTable(prehash) - ) + ) else return new HashTable from H ) @@ -140,14 +213,61 @@ towardStandard = T -> ( -- ZZ k: an index -- Output: -- Subtract one from the kth (in human count, not computer count) row of mu -subtractOne = (mu, k) -> ( - result := {}; - for i from 0 to #mu-1 when true do - if i == k-1 then result = append(result, mu_i - 1) - else result = append(result, mu_i); - return result +subtractOne = (mu, k) -> + for i from 0 to #mu - 1 list (if i == k - 1 then mu_i - 1 else mu_i) + +------------------------------------------------------------------------------- +-- Convention infrastructure (added in this overhaul). +-- +-- These helpers are used by the convention-aware versions of pieri / lrMap / +-- applyLR / pureFree (Convention => "Row" | "Filling" | "Weyl") and by the +-- standalone basis conversions pmToFilling / fillingToPM / pmToWeyl / +-- weylToPM. They depend on the SchurFunctors package being loaded +-- (PackageImports above). +------------------------------------------------------------------------------- + +-- Sign-tracked sort. Returns (sign, sortedList) with sign in {1, -1}, or null +-- if the input has a repeat. +sortWithSignPM = L -> ( + s := new MutableList from L; + n := #s; + swaps := 0; + for i from 0 to n-2 do + for j from 0 to n-i-2 do ( + if s#j === s#(j+1) then return null; + if s#j > s#(j+1) then ( + t := s#(j+1); s#(j+1) = s#j; s#j = t; + swaps = swaps + 1; + ); + ); + ((if swaps % 2 == 0 then 1 else -1), toList s) + ) + +factorialPM = n -> if n <= 1 then 1 else n * factorialPM(n - 1) + +-- Cartesian product enumerator: yields each combination as a List `current` +-- to the callback `body`. +cartesianForEachPM = (lists, body) -> ( + n := #lists; + buf := new MutableList from toList(n : null); + descend := null; + descend = i -> ( + if i == n then body(toList buf) + else for v in lists#i do (buf#i = v; descend(i + 1)); + ); + descend(0); ) +-- Drop trailing zeros from a partition. +trimPartPM = mu -> ( + L := #mu; + while L > 0 and mu#(L-1) == 0 do L = L - 1; + for i from 0 to L - 1 list mu#i + ) + +-- Conjugate of a partition (drops trailing zeros first). +conjPartPM = mu -> toList conjugate (new Partition from trimPartPM mu) + ------------------- -- Main routines -- ------------------- @@ -155,13 +275,11 @@ subtractOne = (mu, k) -> ( -- Returns dual partition dualPart = method() dualPart(List) := mu -> ( - result := {}; - for i from 1 to mu#0 when true do ( + for i from 1 to mu#0 list ( counter := 0; - for j from 0 to #mu-1 when true do if mu#j >= i then counter = counter + 1; - result = append(result, counter); - ); - return result; + for j from 0 to #mu - 1 do if mu#j >= i then counter = counter + 1; + counter + ) ) -- Computes the dimension of S_mu V where V has dimension n and @@ -178,24 +296,26 @@ schurRank(ZZ, List) := (n, mu) -> ( -- List mu: a partition -- Output: -- All standard tableaux of shape mu and with labels from 0..dim-1 +-- Memoize standardTableaux: it's called many times per pieri / lrMap with +-- the same (dim, mu) (e.g., for source and target shapes, repeated across +-- pieri compositions in lrMap, and between successive iterations of +-- benchmarks/loops). The cache is purely a function of (dim, mu). +standardTableauxCache = new MutableHashTable + standardTableaux = method() -standardTableaux(ZZ, List) := (dim, mu) -> ( - if #mu == 0 then return {{}}; - output := {}; - otherrows := standardTableaux(dim, drop(mu, 1)); - firstrow := rsort compositions(dim, mu#0); - for i in firstrow do ( - temp := {}; - for j from 0 to #i-1 do - for k from 1 to i#j when true do - temp = append(temp,j); - for j in otherrows do ( - temp2 := prepend(temp,j); - if isStandard(temp2) === null then - output = append(output, temp2); - ); - ); - return output +standardTableaux(ZZ, List) := (dim, mu) -> standardTableauxCache#(dim, mu) ??= ( + if #mu == 0 then {{}} + else ( + otherrows := standardTableaux(dim, drop(mu, 1)); + firstrow := rsort compositions(dim, mu#0); + flatten for i in firstrow list ( + temp := flatten for j from 0 to #i - 1 list toList(i#j : j); + for j in otherrows list ( + temp2 := prepend(temp, j); + if isStandardPM(temp2) === null then temp2 else continue + ) + ) + ) ) -- Input: @@ -213,77 +333,279 @@ straighten(List) := t -> ( straighten(List, MutableHashTable) := (t, h) -> ( t = apply(t, i -> sort i); if h #? t then return null; - if isStandard(t) === null then - h#t = new HashTable from {t => 1}; - + -- Short-circuit: if t is already standard, the straightening is just t. + -- Avoids an unnecessary towardStandard + recursive straighten call that + -- would otherwise recompute the same answer. + if isStandardPM(t) === null then ( + h#t = new HashTable from {t => 1}; + return null; + ); firstIter := towardStandard(t); H := hashTable({}); -- straightening of t for i in keys firstIter do ( coeff := firstIter#i; straighten(i, h); - temp := {}; - for j in keys h#i do temp = append(temp, (j, coeff * (h#i)#j)); - H = merge(H, hashTable(temp), plus); - temp = {}; - for j in keys H do if H#j != 0 then temp = append(temp,(j,H#j)); - H = hashTable(temp); - ); - h#t = H; + temp := for j in keys h#i list (j, coeff * (h#i)#j); + H = merge(H, hashTable temp, plus); + ); + -- Drop zeros once at the end rather than after every iteration. + h#t = hashTable for j in keys H list if H#j != 0 then (j, H#j) else continue; return null; ) +------------------------------------------------------------------------------- +-- Basis conversion: PM (row-form, in Sym tensor) <--> Filling (column-form, +-- in exterior tensor) <--> WeylFilling (divided-power row-form). +-- +-- pmToFilling : PM-tableau -> HashTable {standard Filling => QQ} +-- via symmetrize-then-antisymmetrize. +-- fillingToPM : Filling -> HashTable {standard PM-tableau => QQ} +-- via antisymmetrize-then-symmetrize. +-- pmToWeyl : PM-tableau -> WeylFilling (data identity) +-- weylToPM : WeylFilling -> PM-tableau (data identity) +-- +-- These are equivariant isos S_lambda V_PM ~= S_lambda V_Filling (resp. WeylF) +-- and are inverse up to a uniform shape-dependent scalar c_lambda (the Young- +-- symmetrizer normalization). +------------------------------------------------------------------------------- + +-- PM tableau -> WeylFilling (just wrap; data is identical). Requires the +-- updated SchurFunctors that exports `weyl` and `WeylFilling`. +pmToWeyl = T -> ( + ensureSchurFn(); + if sfWeylFn === null then error "pmToWeyl: this requires the updated SchurFunctors with WeylFilling support; the system version does not export `weyl`/`WeylFilling`"; + sfWeylFn(toList apply(T, r -> sort toList r)) + ) + +-- WeylFilling -> PM tableau (just unwrap). +weylToPM = W -> apply(toList W, r -> sort toList r) + +-- PM tableau -> raw HashTable {Filling => QQ} (not yet straightened). +pmToFillingExpansion = T -> ( + ensureSchurFn(); + T = apply(T, r -> sort toList r); + lambda := apply(T, r -> #r); + r := #lambda; + if r == 0 then return new HashTable from {}; + s := lambda#0; + for i from 1 to r - 1 do if lambda#i > lambda#(i-1) then error "pmToFilling: shape not a partition"; + lambdaT := for j from 0 to s - 1 list ( + c := 0; + for i from 0 to r - 1 do if lambda#i > j then c = c + 1; + c + ); + rowPerms := apply(T, r -> permutations r); + denom := product apply(lambda, k -> factorialPM k); + accum := new MutableHashTable; + cartesianForEachPM(rowPerms, sigmas -> ( + cols := for j from 0 to s - 1 list + for i from 0 to lambdaT#j - 1 list (sigmas#i)#j; + totalSign := 1; + degenerate := false; + sortedCols := for c in cols list ( + sgn := sortWithSignPM c; + if sgn === null then ( degenerate = true; break {} ); + totalSign = totalSign * (sgn#0); + sgn#1 + ); + if degenerate then return; + F := new sfFillingType from sortedCols; + contribution := (totalSign * 1_QQ) / denom; + accum#F = (accum#F ?? 0) + contribution; + )); + new HashTable from for k in keys accum list if accum#k != 0 then k => accum#k else continue + ) + +-- PM -> standard Filling decomposition (run SchurFunctors' straighten). +-- We iterate over the raw expansion's Filling keys manually rather than +-- passing the whole HashTable to SchurFunctors' straighten -- in some load +-- orders the (HashTable) signature of straighten is not registered. +pmToFilling = T -> ( + ensureSchurFn(); + raw := pmToFillingExpansion T; + out := new MutableHashTable; + for F in keys raw do ( + c := raw#F; + decomp := sfStraightenPM F; + for k in keys decomp do ( + v := c * decomp#k; + out#k = (out#k ?? 0) + v; + ); + ); + new HashTable from for k in keys out list if out#k != 0 then k => out#k else continue + ) + +-- Filling -> raw HashTable {PM-tableau => QQ}. +fillingToPMExpansion = F -> ( + F = toList F; + s := #F; + if s == 0 then return new HashTable from {}; + lambdaT := apply(F, length); + r := lambdaT#0; + for j from 1 to s - 1 do if lambdaT#j > lambdaT#(j-1) then error "fillingToPM: shape not a partition"; + lambda := for i from 0 to r - 1 list ( + c := 0; + for j from 0 to s - 1 do if lambdaT#j > i then c = c + 1; + c + ); + colChoices := for j from 0 to s - 1 list ( + perms := permutations(F#j); + for p in perms list ( + sgn := sortWithSignPM p; + (sgn#0, p) + ) + ); + denom := product apply(lambdaT, k -> factorialPM k); + accum := new MutableHashTable; + cartesianForEachPM(colChoices, choice -> ( + totalSign := 1; + orderedCols := for c in choice list ( + totalSign = totalSign * (c#0); + c#1 + ); + rows := for i from 0 to r - 1 list + for j from 0 to lambda#i - 1 list (orderedCols#j)#i; + sortedRows := apply(rows, sort); + contribution := (totalSign * 1_QQ) / denom; + accum#sortedRows = (accum#sortedRows ?? 0) + contribution; + )); + new HashTable from for k in keys accum list if accum#k != 0 then k => accum#k else continue + ) + +-- Filling -> standard PM decomposition (run PieriMaps' straighten). +fillingToPM = F -> ( + raw := fillingToPMExpansion F; + out := new MutableHashTable; + memo := new MutableHashTable; + for K in keys raw do ( + c := raw#K; + (lookup(straighten, List, MutableHashTable))(K, memo); + decomp := memo#(apply(K, sort)); + for std in keys decomp do ( + cc := c * decomp#std; + out#std = (out#std ?? 0) + cc; + ); + ); + new HashTable from for k in keys out list if out#k != 0 then k => out#k else continue + ) + +weylToFilling = W -> pmToFilling weylToPM W + +fillingToWeyl = F -> ( + ensureSchurFn(); + raw := fillingToPM F; + new HashTable from for k in keys raw list sfWeylFn k => raw#k + ) + +-- QQ change-of-basis matrices on a Schur shape mu over QQ^n. +-- fillingToPMMatrix(mu, n) : rows = pmStandardTab(n, mu), cols = +-- sfStandardTab(n, conjugate mu). Entry (i, j) is the coefficient of +-- pmTab_i in fillingToPM(filling_j). This matrix is invertible (up to a +-- shape-dependent scalar) since pmToFilling/fillingToPM are equivariant +-- isos between PM and Filling realizations of S_mu V. +-- Cache for change-of-basis matrices keyed by (mu_trimmed, n). These +-- matrices are pure functions of (mu, n) and are reused across many pieri / +-- lrMap calls under Convention => "Filling". +fillingToPMMatrixCache = new MutableHashTable +pmToFillingMatrixCache = new MutableHashTable + +fillingToPMMatrix = (mu, n) -> fillingToPMMatrixCache#(mm := trimPartPM mu, n) ??= ( + pmS := standardTableaux(n, mm); + muTcols := conjPartPM mm; + ensureSchurFn(); + sfS := sfStandardTabPM(n, muTcols); + -- Hoisted: fillingToPM(sfS#sj) depends only on sj, not ti -- compute once + -- per column of the result. This converts an O(#pmS * #sfS) call count + -- (in the original loop) to O(#sfS). + decomps := for sj from 0 to #sfS - 1 list fillingToPM sfS#sj; + result := matrix for ti from 0 to #pmS - 1 list ( + for sj from 0 to #sfS - 1 list ( + decomp := decomps#sj; + U := pmS#ti; + decomp#U ?? 0_QQ + ) + ); + -- Source labels = standard Fillings; target labels = standard PM tableaux. + attachBasisLabels(result, sfS, pmS); + result + ) + +pmToFillingMatrix = (mu, n) -> pmToFillingMatrixCache#(mm := trimPartPM mu, n) ??= ( + pmS := standardTableaux(n, mm); + muTcols := conjPartPM mm; + ensureSchurFn(); + sfS := sfStandardTabPM(n, muTcols); + decomps := for pj from 0 to #pmS - 1 list pmToFilling pmS#pj; + result := matrix for fi from 0 to #sfS - 1 list ( + for pj from 0 to #pmS - 1 list ( + decomp := decomps#pj; + F := sfS#fi; + decomp#F ?? 0_QQ + ) + ); + -- Source labels = standard PM tableaux; target labels = standard Fillings. + attachBasisLabels(result, pmS, sfS); + result + ) + -- Input: -- List mu: A partition -- ZZ k: a number specifying which row to remove a box from mu -- PolynomialRing P: the coefficient ring for the Pieri map over a field K -- Output: --- An GL(n,K)-equivariant map P \otimes S_mu K^n -> P \otimes S_lambda K^n --- where lambda is mu with a box removed from the kth row, and S_mu denotes +-- An GL(n,K)-equivariant map P \otimes S_mu K^n -> P \otimes S_lambda K^n +-- where lambda is mu with a box removed from the kth row, and S_mu denotes -- the Schur functor associated to mu pieriHelper = method() pieriHelper(List, ZZ, PolynomialRing) := (mu, k, P) -> ( d := numgens P; X := gens P; - output := {}; Sbasis := standardTableaux(d, mu); Tbasis := standardTableaux(d, subtractOne(mu, k)); mu = prepend(0,mu); A := {}; for p from 0 to k when true do A = join(A, apply(subsets(1..(k-1), p), s -> prepend(0, append(s, k)))); - for s in apply(Sbasis, i->prepend({},i)) do ( - row := {}; + -- Hoisted memo: shared across all source tableaux. The straightening of + -- a given intermediate tableau U does not depend on which source we are + -- processing, so caching results once and reusing them across the s-loop + -- avoids re-running expensive Garnir straightening on the same U. + -- (~1.3x-1.7x speedup on medium/large pieri calls.) + memo := new MutableHashTable; + -- Hoisted Olver coefficients: cJ depends only on the path J, not on the + -- source tableau s. Compute (-1)^#J / cJ once per path so the s-loop + -- doesn't redo the same arithmetic. + hCoefs := for J in A list ( + cJ := 1; + for q from 1 to #J-2 when true do cJ = cJ * (mu_(J_q) - mu_k + k - J_q); + (-1)^#J / cJ + ); + output := for s in apply(Sbasis, i->prepend({},i)) list ( H := new HashTable from {}; - for J in A do ( - cJ := 1; - for q from 1 to #J-2 when true do cJ = cJ * (mu_(J_q) - mu_k + k - J_q); - h := hashTable({(s, (-1)^#J / cJ)}); - for i from 0 to #J-2 when true do ( - temp := {}; - for T in keys h do ( - for b from 0 to #(T_(J_(i+1)))-1 when true do ( + for jIdx from 0 to #A - 1 do ( + J := A#jIdx; + h := hashTable({(s, hCoefs#jIdx)}); + for i from 0 to #J - 2 do ( + temp := flatten for T in keys h list + for b from 0 to #(T_(J_(i+1))) - 1 list ( U := new MutableList from T; U#(J_(i+1)) = drop(U#(J_(i+1)), {b, b}); U#(J_i) = append(U#(J_i), (T_(J_(i+1)))_b); - temp = append(temp, (new List from U, h#T)); + (new List from U, h#T) ); - ); h = hashTable(plus, temp); ); H = merge(H, h, plus); ); H = new MutableHashTable from H; - memo := new MutableHashTable from {}; for T in keys H do ( U := apply(drop(T,1), i->sort(i)); coeff := H#T; remove(H, T); straighten(U, memo); - for i in keys memo#U do - if H #? i then H#i = H#i + coeff * (memo#U)#i * X_((T_0)_0) - else H#i = coeff * (memo#U)#i * X_((T_0)_0); + for i in keys memo#U do + H#i = (H#i ?? 0) + coeff * (memo#U)#i * X_((T_0)_0); ); - for t in Tbasis do if H #? t then row = append(row, H#t) else row = append(row, 0); - output = append(output, row); + for t in Tbasis list (H#t ?? 0) ); return map(P^(#Tbasis), P^{#Sbasis:-1}, transpose output); ) @@ -297,8 +619,18 @@ pieriHelper(List, ZZ, PolynomialRing) := (mu, k, P) -> ( -- A GL(V)-equivariant map P \otimes S_mu V -> P \otimes S_lambda V, where -- S_mu denotes the Schur functor associated to mu, and lambda is the -- partition obtained from mu by deleting boxes_0, boxes_1, ..., in order. -pieri = method() -pieri(List, List, PolynomialRing) := (mu, boxes, P) -> ( +-- Stash source / target basis labels on a matrix's cache so that +-- `symbolicForm` can pretty-print the map in those basis labels. Cache +-- keys are strings (rather than symbols) so that the labels are visible +-- across dictionaries (e.g. from user code or TEST blocks). +attachBasisLabels = (M, srcLabels, tgtLabels) -> ( + M.cache#"sourceBasis" = srcLabels; + M.cache#"targetBasis" = tgtLabels; + M + ); + +pieri = method(Options => {Convention => "Row"}) +pieri(List, List, PolynomialRing) := opts -> (mu, boxes, P) -> ( -- check if row indices are okay for i in boxes do if i < 1 or i > #mu then error "The second argument must specify row indices of the partition in the first argument."; -- check if removal of boxes gives a partition @@ -308,11 +640,154 @@ pieri(List, List, PolynomialRing) := (mu, boxes, P) -> ( if not isDecreasing(lambda) then error "The second argument needs to specify a horizontal strip in the first argument."; ); -- check if mu/lambda is a horizontal strip - for i from 0 to #mu-2 do + for i from 0 to #mu-2 do if lambda#i < mu#(i+1) then error "The second argument needs to specify a horizontal strip in the first argument."; -- error checking done - if char P == 0 then pierizero(mu, boxes, P) - else pierip(mu, boxes, P) + M := if char P == 0 then pierizero(mu, boxes, P) else pierip(mu, boxes, P); + conv := opts.Convention; + n := numgens P; + -- Trim trailing zeros from lambda for label and basis-change shape. + while #lambda > 0 and lambda#(#lambda - 1) == 0 do lambda = drop(lambda, -1); + if conv =!= "Row" and conv =!= "Weyl" and conv =!= "Filling" then + error ("pieri: Convention must be \"Row\", \"Filling\", or \"Weyl\""); + if conv === "Filling" then ( + A := fillingToPMMatrix(mu, n); + B := fillingToPMMatrix(lambda, n); + M = promote(B^-1, P) * M * promote(A, P); + ); + -- Attach symbolicForm labels: source basis = std tabs of mu, target = std tabs of lambda. + attachBasisLabels(M, pmStdTabsByConv(n, mu, conv), + pmStdTabsByConv(n, lambda, conv)) + ) + +------------------------------------------------------------------------------- +-- pieriColumn: native column-form Pieri (vertical strip removal). +-- +-- pieriColumn(mu, cols, P) where P is a SkewCommutative QQ-algebra with +-- numgens P = dim V, returns the matrix of the inclusion +-- S_mu V --> ^^k V (x) S_lambda V +-- where lambda = mu - {bottom box of cols#0, then bottom of cols#1 in the +-- intermediate shape, ...}. The columns specified must form a vertical strip +-- (no two boxes in the same row). Source basis = standard Fillings of mu; +-- target basis = standard Fillings of lambda; strip factor in the wedge ring. +-- +-- The formula is the dual of the Olver row formula with +-- c'_J = prod_{q intermediate} (mu'_k - mu'_(J_q) + J_q - k) +-- (the negative of the literal transpose of the row coefficient, accounting +-- for the wedge anticommutativity). +------------------------------------------------------------------------------- + +pieriColumnHelper = (mu, k, P) -> ( + ensureSchurFn(); + n := numgens P; + X := gens P; + for i from 0 to #mu - 2 do if mu#i < mu#(i+1) then error("not a partition: ", mu); + muTrim := trimPartPM mu; + muT := conjPartPM muTrim; + if k < 1 or k > #muT then error("column index ", k, " out of range"); + rowToShorten := muT#(k-1); + lambda := trimPartPM (for i from 0 to #muTrim - 1 list (if i == rowToShorten - 1 then muTrim#i - 1 else muTrim#i)); + muTaug := prepend(0, muT); + muTcols := muT; + lambdaTcols := conjPartPM lambda; + Sbasis := sfStandardTabPM(n, muTcols); + Tbasis := sfStandardTabPM(n, lambdaTcols); + Tidx := new MutableHashTable; + for i from 0 to #Tbasis - 1 do Tidx#(toList Tbasis#i) = i; + A := {}; + for p from 0 to k do A = join(A, apply(subsets(toList(1..k-1), p), s -> prepend(0, append(s, k)))); + colsList := for F in Sbasis list ( + s := prepend({}, toList F); + rowVec := new MutableList from toList(#Tbasis : 0_P); + H := new MutableHashTable; + for J in A do ( + cJ := 1; + for q from 1 to #J - 2 do cJ = cJ * (muTaug#k - muTaug#(J#q) + J#q - k); + h := new MutableHashTable; + h#s = ((-1)^(#J - 1)) / cJ; + for i from 0 to #J - 2 do ( + temp := new MutableHashTable; + for T in keys h do ( + coeff := h#T; + srcCol := T#(J#(i+1)); + for b from 0 to #srcCol - 1 do ( + dropped := srcCol#b; + U := new MutableList from T; + U#(J#(i+1)) = drop(srcCol, {b, b}); + U#(J#i) = prepend(dropped, U#(J#i)); + sgn := if b % 2 == 0 then 1 else -1; + key := new List from U; + cur := temp#key ?? 0; + temp#key = cur + sgn * coeff; + ); + ); + h = temp; + ); + for K in keys h do ( + cur := H#K ?? 0; + H#K = cur + h#K; + ); + ); + memo := new MutableHashTable; + for T in keys H do ( + coeff := H#T; + if coeff == 0 then continue; + if #(T#0) != 1 then error ("internal: prepended col should have 1 entry"); + poppedEntry := (T#0)#0; + U := drop(T, 1); + while #U > 0 and #(U#(#U - 1)) == 0 do U = drop(U, -1); + UF := new sfFillingType from U; + UFkey := toList U; + decomp := memo#UFkey ??= sfStraightenPM UF; + for std in keys decomp do ( + idx := Tidx#(toList std) ?? null; + if idx === null then continue; + rowVec#idx = rowVec#idx + coeff * (decomp#std) * X#poppedEntry; + ); + ); + toList rowVec + ); + map(P^(#Tbasis), P^{#Sbasis : -1}, transpose colsList) + ) + +pieriColumn = method(Options => {Convention => "Filling"}) +pieriColumn(List, List, PolynomialRing) := opts -> (mu, cols, P) -> ( + ensureSchurFn(); + curMu := trimPartPM mu; + for kk in cols do ( + if kk < 1 then error "column index must be positive"; + muT := conjPartPM curMu; + if kk > #muT then error("intermediate shape ", curMu, " has no column ", kk); + rowR := muT#(kk-1); + newMu := trimPartPM (for i from 0 to #curMu - 1 list (if i == rowR - 1 then curMu#i - 1 else curMu#i)); + for i from 0 to #newMu - 2 do + if newMu#i < newMu#(i+1) then error "second argument does not specify a vertical strip"; + curMu = newMu; + ); + lambda := curMu; + curMu = trimPartPM mu; + result := pieriColumnHelper(curMu, cols#0, P); + muT := conjPartPM curMu; + curMu = trimPartPM (for i from 0 to #curMu - 1 list (if i == muT#(cols#0 - 1) - 1 then curMu#i - 1 else curMu#i)); + for b from 1 to #cols - 1 do ( + stp := pieriColumnHelper(curMu, cols#b, P); + result = stp * result; + muT2 := conjPartPM curMu; + curMu = trimPartPM (for i from 0 to #curMu - 1 list (if i == muT2#(cols#b - 1) - 1 then curMu#i - 1 else curMu#i)); + ); + conv := opts.Convention; + n := numgens P; + muTrim := trimPartPM mu; + if conv =!= "Filling" and conv =!= "Row" and conv =!= "Weyl" then + error("pieriColumn: invalid Convention ", conv); + if conv =!= "Filling" then ( + -- Convert from native Filling basis to PM basis. + A := pmToFillingMatrix(muTrim, n); + B := fillingToPMMatrix(lambda, n); + result = promote(B, P) * result * promote(A, P); + ); + attachBasisLabels(result, pmStdTabsByConv(n, muTrim, conv), + pmStdTabsByConv(n, lambda, conv)) ) pierizero = method() @@ -334,23 +809,18 @@ pierip(List, List, PolynomialRing) := (mu, boxes, P) -> ( R := QQ[X]; f := pierizero(mu, boxes, R); mon := apply(compositions(d, #boxes), i-> (output := 1; for j from 0 to #i-1 do output = output * X_j^(i#j); output)); - result := {}; denom := 1; - for i from 0 to (rank source f)-1 do ( + result := for i from 0 to (rank source f) - 1 list ( row := flatten entries f_{i}; - newrow := {}; - for j from 0 to #row-1 do - for k in mon do ( - coeff := coefficient(k, row#j); - temp := ceiling(1 / gcd(coeff, 1)); - denom = denom * temp / gcd(denom, temp); - newrow = append(newrow, coeff); - ); - result = append(result, newrow); + flatten for j from 0 to #row - 1 list + for k in mon list ( + coeff := coefficient(k, row#j); + temp := ceiling(1 / gcd(coeff, 1)); + denom = denom * temp / gcd(denom, temp); + coeff + ) ); - result2 := {}; - for i in result do - result2 = append(result2, apply(i, j -> round(j*denom))); + result2 := apply(result, i -> apply(i, j -> round(j * denom))); intmat := map(ZZ^((rank target f) * #mon), ZZ^(rank source f), transpose(result2)); (D, S, T) := smithNormalForm(intmat); S' := (S^(-1))_{0..(rank source D)-1}; @@ -363,251 +833,1428 @@ pierip(List, List, PolynomialRing) := (mu, boxes, P) -> ( -- Output: -- An inclusion of GL(V)-representations whose graded minimal resolution is pure -- with degree sequence d -pureFree = method() -pureFree(List, PolynomialRing) := (d, P) -> ( +pureFree = method(Options => {Convention => "Row"}) +pureFree(List, PolynomialRing) := opts -> (d, P) -> ( if not isIncreasing(d) then error "The first argument needs to be a strictly increasing list of degrees."; - e := {}; - for i from 1 to #d-1 do e = append(e, d#i - d#(i-1)); - counter := -#e + 1; - for i in e do counter = counter + i; - lambda := {counter - e#0}; - for i from 1 to #e-1 do - lambda = append(lambda, lambda#(i-1) - e#i + 1); - lambda = prepend(counter, drop(lambda, 1)); - pieri(lambda, toList(e#0 : 1), P) ** P^{-d#0} + e := for i from 1 to #d - 1 list (d#i - d#(i-1)); + counter := -#e + 1 + sum e; + -- lambda#0 = counter - e#0; lambda#i = lambda#(i-1) - e#i + 1 for i >= 1. + -- Build the running tail; the head is later replaced with counter. + cur := counter - e#0; + tail := for i from 1 to #e - 1 list (cur = cur - e#i + 1; cur); + lambda := prepend(counter, tail); + pieri(lambda, toList(e#0 : 1), P, Convention => opts.Convention) ** P^{-d#0} ) -------------------- --- Documentation -- -------------------- +------------------------------------------------------------------------------- +-- Littlewood--Richardson inclusions Psi_Q : S_lambda V -> S_nu V (x) S_mu V +-- +-- An LR skew tableau Q of shape lambda/mu with content nu encodes, for each +-- label a in 1..r (r = #nu), a horizontal strip; iterating row-Pieri maps +-- in the order a = r, r-1, ..., 1 produces a map +-- S_lambda V -> Sym^{nu_r} V (x) ... (x) Sym^{nu_1} V (x) S_mu V. +-- A final straightening (Schur projection) on the Sym tensor side lands in +-- S_nu V (x) S_mu V. Different LR tableaux give linearly independent +-- inclusions; their span has dimension c^lambda_{mu,nu}. +------------------------------------------------------------------------------- -beginDocumentation() -document { - Key => PieriMaps, - Headline => "Pieri inclusions", - "For mathematical background of this package and some examples of use, see:", - BR{}, - "Steven V Sam, Computing inclusions of Schur modules, arXiv:0810.4666", - BR{}, - "Some other references:", - BR{}, - "Andrzej Daszkiewicz, On the Invariant Ideals of the Symmetric Algebra $S.(V \\oplus \\wedge^2 V)$, J. Algebra 125, 1989, 444-473.", - BR{}, - "David Eisenbud, Gunnar Fl\\o ystad, and Jerzy Weyman, The existence of pure free resolutions, arXiv:0709.1529.", - BR{}, - "William Fulton, Young Tableaux: With Applications to Representation Theory and Geometry, London Math. Society Student Texts 35, 1997.", - BR{}, - "Jerzy Weyman, Cohomology of Vector Bundles and Syzygies, Cambridge University Press, 2002.", - BR{}, - BR{}, - "Let V be a vector space over K. If K has characteristic 0, then - given the partition mu and the partition mu' obtained from mu by - removing a single box, there is a unique (up to nonzero scalar) - GL(V)-equivariant inclusion S_mu V -> V otimes S_mu' V, where - S_mu refers to the irreducible representation of GL(V) with - highest weight mu. This can be extended uniquely to a map of P = - Sym(V)-modules P otimes S_mu V -> P otimes S_mu' V. The purpose - of this package is to write down matrix representatives for these - maps. The main function for doing so is ", - TO pieri, - ". Here is an example of the use of the package PieriMaps, which is also - designed to check whether the maps are being constructed correctly - (important, since it is notoriously difficult to get the signs and - coefficients right.) ", - "We will construct by hand the free resolution in 3 variables corresponding - to the degree sequence (0,2,3,6). Let's start with the packaged code:", - EXAMPLE lines /// - R = QQ[a,b,c]; - f = pureFree({0,2,3,6}, R) - betti res coker f - ///, - "By the general theory from Eisenbud-Fl\\o ystad-Weyman, each of these free - modules should be essentially Schur functors corresponding to the - following partitions.", - EXAMPLE lines /// - needsPackage "SchurRings" - schurRing(s,3) - dim s_{2,2} - dim s_{4,2} - dim s_{4,3} - dim s_{4,3,3} - ///, - "This package also provides a routine ", - TO schurRank, - " for computing this dimension:", - EXAMPLE lines /// - schurRank(3, {2,2}) - schurRank(3, {4,2}) - schurRank(3, {4,3}) - schurRank(3, {4,3,3}) - ///, - "We now use ", - TO pieri, - " to construct each of the maps of the resolution separately.", - EXAMPLE lines /// - f1 = pieri({4,2,0},{1,1}, R) - f2 = pieri({4,3,0},{2}, R) - f3 = pieri({4,3,3},{3,3,3}, R) - ///, - "Fix the degrees (i.e. make sure that the target of f2 is the source of f1, - etc). Otherwise the test of exactness below would fail.", - EXAMPLE lines /// - f1 - f2 = map(source f1,,f2) - f3 = map(source f2,,f3) - f1 * f2 - f2 * f3 - ///, - "Check that the complex is exact.", - EXAMPLE lines /// - ker f1 == image f2 - ker f2 == image f3 - ///, - "Looks great! Now let's try it modulo some prime numbers and see if we get exactness.", - EXAMPLE lines /// - p = 32003 - R = ZZ/p[a,b,c]; - f1 = pieri({4,2,0},{1,1},R) - betti res coker f1 - f2 = pieri({4,3,0},{2},R) - f3 = pieri({4,3,3},{3,3,3},R) - f2 = map(source f1,,f2) - f3 = map(source f2,,f3) - f1 * f2 - f2 * f3 - ker f1 == image f2 - ker f2 == image f3 - ///, - "These do not piece together well. The reason is that ", - TO pieri, - " changes the bases of the free modules in a way which is not invertible - (over ZZ) when the ground field has positive characteristic.", - SeeAlso => {pieri, pureFree, schurRank} - } - -document { - Key => {standardTableaux, (standardTableaux, ZZ, List)}, - Headline => "list all standard tableaux of a certain shape with bounded labels", - SeeAlso => schurRank, - Usage => "standardTableaux(dim, mu)", - Inputs => { - "dim" => {ofClass ZZ, ", number of labels to be used"}, - "mu" => {ofClass List, ", a partition which gives the shape"} - }, - Outputs => { - List => {"list of all standard tableaux of shape mu with labels 0,...,dim-1"} - }, - EXAMPLE lines /// - standardTableaux(3, {2,2}) -- lists all standard tableaux on the 2x2 square with entries 0,1,2 - /// - } +-- Pad mu with trailing zeros to length L. +padPart = (mu, L) -> ( + if #mu >= L then toList mu else toList mu | toList(L - #mu : 0) + ) -document { - Key => {straighten, (straighten, List), (straighten, List, MutableHashTable)}, - Headline => "computes straightening of a tableau", - Usage => concatenate("straighten(t)", "straighten(t, h)"), - Inputs => { - "t" => {ofClass List, ", a tableau to straighten; a tableau looks like {{3,4}, {1,2}} for example, where we list the entries from left to right, top to bottom"}, - "h" => {ofClass MutableHashTable, ", where the answers should be stored"} - }, - Consequences => { "If provided, the hashtable h is updated with any calculations which are performed as a result of calling this function. " }, - "If a hashtable h is provided, then this outputs nothing, it simply just modifies h. When looking up values, remember that the keys are stored with rows weakly increasing. ", - "If no hashtable is provided, then the user is simply given the straightening of the tableau in terms of semistandard tableaux. ", - "The answer is in the form a hashtable: each key is a semistandard tableaux, and the value of the key is the coefficient of that semistandard tableaux used to write the input t as a linear combination. ", - EXAMPLE lines /// - h = new MutableHashTable from {} - straighten({{3,4}, {1,2}}, h) - h#{{3,4}, {1,2}} -- get the coefficients - straighten({{3,4}, {1,2}}) -- just get the answer instead - /// - } +-- Subtract one from row k (1-indexed). +subOneRowLR = (mu, k) -> ( + for i from 0 to #mu - 1 list (if i == k - 1 then mu#i - 1 else mu#i) + ) -document { - Key => {pieri, (pieri, List, List, PolynomialRing)}, - Headline => "computes a matrix representation for a Pieri inclusion of representations of a general linear group", - SeeAlso => {pureFree}, - Usage => "pieri(mu, boxes, P)", - Inputs => { - "mu" => {ofClass List, ", a partition (mu_1, ..., mu_r) where mu_i is the number of boxes in the ith row"}, - "boxes" => {ofClass List, ", a list of rows from which to remove boxes (boxes are always removed from the end of the row). This specifies which map of GL(V) representations we want. The row indices start from 1 and not 0, and this must specify a horizontal strip in mu (see description below). "}, - "P" => {ofClass PolynomialRing, ", a polynomial ring over a field K in n variables" } - }, - Outputs => { - Matrix => {"If K has characteristic 0, then given the partition mu and the partition mu' obtained from mu by removing a single box, - there is a unique (up to nonzero scalar) GL(V)-equivariant inclusion S_mu V -> V otimes S_mu' V, where S_mu refers to the - irreducible representation of GL(V) with highest weight mu. This can be extended uniquely to a map of P = Sym(V)-modules - P otimes S_mu V -> P otimes S_mu' V. This method computes the matrix representation for the composition of maps that one obtains by - iterating this procedure of removing boxes, i.e., the final output is a GL(V)-equivariant map P otimes S_mu V -> P otimes S_lambda V - where lambda is the partition obtained from mu by deleting a box from row boxes_0, a box from row boxes_1, etc. - If K has positive characteristic, then the corresponding map is calculated over QQ, lifted to a ZZ-form of the representation which has - the property that the map has a torsion-free cokernel over ZZ, and then the coefficients are reduced to K." - } - }, - "Convention: the partition (d) represents the dth symmetric power, while the partition (1,...,1) represents the dth exterior power. ", - "Using the notation from the output, mu/lambda must be a horizontal strip. Precisely, this means that lambda_i >= mu_(i+1) for all i. - If this condition is not satisfied, the program throws an error because a nonzero equivariant map of the desired form will not exist. ", - EXAMPLE lines /// - pieri({3,1}, {1}, QQ[a,b,c]) -- removes the last box from row 1 of the partition {3,1} - res coker oo -- resolve this map - betti oo -- check that the resolution is pure - /// - } +-- Strip rows (1-indexed) for label a (0-indexed) inside an LR filling Q. +stripRowsForLabel = (Q, a) -> + flatten for i from 0 to #Q - 1 list + for lab in Q#i list if lab == a then i + 1 else continue -document { - Key => {pureFree, (pureFree, List, PolynomialRing)}, - Headline => "computes a GL(V)-equivariant map whose resolution is pure, or the reduction mod p of such a map", - SeeAlso => {pieri}, - Usage => "pureFree(d, P)", - Inputs => { - "d" => {ofClass List, ", a list of degrees (increasing numbers)"}, - "P" => {ofClass PolynomialRing, ", a polynomial ring over a field K in n variables" } - }, - Outputs => { - Matrix => {"A map whose cokernel has Betti diagram with degree sequence d if K has characteristic 0. - If K has positive characteristic p, then the corresponding map is calculated over QQ and is lifted to a ZZ-form which is then reduced mod p." } - }, - "The function translates the data of a degree sequence d for a desired pure free resolution into the data of a Pieri map - according to the formula of Eisenbud-Fl\\o ystad-Weyman and then applies the function ", - TO pieri, - ".", - EXAMPLE lines /// - betti res coker pureFree({0,1,2,4}, QQ[a,b,c]) -- degree sequence {0,1,2,4} - betti res coker pureFree({0,1,2,4}, ZZ/2[a,b,c]) -- same map, but reduced mod 2 - betti res coker pureFree({0,1,2,4}, GF(4)[a,b,c]) -- can also use non prime fields - /// - } +-- LR tableau enumerator. Returns all fillings of lambda/mu with content nu +-- that satisfy: rows weakly increasing, columns strictly increasing among +-- skew cells, reverse-reading-word lattice condition. Each filling is a +-- list of rows, row i a list of length lambda_i - mu_i with entries in +-- 0..#nu - 1 (0-indexed labels). +lrTableaux = method() +lrTableaux(List, List, List) := (lambda, mu, nu) -> ( + if sum lambda != sum mu + sum nu then return {}; + L := #lambda; + lam := toList lambda; + mm := padPart(mu, L); + for i from 0 to L - 1 do if lam#i < mm#i then return {}; + r := #nu; + rowLens := for i from 0 to L - 1 list lam#i - mm#i; + -- Cells in reverse-reading-word order. + cells := flatten for i from 0 to L - 1 list ( + rs := mm#i; + len := rowLens#i; + for t from 0 to len - 1 list (i, rs + len - 1 - t) + ); + cellIndex := new MutableHashTable; + for t from 0 to #cells - 1 do cellIndex#(cells#t) = t; + isSkewRC := (row, col) -> ( + if row < 0 or row >= L then false + else (rs := mm#row; (rs <= col) and (col < rs + rowLens#row)) + ); + assign := new MutableList from toList(#cells : -1); + labelCount := new MutableList from toList(r : 0); + prefixCount := new MutableList from toList(r : 0); + results := new MutableList; + backtrack := null; + backtrack = idx -> ( + if idx == #cells then ( + filling := for i from 0 to L - 1 list ( + rs := mm#i; + for k from 0 to rowLens#i - 1 list assign#(cellIndex#(i, rs + k)) + ); + results#(#results) = filling; + return null; + ); + (row, col) := cells#idx; + rightLab := if isSkewRC(row, col + 1) then assign#(cellIndex#(row, col + 1)) else r; + aboveLab := -1; + pr := row - 1; + while pr >= 0 do ( + if isSkewRC(pr, col) then ( aboveLab = assign#(cellIndex#(pr, col)); pr = -1 ) + else pr = pr - 1; + ); + for lab from 0 to r - 1 do ( + if lab > rightLab then break; + if aboveLab >= 0 and lab <= aboveLab then continue; + if labelCount#lab >= nu#lab then continue; + if lab >= 1 and prefixCount#lab + 1 > prefixCount#(lab - 1) then continue; + assign#(cellIndex#(row, col)) = lab; + labelCount#lab = labelCount#lab + 1; + prefixCount#lab = prefixCount#lab + 1; + backtrack(idx + 1); + assign#(cellIndex#(row, col)) = -1; + labelCount#lab = labelCount#lab - 1; + prefixCount#lab = prefixCount#lab - 1; + ); + return null; + ); + backtrack(0); + toList results + ) + +-- The LR inclusion Psi_Q : S_lambda V -> S_nu V (x) S_mu V where dim V = n. +-- Returns a Matrix over QQ whose +-- columns are indexed by standardTableaux(n, lambda), +-- rows are indexed by (SSYT of nu) (x) (SSYT of mu padded to #lambda), +-- listed in lex order of (T_nu_index, T_mu_index). +lrMap = method(Options => {Convention => "Row"}) +lrMap(Sequence, List, ZZ) := opts -> (shapes, Q, n) -> ( + if #shapes != 3 then error "lrMap: first argument must be the sequence (lambda, mu, nu)"; + lambda := shapes#0; + mu := shapes#1; + nu := shapes#2; + L := #lambda; + lam := padPart(lambda, L); + r := #nu; + -- Auxiliary multi-block ring with r blocks of n variables. Block a + -- (0-indexed) carries the Sym^{nu_a} factor. + z := getSymbol "lrz"; + Pz := QQ[z_(0,0)..z_(r-1, n-1)]; + -- Build composite Pieri matrix M_1 * M_2 * ... * M_r (r applied first). + curShape := lam; + composite := null; + for tt from 0 to r - 1 do ( + aIdx := r - 1 - tt; + rows := stripRowsForLabel(Q, aIdx); + if #rows != nu#aIdx then error "lrMap: strip size mismatch (Q does not match nu)"; + zloc := getSymbol "lrzloc"; + Ploc := QQ[zloc_0..zloc_(n-1)]; + Mloc := pieri(curShape, rows, Ploc); + phi := map(Pz, Ploc, for i from 0 to n - 1 list Pz_(aIdx * n + i)); + Mlift := phi Mloc; + if composite === null then composite = Mlift else composite = Mlift * composite; + for rr in rows do curShape = subOneRowLR(curShape, rr); + ); + Sbasis := standardTableaux(n, lam); + Nmu := standardTableaux(n, curShape); + Nnu := standardTableaux(n, nu); + -- Column index for each source tableau; row index for each (T_nu, T_mu) pair. + muIdx := new MutableHashTable; + for i from 0 to #Nmu - 1 do muIdx#(Nmu#i) = i; + nuIdx := new MutableHashTable; + for i from 0 to #Nnu - 1 do nuIdx#(Nnu#i) = i; + numRows := #Nnu * #Nmu; + numCols := #Sbasis; + -- Fill matrix as nested list of QQ rows. + entries := new MutableList from for ri from 0 to numRows - 1 list new MutableList from toList(numCols : 0_QQ); + straightenH := new MutableHashTable; + for j from 0 to #Sbasis - 1 do ( + for i from 0 to #Nmu - 1 do ( + f := composite_(i, j); + if f == 0 then continue; + for pair in listForm f do ( + expv := pair#0; + c := pair#1; + if c == 0 then continue; + tvec := for a from 0 to r - 1 list + flatten for k from 0 to n - 1 list + toList(expv#(a*n + k) : k); + straighten(tvec, straightenH); + decomp := straightenH#tvec ?? new HashTable from {}; + for ssyt in keys decomp do ( + if not nuIdx#?ssyt then continue; + dc := decomp#ssyt; + ri := nuIdx#ssyt * #Nmu + i; + (entries#ri)#j = (entries#ri)#j + c * dc; + ); + ); + ); + ); + M := map(QQ^numRows, QQ^numCols, toList apply(entries, r0 -> toList r0)); + conv := opts.Convention; + muPad := if #mu < L then mu | toList(L - #mu : 0) else mu; + if conv =!= "Row" and conv =!= "Weyl" and conv =!= "Filling" then + error ("lrMap: Convention must be \"Row\", \"Filling\", or \"Weyl\""); + if conv === "Filling" then ( + -- Change basis on source (lambda) and on both target factors (nu, mu). + Blam := pmToFillingMatrix(lambda, n); + Bnu := pmToFillingMatrix(nu, n); + Bmu := pmToFillingMatrix(muPad, n); + M = (Bnu ** Bmu) * M * (Blam)^-1; + ); + -- Build target labels: pairs (T_nu, T_mu) in the same lex order the matrix uses. + stdsLam := pmStdTabsByConv(n, lambda, conv); + stdsNu := pmStdTabsByConv(n, nu, conv); + stdsMu := pmStdTabsByConv(n, muPad, conv); + tgtLabels := flatten for tNu in stdsNu list for tMu in stdsMu list (tNu, tMu); + attachBasisLabels(M, stdsLam, tgtLabels) + ) + +-- Convenience: accept a polynomial ring and use its number of generators. +lrMap(Sequence, List, PolynomialRing) := opts -> (shapes, Q, P) -> lrMap(shapes, Q, numgens P, Convention => opts.Convention) + +-- Pretty-printer for one tableau as a Net (rows stacked, entries 1-indexed +-- for human readability so they match the displayed mu/nu/lambda shapes). +netTableau = T -> ( + if T === {} or all(T, r -> #r == 0) then return net "()"; + stack apply(T, r -> horizontalJoin apply(r, x -> net (x + 1) | " ")) + ) + +-- Apply Psi_Q to one input tableau T of shape lambda; return its image as a +-- list of triples {coeff, T_nu, T_mu}. T may be any list of rows of shape +-- lambda (entries 0..n-1). Non-standard tableaux are first straightened into +-- the SSYT basis of S_lambda V; the GL-equivariant inclusion is then applied. +-- +-- The output triples are filtered to nonzero coefficients and sorted by +-- (T_nu, T_mu) for deterministic display. +applyLR = method(Options => {Convention => "Row"}) +applyLR(Sequence, List, BasicList, ZZ) := opts -> (shapes, Q, T, n) -> ( + if #shapes != 3 then error "applyLR: first argument must be (lambda, mu, nu)"; + lambda := shapes#0; + mu := shapes#1; + nu := shapes#2; + L := #lambda; + conv := opts.Convention; + -- Build the LR matrix in the chosen convention. + M := lrMap(shapes, Q, n, Convention => conv); + muPad := if #mu < L then mu | toList(L - #mu : 0) else mu; + -- Build source basis and look up T via convention-specific straightening. + local Sb; + local Nmu; + local Nnu; + local Tcoords; + if conv === "Row" or conv === "Weyl" then ( + Sb = standardTableaux(n, lambda); + Nmu = standardTableaux(n, muPad); + Nnu = standardTableaux(n, nu); + Tlist := apply(toList T, r -> sort toList r); + h := new MutableHashTable; + straighten(Tlist, h); + decomp := h#Tlist ?? new HashTable from {}; + idx := new MutableHashTable; + for i from 0 to #Sb - 1 do idx#(Sb#i) = i; + Tcoords = new MutableList from toList(#Sb : 0_QQ); + for K in keys decomp do + if idx#?K then Tcoords#(idx#K) = decomp#K; + ) + else if conv === "Filling" then ( + ensureSchurFn(); + Sb = sfStandardTabPM(n, conjPartPM lambda); + Nmu = sfStandardTabPM(n, conjPartPM muPad); + Nnu = sfStandardTabPM(n, conjPartPM nu); + Tcast := if instance(T, sfFillingType) then T else new sfFillingType from toList T; + decompF := sfStraightenPM Tcast; + idxF := new MutableHashTable; + for i from 0 to #Sb - 1 do idxF#(toList Sb#i) = i; + Tcoords = new MutableList from toList(#Sb : 0_QQ); + for K in keys decompF do ( + Klist := toList K; + if idxF#?Klist then Tcoords#(idxF#Klist) = decompF#K; + ); + ) + else error("applyLR: invalid Convention ", conv); + Tcol := transpose matrix {toList Tcoords}; + wcol := M * Tcol; + out := new MutableList; + for r from 0 to numRows wcol - 1 do ( + v := wcol_(r, 0); + if v == 0 then continue; + i := r // (#Nmu); + jj := r % (#Nmu); + out#(#out) = {v, Nnu#i, Nmu#jj}; + ); + toList out + ) + +-- Pretty-print the result of applyLR as a sum c_i * T_nu^(i) ⊗ T_mu^(i). +-- Each term is rendered with the coefficient (as a single-line string) on the +-- top row and the two tableaux side-by-side, separated by "⊗". +displayLRImage = method() +displayLRImage List := terms -> ( + if #terms == 0 then return net "0"; + parts := for trip in terms list ( + c := trip#0; + Tnu := trip#1; + Tmu := trip#2; + coeffStr := if c == 1 then " + " + else if c == -1 then " - " + else ( + s := toString c; + if c > 0 then " + " | s | " . " + else " - " | substring(s, 1) | " . " + ); + horizontalJoin {coeffStr, "[", netTableau Tnu, "] ⊗ [", netTableau Tmu, "]"} + ); + -- Drop the leading "+ " from the very first term. + first0 := parts#0; + parts0 := if #(toString first0) > 0 then ( + firstStr := toString first0; + (replace("^ \\+ ", " ", firstStr)); + first0 -- keep as-is for now; stacking handles it + ) else first0; + stack parts + ) + +------------------------------------------------------------------------------- +-- verifyWellDefined: check that an LR map respects the straightening +-- relations of the chosen tableau convention. +-- +-- Strategy. For each "test" non-standard tableau T of shape lambda: +-- (a) compute applyLR(shapes, Q, T, n, Convention => conv) directly; +-- (b) independently straighten T via the convention's straightening into a +-- sum T = sum_i c_i T_std_i; +-- (c) compute sum_i c_i applyLR(shapes, Q, T_std_i, n, Convention => conv); +-- (d) verify (a) and (c) agree. +-- +-- Returns true on success. Prints offending case and returns false on +-- failure. +------------------------------------------------------------------------------- + +genNonStdTableauxPM = (lambda, n, convention) -> ( + out := new MutableList; + if convention === "Filling" then ( + ensureSchurFn(); + shape := conjPartPM lambda; + stdF := sfStandardTabPM(n, shape); + if #stdF == 0 then return {}; + for F in stdF do ( + Fl := apply(toList F, toList); + for c from 0 to #Fl - 1 do ( + if #(Fl#c) >= 2 then ( + swapped := for j from 0 to #(Fl#c) - 1 list ( + if j == 0 then (Fl#c)#1 + else if j == 1 then (Fl#c)#0 + else (Fl#c)#j + ); + newF := for cc from 0 to #Fl - 1 list (if cc == c then swapped else Fl#cc); + out#(#out) = new sfFillingType from newF; + if #out >= 3 then return toList out; + ); + ); + ); + ) else ( + std := standardTableaux(n, lambda); + if #std == 0 then return {}; + for T in std do ( + for ri from 0 to #T - 1 do ( + if #(T#ri) >= 2 and (T#ri)#0 != (T#ri)#1 then ( + swapped := for j from 0 to #(T#ri) - 1 list ( + if j == 0 then (T#ri)#1 + else if j == 1 then (T#ri)#0 + else (T#ri)#j + ); + newT := for rr from 0 to #T - 1 list (if rr == ri then swapped else T#rr); + out#(#out) = newT; + if #out >= 3 then return toList out; + ); + ); + ); + ); + toList out + ) + +straightenByConvPM = (T, convention) -> ( + if convention === "Row" or convention === "Weyl" then ( + Tlist := apply(toList T, r -> sort toList r); + h := new MutableHashTable; + straighten(Tlist, h); + h#Tlist ?? new HashTable from {} + ) + else if convention === "Filling" then ( + ensureSchurFn(); + Tcast := if instance(T, sfFillingType) then T else new sfFillingType from toList T; + sfStraightenPM Tcast + ) + else error "straightenByConv: invalid convention" + ) + +-- Generic well-definedness driver for an apply-style function whose output +-- pairs are {value, basisKey}. Compares applyFn(T) (T non-standard) against +-- sum c_i * applyFn(T_std_i) where T = sum c_i T_std_i in the Schur module. +testWellDefinedTwoTuple0 = (applyFn, srcShape, n, conv, zeroVal, verbose) -> ( + tests := genNonStdTableauxPM(srcShape, n, conv); + if #tests == 0 then ( + if verbose then stdio << " (no non-standard test tableaux for " << toString srcShape << " in convention " << conv << ")" << endl; + return true; + ); + ok := true; + for T in tests do ( + imgD := applyFn T; + decomp := straightenByConvPM(T, conv); + imgM := new MutableHashTable; + for K in keys decomp do ( + c := decomp#K; + img := applyFn K; + for pair in img do ( + key := if class pair#1 === List then pair#1 else toList pair#1; + cur := imgM#key ?? zeroVal; + imgM#key = cur + c * pair#0; + ); + ); + imgDmap := new MutableHashTable; + for pair in imgD do ( + key := if class pair#1 === List then pair#1 else toList pair#1; + imgDmap#key = pair#0; + ); + allKeys := unique (keys imgDmap | keys imgM); + caseOk := true; + for k in allKeys do ( + a := imgDmap#k ?? zeroVal; + b := imgM#k ?? zeroVal; + if a != b then caseOk = false; + ); + if not caseOk then ( + ok = false; + if verbose then stdio << " FAIL on T = " << toString T << endl; + ); + ); + ok + ); + +verifyWellDefined = method(Options => {Convention => "Row", Verbose => true, Direction => "Inclusion"}) + +-- pieri / pieriColumn / dualPieri / dualPieriColumn well-definedness. +-- The PolynomialRing P determines which family: SkewCommutative => "column" form +-- (pieriColumn / dualPieriColumn), else symmetric (pieri / dualPieri). The +-- Direction option chooses inclusion vs projection (default "Inclusion"). +verifyWellDefined(List, List, PolynomialRing) := opts -> (mu, boxes, P) -> ( + n := numgens P; + conv := opts.Convention; + skewP := isSkewCommutative P; + dual := opts.Direction === "Dual"; + verbose := opts.Verbose; + if dual then ( + -- Dual: source = wedge/Sym^d V (x) S_lambda; vary the S_lambda factor. + lambda := mu; + for b in boxes do lambda = subtractOne(lambda, b); + while #lambda > 0 and lambda#-1 == 0 do lambda = drop(lambda, -1); + d := #boxes; + monBasis := flatten entries basis(d, P); + if #monBasis == 0 then return true; + poly := monBasis#0; + applyFn := if skewP then + (T -> applyDualPieriColumn((mu, boxes), poly, T, n, Convention => conv)) + else (T -> applyDualPieri((mu, boxes), poly, T, n, Convention => conv)); + testWellDefinedTwoTuple0(applyFn, lambda, n, conv, 0_QQ, verbose) + ) + else ( + -- Inclusion: source = S_mu; vary T_mu. + applyFnInc := if skewP then + (Targ -> applyPieriColumn((mu, boxes), Targ, P, Convention => conv)) + else (Targ -> applyPieri((mu, boxes), Targ, P, Convention => conv)); + testWellDefinedTwoTuple0(applyFnInc, mu, n, conv, 0_P, verbose) + ) + ); + +verifyWellDefined(Sequence, List, ZZ) := opts -> (shapes, Q, n) -> ( + if opts.Direction === "Dual" then ( + -- Dual LR: source = S_nu (x) S_mu; vary T_mu (we just pick one factor). + (lamD, muD, nuD) := shapes; + convD := opts.Convention; + -- Pick a fixed standard T_nu for the test. + stdsNuD := pmStdTabsByConv(n, nuD, convD); + if #stdsNuD == 0 then return true; + TnuD := stdsNuD#0; + muPadD := muD | toList(#lamD - #muD : 0); + applyFnD := (TmuArg -> applyDualLR(shapes, Q, {TnuD, TmuArg}, n, Convention => convD)); + return testWellDefinedTwoTuple0(applyFnD, muPadD, n, convD, 0_QQ, opts.Verbose); + ); + conv := opts.Convention; + verbose := opts.Verbose; + lambda := shapes#0; + tests := genNonStdTableauxPM(lambda, n, conv); + if #tests == 0 then ( + if verbose then stdio << "verifyWellDefined: no non-standard test tableaux for shape " << toString lambda << " in convention " << conv << "; nothing to verify." << endl; + return true; + ); + ok := true; + numTests := 0; + for T in tests do ( + numTests = numTests + 1; + imgD := applyLR(shapes, Q, T, n, Convention => conv); + decomp := straightenByConvPM(T, conv); + imgM := new MutableHashTable; + for K in keys decomp do ( + c := decomp#K; + img := applyLR(shapes, Q, K, n, Convention => conv); + for trip in img do ( + key := (toList trip#1, toList trip#2); + cur := imgM#key ?? 0; + imgM#key = cur + c * trip#0; + ); + ); + imgDmap := new MutableHashTable; + for trip in imgD do imgDmap#(toList trip#1, toList trip#2) = trip#0; + allKeys := unique (keys imgDmap | keys imgM); + caseOk := true; + for k in allKeys do ( + a := imgDmap#k ?? 0; + b := imgM#k ?? 0; + if a != b then ( + caseOk = false; + if verbose then + stdio << " MISMATCH at " << toString k + << ": direct=" << a << " manual=" << b << endl; + ); + ); + if not caseOk then ( + ok = false; + if verbose then stdio << " Test #" << numTests << " (T = " << toString T << "): FAIL" << endl; + ) + else if verbose then + stdio << " Test #" << numTests << " (T = " << toString T << "): OK" << endl; + ); + if verbose then + stdio << "verifyWellDefined: " << numTests << " test(s) " << (if ok then "all PASSED" else "FAILED") << " for convention " << conv << "." << endl; + ok + ) + +-- ==================================================================== +-- Dual maps (projection direction). +-- +-- By Schur's lemma, a GL_n-equivariant projection +-- Sym^d V (x) S_lambda V --> S_mu V (when mu = lambda + horizontal d-strip) +-- S_nu V (x) S_mu V --> S_lambda V (per LR tableau Q) +-- is unique up to scalar. We construct each by stacking the relevant +-- inclusions for ALL summands of the source (Pieri's rule for the first, +-- the full LR decomposition for the second), inverting the resulting +-- square matrix, and reading off the rows for our chosen target. +-- +-- This guarantees `dualPieri ∘ pieri = Id` exactly. +-- ==================================================================== + +-- Enumerate (mu', canonicalBoxes) for every mu' = lambda + horizontal +-- d-strip with at most n rows. canonicalBoxes is the list of row +-- indices (1-indexed) sorted ascending. +pmAddableHStrips = (lambda, d, n) -> ( + lam := lambda; + while #lam > 0 and lam#-1 == 0 do lam = drop(lam, -1); + if #lam > n then return {}; + lamPad := lam | toList(n - #lam : 0); + for comp in compositions(n, d) list ( + muNew := for i from 0 to n-1 list lamPad_i + comp_i; + if not all(0..n-2, i -> muNew_i >= muNew_(i+1)) then continue; + -- horizontal strip: each column has at most one new cell, i.e. + -- muNew_(i+1) <= lamPad_i for all i. + if not all(0..n-2, i -> muNew_(i+1) <= lamPad_i) then continue; + muTrim := muNew; + while #muTrim > 0 and muTrim#-1 == 0 do muTrim = drop(muTrim, -1); + boxes := flatten for i from 0 to n-1 list toList((comp_i):(i+1)); + (muTrim, boxes) + ) + ); + +-- All partitions of n with at most maxParts parts. +pmPartitionsAtMost = (n, maxParts) -> + select(toList partitions n, p -> #(toList p) <= maxParts); + +-- Enumerate (mu', canonicalCols) for every mu' = lambda + vertical d-strip +-- with at most n rows. canonicalCols is the list of column indices (1-indexed) +-- where boxes are added, sorted descending so iterated single-column removal +-- yields valid intermediate partitions. +pmAddableVStrips = (lambda, d, n) -> ( + lam := lambda; + while #lam > 0 and lam#-1 == 0 do lam = drop(lam, -1); + if #lam > n then return {}; + lamPad := lam | toList(n - #lam : 0); + for sub in subsets(toList(0..n-1), d) list ( + muNew := for i from 0 to n-1 list (lamPad_i + (if member(i, sub) then 1 else 0)); + if not all(0..n-2, i -> muNew_i >= muNew_(i+1)) then continue; + muTrim := muNew; + while #muTrim > 0 and muTrim#-1 == 0 do muTrim = drop(muTrim, -1); + -- For each row r in sub, the new box is at column lamPad_r + 1. + -- Sort by column descending (with ties broken by lower row first) so + -- iterated pieriColumn produces valid intermediate partitions. + pairs := sort apply(sub, r -> (lamPad_r + 1, r)); + pairs = reverse pairs; + cols := apply(pairs, p -> p#0); + (muTrim, cols) + ) + ); + +-- Encode a P-matrix M (dim_lambda x dim_mu, entries deg d) as a K-matrix +-- of size (dim_lambda * #monBasis) x dim_mu. Row index = (T_lambda, alpha) +-- in lex order with T_lambda outer (block size #monBasis), alpha inner. +pmEncodePieriKMat = (M, monBasis, K) -> ( + dimLam := numRows M; + dimMu := numColumns M; + matrix(K, flatten for tLam from 0 to dimLam-1 list ( + for alpha in monBasis list ( + for tMu from 0 to dimMu-1 list ( + lift(coefficient(alpha, M_(tLam, tMu)), K) + ) + ) + )) + ); + +-- ==================================================================== +-- Dual-map block factorization cache. +-- +-- dualPieri / dualPieriColumn / dualLR all build the same stacked +-- block matrix A and only differ in which rows of A^{-1} they extract. +-- Caching A by (kind, lambda, d, n, K, conv) (resp. (mu, nu, n, K, conv) +-- for LR) lets sibling calls share the heavy block construction, and +-- using `solve` instead of `A^{-1}` extracts only the rows we want +-- (cost O(N^2 * blockSize) instead of O(N^3) for a full inverse). +-- +-- A typical workflow projects onto many sister summands of the same +-- ambient tensor product; the cache turns those into nearly-free calls. +-- ==================================================================== + +pmDualBlockCache = new MutableHashTable; + +-- Sign of the permutation that takes `ref` to `a` (assuming both are +-- permutations of the same multiset). Returns 0 if the multisets do +-- not match. Equal entries are treated as indistinguishable. +pmPermSign = (a, ref) -> ( + n := #a; + if n =!= #ref then return 0; + b := new MutableList from ref; + sgn := 1; + for i from 0 to n - 1 do ( + if b#i === a#i then continue; + j := i + 1; + while j < n and b#j =!= a#i do j = j + 1; + if j >= n then return 0; + tmp := b#i; b#i = b#j; b#j = tmp; + sgn = -sgn; + ); + sgn + ); + +-- Build (or look up) the stacked K-matrix and addable-strips list for +-- the horizontal-strip dual map at parameters (lambda, d, n, conv). +-- Returns (addable, A, offsets) with offsets#i = first column of block i. +pmGetDualHBlocks = (lambda, d, n, conv, P, K) -> + pmDualBlockCache#("row", toList lambda, d, n, K, conv) ??= ( + monBasis := flatten entries basis(d, P); + addable := pmAddableHStrips(lambda, d, n); + blocks := for pair in addable list ( + (muI, canBoxes) := pair; + pmEncodePieriKMat( + pieri(muI, canBoxes, P, Convention => conv), + monBasis, K) + ); + A := fold((a, b) -> a | b, blocks); + offsets := prepend(0, accumulate(plus, 0, apply(blocks, numColumns))); + (addable, A, offsets) + ); + +-- Vertical-strip analogue. Requires P SkewCommutative. +pmGetDualVBlocks = (lambda, d, n, conv, P, K) -> + pmDualBlockCache#("col", toList lambda, d, n, K, conv) ??= ( + monBasis := flatten entries basis(d, P); + addable := pmAddableVStrips(lambda, d, n); + blocks := for pair in addable list ( + (muI, canCols) := pair; + pmEncodePieriKMat( + pieriColumn(muI, canCols, P, Convention => conv), + monBasis, K) + ); + A := fold((a, b) -> a | b, blocks); + offsets := prepend(0, accumulate(plus, 0, apply(blocks, numColumns))); + (addable, A, offsets) + ); + +-- Stacked LR-block matrix for dualLR with parameters (mu, nu, n, conv). +-- The key intentionally omits lambda (the target), since the same +-- decomposition supports projection onto every summand at once. +pmGetDualLRBlocks = (mu, nu, n, conv) -> + pmDualBlockCache#("lr", toList mu, toList nu, n, conv) ??= ( + totalCells := sum mu + sum nu; + cands := apply(pmPartitionsAtMost(totalCells, n), p -> toList p); + -- Per-candidate (lambda', Q') pairs along with their lrMap blocks. + pairs := flatten for lamP in cands list + for Qp in lrTableaux(lamP, mu, nu) list ( + (lamP, Qp, lrMap((lamP, mu, nu), Qp, n, Convention => conv)) + ); + blockInfo := apply(pairs, p -> (p#0, p#1)); + blocks := apply(pairs, p -> p#2); + if #blocks == 0 then error "dualLR: no GL_n summands in S_nu V (x) S_mu V"; + A := fold((a, b) -> a | b, blocks); + if numRows A =!= numColumns A then + error "dualLR: stacked LR matrix not square (decomposition incomplete)"; + offsets := prepend(0, accumulate(plus, 0, apply(blocks, numColumns))); + (blockInfo, A, offsets) + ); + +-- Extract rows [startRow .. startRow + blockSize - 1] of A^{-1} via a +-- single `solve` against a block of standard-basis columns, avoiding +-- the cost (and memory) of forming the full inverse. +-- +-- Math: if Y is the desired row block, then Y * A = E where E is the +-- (blockSize x N) "rectangular identity" picking out those rows, so +-- transpose(A) * transpose(Y) = transpose(E), giving +-- Y = transpose solve(transpose A, transpose E). +pmDualSolveRows = (A, startRow, blockSize) -> ( + N := numRows A; + K := ring A; + E := submatrix(id_(K^N), , toList(startRow .. startRow + blockSize - 1)); + transpose solve(transpose A, E) + ); + + +dualPieri = method(Options => {Convention => "Row"}) +dualPieri(List, List, PolynomialRing) := opts -> (mu, boxes, P) -> ( + n := numgens P; + d := #boxes; + K := coefficientRing P; + -- Normalize boxes to canonical (sorted ascending) order so the + -- result matches `pieri(mu, sort boxes, P)` exactly on round trip. + -- (Different orderings of the same box-multiset produce pieri + -- matrices that differ by a scalar, so we standardize.) + canBoxes := sort toList boxes; + lambda := mu; + for b in canBoxes do lambda = subtractOne(lambda, b); + while #lambda > 0 and lambda#-1 == 0 do lambda = drop(lambda, -1); + (addable, A, offsets) := pmGetDualHBlocks(lambda, d, n, opts.Convention, P, K); + muIdx := position(addable, p -> p#0 == mu); + if muIdx === null then error("dualPieri: mu = ", mu, + " is not a horizontal ", d, + "-strip addition to lambda = ", lambda, + " in ", n, " variables"); + startRow := offsets#muIdx; + dimMu := offsets#(muIdx + 1) - startRow; + N := pmDualSolveRows(A, startRow, dimMu); + -- Source basis: pairs (T_lambda, alpha) in lex order with T_lambda outer + -- (matches pmEncodePieriKMat's row layout). Target basis: T_mu. + monBasis := flatten entries basis(d, P); + stdsLam := pmStdTabsByConv(n, lambda, opts.Convention); + stdsMu := pmStdTabsByConv(n, mu, opts.Convention); + srcLabels := flatten for tLam in stdsLam list for alpha in monBasis list (tLam, alpha); + attachBasisLabels(N, srcLabels, stdsMu) + ); + +dualLR = method(Options => {Convention => "Row"}) +dualLR(Sequence, List, ZZ) := opts -> (shapes, Q, n) -> ( + (lambda, mu, nu) := shapes; + (blockInfo, A, offsets) := pmGetDualLRBlocks(mu, nu, n, opts.Convention); + idx := position(blockInfo, + pair -> pair#0 == lambda and pair#1 == Q); + if idx === null then error("dualLR: (lambda, Q) not found in LR decomposition of S_nu V (x) S_mu V"); + startRow := offsets#idx; + dimLam := offsets#(idx + 1) - startRow; + N := pmDualSolveRows(A, startRow, dimLam); + -- Source basis: pairs (T_nu, T_mu) in lex order matching the lrMap row layout. + muPad := mu | toList(#lambda - #mu : 0); + stdsLam := pmStdTabsByConv(n, lambda, opts.Convention); + stdsNu := pmStdTabsByConv(n, nu, opts.Convention); + stdsMu := pmStdTabsByConv(n, muPad, opts.Convention); + srcLabels := flatten for tNu in stdsNu list for tMu in stdsMu list (tNu, tMu); + attachBasisLabels(N, srcLabels, stdsLam) + ); + +dualLR(Sequence, List, PolynomialRing) := opts -> (shapes, Q, P) -> + dualLR(shapes, Q, numgens P, opts); + +-- dualPieriColumn: GL-equivariant projection wedge^d V (x) S_lambda V --> S_mu V +-- where mu = lambda + vertical d-strip. Cached stack-and-solve construction +-- analogous to dualPieri. The cache uses the canonical column ordering +-- produced by pmAddableVStrips; if the user passes a permuted cols list, +-- a single sign flip suffices since pieriColumn is sgn-equivariant under +-- column permutation (the wedge factor is antisymmetric). +-- Requires P to be a SkewCommutative ring (the wedge factor is encoded there). +dualPieriColumn = method(Options => {Convention => "Filling"}) +dualPieriColumn(List, List, PolynomialRing) := opts -> (mu, cols, P) -> ( + n := numgens P; + d := #cols; + K := coefficientRing P; + -- Compute lambda = mu after the cols vertical strip is removed. + curMu := trimPartPM mu; + for kk in cols do ( + muT := conjPartPM curMu; + rowR := muT#(kk-1); + curMu = trimPartPM (for i from 0 to #curMu - 1 list (if i == rowR - 1 then curMu#i - 1 else curMu#i)); + ); + lambda := curMu; + (addable, A, offsets) := pmGetDualVBlocks(lambda, d, n, opts.Convention, P, K); + muTrim := trimPartPM mu; + muIdx := position(addable, p -> p#0 == muTrim); + if muIdx === null then error("dualPieriColumn: mu = ", mu, + " is not a vertical ", d, "-strip addition to lambda = ", + lambda, " in ", n, " variables"); + -- Sign correction: A was built with the canonical cols ordering. + canCols := addable#muIdx#1; + sgn := pmPermSign(toList cols, canCols); + if sgn == 0 then error("dualPieriColumn: cols ", cols, + " are not a permutation of canonical cols ", canCols); + startRow := offsets#muIdx; + dimMu := offsets#(muIdx + 1) - startRow; + N := pmDualSolveRows(A, startRow, dimMu); + if sgn == -1 then N = -N; + monBasis := flatten entries basis(d, P); + stdsLam := pmStdTabsByConv(n, lambda, opts.Convention); + stdsMu := pmStdTabsByConv(n, muTrim, opts.Convention); + srcLabels := flatten for tLam in stdsLam list for alpha in monBasis list (tLam, alpha); + attachBasisLabels(N, srcLabels, stdsMu) + ); + +-- applyDualPieriColumn: point evaluation of dualPieriColumn on a basis pair +-- (wedge_polynomial, T_lambda). Returns a list of {coefficient, T_mu} pairs. +applyDualPieriColumn = method(Options => {Convention => "Filling"}) +applyDualPieriColumn(Sequence, RingElement, BasicList, ZZ) := opts -> + (muCols, poly, Tlam, n) -> ( + if poly == 0 then return {}; + P := ring poly; + d := #(muCols#1); + if not isHomogeneous poly or first degree poly =!= d then + error("applyDualPieriColumn: poly must be homogeneous of degree d = ", d); + K := coefficientRing P; + monBasis := flatten entries basis(d, P); + (mu, cols) := muCols; + -- compute lambda + curMu := trimPartPM mu; + for kk in cols do ( + muT := conjPartPM curMu; + rowR := muT#(kk-1); + curMu = trimPartPM (for i from 0 to #curMu - 1 list (if i == rowR - 1 then curMu#i - 1 else curMu#i)); + ); + lambda := curMu; + coordsLam := pmTableauToCoords(Tlam, lambda, n, opts.Convention); + polyCoords := for alpha in monBasis list lift(coefficient(alpha, poly), K); + sourceVec := flatten for c in coordsLam list for pc in polyCoords list c * pc; + N := dualPieriColumn(mu, cols, P, Convention => opts.Convention); + resultCol := N * transpose matrix(K, {sourceVec}); + stds := pmStdTabsByConv(n, mu, opts.Convention); + for i from 0 to numRows resultCol - 1 list ( + c := lift(resultCol_(i, 0), K); + if c != 0 then {c, stds#i} else continue + ) + ); + +-- Convert a tableau T to a coordinate vector in the standard basis of +-- S_shape under the active Convention. +-- +-- A List is interpreted in the natural form of the convention: +-- * Convention => "Row" or "Weyl": List = PM row form (rows of the tableau). +-- * Convention => "Filling": List = column form (columns of the tableau, +-- top-to-bottom), automatically wrapped as +-- a SchurFunctors Filling. +-- A Filling argument is always treated as column form (its native meaning), +-- regardless of the convention -- this lets the user supply a column-form +-- tableau even in Row/Weyl convention. +-- +-- Non-standard inputs are straightened first using the convention's +-- straightening relations (PieriMaps' row-Garnir for Row/Weyl, SchurFunctors' +-- column-Garnir for Filling). +pmTableauToCoords = (T, shape, n, conv) -> ( + ensureSchurFn(); + if conv === "Filling" then ( + stdsF := sfStandardTabPM(n, conjPartPM shape); + Tcast := if class T === sfFillingType then T + else if class T === List then new sfFillingType from T + else error "tableau must be a List (column form) or Filling"; + h := sfStraightenPM Tcast; + for s in stdsF list (h#s ?? 0_QQ) + ) + else if conv === "Row" or conv === "Weyl" then ( + stds := standardTableaux(n, shape); + hRow := if class T === List then straighten T + else if class T === sfFillingType then fillingToPM T + else error "tableau must be a List (PM row form) or Filling (column form)"; + for s in stds list (hRow#s ?? 0_QQ) + ) + else error("unknown Convention: ", conv) + ); + +pmStdTabsByConv = (n, shape, conv) -> ( + if conv === "Filling" then (ensureSchurFn(); sfStandardTabPM(n, conjPartPM shape)) + else if conv === "Row" or conv === "Weyl" then standardTableaux(n, shape) + else error("unknown Convention: ", conv) + ); + +-- applyPieri: point evaluation of the inclusion-direction Pieri map. +-- Takes a single source tableau T of shape mu (List PM-row form, or Filling +-- column form) and returns the image as a list of {polynomial, T_lambda} pairs: +-- each polynomial is a homogeneous degree-d form in P encoding the V^d factor. +-- Non-standard inputs are straightened first via the Convention's relations. +applyPieri = method(Options => {Convention => "Row"}) +applyPieri(Sequence, BasicList, PolynomialRing) := opts -> + (muBoxes, T, P) -> ( + (mu, boxes) := muBoxes; + n := numgens P; + K := coefficientRing P; + lambda := mu; + for b in boxes do lambda = subtractOne(lambda, b); + while #lambda > 0 and lambda#-1 == 0 do lambda = drop(lambda, -1); + M := pieri(mu, boxes, P, Convention => opts.Convention); + coordsMu := pmTableauToCoords(T, mu, n, opts.Convention); + sourceVecP := transpose matrix(P, {apply(coordsMu, c -> promote(c, P))}); + resultCol := M * sourceVecP; + stdsLam := pmStdTabsByConv(n, lambda, opts.Convention); + for i from 0 to numRows resultCol - 1 list ( + f := resultCol_(i, 0); + if f == 0 then continue else {f, stdsLam#i} + ) + ); + +-- applyPieriColumn: same but for the column-form Pieri (target wedge^d V). +-- Requires P to be a SkewCommutative ring (the wedge factor is encoded there). +applyPieriColumn = method(Options => {Convention => "Filling"}) +applyPieriColumn(Sequence, BasicList, PolynomialRing) := opts -> + (muCols, T, P) -> ( + (mu, cols) := muCols; + n := numgens P; + K := coefficientRing P; + -- Compute lambda by removing the vertical strip described by cols. + curMu := trimPartPM mu; + for kk in cols do ( + muT := conjPartPM curMu; + rowR := muT#(kk-1); + curMu = trimPartPM (for i from 0 to #curMu - 1 list (if i == rowR - 1 then curMu#i - 1 else curMu#i)); + ); + lambda := curMu; + M := pieriColumn(mu, cols, P, Convention => opts.Convention); + coordsMu := pmTableauToCoords(T, mu, n, opts.Convention); + sourceVecP := transpose matrix(P, {apply(coordsMu, c -> promote(c, P))}); + resultCol := M * sourceVecP; + stdsLam := pmStdTabsByConv(n, lambda, opts.Convention); + for i from 0 to numRows resultCol - 1 list ( + f := resultCol_(i, 0); + if f == 0 then continue else {f, stdsLam#i} + ) + ); + +applyDualPieri = method(Options => {Convention => "Row"}) +applyDualPieri(Sequence, RingElement, BasicList, ZZ) := opts -> + (muBoxes, poly, Tlam, n) -> ( + (mu, boxes) := muBoxes; + if poly == 0 then return {}; + P := ring poly; + d := #boxes; + if not isHomogeneous poly or first degree poly =!= d then + error("applyDualPieri: poly must be homogeneous of degree d = ", d); + K := coefficientRing P; + monBasis := flatten entries basis(d, P); + lambda := mu; + for b in boxes do lambda = subtractOne(lambda, b); + while #lambda > 0 and lambda#-1 == 0 do lambda = drop(lambda, -1); + coordsLam := pmTableauToCoords(Tlam, lambda, n, opts.Convention); + polyCoords := for alpha in monBasis list lift(coefficient(alpha, poly), K); + -- Source basis ordering: (T_lambda outer, alpha inner) -- match dualPieri. + sourceVec := flatten for c in coordsLam list for pc in polyCoords list c * pc; + N := dualPieri(mu, boxes, P, Convention => opts.Convention); + resultCol := N * transpose matrix(K, {sourceVec}); + stds := pmStdTabsByConv(n, mu, opts.Convention); + for i from 0 to numRows resultCol - 1 list ( + c := lift(resultCol_(i, 0), K); + if c != 0 then {c, stds#i} else continue + ) + ); + +applyDualLR = method(Options => {Convention => "Row"}) +-- NOTE: takes the (Tnu, Tmu) pair as a List {Tnu, Tmu}, not a Sequence, +-- because M2 flattens nested Sequences in function-call arg lists, but +-- Lists do not flatten. +applyDualLR(Sequence, List, List, ZZ) := opts -> + (shapes, Q, TnuTmuList, n) -> ( + if #TnuTmuList =!= 2 then + error "applyDualLR: third argument must be a List {Tnu, Tmu}"; + (Tnu, Tmu) := (TnuTmuList#0, TnuTmuList#1); + (lambda, mu, nu) := shapes; + coordsNu := pmTableauToCoords(Tnu, nu, n, opts.Convention); + muPad := mu | toList(#lambda - #mu : 0); + coordsMu := pmTableauToCoords(Tmu, muPad, n, opts.Convention); + -- Source basis ordering: (T_nu outer, T_mu inner) -- matches lrMap row indexing. + sourceVec := flatten for cn in coordsNu list for cm in coordsMu list cn * cm; + N := dualLR(shapes, Q, n, Convention => opts.Convention); + resultCol := N * transpose matrix(QQ, {sourceVec}); + stds := pmStdTabsByConv(n, lambda, opts.Convention); + for i from 0 to numRows resultCol - 1 list ( + c := lift(resultCol_(i, 0), QQ); + if c != 0 then {c, stds#i} else continue + ) + ); + +-- ==================================================================== +-- verifyEquivariant: rigorous GL_n-equivariance check. +-- +-- Tests whether a matrix M between Schur-functor representations +-- commutes with the action of every Chevalley generator E_{i,j} of +-- gl_n (i != j), which suffices for full GL_n-equivariance (since the +-- E_{i,j} together with the diagonal -- under which our weight-graded +-- matrices commute automatically -- generate gl_n). +-- +-- Action conventions: +-- * E_{i,j} acts on V = K^n by x_l |-> delta_{l,j} * x_i +-- (i.e. as the differential operator x_i * d/dx_j on polynomials). +-- * E_{i,j} acts on S_lambda V (PM row-SSYT basis) by replacing each +-- entry equal to j with i, summing over positions, and applying +-- row-Garnir straightening. +-- * E_{i,j} acts on S_lambda V (Filling column basis) similarly but +-- with SchurFunctors' column-Garnir straightening. +-- * Action on a tensor product is via Leibniz rule. +-- +-- Five overloads cover our existing maps: +-- verifyEquivariant(M, mu, boxes, P) -- pieri / pieriColumn +-- verifyEquivariant(M, shapes, n) -- lrMap +-- verifyEquivariant(M, mu, boxes, P, "Dual") -- dualPieri +-- verifyEquivariant(M, shapes, n, "Dual") -- dualLR +-- In each, the Convention option matches the matrix's basis. +-- ==================================================================== + +-- Lie-algebra E_{i,j} action on a PM row-SSYT tableau (List of rows). +-- Returns a HashTable {std PM tab => coeff}. Uses PieriMaps' straighten. +pmActE0 = (i, j, T) -> ( + totalH := new MutableHashTable; + for r from 0 to #T-1 do + for c from 0 to #(T#r)-1 do + if T#r#c == j then ( + Tprime := for r2 from 0 to #T-1 list + (if r2 == r then + for c2 from 0 to #(T#r2)-1 list + (if c2 == c then i else (T#r2)#c2) + else T#r2); + h := straighten Tprime; + for k in keys h do + totalH#k = (totalH#k ?? 0) + h#k; + ); + new HashTable from totalH + ); + +-- E_{i,j} on a Filling F: replace each j entry by i, sum, then run +-- SchurFunctors' straighten (column-Garnir). Returns HashTable {std Filling => coeff}. +-- Cache c_lambda values: the round-trip scalar of pmToFilling/fillingToPM. +pmCLambdaCache = new MutableHashTable + +-- Compute c_lambda for the round-trip pmToFilling -> fillingToPM = c * Id. +-- We extract it as a diagonal entry of the product matrix. +pmComputeCLambda = (lambda, n) -> pmCLambdaCache#(trimPartPM lambda, n) ??= ( + A := pmToFillingMatrix(lambda, n); + B := fillingToPMMatrix(lambda, n); + (B * A)_(0, 0) + ); + +-- E_{i,j} action on a Filling F of shape `lambda` over K^n. We route through +-- the PM round-trip and divide out the c_lambda factor: +-- E.F = (1/c_lambda) * pmToFilling( pmActE0( fillingToPM(F) ) ). +-- This avoids depending on SchurFunctors' single-argument `straighten Filling` +-- signature (which can fail to register when SchurFunctors is loaded via +-- needsPackage *after* PieriMaps). +pmActEFilling0 = (i, j, F, lambda, n) -> ( + ensureSchurFn(); + raw := fillingToPM F; + pmResult := new MutableHashTable; + for T in keys raw do ( + c := raw#T; + actT := pmActE0(i, j, T); + for k in keys actT do + pmResult#k = (pmResult#k ?? 0_QQ) + c * actT#k; + ); + fillResult := new MutableHashTable; + for T in keys pmResult do ( + c := pmResult#T; + fillExp := pmToFilling T; + for F2 in keys fillExp do + fillResult#F2 = (fillResult#F2 ?? 0_QQ) + c * fillExp#F2; + ); + if #fillResult == 0 then return new HashTable from {}; + cLam := pmComputeCLambda(lambda, n); + new HashTable from for k in keys fillResult list ( + if fillResult#k != 0 then k => fillResult#k / cLam else continue + ) + ); + +-- Pick the appropriate gl_n action based on Convention. +-- shape, n: only used in the Filling case (for c_lambda correction). +pmActEConv0 = (i, j, T, conv, shape, n) -> ( + if conv === "Row" or conv === "Weyl" then pmActE0(i, j, T) + else if conv === "Filling" then pmActEFilling0(i, j, T, shape, n) + else error("unknown Convention: ", conv) + ); + +verifyEquivariant = method(Options => {Convention => "Row", Verbose => false, Direction => "Inclusion"}) + +-- Pieri (or pieriColumn) inclusion. M : S_mu V -> Sym^d V (x) S_lambda V +-- (or wedge instead of Sym for SkewCommutative P). M is dim_lambda x dim_mu +-- over P, entries homogeneous of degree d. +verifyEquivariant(Matrix, List, List, PolynomialRing) := opts -> + (M, mu, boxes, P) -> ( + if opts.Direction === "Dual" then + return verifyEquivariantDual0(M, mu, boxes, P, opts); + n := numgens P; + X := gens P; + conv := opts.Convention; + lambda := mu; + for b in boxes do lambda = subtractOne(lambda, b); + while #lambda > 0 and lambda#-1 == 0 do lambda = drop(lambda, -1); + stdsMu := pmStdTabsByConv(n, mu, conv); + stdsLam := pmStdTabsByConv(n, lambda, conv); + -- Index lookup tables: O(1) replacement for `position(stds, s -> s == k)`. + idxOfMu := hashTable for i from 0 to #stdsMu - 1 list stdsMu#i => i; + idxOfLam := hashTable for i from 0 to #stdsLam - 1 list stdsLam#i => i; + -- Cache columns once per tIdx (used by both LHS and RHS branches). + colsM := for tIdx from 0 to #stdsMu - 1 list flatten entries M_{tIdx}; + ok := true; + for i from 0 to n-1 do for j from 0 to n-1 do ( + if i == j then continue; + -- Hoist: pmActEConv0(i, j, stdsLam#idx, conv) and (i, j, stdsMu#tIdx, conv) + -- depend ONLY on (i, j, idx) -- not on the outer tIdx loop variable. + -- Without this hoist they'd be recomputed #stdsMu times each. + lamActs := for idx from 0 to #stdsLam - 1 list pmActEConv0(i, j, stdsLam#idx, conv, lambda, n); + muActs := for tIdx from 0 to #stdsMu - 1 list pmActEConv0(i, j, stdsMu#tIdx, conv, mu, n); + for tIdx from 0 to #stdsMu-1 do ( + colT := colsM#tIdx; + -- RHS = E . M(T) + vAction := for f in colT list X#i * diff(X#j, f); + sAction := new MutableList from for s in stdsLam list 0_P; + for idx from 0 to #stdsLam-1 do ( + h := lamActs#idx; + for kk in keys h do + if idxOfLam#?kk then + sAction#(idxOfLam#kk) = sAction#(idxOfLam#kk) + h#kk * colT#idx; + ); + RHS := for idx from 0 to #stdsLam-1 list (vAction#idx + sAction#idx); + -- LHS = M(E . T) + LHS := new MutableList from for s in stdsLam list 0_P; + hT := muActs#tIdx; + for kk in keys hT do + if idxOfMu#?kk then ( + colK := colsM#(idxOfMu#kk); + ck := hT#kk; + for r from 0 to #stdsLam-1 do LHS#r = LHS#r + ck * colK#r; + ); + if toList LHS =!= RHS then ( + ok = false; + if opts.Verbose then + stdio << " [verifyEquivariant] FAIL at E_{" << i << "," << j + << "}, T = " << toString stdsMu#tIdx << endl; + ); + ); + ); + ok + ); + +-- LR inclusion. M : S_lambda V -> S_nu V (x) S_mu V. +-- M is (dim_nu * dim_mu) x dim_lambda over QQ; rows in lex order with T_nu outer. +verifyEquivariant(Matrix, Sequence, ZZ) := opts -> (M, shapes, n) -> ( + if opts.Direction === "Dual" then + return verifyEquivariantDualLR0(M, shapes, n, opts); + (lambda, mu, nu) := shapes; + muPad := mu | toList(#lambda - #mu : 0); + conv := opts.Convention; + stdsLam := pmStdTabsByConv(n, lambda, conv); + stdsMu := pmStdTabsByConv(n, muPad, conv); + stdsNu := pmStdTabsByConv(n, nu, conv); + dimNu := #stdsNu; dimMu := #stdsMu; dimLam := #stdsLam; + -- Index tables for O(1) tableau-to-position lookups. + idxOfLam := hashTable for i from 0 to dimLam-1 list stdsLam#i => i; + idxOfMu := hashTable for i from 0 to dimMu-1 list stdsMu#i => i; + idxOfNu := hashTable for i from 0 to dimNu-1 list stdsNu#i => i; + -- Cache columns of M. + colsM := for tIdx from 0 to dimLam-1 list flatten entries M_{tIdx}; + ok := true; + for i from 0 to n-1 do for j from 0 to n-1 do ( + if i == j then continue; + -- Hoist actions: each pmActEConv0(i, j, std, conv) depends only on (i, j, std). + nuActs := for iN from 0 to dimNu-1 list pmActEConv0(i, j, stdsNu#iN, conv, nu, n); + muActs := for iM from 0 to dimMu-1 list pmActEConv0(i, j, stdsMu#iM, conv, muPad, n); + lamActs := for iL from 0 to dimLam-1 list pmActEConv0(i, j, stdsLam#iL, conv, lambda, n); + for tIdx from 0 to dimLam-1 do ( + colT := colsM#tIdx; + -- RHS = E . M(T): Leibniz on S_nu (x) S_mu + RHSrows := new MutableList from for r from 0 to dimNu*dimMu-1 list 0_QQ; + for iN from 0 to dimNu-1 do for iM from 0 to dimMu-1 do ( + coef := colT#(iN*dimMu + iM); + if coef == 0 then continue; + -- E acts on T_nu^{iN} + hN := nuActs#iN; + for kN in keys hN do + if idxOfNu#?kN then + RHSrows#((idxOfNu#kN)*dimMu + iM) = RHSrows#((idxOfNu#kN)*dimMu + iM) + hN#kN * coef; + -- E acts on T_mu^{iM} + hM := muActs#iM; + for kM in keys hM do + if idxOfMu#?kM then + RHSrows#(iN*dimMu + (idxOfMu#kM)) = RHSrows#(iN*dimMu + (idxOfMu#kM)) + hM#kM * coef; + ); + RHS := toList RHSrows; + -- LHS = M(E . T) + LHS := new MutableList from for r from 0 to dimNu*dimMu-1 list 0_QQ; + hT := lamActs#tIdx; + for kk in keys hT do + if idxOfLam#?kk then ( + colK := colsM#(idxOfLam#kk); + ck := hT#kk; + for r from 0 to dimNu*dimMu-1 do + LHS#r = LHS#r + ck * colK#r; + ); + if toList LHS =!= RHS then ( + ok = false; + if opts.Verbose then + stdio << " [verifyEquivariant LR] FAIL at E_{" << i << "," << j + << "}, T = " << toString stdsLam#tIdx << endl; + ); + ); + ); + ok + ); + +-- Dual Pieri projection. M : Sym^d V (x) S_lambda V -> S_mu V. M is K-matrix +-- of size dim_mu x (dim_lambda * #monsDeg_d), columns in (T_lambda, alpha) order. +-- Activated by Direction => "Dual"; with Direction => "Inclusion" (default), the +-- 4-arg overload above handles the inclusion direction. +verifyEquivariantDual0 = (M, mu, boxes, P, opts) -> ( + n := numgens P; + X := gens P; + d := #boxes; + conv := opts.Convention; + K := coefficientRing P; + monBasis := flatten entries basis(d, P); + nMons := #monBasis; + lambda := mu; + for b in boxes do lambda = subtractOne(lambda, b); + while #lambda > 0 and lambda#-1 == 0 do lambda = drop(lambda, -1); + stdsMu := pmStdTabsByConv(n, mu, conv); + stdsLam := pmStdTabsByConv(n, lambda, conv); + dimLam := #stdsLam; dimMu := #stdsMu; + srcIdx := (iLam, iAlpha) -> iLam * nMons + iAlpha; + -- Index tables for O(1) tableau lookup (replaces linear `position`). + idxOfLam := hashTable for i from 0 to dimLam-1 list stdsLam#i => i; + idxOfMu := hashTable for i from 0 to dimMu-1 list stdsMu#i => i; + -- Cache all columns of M once (each column read used by many iterations). + totalCols := dimLam * nMons; + colsM := for c from 0 to totalCols - 1 list flatten entries M_{c}; + -- Pre-decompose E_{i,j}.x^alpha in the monomial basis ONCE for all alpha. + ok := true; + for i from 0 to n-1 do for j from 0 to n-1 do ( + if i == j then continue; + -- Pre-compute the monomial-decomposition of E.x^alpha for each alpha. + monActDecomp := for iAlpha from 0 to nMons-1 list ( + newPoly := X#i * diff(X#j, monBasis#iAlpha); + if newPoly == 0 then {} else + for iA2 from 0 to nMons-1 list ( + c := lift(coefficient(monBasis#iA2, newPoly), K); + if c == 0 then continue else (iA2, c) + ) + ); + -- Pre-compute pmActEConv0(i, j, std, conv) for each std (lam, mu). + lamActs := for iLam from 0 to dimLam-1 list pmActEConv0(i, j, stdsLam#iLam, conv, lambda, n); + muActs := for iM from 0 to dimMu-1 list pmActEConv0(i, j, stdsMu#iM, conv, mu, n); + for iLam from 0 to dimLam-1 do for iAlpha from 0 to nMons-1 do ( + sIdx := srcIdx(iLam, iAlpha); + -- LHS: M(E . source) + LHS := new MutableList from for r from 0 to dimMu-1 list 0_K; + -- (a) E acts on x^alpha + for pair in monActDecomp#iAlpha do ( + (iA2, c) := pair; + colNew := colsM#(srcIdx(iLam, iA2)); + for r from 0 to dimMu-1 do LHS#r = LHS#r + c * colNew#r; + ); + -- (b) E acts on T_lambda + hL := lamActs#iLam; + for kk in keys hL do + if idxOfLam#?kk then ( + colNew := colsM#(srcIdx(idxOfLam#kk, iAlpha)); + ck := hL#kk; + for r from 0 to dimMu-1 do LHS#r = LHS#r + ck * colNew#r; + ); + -- RHS: E . M(source) + colSource := colsM#sIdx; + RHS := new MutableList from for r from 0 to dimMu-1 list 0_K; + for iM from 0 to dimMu-1 do ( + coef := colSource#iM; + if coef == 0 then continue; + hM := muActs#iM; + for kk in keys hM do + if idxOfMu#?kk then + RHS#(idxOfMu#kk) = RHS#(idxOfMu#kk) + hM#kk * coef; + ); + if toList LHS =!= toList RHS then ( + ok = false; + if opts.Verbose then stdio << " [verifyEquivariant dualPieri] FAIL at E_{" + << i << "," << j << "}, (T_lambda, alpha) = (" << toString stdsLam#iLam + << ", " << toString monBasis#iAlpha << ")" << endl; + ); + ); + ); + ok + ); + +-- Dual LR projection. M : S_nu V (x) S_mu V -> S_lambda V. M is QQ-matrix +-- of size dim_lambda x (dim_nu * dim_mu). +verifyEquivariantDualLR0 = (M, shapes, n, opts) -> ( + (lambda, mu, nu) := shapes; + muPad := mu | toList(#lambda - #mu : 0); + conv := opts.Convention; + stdsLam := pmStdTabsByConv(n, lambda, conv); + stdsMu := pmStdTabsByConv(n, muPad, conv); + stdsNu := pmStdTabsByConv(n, nu, conv); + dimNu := #stdsNu; dimMu := #stdsMu; dimLam := #stdsLam; + srcIdx := (iN, iM) -> iN * dimMu + iM; + -- Index tables and column cache for O(1) lookups. + idxOfLam := hashTable for i from 0 to dimLam-1 list stdsLam#i => i; + idxOfMu := hashTable for i from 0 to dimMu-1 list stdsMu#i => i; + idxOfNu := hashTable for i from 0 to dimNu-1 list stdsNu#i => i; + totalCols := dimNu * dimMu; + colsM := for c from 0 to totalCols - 1 list flatten entries M_{c}; + ok := true; + for i from 0 to n-1 do for j from 0 to n-1 do ( + if i == j then continue; + -- Hoist actions: each pmActEConv0(i, j, std, conv) computed once per std. + nuActs := for iN from 0 to dimNu-1 list pmActEConv0(i, j, stdsNu#iN, conv, nu, n); + muActs := for iM from 0 to dimMu-1 list pmActEConv0(i, j, stdsMu#iM, conv, muPad, n); + lamActs := for iL from 0 to dimLam-1 list pmActEConv0(i, j, stdsLam#iL, conv, lambda, n); + for iN from 0 to dimNu-1 do for iM from 0 to dimMu-1 do ( + sIdx := srcIdx(iN, iM); + -- LHS = M(E . (T_nu (x) T_mu)) + LHS := new MutableList from for r from 0 to dimLam-1 list 0_QQ; + hN := nuActs#iN; + for kN in keys hN do + if idxOfNu#?kN then ( + colNew := colsM#(srcIdx(idxOfNu#kN, iM)); + ck := hN#kN; + for r from 0 to dimLam-1 do LHS#r = LHS#r + ck * colNew#r; + ); + hM := muActs#iM; + for kM in keys hM do + if idxOfMu#?kM then ( + colNew := colsM#(srcIdx(iN, idxOfMu#kM)); + ck := hM#kM; + for r from 0 to dimLam-1 do LHS#r = LHS#r + ck * colNew#r; + ); + -- RHS = E . M(source) + colSource := colsM#sIdx; + RHS := new MutableList from for r from 0 to dimLam-1 list 0_QQ; + for iL from 0 to dimLam-1 do ( + coef := colSource#iL; + if coef == 0 then continue; + hL := lamActs#iL; + for kk in keys hL do + if idxOfLam#?kk then + RHS#(idxOfLam#kk) = RHS#(idxOfLam#kk) + hL#kk * coef; + ); + if toList LHS =!= toList RHS then ( + ok = false; + if opts.Verbose then stdio << " [verifyEquivariant dualLR] FAIL at E_{" + << i << "," << j << "}, (T_nu, T_mu) = (" << toString stdsNu#iN + << ", " << toString stdsMu#iM << ")" << endl; + ); + ); + ); + ok + ); + +-- ==================================================================== +-- symbolicForm: pretty-print a labeled matrix as a readable basis-element +-- map. Each row of the netList shows a source basis element and its image +-- as a list of (target_label, coefficient) terms. +-- +-- When the matrix M was produced by pieri / pieriColumn / lrMap / dualPieri / +-- dualPieriColumn / dualLR, the source/target labels are pulled from +-- M.cache.sourceBasis / M.cache.targetBasis. For other matrices, integer +-- indices 1..rank are used. +-- ==================================================================== +-- Pretty-print a PM tableau (List of rows) as a boxed grid, columns top-down. +prettyPMTabPM = T -> ( + if T === {} or all(T, r -> #r == 0) then return net "()"; + width := max apply(T, r -> #r); + cols := for j from 0 to width-1 list ( + for i from 0 to #T - 1 list (if j < #(T#i) then T#i#j else continue)); + netList {apply(cols, col -> stack apply(col, e -> net e))} + ); + +-- Heuristic check: does a List look like a row-form tableau (list of lists of integers)? +isPMTabPM = T -> instance(T, List) and all(T, r -> instance(r, List)) and + all(T, r -> all(r, e -> instance(e, ZZ))); + +-- Render a single basis label, recursively handling tuples. +prettyLabelPM = lab -> ( + if isPMTabPM lab then prettyPMTabPM lab + else if instance(lab, Sequence) then ( + parts := apply(toList lab, prettyLabelPM); + if #parts == 0 then net "()" + else fold((a, b) -> horizontalJoin {a, net " ⊗ ", b}, parts) + ) + else net lab + ); + +-- Render the image of one source basis element (a list of (target_label, coef) pairs). +prettyImagePM = img -> ( + if #img == 0 then net "0" + else stack apply(img, term -> ( + (lab, coef) := (term#0, term#1); + horizontalJoin {net coef, net " * ", prettyLabelPM lab})) + ); + +-- Robust label-comparison key. Lists compare by value automatically; Filling +-- instances need to be flattened to a List first since they don't have ==. +labelKeyPM = lab -> ( + if instance(lab, List) then lab + else if instance(lab, Sequence) then toList apply(toList lab, labelKeyPM) + else if instance(lab, BasicList) then toList lab + else lab + ); + +symbolicForm = method() +symbolicForm Matrix := M -> ( + M.cache#"sourceBasis" ??= toList(0 .. rank source M - 1); + M.cache#"targetBasis" ??= toList(0 .. rank target M - 1); + M.cache#"rule" ??= ( + -- Build label-to-index map once for O(1) lookup. + srcLabels := M.cache#"sourceBasis"; + idxOfSrc := hashTable for k from 0 to #srcLabels - 1 list + labelKeyPM(srcLabels#k) => k; + i -> ( + key := labelKeyPM i; + if not idxOfSrc#?key then error "symbolicForm: source label not found"; + l := idxOfSrc#key; + col := flatten entries M_{l}; + L := positions(col, j -> not(j == 0_(ring M))); + for j in L list ((M.cache#"targetBasis")_j, col_j) + ) + ); + netList for i in M.cache#"sourceBasis" list ( + {prettyLabelPM i, prettyImagePM ((M.cache#"rule")(i))}) + ); + +---------------------------------------------------------------------- +-- Tests and Documentation are kept in auxiliary files; see PieriMaps/ +---------------------------------------------------------------------- + +load "./PieriMaps/tests.m2" + +beginDocumentation() +load "./PieriMaps/doc.m2" -document { - Key => {schurRank, (schurRank, ZZ, List)}, - Headline => "computes the dimension of the irreducible GL(QQ^n) representation associated to a partition", - SeeAlso => standardTableaux, - Usage => "schurRank(n, mu)", - Inputs => { - "n" => {ofClass ZZ, ", the size of the matrix group GL(QQ^n)"}, - "mu" => {ofClass List, ", a partition (mu_1, ..., m_r) where mu_i is the number of boxes in the ith row"} - }, - Outputs => { - ZZ => {"The dimension of the irreducible GL(QQ^n) representation associated to mu"} - }, - "The dimension is computed using the determinantal formula given by the Weyl character formula.", - EXAMPLE lines /// - schurRank(5, {4,3}) -- should be 560 - /// - } - -TEST /// -t = new BettiTally from {(0,{0},0)=>8, (1,{1},1) =>15, (2,{3},3)=>10, (3,{5},5)=>3}; -assert (t == (betti res coker pieri({3,1},{1},QQ[a,b,c]))) -t = new BettiTally from {(0,{0},0)=>15, (1,{1},1) =>24, (2,{4},4)=>15, (3,{6},6)=>6}; -assert (t == (betti res coker pieri({4,1},{1},QQ[a,b,c]))) -t = new BettiTally from {(0,{0},0)=>20, (1,{2},2) =>60, (2,{3},3)=>64, (3,{4},4)=>20}; -assert (t == (betti res coker pieri({3,2},{2,2},QQ[a,b,c,d]))) -assert(schurRank(5, {4,3}) == 560) -t = new BettiTally from {(0,{0},0) => 3, (1,{1},1)=>8, (2,{2},2) => 6, -(3,{4},4)=>1}; -assert(t == (betti res coker pureFree({0,1,2,4}, GF(4)[a,b,c]))) -/// - -end +end loadPackage "PieriMaps" installPackage PieriMaps diff --git a/M2/Macaulay2/packages/PieriMaps/doc.m2 b/M2/Macaulay2/packages/PieriMaps/doc.m2 new file mode 100644 index 00000000000..67d7395039d --- /dev/null +++ b/M2/Macaulay2/packages/PieriMaps/doc.m2 @@ -0,0 +1,1431 @@ +document { + Key => PieriMaps, + Headline => "Pieri inclusions", + Citation => {///@article{Sam2009, + AUTHOR = {Sam, Steven V}, + TITLE = {Computing inclusions of {S}chur modules}, + JOURNAL = {The Journal of Software for Algebra and Geometry: Macaulay2}, + VOLUME = {1}, + YEAR = {2009}, + PAGES = {5--10}, + DOI = {10.2140/jsag.2009.1.5}, + URL = {https://msp.org/jsag/2009/1-1/p02.xhtml} +}///}, + "For mathematical background of this package and some examples of use, see:", + BR{}, + "Steven V Sam, Computing inclusions of Schur modules, arXiv:0810.4666", + BR{}, + "Some other references:", + BR{}, + "Andrzej Daszkiewicz, On the Invariant Ideals of the Symmetric Algebra $S.(V \\oplus \\wedge^2 V)$, J. Algebra 125, 1989, 444-473.", + BR{}, + "David Eisenbud, Gunnar Fl\\o ystad, and Jerzy Weyman, The existence of pure free resolutions, arXiv:0709.1529.", + BR{}, + "William Fulton, Young Tableaux: With Applications to Representation Theory and Geometry, London Math. Society Student Texts 35, 1997.", + BR{}, + "Jerzy Weyman, Cohomology of Vector Bundles and Syzygies, Cambridge University Press, 2002.", + BR{}, + BR{}, + "Let ", TEX "$V$", " be a vector space over ", TEX "$K$", ". If ", + TEX "$K$", " has characteristic 0, then given the partition ", + TEX "$\\mu$", " and the partition ", TEX "$\\mu'$", " obtained from ", + TEX "$\\mu$", " by removing a single box, there is a unique (up to + nonzero scalar) ", TEX "$GL(V)$", "-equivariant inclusion ", + TEX "$S_\\mu V \\to V \\otimes S_{\\mu'} V$", ", where ", + TEX "$S_\\mu$", " refers to the irreducible representation of ", + TEX "$GL(V)$", " with highest weight ", TEX "$\\mu$", + ". This can be extended uniquely to a map of ", + TEX "$P = \\mathrm{Sym}(V)$", "-modules ", + TEX "$P \\otimes S_\\mu V \\to P \\otimes S_{\\mu'} V$", + ". The purpose of this package is to write down matrix + representatives for these maps. The main function for doing so is ", + TO pieri, + ". Here is an example of the use of the package PieriMaps, which is also + designed to check whether the maps are being constructed correctly + (important, since it is notoriously difficult to get the signs and + coefficients right.) ", + "We will construct by hand the free resolution in 3 variables corresponding + to the degree sequence (0,2,3,6). Let's start with the packaged code:", + EXAMPLE lines /// + R = QQ[a,b,c]; + f = pureFree({0,2,3,6}, R) + betti res coker f + ///, + "By the general theory from Eisenbud-Fl\\o ystad-Weyman, each of these free + modules should be essentially Schur functors corresponding to the + following partitions.", + EXAMPLE lines /// + needsPackage "SchurRings" + schurRing(s,3) + dim s_{2,2} + dim s_{4,2} + dim s_{4,3} + dim s_{4,3,3} + ///, + "This package also provides a routine ", + TO schurRank, + " for computing this dimension:", + EXAMPLE lines /// + schurRank(3, {2,2}) + schurRank(3, {4,2}) + schurRank(3, {4,3}) + schurRank(3, {4,3,3}) + ///, + "We now use ", + TO pieri, + " to construct each of the maps of the resolution separately.", + EXAMPLE lines /// + f1 = pieri({4,2,0},{1,1}, R) + f2 = pieri({4,3,0},{2}, R) + f3 = pieri({4,3,3},{3,3,3}, R) + ///, + "Fix the degrees (i.e. make sure that the target of f2 is the source of f1, + etc). Otherwise the test of exactness below would fail.", + EXAMPLE lines /// + f1 + f2 = map(source f1,,f2) + f3 = map(source f2,,f3) + f1 * f2 + f2 * f3 + ///, + "Check that the complex is exact.", + EXAMPLE lines /// + ker f1 == image f2 + ker f2 == image f3 + ///, + "Looks great! Now let's try it modulo some prime numbers and see if we get exactness.", + EXAMPLE lines /// + p = 32003 + R = ZZ/p[a,b,c]; + f1 = pieri({4,2,0},{1,1},R) + betti res coker f1 + f2 = pieri({4,3,0},{2},R) + f3 = pieri({4,3,3},{3,3,3},R) + f2 = map(source f1,,f2) + f3 = map(source f2,,f3) + f1 * f2 + f2 * f3 + ker f1 == image f2 + ker f2 == image f3 + ///, + "These do not piece together well. The reason is that ", + TO pieri, + " changes the bases of the free modules in a way which is not invertible + (over ZZ) when the ground field has positive characteristic.", + PARA{}, + BOLD "Version 2.0 overhaul.", " This release extends the original + PieriMaps package in three respects:", + UL { + {BOLD "Multiple basis conventions.", " Every map can be expressed in + the package's original convention (default ", + TT "Convention => \"Row\"", "), the column-form ", TT "Filling", + " basis from ", + TO2 {"SchurFunctors :: SchurFunctors", "SchurFunctors"}, " (", + TT "Convention => \"Filling\"", "), or the divided-power Weyl-module + basis (", TT "Convention => \"Weyl\"", "). See ", TO Convention, "."}, + {BOLD "Both directions of every map.", " In addition to the inclusion + direction (", TO pieri, ", ", TO pieriColumn, ", ", TO lrMap, + "), the projection direction is provided via ", + TO dualPieri, ", ", TO dualPieriColumn, ", and ", TO dualLR, + ". Each pair satisfies ", + TEX "$\\mathrm{dual} \\circ \\mathrm{incl} = \\mathrm{Id}$", + ", the canonical ", TEX "$GL$", + "-equivariant projection given by Schur's lemma."}, + {BOLD "Verifications.", " ", TO verifyEquivariant, " checks ", + TEX "$GL_n$", + "-equivariance of any matrix between Schur modules via the + Chevalley-generator commutativity test; ", TO verifyWellDefined, + " checks compatibility with the Garnir relations of the chosen + convention."} + }, + BOLD "Examples.", + PARA{}, + "The single-box Pieri inclusion ", + TEX "$S_{(2,1)} V \\to V \\otimes S_{(1,1)} V$", + " in three variables. Each row of ", + TT "symbolicForm", + " displays a source standard tableau on the left and its image as a sum of (target tableau, coefficient) pairs on the right:", + EXAMPLE lines /// + R = QQ[a,b,c]; + M = pieri({2,1}, {1}, R); + symbolicForm M + ///, + "The same map in the column-form (Filling) convention is the literal change of basis. Both matrices are equivariant injections and have the same rank:", + EXAMPLE lines /// + R = QQ[a,b,c]; + Mrow = pieri({2,1}, {1}, R, Convention => "Row"); + Mfil = pieri({2,1}, {1}, R, Convention => "Filling"); + (rank Mrow, rank Mfil) + ///, + "The projection ", TO dualPieri, " left-inverts ", TO pieri, + ". Verified by ", + TEX "$\\mathrm{dualPieri} \\circ \\mathrm{applyPieri}(T_\\mu) = T_\\mu$", + " on the highest-weight vector:", + EXAMPLE lines /// + R = QQ[a,b,c]; + Tmu = (standardTableaux(3, {2,1}))#0; + img = applyPieri(({2,1}, {1}), Tmu, R); + back = flatten for term in img list applyDualPieri(({2,1}, {1}), term#0, term#1, 3); + back + ///, + "When the Littlewood-Richardson coefficient ", + TEX "$c^\\lambda_{\\mu,\\nu}$", " exceeds 1, distinct LR tableaux ", + TEX "$Q$", " produce ", TEX "$c$", + " linearly independent inclusions ", + TEX "$\\Psi_Q : S_\\lambda V \\hookrightarrow S_\\nu V \\otimes S_\\mu V$", + ":", + EXAMPLE lines /// + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); -- c^{(3,2,1)}_{(2,1),(2,1)} = 2 + M1 = lrMap(({3,2,1}, {2,1}, {2,1}), Qs#0, 3); + M2 = lrMap(({3,2,1}, {2,1}, {2,1}), Qs#1, 3); + rank(M1 | M2) == 16 -- 2 * dim S_{(3,2,1)} -- linearly independent + ///, + "Each ", TO dualLR, " projects onto exactly its own ", + TEX "$Q$", "-summand and annihilates the others:", + EXAMPLE lines /// + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); + M0 = lrMap(({3,2,1},{2,1},{2,1}), Qs#0, 3); + M1 = lrMap(({3,2,1},{2,1},{2,1}), Qs#1, 3); + N0 = dualLR(({3,2,1},{2,1},{2,1}), Qs#0, 3); + N0 * M0 == id_(QQ^(numColumns M0)) + N0 * M1 == 0 + ///, + TO verifyEquivariant, + " checks Chevalley-generator commutativity for any matrix between Schur reps -- a direct ", + TEX "$GL_n$", + "-equivariance test:", + EXAMPLE lines /// + R = QQ[a,b,c]; + M = pieri({3,2,1}, {2}, R); -- inclusion at the interior row + verifyEquivariant(M, {3,2,1}, {2}, R) + ///, + "And ", TO verifyWellDefined, + " confirms that an LR map factors through the Garnir relations of the chosen convention:", + EXAMPLE lines /// + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); + verifyWellDefined(({3,2,1},{2,1},{2,1}), Qs#0, 3, + Convention => "Filling", Verbose => false) + ///, + SeeAlso => {pieri, pieriColumn, dualPieri, dualPieriColumn, + lrMap, dualLR, applyLR, applyPieri, applyDualPieri, + Convention, verifyEquivariant, verifyWellDefined, + symbolicForm, pureFree, schurRank} + } + +document { + Key => {standardTableaux, (standardTableaux, ZZ, List)}, + Headline => "list all standard tableaux of a certain shape with bounded labels", + SeeAlso => schurRank, + Usage => "standardTableaux(dim, mu)", + Inputs => { + "dim" => {ofClass ZZ, ", number of labels to be used"}, + "mu" => {ofClass List, ", a partition which gives the shape"} + }, + Outputs => { + List => {"list of all standard tableaux of shape ", TEX "$\\mu$", + " with labels ", TEX "$0, \\ldots, \\mathrm{dim}-1$"} + }, + EXAMPLE lines /// + standardTableaux(3, {2,2}) -- lists all standard tableaux on the 2x2 square with entries 0,1,2 + /// + } + +document { + Key => {straighten, (straighten, List), (straighten, List, MutableHashTable)}, + Headline => "computes straightening of a tableau", + Usage => concatenate("straighten(t)", "straighten(t, h)"), + Inputs => { + "t" => {ofClass List, ", a tableau to straighten; a tableau looks like {{3,4}, {1,2}} for example, where we list the entries from left to right, top to bottom"}, + "h" => {ofClass MutableHashTable, ", where the answers should be stored"} + }, + Consequences => { "If provided, the hashtable h is updated with any calculations which are performed as a result of calling this function. " }, + "If a hashtable h is provided, then this outputs nothing, it simply just modifies h. When looking up values, remember that the keys are stored with rows weakly increasing. ", + "If no hashtable is provided, then the user is simply given the straightening of the tableau in terms of semistandard tableaux. ", + "The answer is in the form a hashtable: each key is a semistandard tableaux, and the value of the key is the coefficient of that semistandard tableaux used to write the input t as a linear combination. ", + EXAMPLE lines /// + h = new MutableHashTable from {} + straighten({{3,4}, {1,2}}, h) + h#{{3,4}, {1,2}} -- get the coefficients + straighten({{3,4}, {1,2}}) -- just get the answer instead + /// + } + +document { + Key => {pieri, (pieri, List, List, PolynomialRing), [pieri, Convention]}, + Headline => "computes a matrix representation for a Pieri inclusion of representations of a general linear group", + SeeAlso => {pureFree, pieriColumn, lrMap}, + Usage => "pieri(mu, boxes, P)\npieri(mu, boxes, P, Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "mu" => {ofClass List, ", a partition ", + TEX "$(\\mu_1, \\ldots, \\mu_r)$", " where ", TEX "$\\mu_i$", + " is the number of boxes in the ", TEX "$i$", "th row"}, + "boxes" => {ofClass List, ", a list of rows from which to remove boxes (boxes are always removed from the end of the row). This specifies which map of ", + TEX "$GL(V)$", + " representations we want. The row indices start from 1 and not 0, and this must specify a horizontal strip in ", + TEX "$\\mu$", " (see description below). "}, + "P" => {ofClass PolynomialRing, ", a polynomial ring over a field ", + TEX "$K$", " in ", TEX "$n$", " variables" }, + Convention => String => {", the tableau basis convention used to label the source ", + TEX "$S_\\mu V$", " and target ", TEX "$S_\\lambda V$", + ". One of ", + TT "\"Row\"", " (default; PieriMaps row-form SSYT basis), ", + TT "\"Filling\"", " (SchurFunctors column-form ", TT "Filling", " basis on both sides), or ", + TT "\"Weyl\"", " (data-equivalent to Row, but the bases are interpreted as ", TT "WeylFilling", "s on the divided-power side). ", + "The strip factor ", TEX "$(\\mathrm{Sym}^k V)$", " stays in P regardless."} + }, + Outputs => { + Matrix => {"If ", TEX "$K$", + " has characteristic 0, then given the partition ", TEX "$\\mu$", + " and the partition ", TEX "$\\mu'$", " obtained from ", + TEX "$\\mu$", + " by removing a single box, there is a unique (up to nonzero scalar) ", + TEX "$GL(V)$", "-equivariant inclusion ", + TEX "$S_\\mu V \\to V \\otimes S_{\\mu'} V$", ", where ", + TEX "$S_\\mu$", " refers to the irreducible representation of ", + TEX "$GL(V)$", " with highest weight ", TEX "$\\mu$", + ". This can be extended uniquely to a map of ", + TEX "$P = \\mathrm{Sym}(V)$", "-modules ", + TEX "$P \\otimes S_\\mu V \\to P \\otimes S_{\\mu'} V$", + ". This method computes the matrix representation for the composition of maps that one obtains by + iterating this procedure of removing boxes, i.e., the final output is a ", + TEX "$GL(V)$", "-equivariant map ", + TEX "$P \\otimes S_\\mu V \\to P \\otimes S_\\lambda V$", + " where ", TEX "$\\lambda$", " is the partition obtained from ", + TEX "$\\mu$", " by deleting a box from row ", + TEX "$\\mathrm{boxes}_0$", ", a box from row ", + TEX "$\\mathrm{boxes}_1$", ", etc. + If ", TEX "$K$", + " has positive characteristic, then the corresponding map is calculated over ", + TEX "$\\mathbb{Q}$", ", lifted to a ", TEX "$\\mathbb{Z}$", + "-form of the representation which has + the property that the map has a torsion-free cokernel over ", + TEX "$\\mathbb{Z}$", ", and then the coefficients are reduced to ", + TEX "$K$", "." + } + }, + "Convention: the partition ", TEX "$(d)$", + " represents the ", TEX "$d$", "th symmetric power, while the partition ", + TEX "$(1, \\ldots, 1)$", " represents the ", TEX "$d$", + "th exterior power. ", + "Using the notation from the output, ", TEX "$\\mu/\\lambda$", + " must be a horizontal strip. Precisely, this means that ", + TEX "$\\lambda_i \\geq \\mu_{i+1}$", " for all ", TEX "$i$", + ". If this condition is not satisfied, the program throws an error because a nonzero equivariant map of the desired form will not exist. ", + PARA{}, + "The ", TT "Convention", + " option re-expresses the same equivariant map in different tableau bases on the source ", + TEX "$S_\\mu V$", " and target ", TEX "$S_\\lambda V$", ". In ", + TT "\"Row\"", + " (default), the bases are PM-style SSYT (rows weakly increasing). In ", + TT "\"Filling\"", + ", they are SchurFunctors-style column-form Fillings (the matrix is obtained by post-hoc basis change with the equivariant iso ", + TO pmToFilling, "). In ", TT "\"Weyl\"", + ", the matrix data is the same as ", TT "\"Row\"", + " but is interpreted on the WeylFilling (divided-power) side. All three give matrices of the same rank.", + EXAMPLE lines /// + pieri({3,1}, {1}, QQ[a,b,c]) -- removes the last box from row 1 of the partition {3,1} + res coker oo -- resolve this map + betti oo -- check that the resolution is pure + ///, + EXAMPLE lines /// + P = QQ[a,b,c]; + Mrow = pieri({2,1}, {1}, P, Convention => "Row") + ///, + EXAMPLE lines /// + P = QQ[a,b,c]; + Mfil = pieri({2,1}, {1}, P, Convention => "Filling") + ///, + EXAMPLE lines /// + P = QQ[a,b,c]; + Mwey = pieri({2,1}, {1}, P, Convention => "Weyl") + ///, + "The Row and Weyl matrices share raw data (only the source/target bases are reinterpreted on the divided-power side); the Filling matrix is obtained by post-hoc basis change. All three have the same rank.", + EXAMPLE lines /// + P = QQ[a,b,c]; + Mrow = pieri({2,1}, {1}, P, Convention => "Row"); + Mfil = pieri({2,1}, {1}, P, Convention => "Filling"); + Mwey = pieri({2,1}, {1}, P, Convention => "Weyl"); + rank Mrow, rank Mfil, rank Mwey + ///, + "The Betti table of the cokernel is the same in every convention (the + resolutions are isomorphic up to an equivariant change of basis):", + EXAMPLE lines /// + P = QQ[a,b,c]; + betti res coker pieri({2,1}, {1}, P, Convention => "Row") + ///, + EXAMPLE lines /// + P = QQ[a,b,c]; + betti res coker pieri({2,1}, {1}, P, Convention => "Filling") + ///, + PARA{}, + "Multi-box horizontal strips: ", TT "boxes", + " can list more than one row index (with repeats), and the result is the + composition of single-box Pieri inclusions. The same row index can appear + repeatedly when row 1 has enough boxes; entries become degree-", + TEX "$d$", " polynomials.", + EXAMPLE lines /// + P = QQ[a,b,c]; + pieri({3,1}, {1,1}, P) -- removes 2 boxes from row 1; entries are deg 2 + ///, + EXAMPLE lines /// + P = QQ[a,b,c]; + pieri({3,2,1}, {1,3}, P) -- removes one box from row 1, then row 3 + ///, + "And ", TO symbolicForm, + " makes the basis-by-basis action of a multi-box map readable:", + EXAMPLE lines /// + P = QQ[a,b,c]; + symbolicForm pieri({3,1}, {1,1}, P) + /// + } + +document { + Key => {pureFree, (pureFree, List, PolynomialRing), [pureFree, Convention]}, + Headline => "computes a GL(V)-equivariant map whose resolution is pure, or the reduction mod p of such a map", + SeeAlso => {pieri}, + Usage => "pureFree(d, P)\npureFree(d, P, Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "d" => {ofClass List, ", a list of degrees (increasing numbers)"}, + "P" => {ofClass PolynomialRing, ", a polynomial ring over a field ", + TEX "$K$", " in ", TEX "$n$", " variables" }, + Convention => String => {", basis convention used internally by ", TO pieri, ". Defaults to ", TT "\"Row\"", "; the resulting Betti table does not depend on the choice."} + }, + Outputs => { + Matrix => {"A map whose cokernel has Betti diagram with degree sequence ", + TEX "$d$", " if ", TEX "$K$", + " has characteristic 0. If ", TEX "$K$", + " has positive characteristic ", TEX "$p$", + ", then the corresponding map is calculated over ", + TEX "$\\mathbb{Q}$", " and is lifted to a ", TEX "$\\mathbb{Z}$", + "-form which is then reduced mod ", TEX "$p$", "." } + }, + "The function translates the data of a degree sequence ", TEX "$d$", + " for a desired pure free resolution into the data of a Pieri map + according to the formula of Eisenbud-Fl\\o ystad-Weyman and then applies the function ", + TO pieri, + ". The ", TT "Convention", " option is forwarded to that internal Pieri call so that the resulting matrix is presented in the user's preferred basis convention; the Betti table of the resulting resolution is the same in all conventions.", + EXAMPLE lines /// + betti res coker pureFree({0,1,2,4}, QQ[a,b,c]) -- degree sequence {0,1,2,4} + betti res coker pureFree({0,1,2,4}, ZZ/2[a,b,c]) -- same map, but reduced mod 2 + betti res coker pureFree({0,1,2,4}, GF(4)[a,b,c]) -- can also use non prime fields + ///, + EXAMPLE lines /// + -- The Betti table is independent of the chosen convention: + P = QQ[a,b,c]; + bettiR = betti res coker pureFree({0,1,2,4}, P, Convention => "Row"); + bettiF = betti res coker pureFree({0,1,2,4}, P, Convention => "Filling"); + bettiR == bettiF + /// + } + +document { + Key => {schurRank, (schurRank, ZZ, List)}, + Headline => "computes the dimension of the irreducible GL(QQ^n) representation associated to a partition", + SeeAlso => standardTableaux, + Usage => "schurRank(n, mu)", + Inputs => { + "n" => {ofClass ZZ, ", the size of the matrix group ", + TEX "$GL(\\mathbb{Q}^n)$"}, + "mu" => {ofClass List, ", a partition ", + TEX "$(\\mu_1, \\ldots, \\mu_r)$", " where ", TEX "$\\mu_i$", + " is the number of boxes in the ", TEX "$i$", "th row"} + }, + Outputs => { + ZZ => {"The dimension of the irreducible ", + TEX "$GL(\\mathbb{Q}^n)$", " representation associated to ", + TEX "$\\mu$"} + }, + "The dimension is computed using the determinantal formula given by the Weyl character formula.", + EXAMPLE lines /// + schurRank(5, {4,3}) -- should be 560 + /// + } + +document { + Key => {lrTableaux, (lrTableaux, List, List, List)}, + Headline => "enumerate Littlewood-Richardson tableaux", + Usage => "lrTableaux(lambda, mu, nu)", + Inputs => { + "lambda" => {ofClass List, ", a partition"}, + "mu" => {ofClass List, ", a partition contained in lambda"}, + "nu" => {ofClass List, ", a partition with |nu| = |lambda| - |mu|"} + }, + Outputs => { + List => {"the list of LR fillings of the skew shape lambda/mu with content nu"} + }, + "An LR tableau is a semistandard skew tableau (rows weakly increasing, columns strictly increasing) ", + "whose reverse reading word is a lattice word. Each filling is returned as a list of rows; row ", + TEX "$i$", " has length ", TEX "$\\lambda_i - \\mu_i$", " and ", + "entries in ", TEX "$\\{0, \\ldots, \\#\\nu - 1\\}$", + " (so label 0 corresponds to row 1 of ", TEX "$\\nu$", ", etc.). ", + "The number of LR tableaux equals the Littlewood-Richardson coefficient ", + TEX "$c^\\lambda_{\\mu,\\nu}$", ".", + EXAMPLE lines /// + #lrTableaux({3,2,1}, {2,1}, {2,1}) -- = c^{(3,2,1)}_{(2,1),(2,1)} = 2 + #lrTableaux({2,1}, {1}, {1,1}) -- = 1 + ///, + SeeAlso => lrMap + } + +document { + Key => {lrMap, (lrMap, Sequence, List, ZZ), (lrMap, Sequence, List, PolynomialRing), [lrMap, Convention]}, + Headline => "GL(V)-equivariant Littlewood-Richardson inclusion", + Usage => "lrMap((lambda, mu, nu), Q, n)\nlrMap((lambda, mu, nu), Q, P)\nlrMap(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(lambda, mu, nu)" => {ofClass Sequence, ", three partitions with ", + TEX "$|\\lambda| = |\\mu| + |\\nu|$", " and ", TEX "$\\mu$", + " contained in ", TEX "$\\lambda$"}, + "Q" => {ofClass List, ", an LR tableau of shape ", + TEX "$\\lambda/\\mu$", " with content ", TEX "$\\nu$", + ", as returned by ", TO lrTableaux}, + "n" => {ofClass ZZ, ", the dimension of ", TEX "$V$", + " (or pass a ", TO PolynomialRing, " whose ", TT "numgens", + " is used)"}, + Convention => String => {", basis convention for both the source ", + TEX "$S_\\lambda V$", " and the target ", + TEX "$S_\\nu V \\otimes S_\\mu V$", ". ", + TT "\"Row\"", " (default), ", TT "\"Filling\"", ", or ", TT "\"Weyl\"", "."} + }, + Outputs => { + Matrix => {"a matrix over ", TEX "$\\mathbb{Q}$", + " whose columns index a basis of ", TEX "$S_\\lambda V$", + " and rows index a basis of ", + TEX "$S_\\nu V \\otimes S_\\mu V$"} + }, + "Builds the ", TEX "$GL(V)$", "-equivariant inclusion ", + TEX "$\\Psi_Q \\colon S_\\lambda V \\to S_\\nu V \\otimes S_\\mu V$", + " associated to the LR tableau ", TT "Q", + ". The map is constructed by iterating row-Pieri inclusions ", + TEX "$S_{\\lambda^{(a)}} V \\to \\mathrm{Sym}^{\\nu_a} V \\otimes S_{\\lambda^{(a-1)}} V$", + " in the order ", TEX "$a = r, r-1, \\ldots, 1$", + ", then projecting the ", + TEX "$\\mathrm{Sym}$", " tensor side onto ", TEX "$S_\\nu V$", + " via straightening.", + PARA{}, + "The columns of the output are indexed by ", TT "standardTableaux(n, lambda)", " (or the corresponding Filling / WeylFilling basis if a non-default ", TT "Convention", " is chosen). ", + "The rows are indexed by pairs ", TEX "$(T_\\nu, T_\\mu)$", + " in lex order: row ", TEX "$i \\cdot \\#S_\\mu + j$", + " corresponds to ", + TEX "$(T_\\nu = N\\nu\\#i, T_\\mu = N\\mu\\#j)$", " where ", + TT "Nnu = standardTableaux(n, nu)", " and ", + TT "Nmu = standardTableaux(n, mu padded to length #lambda)", + ". When ", TT "Convention => \"Filling\"", + " is used, the equivariant change of basis ", + TO pmToFillingMatrix, " is applied on each side, and the rank is preserved.", + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); -- a multiplicity-1 example + Q = (lrTableaux shapes)#0; + Mrow = lrMap(shapes, Q, 3, Convention => "Row") + ///, + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + Mfilling = lrMap(shapes, Q, 3, Convention => "Filling") + ///, + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + Mweyl = lrMap(shapes, Q, 3, Convention => "Weyl") + ///, + "All three are GL-equivariant injections of ", TEX "$S_\\lambda V$", + " into ", TEX "$S_\\nu V \\otimes S_\\mu V$", + ". The Row and Weyl matrices have the same raw entries (only the source/target bases are reinterpreted on the divided-power side); the Filling matrix is obtained by post-hoc basis change. All three have the same rank.", + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + Mrow = lrMap(shapes, Q, 3, Convention => "Row"); + Mfilling = lrMap(shapes, Q, 3, Convention => "Filling"); + Mweyl = lrMap(shapes, Q, 3, Convention => "Weyl"); + (rank Mrow, rank Mfilling, rank Mweyl) + ///, + "When ", TEX "$c^\\lambda_{\\mu,\\nu} > 1$", + ", distinct LR tableaux ", TEX "$Q$", + " give linearly independent inclusions; the joint matrix has rank ", + TEX "$c^\\lambda_{\\mu,\\nu} \\cdot \\dim S_\\lambda V$", + ". The canonical multiplicity-2 example is ", + TEX "$c^{(3,2,1)}_{(2,1),(2,1)} = 2$", ":", + EXAMPLE lines /// + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); -- two distinct LR tableaux + M1 = lrMap(({3,2,1}, {2,1}, {2,1}), Qs#0, 3); + M2 = lrMap(({3,2,1}, {2,1}, {2,1}), Qs#1, 3); + rank M1, rank M2 -- each = dim S_(3,2,1) = 8 + rank(M1 | M2) == 16 -- 2 * 8: M1 and M2 are linearly independent + ///, + "Use ", TO symbolicForm, " to read off the action of ", + TEX "$\\Psi_Q$", " on each standard tableau of ", + TEX "$S_\\lambda V$", + ", as a sum over basis pairs of ", TEX "$S_\\nu V \\otimes S_\\mu V$", ":", + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + symbolicForm lrMap(shapes, Q, 3) + ///, + SeeAlso => {lrTableaux, pieri, straighten, applyLR, verifyWellDefined, symbolicForm} + } + +document { + Key => {applyLR, (applyLR, Sequence, List, BasicList, ZZ), [applyLR, Convention]}, + Headline => "apply Psi_Q to a single tableau of shape lambda", + Usage => "applyLR((lambda, mu, nu), Q, T, n)\napplyLR(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(lambda, mu, nu)" => {ofClass Sequence, ", three partitions specifying the LR map"}, + "Q" => {ofClass List, ", an LR tableau as returned by ", TO lrTableaux}, + "T" => {ofClass BasicList, ", a tableau of shape ", TEX "$\\lambda$", + ". May be supplied as a ", TT "List", " or as a ", + TT "Filling", ". In ", TT "\"Row\"", "/", TT "\"Weyl\"", + " convention a ", TT "List", " is interpreted as PM row form; + in ", TT "\"Filling\"", " convention a ", TT "List", + " is interpreted as column form (and wrapped automatically). + A ", TT "Filling", " is always treated as column form. ", + TT "T", " need not be standard; it is straightened internally + using the convention's straightening relations."}, + "n" => {ofClass ZZ, ", the dimension of ", TEX "$V$"}, + Convention => String => {", basis convention used to interpret ", + TT "T", " and label the output ", + TEX "$T_\\nu$", ", ", TEX "$T_\\mu$", "."} + }, + Outputs => { + List => {"a list of triples ", TT "{c, T_nu, T_mu}", + " representing the image as the sum ", + TEX "$\\sum c_i \\cdot T_\\nu^{(i)} \\otimes T_\\mu^{(i)}$", + " in ", TEX "$S_\\nu V \\otimes S_\\mu V$"} + }, + "Computes the image of a single basis vector (or, more generally, of any + tableau, which is first straightened into the SSYT basis of ", + TEX "$S_\\lambda V$", + ") under the LR inclusion ", TEX "$\\Psi_Q$", + ". The output is intended for human + reading or interactive exploration when the full ", TO lrMap, " matrix is + too large to display. Use ", TO displayLRImage, " for pretty-printing.", + PARA{}, + "When ", TT "Convention => \"Filling\"", " is chosen, the input ", + TT "T", " must be a SchurFunctors ", TT "Filling", + ", and the output uses Fillings on both target sides. Straightening uses SchurFunctors' column-Garnir relations. When ", + TT "Convention => \"Row\"", " or ", TT "\"Weyl\"", ", ", TT "T", + " is a list of rows and PieriMaps' row-Garnir straightening is applied.", + EXAMPLE lines /// + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); + T = {{0,0,0},{1,1},{2}}; -- highest-weight vector of S_(3,2,1) QQ^3 + applyLR(({3,2,1}, {2,1}, {2,1}), Qs#0, T, 3) + displayLRImage applyLR(({3,2,1}, {2,1}, {2,1}), Qs#0, T, 3) + ///, + EXAMPLE lines /// + -- Non-standard input: row-Garnir straightening before applying. + Tnonstd = {{1,0},{2}}; -- row 0 not weakly-increasing + applyLR(({2,1}, {1}, {1,1}), (lrTableaux({2,1},{1},{1,1}))#0, Tnonstd, 3) + ///, + SeeAlso => {lrMap, lrTableaux, displayLRImage, verifyWellDefined} + } + +document { + Key => {displayLRImage, (displayLRImage, List)}, + Headline => "pretty-print the result of applyLR", + Usage => "displayLRImage(image)", + Inputs => { + "image" => {ofClass List, ", as returned by ", TO applyLR} + }, + Outputs => { + Net => {"a stacked display of the symbolic sum ", + TEX "$\\sum c_i \\cdot T_\\nu^{(i)} \\otimes T_\\mu^{(i)}$"} + }, + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + T = (standardTableaux(3, {2,1}))#0; + img = applyLR(shapes, Q, T, 3); + displayLRImage img + ///, + SeeAlso => {applyLR, lrMap} + } + +document { + Key => Convention, + Headline => "tableau basis convention used by PieriMaps' overhauled functions", + "An option accepted by ", TO pieri, ", ", TO lrMap, ", ", TO applyLR, ", ", TO pureFree, ", and ", TO verifyWellDefined, + ". Possible values:", + UL { + {TT "\"Row\"", " (default): the original PieriMaps row-form basis. Source/target Schur modules are indexed by SSYTs as lists of weakly-increasing rows."}, + {TT "\"Filling\"", ": the SchurFunctors column-form basis. Source/target are indexed by ", TT "Filling", "s, with column-Garnir straightening relations. The matrix is obtained by post-hoc basis change with the equivariant iso ", TO pmToFilling, "."}, + {TT "\"Weyl\"", ": the divided-power row-form basis. Source/target are indexed by ", TT "WeylFilling", "s. Numerically the same data as the Row matrix; it is just that the bases are interpreted on the Weyl (divided-power) side rather than the Schur side."} + }, + "Use ", TO verifyWellDefined, " to verify that the chosen-convention map respects the appropriate straightening relations.", + EXAMPLE lines /// + P = QQ[a,b,c]; + pieri({2,1}, {1}, P, Convention => "Row") + pieri({2,1}, {1}, P, Convention => "Filling") + ///, + SeeAlso => {pmToFilling, fillingToPM, pmToWeyl, weylToPM, verifyWellDefined} + } + +document { + Key => {pmToFilling}, + Headline => "convert a PieriMaps row-form tableau to SchurFunctors column-form Fillings", + Usage => "pmToFilling T", + Inputs => { + "T" => {ofClass List, ", a PieriMaps row-form tableau (list of weakly-increasing rows of the same shape)"} + }, + Outputs => { + HashTable => {"the image of ", TT "T", + " under the symmetrize-then-antisymmetrize equivariant map ", + TEX "$\\mathrm{Sym}^\\lambda V \\to \\wedge^{\\lambda'} V$", + ", expressed as a sum of standard ", TT "Filling", + "s with rational coefficients (after SchurFunctors straightening)."} + }, + "PieriMaps' row-form representation ", + TEX "$S_\\lambda V \\subset \\mathrm{Sym}^{\\lambda_1} V \\otimes \\cdots$", + " and SchurFunctors' column-form representation ", + TEX "$S_\\lambda V \\subset \\wedge^{\\lambda'_1} V \\otimes \\cdots$", + " are equivariant isomorphisms (over ", TEX "$\\mathbb{Q}$", + "). ", + "This function realizes that iso explicitly: each PM monomial ", + TEX "$T = (m_1, \\ldots, m_r)$", " is symmetrized into ", + TEX "$V^{\\otimes |\\lambda|}$", + " via the row-symmetrizer (with normalization ", + TEX "$1/\\prod \\lambda_i!$", + "), reordered by columns, and then projected to wedges (sort ", + TEX "$+$", + " sign) per column. The result is then straightened into the standard ", + TT "Filling", " basis via SchurFunctors' ", TT "straighten", ".", + PARA{}, + "Round-tripping ", TT "pmToFilling", " then ", TO fillingToPM, + " gives a uniform shape-dependent scalar ", + TEX "$c_\\lambda \\cdot \\mathrm{id}$", + " (the Young symmetrizer normalization).", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + pmToFilling {{0,1},{2}} + pmToFilling {{0,0,0},{1,1},{2}} -- highest-weight vector of S_(3,2,1) Q^3 + ///, + SeeAlso => {fillingToPM, pmToWeyl, pmToFillingMatrix, Convention} + } + +document { + Key => {fillingToPM}, + Headline => "convert a SchurFunctors column-form Filling to PieriMaps row-form tableaux", + Usage => "fillingToPM F", + Inputs => { + "F" => {TT "Filling", ", a SchurFunctors column-form Filling"} + }, + Outputs => { + HashTable => {"the image of ", TT "F", + " under the antisymmetrize-then-symmetrize equivariant map ", + TEX "$\\wedge^{\\lambda'} V \\to \\mathrm{Sym}^\\lambda V$", + ", expressed as a sum of standard PM-form tableaux with rational coefficients (after PieriMaps straightening)."} + }, + "Inverse (up to a uniform scalar) of ", TO pmToFilling, + ". Each column of ", TT "F", " is wedge-expanded into ", + TEX "$V^{\\otimes |\\lambda|}$", + " via the column antisymmetrizer (signed sum over permutations, with normalization ", + TEX "$1/\\prod \\lambda'_j!$", + "), reordered by rows, and projected to symmetric (sort) per row. The result is straightened into the standard PM basis.", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + F = new Filling from {{0,2},{1}}; + fillingToPM F + ///, + SeeAlso => {pmToFilling, weylToFilling, fillingToPMMatrix} + } + +document { + Key => {pmToWeyl}, + Headline => "convert a PieriMaps row-form tableau to a SchurFunctors WeylFilling", + Usage => "pmToWeyl T", + Inputs => {"T" => {ofClass List, ", a PieriMaps row-form tableau"}}, + Outputs => {{TT "WeylFilling", + " with the same row-data (a SchurFunctors basis element of ", + TEX "$K_\\lambda V$", ")"}}, + "PM tableaux and ", TT "WeylFilling", "s have the same data layout (rows weakly increasing). This is purely a relabel: ", + TT "pmToWeyl", " wraps a list of rows as a WeylFilling (interpreting it as a basis element of the divided-power module ", + TEX "$K_\\lambda V$", "), and ", TO weylToPM, + " unwraps. The conversion is reversible.", + "Requires the updated SchurFunctors package that exports ", TT "weyl", " and ", TT "WeylFilling", ".", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + pmToWeyl {{0,1},{2}} + weylToPM pmToWeyl {{0,1},{2}} + ///, + SeeAlso => {weylToPM, pmToFilling, weylToFilling} + } + +document { + Key => {weylToPM}, + Headline => "convert a SchurFunctors WeylFilling to a PieriMaps row-form tableau", + Usage => "weylToPM W", + Inputs => {"W" => {TT "WeylFilling"}}, + Outputs => {List => {"a list of rows (PM-form tableau) with the same data"}}, + "Inverse of ", TO pmToWeyl, ". Both WeylFilling and PM-form share the row-of-multisets data layout, so this is purely an unwrapping.", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + W = pmToWeyl {{0,1},{2}} + weylToPM W + ///, + SeeAlso => {pmToWeyl, fillingToWeyl} + } + +document { + Key => {weylToFilling}, + Headline => "convert a WeylFilling to a SchurFunctors Filling (compose pmToFilling with weylToPM)", + Usage => "weylToFilling W", + Inputs => {"W" => {TT "WeylFilling"}}, + Outputs => {HashTable => {"image as a sum of standard ", TT "Filling", "s after straightening"}}, + EXAMPLE lines /// + needsPackage "SchurFunctors"; + W = pmToWeyl {{0,1},{2}}; + weylToFilling W + ///, + SeeAlso => {pmToFilling, weylToPM, fillingToWeyl} + } + +document { + Key => {fillingToWeyl}, + Headline => "convert a Filling to a sum of WeylFillings (compose pmToWeyl with fillingToPM)", + Usage => "fillingToWeyl F", + Inputs => {"F" => {TT "Filling"}}, + Outputs => {HashTable => {"a HashTable {WeylFilling => coeff}"}}, + EXAMPLE lines /// + needsPackage "SchurFunctors"; + F = new Filling from {{0,2},{1}}; + fillingToWeyl F + ///, + SeeAlso => {pmToWeyl, fillingToPM, weylToFilling} + } + +document { + Key => {pmToFillingMatrix}, + Headline => "QQ matrix of the PM-to-Filling change of basis on a Schur module", + Usage => "pmToFillingMatrix(mu, n)", + Inputs => { + "mu" => {ofClass List, ", a partition (the Schur shape)"}, + "n" => {ofClass ZZ, ", the dimension of ", TEX "$V$"} + }, + Outputs => { + Matrix => {"a ", TEX "$\\mathbb{Q}$", + " matrix; rows indexed by ", TT "Filling", + "s of column heights ", + TEX "$\\mu^t$", + ", columns indexed by PieriMaps' standard tableaux of shape ", + TEX "$\\mu$", ". Entry ", TEX "$(i, j)$", + " is the coefficient of Filling ", TEX "$i$", " in ", + TT "pmToFilling(PMtab_j)", "."} + }, + "This matrix realizes the equivariant change of basis between PieriMaps' row-form basis of ", + TEX "$S_\\mu V$", + " and SchurFunctors' column-form basis. It is invertible over ", + TEX "$\\mathbb{Q}$", + " (up to a shape-dependent overall scalar) and is the workhorse behind ", + TO [pieri, Convention], "/", TO [lrMap, Convention], + " for the Filling convention.", + PARA{}, + "Source/target basis labels are attached so ", TO symbolicForm, + " can display the change-of-basis on each PM tableau:", + EXAMPLE lines /// + symbolicForm pmToFillingMatrix({2,1}, 3) + ///, + SeeAlso => {fillingToPMMatrix, pmToFilling, symbolicForm} + } + +document { + Key => {fillingToPMMatrix}, + Headline => "QQ matrix of the Filling-to-PM change of basis on a Schur module", + Usage => "fillingToPMMatrix(mu, n)", + Inputs => { + "mu" => {ofClass List, ", a partition"}, + "n" => {ofClass ZZ} + }, + Outputs => { + Matrix => {"a ", TEX "$\\mathbb{Q}$", + " matrix; rows = PM tableaux of ", TEX "$\\mu$", + ", cols = Fillings of ", TEX "$\\mu^t$", ". Entry ", + TEX "$(i, j)$", " = coefficient of PM tableau ", TEX "$i$", + " in ", TT "fillingToPM(filling_j)", "."} + }, + "Inverse direction of ", TO pmToFillingMatrix, "; same equivariant iso, transposed convention.", + EXAMPLE lines /// + symbolicForm fillingToPMMatrix({2,1}, 3) + ///, + SeeAlso => {pmToFillingMatrix, fillingToPM, symbolicForm} + } + +document { + Key => {pieriColumn, (pieriColumn, List, List, PolynomialRing), [pieriColumn, Convention]}, + Headline => "native column-form (vertical-strip) Pieri inclusion", + Usage => "pieriColumn(mu, cols, P)", + Inputs => { + "mu" => {ofClass List, ", a partition"}, + "cols" => {ofClass List, ", a list of 1-indexed column indices. One box is removed from the bottom of each listed column; the cumulative removal must form a vertical strip (no two boxes in the same row)."}, + "P" => {ofClass PolynomialRing, ", a SkewCommutative ", + TEX "$\\mathbb{Q}$", "-algebra in ", TEX "$n$", + " variables. Its generators carry the wedge factor ", + TEX "$\\wedge^{|\\mathrm{cols}|} V$", + " on the output side."} + }, + Outputs => { + Matrix => {"a matrix from ", TEX "$S_\\mu V$", " to ", + TEX "$\\wedge^{|\\mathrm{cols}|} V \\otimes S_\\lambda V$", + ", with rows indexed by standard ", TT "Filling", "s of ", + TEX "$\\lambda$", + " and columns indexed by standard Fillings of ", TEX "$\\mu$", + "."} + }, + "The dual of ", TO pieri, ". Where ", TT "pieri(mu, rows, P)", + " removes a horizontal strip and lands in ", + TEX "$\\mathrm{Sym}^k V$", ", ", TT "pieriColumn(mu, cols, P)", + " removes a vertical strip and lands in ", TEX "$\\wedge^k V$", ". ", + "Both maps are unique (up to scalar) ", TEX "$GL(V)$", + "-equivariant inclusions ", + TEX "$S_\\mu V \\to (\\,?\\text{-tensor}\\,) \\otimes S_\\lambda V$", + "; ", TT "pieri", " uses Olver's formula with ", + TEX "$c_J = \\prod (\\mu_{J_q} - \\mu_k + k - J_q)$", + " on the symmetric (", TEX "$\\mathrm{Sym}$", + ") side, while ", TT "pieriColumn", " uses the dual formula with ", + TEX "$c'_J = \\prod (\\mu'_k - \\mu'_{J_q} + J_q - k)$", + " on the wedge side, where ", TEX "$\\mu'$", + " is the conjugate partition. The extra sign in ", TEX "$c'_J$", + " relative to a literal transpose accounts for the wedge anticommutativity.", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + P = QQ[e_0..e_3, SkewCommutative => true]; + M = pieriColumn({3,2,1}, {1}, P); + numRows M, numColumns M + ///, + EXAMPLE lines /// + -- Multi-strip: remove a vertical strip of length 2. + P = QQ[e_0..e_3, SkewCommutative => true]; + M = pieriColumn({3,2,1}, {1,2}, P); + numRows M, numColumns M + ///, + SeeAlso => {pieri, fillingToPM, [pieri, Convention]} + } + +document { + Key => {verifyWellDefined, (verifyWellDefined, Sequence, List, ZZ), + (verifyWellDefined, List, List, PolynomialRing), + [verifyWellDefined, Convention], [verifyWellDefined, Verbose], + [verifyWellDefined, Direction]}, + Headline => "verify an LR map respects the chosen convention's straightening relations", + Usage => "verifyWellDefined((lambda, mu, nu), Q, n, Convention => ...)", + Inputs => { + "(lambda, mu, nu)" => {ofClass Sequence, ", three partitions for the LR data"}, + "Q" => {ofClass List, ", an LR tableau as returned by ", TO lrTableaux}, + "n" => {ofClass ZZ, ", the dimension of ", TEX "$V$"}, + Convention => String => {", basis convention to check"}, + Verbose => Boolean => {", whether to print per-test progress (default true)"} + }, + Outputs => {Boolean => {"true if all generated tests pass, false otherwise"}}, + "For skeptical users. Generates a few non-standard tableaux of shape lambda in the chosen basis convention, then for each:", + UL { + "(a) computes ", TT "applyLR(shapes, Q, T, n, Convention => conv)", " directly (which uses the convention's straightening internally),", + "(b) independently straightens T into a sum of standard tableaux,", + "(c) computes the same sum applied to the standards,", + "(d) asserts (a) and (c) agree." + }, + "If the matrix were silently using the wrong straightening relations (e.g. PieriMaps row-Garnir on a SchurFunctors column-form Filling input), the check fails and prints the offending case.", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); + verifyWellDefined(({3,2,1},{2,1},{2,1}), Qs#0, 3, Convention => "Filling") + verifyWellDefined(({3,2,1},{2,1},{2,1}), Qs#0, 3, Convention => "Row") + ///, + SeeAlso => {lrMap, applyLR, pmToFilling, Convention} + } + +document { + Key => {dualPieri, (dualPieri, List, List, PolynomialRing), [dualPieri, Convention]}, + Headline => "GL-equivariant projection Sym^d V ⊗ S_lambda V --> S_mu V", + Usage => "dualPieri(mu, boxes, P)\ndualPieri(mu, boxes, P, Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "mu" => {ofClass List, + ", a partition (the target shape): ", + TEX "$\\mu = \\lambda + (\\text{horizontal $d$-strip})$", + " where ", TEX "$d = \\#\\mathrm{boxes}$", "."}, + "boxes" => {ofClass List, + ", a list of 1-indexed row indices recording the horizontal strip ", + TEX "$\\mu/\\lambda$", ". Same convention as ", TO pieri, "."}, + "P" => {ofClass PolynomialRing, + ", a polynomial ring whose generators carry the symmetric factor ", + TEX "$\\mathrm{Sym}^d V$", " on the source side."}, + Convention => String => {", basis convention for both source and target Schur factors. ", + TT "\"Row\"", " (default), ", TT "\"Filling\"", ", or ", TT "\"Weyl\"", "."} + }, + Outputs => { + Matrix => {"a ", TEX "$K$", "-matrix of size ", + TEX "$\\dim S_\\mu V \\times \\bigl(\\dim S_\\lambda V \\cdot \\binom{n + d - 1}{d}\\bigr)$", + ". Columns are indexed by pairs ", TEX "$(T_\\lambda, \\alpha)$", + " (a standard tableau of ", TEX "$\\lambda$", + " paired with a degree-", TEX "$d$", " monomial of ", + TT "P", "), in lex order with ", TEX "$T_\\lambda$", + " outer and ", TEX "$\\alpha$", " inner."} + }, + "Returns the ", TEX "$GL_n$", + "-equivariant projection ", + TEX "$\\mathrm{Sym}^d V \\otimes S_\\lambda V \\to S_\\mu V$", + " (unique up to scalar by Schur's lemma). By Pieri's rule, ", + TEX "$\\mathrm{Sym}^d V \\otimes S_\\lambda V$", + " decomposes as a direct sum of ", TEX "$S_{\\mu'}$", " over all ", + TEX "$\\mu' = \\lambda + (\\text{horizontal $d$-strip})$", + "; this function returns the projection onto the summand selected by ", + TEX "$\\mu$", ".", + PARA{}, + "The construction stacks the inclusion matrices ", TO pieri, + " for every addable horizontal ", TEX "$d$", + "-strip into a square invertible matrix, then reads off the rows of its inverse corresponding to ", + TEX "$S_\\mu$", ". This realizes ", + TEX "$\\mathrm{dualPieri} \\circ \\mathrm{pieri} = \\mathrm{Id}$", + " on ", TEX "$S_\\mu V$", " exactly.", + PARA{}, + "When ", TT "Convention => \"Filling\"", + ", the source ", TEX "$S_\\lambda$", " factor and target ", + TEX "$S_\\mu$", + " are expressed in the SchurFunctors column-form basis; ", + TT "\"Weyl\"", + " uses the divided-power Weyl-module basis (matrix data is identical to ", + TT "\"Row\"", ").", + EXAMPLE lines /// + P = QQ[a,b,c]; + N = dualPieri({3,2,1}, {1}, P); + M = pieri({3,2,1}, {1}, P); + numRows N == numColumns M and numRows M == 3 + ///, + EXAMPLE lines /// + P = QQ[a,b,c,d]; -- interior-row removal mu={3,2,1}, k=2, lambda={3,1,1} + N = dualPieri({3,2,1}, {2}, P); + numRows N, numColumns N + ///, + PARA{}, + BOLD "Performance.", " ", TO dualPieri, " caches the stacked block + matrix used to invert the GL_n decomposition, keyed by ", + TEX "$(\\lambda, d, n, K, \\mathrm{Convention})$", + ". The first call at a given key builds every Pieri inclusion ", + TEX "$S_{\\mu'}\\to S_\\lambda \\otimes \\mathrm{Sym}^d V$", + " for ", TEX "$\\mu'$", " ranging over all addable horizontal ", + TEX "$d$", "-strips and stacks them into one square matrix ", + TEX "$A$", + "; subsequent calls (for sister summands ", TEX "$\\mu$", + " of the same ambient tensor product) reuse ", TEX "$A$", + " and only run a single back-solve, so they cost less than the + corresponding ", TO pieri, " inclusion. The selected rows of ", + TEX "$A^{-1}$", " are computed via ", TT "solve(transpose A, E)", + " (", TT "E", " a small standard-basis block) rather than by + forming the full inverse. ", TT "boxes", + " is normalized to canonical (sorted ascending) order; pass the + same canonical order to ", TO pieri, " for the round-trip ", + TEX "$\\mathrm{dualPieri} \\circ \\mathrm{pieri} = \\mathrm{Id}$", + ".", + SeeAlso => {pieri, applyDualPieri, dualLR, verifyEquivariant, Convention} + } + +document { + Key => {applyDualPieri, (applyDualPieri, Sequence, RingElement, BasicList, ZZ), [applyDualPieri, Convention]}, + Headline => "apply dualPieri to a single basis pair (poly, T_lambda)", + Usage => "applyDualPieri((mu, boxes), poly, T, n)\napplyDualPieri(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(mu, boxes)" => {ofClass Sequence, ", the same data passed to ", TO dualPieri}, + "poly" => {ofClass RingElement, + ", a homogeneous polynomial of degree ", + TEX "$d = \\#\\mathrm{boxes}$", + " in the polynomial ring ", TT "P", " (an element of ", + TEX "$\\mathrm{Sym}^d V$", ")"}, + "T" => {ofClass BasicList, ", a tableau of shape ", + TEX "$\\lambda$", ". Accepted as either a ", TT "List", + " or a ", TT "Filling", ". In ", TT "\"Row\"", "/", + TT "\"Weyl\"", " convention a ", TT "List", + " is the PM row form; in ", TT "\"Filling\"", " convention a ", + TT "List", " is column form (auto-wrapped). A ", TT "Filling", + " is always treated as column form. Non-standard inputs are straightened first using the convention's relations."}, + "n" => {ofClass ZZ, ", ", TEX "$\\dim V$"}, + Convention => String => {", basis for source ", TEX "$S_\\lambda$", + " and target ", TEX "$S_\\mu$", "."} + }, + Outputs => { + List => {"a list of pairs ", TT "{coefficient, T_mu}", + " representing the image as a sum ", + TEX "$\\sum c_i \\cdot T_\\mu^{(i)}$", " in ", + TEX "$S_\\mu V$", ". Zero coefficients are dropped."} + }, + "Convenience wrapper for ", TO dualPieri, + ": evaluates the projection on a single source basis vector ", + TEX "$\\mathrm{poly} \\otimes T_\\lambda$", + " and returns a sparse representation of its image. Useful for hand-checks and interactive exploration when the full ", + TO dualPieri, " matrix is too large.", + PARA{}, + "Input flexibility: ", TT "T", " may be in either tableau type. For ", + TT "Convention => \"Row\"", ", a Filling input is converted via ", + TO fillingToPM, " before straightening; for ", + TT "Convention => \"Filling\"", ", a List input is converted via ", + TO pmToFilling, ". Non-standard tableaux straighten first.", + EXAMPLE lines /// + P = QQ[a,b,c]; + Tlam = {{0,0},{1}}; -- standard PM tableau of {2,1} + applyDualPieri(({3,1}, {1}), a, Tlam, 3) + ///, + EXAMPLE lines /// + -- Non-standard input is straightened first (row-Garnir). + P = QQ[a,b,c,d]; + applyDualPieri(({3,2,1}, {3}), a, {{1,0},{2}}, 4) + ///, + SeeAlso => {dualPieri, pieri, applyLR, applyDualLR, Convention} + } + +document { + Key => {dualLR, (dualLR, Sequence, List, ZZ), (dualLR, Sequence, List, PolynomialRing), [dualLR, Convention]}, + Headline => "GL-equivariant projection S_nu V ⊗ S_mu V --> S_lambda V at a chosen LR tableau", + Usage => "dualLR((lambda, mu, nu), Q, n)\ndualLR((lambda, mu, nu), Q, P)\ndualLR(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(lambda, mu, nu)" => {ofClass Sequence, ", three partitions with ", + TEX "$|\\lambda| = |\\mu| + |\\nu|$", " and ", + TEX "$S_\\lambda V$", " appearing in ", + TEX "$S_\\nu V \\otimes S_\\mu V$", " (i.e. ", + TEX "$c^\\lambda_{\\mu,\\nu} > 0$", ")"}, + "Q" => {ofClass List, ", an LR tableau of shape ", + TEX "$\\lambda/\\mu$", " with content ", TEX "$\\nu$", + " (as returned by ", TO lrTableaux, + ") singling out one of the ", TEX "$c^\\lambda_{\\mu,\\nu}$", + " copies of ", TEX "$S_\\lambda V$"}, + "n" => {ofClass ZZ, ", or pass a polynomial ring whose ", + TT "numgens", " is used"}, + Convention => String => {", basis convention for the three Schur factors."} + }, + Outputs => { + Matrix => {"a ", TEX "$\\mathbb{Q}$", "-matrix of size ", + TEX "$\\dim S_\\lambda V \\times (\\dim S_\\nu V \\cdot \\dim S_\\mu V)$", + ", the ", TEX "$GL_n$", + "-equivariant projection onto ", TT "Q", + "'s specific copy of ", TEX "$S_\\lambda V$", " inside ", + TEX "$S_\\nu V \\otimes S_\\mu V$"} + }, + "Returns the ", TEX "$GL_n$", + "-equivariant projection onto the LR-summand of ", + TEX "$S_\\nu V \\otimes S_\\mu V$", " indexed by ", TT "Q", + " (unique up to scalar by Schur's lemma). When the LR coefficient ", + TEX "$c^\\lambda_{\\mu,\\nu}$", + " exceeds 1, distinct LR tableaux ", TT "Q", + " give different projections, each onto its own copy of ", + TEX "$S_\\lambda V$", "; ", + TEX "$\\mathrm{dualLR}_Q \\circ \\mathrm{lrMap}_{Q'} = 0$", + " when ", TEX "$Q \\neq Q'$", ", and ", + TEX "$\\mathrm{dualLR}_Q \\circ \\mathrm{lrMap}_Q = \\mathrm{Id}$", + ".", + PARA{}, + "Construction: stack the LR inclusions ", TO lrMap, + " for every ", TEX "$(\\lambda', Q')$", " with ", + TEX "$c^{\\lambda'}_{\\mu,\\nu} > 0$", + " into a square invertible matrix on ", + TEX "$S_\\nu V \\otimes S_\\mu V$", + " (square by the LR/Pieri rule) and read off the rows of its inverse corresponding to ", + TEX "$(\\lambda, Q)$", ".", + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + M = lrMap(shapes, Q, 3); -- inclusion S_(2,1) V -> S_(1,1) V ⊗ S_(1) V + N = dualLR(shapes, Q, 3); -- projection back onto Q's copy + N * M == id_(QQ^(numColumns M)) + ///, + EXAMPLE lines /// + Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); -- multiplicity-2 case + M0 = lrMap(({3,2,1},{2,1},{2,1}), Qs#0, 3); + M1 = lrMap(({3,2,1},{2,1},{2,1}), Qs#1, 3); + N0 = dualLR(({3,2,1},{2,1},{2,1}), Qs#0, 3); + N0 * M0 == id_(QQ^(numColumns M0)) -- recovers Q_0's copy + N0 * M1 == 0 -- annihilates Q_1's copy + ///, + SeeAlso => {lrMap, lrTableaux, applyDualLR, dualPieri, verifyEquivariant, Convention} + } + +document { + Key => {applyDualLR, (applyDualLR, Sequence, List, List, ZZ), [applyDualLR, Convention]}, + Headline => "apply dualLR to a single basis pair (T_nu, T_mu)", + Usage => "applyDualLR((lambda, mu, nu), Q, {T_nu, T_mu}, n)\napplyDualLR(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(lambda, mu, nu)" => {ofClass Sequence}, + "Q" => {ofClass List, ", an LR tableau"}, + "{T_nu, T_mu}" => {ofClass List, + ", a 2-element list containing the source basis pair (a tableau of shape ", + TEX "$\\nu$", " and a tableau of shape ", TEX "$\\mu$", + "). Each may be a ", TT "List", " or a ", TT "Filling", + ", interpreted in the natural form of the convention (see ", + TO applyPieri, "), independently."}, + "n" => {ofClass ZZ}, + Convention => String + }, + Outputs => { + List => {"list of pairs ", TT "{coefficient, T_lambda}", + " representing the image in ", TEX "$S_\\lambda V$"} + }, + "Note: the ", TT "{T_nu, T_mu}", " argument must be a List (not a Sequence), since M2 flattens nested Sequences in function-call arg lists but does not flatten Lists. Non-standard inputs are straightened first.", + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + applyDualLR(shapes, Q, {{{0},{1}}, {{0},{}}}, 3) + ///, + SeeAlso => {dualLR, lrMap, applyLR, applyDualPieri, Convention} + } + +document { + Key => {verifyEquivariant, + (verifyEquivariant, Matrix, List, List, PolynomialRing), + (verifyEquivariant, Matrix, Sequence, ZZ), + [verifyEquivariant, Convention], + [verifyEquivariant, Verbose], + [verifyEquivariant, Direction], + Direction + }, + Headline => "rigorously verify GL_n-equivariance of a Schur-rep matrix", + Usage => "verifyEquivariant(M, mu, boxes, P) -- pieri-type inclusion\nverifyEquivariant(M, shapes, n) -- lrMap-type inclusion\nverifyEquivariant(M, mu, boxes, P, Direction => \"Dual\") -- dualPieri-type projection\nverifyEquivariant(M, shapes, n, Direction => \"Dual\") -- dualLR-type projection", + Inputs => { + "M" => {ofClass Matrix, ", the matrix to test for ", TEX "$GL_n$", + "-equivariance"}, + Convention => String => {", basis convention used by ", TT "M", + "'s rows and columns. Default ", TT "\"Row\"", "."}, + Verbose => Boolean => {", if ", TT "true", + " print the first failing ", TEX "$(i, j, T)$", + " when the test fails. Default ", TT "false", "."}, + Direction => String => {", ", TT "\"Inclusion\"", " (default) or ", + TT "\"Dual\"", " to indicate the direction of the map."} + }, + Outputs => { + Boolean => {"true if ", TT "M", + " commutes with every Chevalley generator ", TEX "$E_{i,j}$", + " of ", TEX "$\\mathfrak{gl}_n$", " (", + TEX "$i \\neq j$", "); false otherwise"} + }, + "Tests whether a matrix ", TT "M", + " between Schur-functor representations is genuinely ", TEX "$GL_n$", + "-equivariant by checking that ", + TEX "$M \\circ \\rho_{\\mathrm{src}}(E_{i,j}) = \\rho_{\\mathrm{tgt}}(E_{i,j}) \\circ M$", + " for every Chevalley generator ", TEX "$E_{i,j}$", ". Since the ", + TEX "$E_{i,j}$ ($i \\neq j$)", + " generate the strictly upper- and strictly lower-triangular parts of ", + TEX "$\\mathfrak{gl}_n$", + ", and our matrices commute with the diagonal automatically (entries are weight-homogeneous), commutativity with all ", + TEX "$E_{i,j}$", " is equivalent to full ", TEX "$GL_n$", + "-equivariance.", + PARA{}, + "Action conventions: ", TEX "$E_{i,j}$", " acts on ", + TEX "$V = K^n$", " by ", + TEX "$x_l \\mapsto \\delta_{l,j}\\, x_i$", + " (i.e. the differential operator ", + TEX "$x_i\\, \\partial/\\partial x_j$", " on polynomials), on a Schur module ", + TEX "$S_\\lambda V$", " by replacing each entry equal to ", + TEX "$j$", " with ", TEX "$i$", + " in a tableau (summed over positions, then straightened), and on a tensor product by Leibniz.", + PARA{}, + "Four overloads cover the maps in this package:", + UL { + {TT "verifyEquivariant(M, mu, boxes, P)", " -- ", + TEX "$M \\colon S_\\mu V \\to \\mathrm{Sym}^d V \\otimes S_\\lambda V$", + " (or ", TEX "$\\wedge^d V$", + " for skew-commutative ", TT "P", + "), the inclusion produced by ", TO pieri, " or ", TO pieriColumn, "."}, + {TT "verifyEquivariant(M, (lambda, mu, nu), n)", " -- ", + TEX "$M \\colon S_\\lambda V \\to S_\\nu V \\otimes S_\\mu V$", + ", the inclusion produced by ", TO lrMap, "."}, + {TT "verifyEquivariant(M, mu, boxes, P, Direction => \"Dual\")", + " -- ", + TEX "$M \\colon \\mathrm{Sym}^d V \\otimes S_\\lambda V \\to S_\\mu V$", + ", the projection produced by ", TO dualPieri, "."}, + {TT "verifyEquivariant(M, (lambda, mu, nu), n, Direction => \"Dual\")", + " -- ", + TEX "$M \\colon S_\\nu V \\otimes S_\\mu V \\to S_\\lambda V$", + ", the projection produced by ", TO dualLR, "."} + }, + EXAMPLE lines /// + P = QQ[a,b,c]; + M = pieri({3,2,1}, {2}, P); + verifyEquivariant(M, {3,2,1}, {2}, P) + ///, + EXAMPLE lines /// + P = QQ[a,b,c]; -- verify the dual Pieri projection + N = dualPieri({3,2,1}, {2}, P); + verifyEquivariant(N, {3,2,1}, {2}, P, Direction => "Dual") + ///, + EXAMPLE lines /// + Q = (lrTableaux({3,2,1}, {2,1}, {2,1}))#0; -- multiplicity-2 LR inclusion + M = lrMap(({3,2,1}, {2,1}, {2,1}), Q, 3); + verifyEquivariant(M, ({3,2,1}, {2,1}, {2,1}), 3) + ///, + SeeAlso => {pieri, lrMap, dualPieri, dualLR, verifyWellDefined} + } + +document { + Key => {applyPieri, (applyPieri, Sequence, BasicList, PolynomialRing), + [applyPieri, Convention]}, + Headline => "apply pieri to a single source tableau T_mu", + Usage => "applyPieri((mu, boxes), T, P)\napplyPieri(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(mu, boxes)" => {ofClass Sequence, ", the same data passed to ", TO pieri}, + "T" => {ofClass BasicList, ", a tableau of shape ", TEX "$\\mu$", + ". May be a ", TT "List", " or a ", TT "Filling", ". In ", + TT "\"Row\"", "/", TT "\"Weyl\"", + " convention a ", TT "List", " is the PM row form; in ", + TT "\"Filling\"", " convention a ", TT "List", + " is column form (auto-wrapped). A ", TT "Filling", + " is always treated as column form."}, + "P" => {ofClass PolynomialRing, ", whose generators carry ", + TEX "$\\mathrm{Sym}^d V$"}, + Convention => String + }, + Outputs => { + List => {"a list of pairs ", TT "{poly, T_lambda}", + " where each poly is a homogeneous degree-", TEX "$d$", + " form and ", TEX "$T_\\lambda$", + " is a standard tableau of ", TEX "$\\lambda$", + "; the image is ", + TEX "$\\sum \\mathrm{poly}_i \\otimes T_{\\lambda,i}$", " in ", + TEX "$\\mathrm{Sym}^d V \\otimes S_\\lambda V$"} + }, + "Convenience point-evaluator for ", TO pieri, + ": applies the inclusion map to a single source basis vector ", + TT "T", + " and returns its image as a sparse representation. Non-standard inputs are straightened first.", + EXAMPLE lines /// + P = QQ[a,b,c]; + T = (standardTableaux(3, {3,2,1}))#0; + applyPieri(({3,2,1}, {1}), T, P) + ///, + SeeAlso => {pieri, applyDualPieri, applyLR, applyPieriColumn, Convention} + } + +document { + Key => {applyPieriColumn, + (applyPieriColumn, Sequence, BasicList, PolynomialRing), + [applyPieriColumn, Convention]}, + Headline => "apply pieriColumn to a single source tableau T_mu", + Usage => "applyPieriColumn((mu, cols), T, P)\napplyPieriColumn(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(mu, cols)" => {ofClass Sequence, ", the same data passed to ", TO pieriColumn}, + "T" => {ofClass BasicList, ", a tableau of shape ", TEX "$\\mu$", + ". May be a ", TT "List", " or a ", TT "Filling", + " (interpreted in the natural form of the convention; see ", + TO applyPieri, ")."}, + "P" => {ofClass PolynomialRing, ", a SkewCommutative ring whose generators carry ", + TEX "$\\wedge^d V$"}, + Convention => String + }, + Outputs => { + List => {"a list of pairs ", TT "{wedge_poly, T_lambda}", + " representing the image in ", + TEX "$\\wedge^d V \\otimes S_\\lambda V$"} + }, + "Column-form analogue of ", TO applyPieri, ".", + EXAMPLE lines /// + needsPackage "SchurFunctors"; + P = QQ[e_0..e_2, SkewCommutative => true]; + sfStdTab = value (SchurFunctors#"private dictionary"#"standardTableaux"); + F = (sfStdTab(3, {2,1}))#0; + applyPieriColumn(({2,1}, {1}), F, P) + ///, + SeeAlso => {pieriColumn, applyPieri, dualPieriColumn, Convention} + } + +document { + Key => {dualPieriColumn, + (dualPieriColumn, List, List, PolynomialRing), + [dualPieriColumn, Convention]}, + Headline => "GL-equivariant projection wedge^d V ⊗ S_lambda V --> S_mu V", + Usage => "dualPieriColumn(mu, cols, P)\ndualPieriColumn(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "mu" => {ofClass List, ", the target shape: ", + TEX "$\\mu = \\lambda + (\\text{vertical $d$-strip})$", + " with ", TEX "$d = \\#\\mathrm{cols}$"}, + "cols" => {ofClass List, ", a list of column indices recording the strip ", + TEX "$\\mu/\\lambda$"}, + "P" => {ofClass PolynomialRing, ", a SkewCommutative ring (", + TEX "$\\wedge^d V$", " is encoded there)"}, + Convention => String + }, + Outputs => { + Matrix => {"a ", TEX "$K$", "-matrix of size ", + TEX "$\\dim S_\\mu V \\times \\bigl(\\dim S_\\lambda V \\cdot \\binom{n}{d}\\bigr)$", + ". Columns indexed by ", + TEX "$(T_\\lambda, \\alpha)$", + " pairs (", TEX "$\\alpha$", " an exterior monomial)."} + }, + "Column-form analogue of ", TO dualPieri, + ": the ", TEX "$GL_n$", + "-equivariant projection from ", + TEX "$\\wedge^d V \\otimes S_\\lambda V$", " onto the chosen ", + TEX "$S_\\mu V$", + " summand (unique up to scalar). Construction stacks ", + TO pieriColumn, + " inclusions for every addable vertical ", TEX "$d$", + "-strip and inverts.", + EXAMPLE lines /// + P = QQ[e_0..e_2, SkewCommutative => true]; + M = pieriColumn({2,1}, {1}, P); + N = dualPieriColumn({2,1}, {1}, P); + numRows N, numColumns N + ///, + SeeAlso => {pieriColumn, dualPieri, applyDualPieriColumn, Convention} + } + +document { + Key => {applyDualPieriColumn, + (applyDualPieriColumn, Sequence, RingElement, BasicList, ZZ), + [applyDualPieriColumn, Convention]}, + Headline => "apply dualPieriColumn to a single basis pair (wedge_poly, T_lambda)", + Usage => "applyDualPieriColumn((mu, cols), poly, T, n)\napplyDualPieriColumn(..., Convention => \"Row\" | \"Filling\" | \"Weyl\")", + Inputs => { + "(mu, cols)" => {ofClass Sequence, ", the data passed to ", TO dualPieriColumn}, + "poly" => {ofClass RingElement, ", a homogeneous degree-", + TEX "$d$", " wedge polynomial"}, + "T" => {ofClass BasicList, ", a tableau of shape ", TEX "$\\lambda$", + ". May be a ", TT "List", " or a ", TT "Filling", + " (interpreted in the natural form of the convention; see ", + TO applyPieri, ")."}, + "n" => {ofClass ZZ, ", ", TEX "$\\dim V$"}, + Convention => String + }, + Outputs => { + List => {"a list of pairs ", TT "{coefficient, T_mu}", + " representing the image in ", TEX "$S_\\mu V$"} + }, + EXAMPLE lines /// + needsPackage "SchurFunctors"; + P = QQ[e_0..e_2, SkewCommutative => true]; + sfStdTab = value (SchurFunctors#"private dictionary"#"standardTableaux"); + T = (sfStdTab(3, {1,1}))#0; + applyDualPieriColumn(({2,1}, {1}), e_0, T, 3) + ///, + SeeAlso => {dualPieriColumn, pieriColumn, applyDualPieri, Convention} + } + +document { + Key => {symbolicForm, (symbolicForm, Matrix)}, + Headline => "print a Schur-rep matrix as a basis-labeled symbolic map", + Usage => "symbolicForm M", + Inputs => { + "M" => {ofClass Matrix, ", typically the output of ", TO pieri, ", ", + TO pieriColumn, ", ", TO lrMap, ", ", TO dualPieri, ", ", + TO dualPieriColumn, ", or ", TO dualLR, " (which attach + source/target basis labels to ", TT "M.cache", + "). Plain matrices are accepted too: integer indices ", + TT "1..rank", " are used as labels."} + }, + Outputs => { + Net => {"a netList; each row shows a source basis element ", + TT "i", " and the nonzero terms of ", TT "M(i)", + " as a list of ", TT "(target_label, coefficient)", " pairs"} + }, + "Pretty-prints the matrix as the GL-equivariant map it represents on the + labeled bases. Each row of the netList corresponds to a source basis + element (a tableau, or a (tableau, monomial) pair, etc.) and lists the + image as a sum over target basis elements with their coefficients. Rows + where the image is identically zero appear with an empty list.", + PARA{}, + "After ", TT "symbolicForm M", " is called once, the rule is cached on ", + TT "M.cache#\"rule\"", " so subsequent calls reuse it. ", + "For interactive exploration, you can also evaluate the rule on a single + basis element directly via ", TT "(M.cache#\"rule\")(label)", ".", + EXAMPLE lines /// + P = QQ[a,b,c]; + M = pieri({2,1}, {1}, P); + symbolicForm M + ///, + EXAMPLE lines /// + shapes = ({2,1}, {1}, {1,1}); + Q = (lrTableaux shapes)#0; + N = lrMap(shapes, Q, 3); + symbolicForm N + ///, + SeeAlso => {pieri, lrMap, dualPieri, dualLR, applyLR, applyPieri} + } diff --git a/M2/Macaulay2/packages/PieriMaps/tests.m2 b/M2/Macaulay2/packages/PieriMaps/tests.m2 new file mode 100644 index 00000000000..3a9c0d85f5d --- /dev/null +++ b/M2/Macaulay2/packages/PieriMaps/tests.m2 @@ -0,0 +1,630 @@ +TEST /// +-- symbolicForm output structure. +P = QQ[a,b,c]; +M = pieri({2,1}, {1}, P); +sf = symbolicForm M; -- runs without error and caches rule +assert(M.cache#?"rule"); +assert(#(M.cache#"sourceBasis") == numColumns M); +assert(#(M.cache#"targetBasis") == numRows M); +-- Direct rule evaluation matches the column. +T = (M.cache#"sourceBasis")#0; +img = (M.cache#"rule")(T); +col = flatten entries M_{0}; +nonzero = positions(col, x -> x != 0); +assert(#img == #nonzero); +/// + +TEST /// +-- applyLR matches the corresponding column of lrMap. +n = 3; +shapes = ({3,2,1}, {2,1}, {2,1}); +Qs = lrTableaux shapes; +M = lrMap(shapes, Qs#0, n); +Sb = standardTableaux(n, shapes#0); +Nmu = standardTableaux(n, shapes#1 | toList(#(shapes#0) - #(shapes#1) : 0)); +Nnu = standardTableaux(n, shapes#2); +for j from 0 to #Sb - 1 do ( + img := applyLR(shapes, Qs#0, Sb#j, n); + -- Reconstruct as hash for comparison. + h := new MutableHashTable; + for trip in img do h#(trip#1, trip#2) = trip#0; + for r from 0 to numRows M - 1 do ( + v := M_(r, j); + i := r // (#Nmu); + jj := r % (#Nmu); + key := (Nnu#i, Nmu#jj); + expected := if h#?key then h#key else 0; + assert(expected == v); + ); + ); +/// + +TEST /// +t = new BettiTally from {(0,{0},0)=>8, (1,{1},1) =>15, (2,{3},3)=>10, (3,{5},5)=>3}; +assert (t == (betti res coker pieri({3,1},{1},QQ[a,b,c]))) +t = new BettiTally from {(0,{0},0)=>15, (1,{1},1) =>24, (2,{4},4)=>15, (3,{6},6)=>6}; +assert (t == (betti res coker pieri({4,1},{1},QQ[a,b,c]))) +t = new BettiTally from {(0,{0},0)=>20, (1,{2},2) =>60, (2,{3},3)=>64, (3,{4},4)=>20}; +assert (t == (betti res coker pieri({3,2},{2,2},QQ[a,b,c,d]))) +assert(schurRank(5, {4,3}) == 560) +t = new BettiTally from {(0,{0},0) => 3, (1,{1},1)=>8, (2,{2},2) => 6, +(3,{4},4)=>1}; +assert(t == (betti res coker pureFree({0,1,2,4}, GF(4)[a,b,c]))) +/// + +-- LR-coefficient counts (number of LR tableaux). +TEST /// +assert(#lrTableaux({3,2,1}, {2,1}, {2,1}) == 2) +assert(#lrTableaux({2,1}, {1}, {1,1}) == 1) +assert(#lrTableaux({2,2}, {1,1}, {1,1}) == 1) +assert(#lrTableaux({3,2,1}, {1,1}, {2,2}) == 1) +assert(#lrTableaux({4,2,1}, {2,1}, {2,1,1}) == 1) +assert(#lrTableaux({4,3,2,1}, {2,2}, {2,2,1,1}) == 1) +assert(#lrTableaux({5,3,2,1}, {3,2,1}, {3,2}) == 3) +assert(#lrTableaux({3,3}, {2,2}, {2}) == 0) -- skew has both cells in same column +assert(#lrTableaux({3,2,1}, {2,1}, {2,2}) == 0) -- |lambda| != |mu| + |nu| +assert(#lrTableaux({3,2,1}, {3,2,1}, {}) == 1) -- trivial: only empty filling +/// + +-- LR maps: rank checks for several cases. +-- For an LR tableau Q, Psi_Q is a GL-equivariant injection of the irrep +-- S_lambda V into S_nu V (x) S_mu V, hence has rank = dim S_lambda. Distinct +-- LR tableaux give linearly independent maps; the joint image has rank +-- c^lambda_{mu,nu} * dim S_lambda. +TEST /// +checkLR := (lambda, mu, nu, n) -> ( + Qs := lrTableaux(lambda, mu, nu); + Ms := for Q in Qs list lrMap((lambda, mu, nu), Q, n); + dimSL := schurRank(n, lambda); + for M in Ms do assert(rank M == dimSL); + if #Ms >= 2 then ( + Mall := fold((a, b) -> a | b, Ms); + assert(rank Mall == #Ms * dimSL); + ); + ) +checkLR({3,2,1}, {2,1}, {2,1}, 3) +checkLR({3,2,1}, {2,1}, {2,1}, 4) +checkLR({2,1}, {1}, {1,1}, 3) +checkLR({2,2}, {1,1}, {1,1}, 3) +checkLR({4,2,1}, {2,1}, {2,1,1}, 4) +/// + +-- Targets of lrMap respect the documented row indexing. +TEST /// +n = 4; +lambda = {3,2,1}; +mu = {2,1}; +nu = {2,1}; +Qs = lrTableaux(lambda, mu, nu); +M = lrMap((lambda, mu, nu), Qs#0, n); +muPad = mu | toList(#lambda - #mu : 0); +Nmu = standardTableaux(n, muPad); +Nnu = standardTableaux(n, nu); +Sb = standardTableaux(n, lambda); +assert(numRows M == #Nnu * #Nmu); +assert(numColumns M == #Sb); +-- Both signatures should give identical matrices. +P = QQ[a,b,c,d]; +assert(M == lrMap((lambda, mu, nu), Qs#0, P)); +/// + +-- Convention rank checks: pieri/lrMap/pureFree return rank-correct matrices in +-- all three conventions and Row == Weyl numerically. +TEST /// +needsPackage "SchurFunctors"; +linearizeSym = (M, P, k, n) -> ( + X := gens P; + symBasis := flatten entries basis(k, P); + entriesM := entries M; + newRows := flatten for r in entriesM list ( + for mon in symBasis list for v in r list lift(coefficient(mon, v), QQ)); + matrix newRows +); +-- pieri across conventions. +P = QQ[a,b,c,d]; +M_row = pieri({3,2,1}, {1}, P); +M_fil = pieri({3,2,1}, {1}, P, Convention => "Filling"); +M_wey = pieri({3,2,1}, {1}, P, Convention => "Weyl"); +assert(M_row == M_wey); +assert(rank linearizeSym(M_row, P, 1, 4) == 64); +assert(rank linearizeSym(M_fil, P, 1, 4) == 64); + +-- lrMap across conventions. +Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); +for Q in Qs do ( + for conv in {"Row","Filling","Weyl"} do + assert(rank lrMap(({3,2,1},{2,1},{2,1}), Q, 3, Convention => conv) == 8); + ); + +-- pureFree across conventions: same Betti table. +Pp = QQ[a,b,c]; +b0 = betti res coker pureFree({0,1,2,4}, Pp, Convention => "Row"); +b1 = betti res coker pureFree({0,1,2,4}, Pp, Convention => "Filling"); +b2 = betti res coker pureFree({0,1,2,4}, Pp, Convention => "Weyl"); +assert(b0 == b1 and b0 == b2); +/// + +-- PM <-> Filling round-trip is c_lambda * id; PM <-> Weyl is a literal +-- bijection on standard tableaux. +TEST /// +-- Cache PieriMaps' standardTableaux BEFORE SchurFunctors gets loaded (it would +-- shadow the symbol in the user dict). +pmStdTab = value (PieriMaps#"private dictionary"#"standardTableaux"); +needsPackage "SchurFunctors"; +for shape in {{2,1}, {3,2,1}, {3,2}, {2,2,1}} do ( + for T in pmStdTab(3, shape) do ( + fil := pmToFilling T; + back := new MutableHashTable; + for k in keys fil do ( + sub := fillingToPM k; + for K in keys sub do ( + cc := fil#k * sub#K; + if back#?K then back#K = back#K + cc else back#K = cc; + ); + ); + cleaned := for k in keys back list if back#k != 0 then (k => back#k) else continue; + -- Round-trip should be a single nonzero coefficient on T itself. + assert(#cleaned == 1); + assert((cleaned#0)#0 == T); + ); + ); +-- PM <-> Weyl bijection. +for shape in {{3,2,1}, {2,2,1}, {3,2}} do ( + pm := pmStdTab(3, shape); + assert(apply(apply(pm, pmToWeyl), weylToPM) == pm); + ); +/// + +-- Column-Pieri proportional to row-Pieri after PM<->Filling change of basis. +TEST /// +pmStdTab = value (PieriMaps#"private dictionary"#"standardTableaux"); +needsPackage "SchurFunctors"; +sfStdTab = value (SchurFunctors#"private dictionary"#"standardTableaux"); +mu = {2,1}; r = 1; col = 2; n = 3; +Psym = QQ[x_0..x_(n-1)]; +Pskew = QQ[e_0..e_(n-1), SkewCommutative => true]; +Mrow = pieri(mu, {r}, Psym); +Mcol = pieriColumn(mu, {col}, Pskew); +pmS = pmStdTab(n, mu); +sfS = sfStdTab(n, toList conjugate (new Partition from mu)); +lambda = {1,1}; +pmL = pmStdTab(n, lambda); +sfL = sfStdTab(n, toList conjugate (new Partition from lambda)); +A = pmToFillingMatrix(mu, n); +B = fillingToPMMatrix(lambda, n); +rowFlat = matrix flatten for ti from 0 to #pmL-1 list + for kk from 0 to n-1 list + for sj from 0 to #pmS - 1 list lift(coefficient(Psym_kk, Mrow_(ti, sj)), QQ); +colFlat = matrix flatten for tj from 0 to #sfL-1 list + for kk from 0 to n-1 list + for siF from 0 to #sfS - 1 list lift(coefficient(Pskew_kk, Mcol_(tj, siF)), QQ); +McolPM = (B ** id_(QQ^n)) * colFlat * A; +assert(rank rowFlat == rank McolPM); +assert(rank (rowFlat | McolPM) == rank rowFlat); -- proportional +/// + +-- verifyWellDefined: the Convention option respects the appropriate +-- straightening relations. +TEST /// +needsPackage "SchurFunctors"; +Qs = lrTableaux({3,2,1}, {2,1}, {2,1}); +for Q in Qs do + for conv in {"Row","Filling","Weyl"} do + assert(verifyWellDefined(({3,2,1},{2,1},{2,1}), Q, 3, + Convention => conv, Verbose => false)); +/// + +-- Direct GL_n equivariance check for the row-Pieri inclusion S_mu V --> +-- V (x) S_lambda V. +-- +-- The Lie algebra gl_n acts via Chevalley generators E_{i,j} (i != j): +-- * on V = K^n by E_{i,j}(x_l) = delta_{l,j} * x_i (i.e. x_i d/dx_j), +-- * on S_mu V (PM row-SSYT basis) by replacing each entry equal to j +-- with i, summing over positions, then row-Garnir straightening, +-- * on V (x) S_lambda V by the Leibniz rule. +-- +-- For the Pieri matrix M : S_mu V --> V (x) S_lambda V encoded as a +-- (#stdLambda) x (#stdMu) matrix over P with degree-1-form entries, the +-- equivariance test is, for each column index T of M: +-- LHS = sum c_k * M_{*, T_k}, where E_{i,j}.T = sum c_k * T_k +-- RHS = (V-action on M_{*,T} entries) + (S_lambda-action on the +-- target row indices). +-- The matrix is GL_n-equivariant iff LHS = RHS for every T and every +-- (i, j). Since our Pieri map is rationally constructed and S_mu V is +-- an irreducible GL representation, equivariance is the unique +-- characterization (up to scalar) by Schur's lemma. +TEST /// +-- Action of E_{i,j} on a row-SSYT tableau, returning a HashTable of std +-- tableau coefficients via straightening. +actE = (i, j, T) -> ( + totalH := new MutableHashTable; + for r from 0 to #T-1 do + for c from 0 to #(T#r)-1 do + if T#r#c == j then ( + Tprime := for r2 from 0 to #T-1 list + (if r2 == r then + for c2 from 0 to #(T#r2)-1 list + (if c2 == c then i else (T#r2)#c2) + else T#r2); + h := straighten Tprime; + for k in keys h do + if totalH#?k then totalH#k = totalH#k + h#k + else totalH#k = h#k; + ); + new HashTable from totalH + ); + +-- Test M is gl_n-equivariant by checking commutativity with each E_{i,j}. +checkPieriEquivariant = (mu, boxes, n) -> ( + P := QQ[x_0..x_(n-1)]; + X := gens P; + M := pieri(mu, boxes, P); + subOne := (m, k) -> for ii from 0 to #m-1 list (if ii == k-1 then m_ii - 1 else m_ii); + lambda := mu; + for b in boxes do lambda = subOne(lambda, b); + stdsMu := standardTableaux(n, mu); + stdsLam := standardTableaux(n, lambda); + for i from 0 to n-1 do for j from 0 to n-1 do ( + if i == j then continue; + for tIdx from 0 to #stdsMu-1 do ( + T := stdsMu#tIdx; + colT := flatten entries M_{tIdx}; + -- RHS = E.M(T): V-action + S_lambda-action + -- V-action on Sym^m V (x) S_lambda part: x_i * d/dx_j applied + -- to each entry (entries are degree-m forms for m-box strips). + vAction := for f in colT list X#i * diff(X#j, f); + sAction := new MutableList from for s in stdsLam list 0_P; + for idx from 0 to #stdsLam-1 do ( + h := actE(i, j, stdsLam#idx); + for kk in keys h do ( + idxK := position(stdsLam, s -> s == kk); + if idxK =!= null then + sAction#idxK = sAction#idxK + h#kk * colT#idx; + ); + ); + RHS := for idx from 0 to #stdsLam-1 list (vAction#idx + sAction#idx); + -- LHS = M(E.T) + LHS := for s in stdsLam list 0_P; + hT := actE(i, j, T); + for kk in keys hT do ( + idxK := position(stdsMu, s -> s == kk); + if idxK =!= null then ( + colK := flatten entries M_{idxK}; + LHS = for idx from 0 to #stdsLam-1 list + (LHS#idx + hT#kk * colK#idx); + ); + ); + assert(LHS == RHS); + ); + ); + ); + +-- Single-box, every row removable: the cases that catch the "interior k" +-- bug discussed in the documentation tests for path-based pieri. +checkPieriEquivariant({2,1}, {1}, 3); +checkPieriEquivariant({2,1}, {2}, 3); +checkPieriEquivariant({3,2,1}, {1}, 3); +checkPieriEquivariant({3,2,1}, {2}, 3); -- INTERIOR row removal +checkPieriEquivariant({3,2,1}, {3}, 3); + +-- Multi-box horizontal strips, including patterns through interior rows. +checkPieriEquivariant({3,2,1}, {1,3}, 3); +checkPieriEquivariant({3,2,1}, {1,2,3}, 3); +checkPieriEquivariant({3,1}, {1,1}, 3); +/// + +-- dualPieri ∘ pieri == Id (the defining property of the GL-equivariant +-- projection by Schur's lemma). Tested across single- and multi-box +-- strips and across all three Conventions. +TEST /// +encodeKMat = (M, monBasis, K) -> ( + dimLam := numRows M; + dimMu := numColumns M; + matrix(K, flatten for tLam from 0 to dimLam-1 list ( + for alpha in monBasis list ( + for tMu from 0 to dimMu-1 list ( + lift(coefficient(alpha, M_(tLam, tMu)), K))))) + ); + +checkDualPieriId = (mu, boxes, P, conv) -> ( + M := pieri(mu, boxes, P, Convention => conv); + N := dualPieri(mu, boxes, P, Convention => conv); + monBasis := flatten entries basis(#boxes, P); + Mk := encodeKMat(M, monBasis, QQ); + assert(N * Mk == id_(QQ^(numColumns Mk))); + ); + +P = QQ[a,b,c]; +for conv in {"Row", "Filling", "Weyl"} do ( + checkDualPieriId({2,1}, {1}, P, conv); + checkDualPieriId({2,1}, {2}, P, conv); + checkDualPieriId({3,2,1}, {1}, P, conv); + checkDualPieriId({3,2,1}, {2}, P, conv); + checkDualPieriId({3,2,1}, {3}, P, conv); + checkDualPieriId({3,2,1}, {1,3}, P, conv); + checkDualPieriId({3,1}, {1,1}, P, conv); + ); +/// + +-- dualLR ∘ lrMap == Id (per Q), and for multiplicity > 1, the dual at Q +-- annihilates the inclusions for OTHER LR tableaux Q': dualLR_Q ∘ lrMap_{Q'} = 0. +TEST /// +checkDualLRId = (shapes, Q, n, conv) -> ( + M := lrMap(shapes, Q, n, Convention => conv); + N := dualLR(shapes, Q, n, Convention => conv); + assert(N * M == id_(QQ^(numColumns M))); + ); + +checkDualLRCrossZero = (shapes, Q, Qother, n, conv) -> ( + N := dualLR(shapes, Q, n, Convention => conv); + M := lrMap(shapes, Qother, n, Convention => conv); + assert(N * M == 0); + ); + +for conv in {"Row", "Filling", "Weyl"} do ( + -- multiplicity-1 cases + checkDualLRId(({2,1}, {1}, {1,1}), (lrTableaux({2,1},{1},{1,1}))#0, 3, conv); + checkDualLRId(({2,2}, {1,1}, {1,1}), (lrTableaux({2,2},{1,1},{1,1}))#0, 3, conv); + -- multiplicity 2: c^{(3,2,1)}_{(2,1),(2,1)} = 2 -- both directions + Qs := lrTableaux({3,2,1}, {2,1}, {2,1}); + for Q in Qs do checkDualLRId(({3,2,1},{2,1},{2,1}), Q, 3, conv); + -- cross-Q orthogonality + checkDualLRCrossZero(({3,2,1},{2,1},{2,1}), Qs#0, Qs#1, 3, conv); + checkDualLRCrossZero(({3,2,1},{2,1},{2,1}), Qs#1, Qs#0, 3, conv); + ); +/// + +-- applyDualPieri / applyDualLR: point evaluation respects the matrix product +-- and handles non-standard input via convention-aware straightening. Inputs +-- can be passed as either a List (PM row form) or a Filling (column form), +-- regardless of Convention. +TEST /// +-- Cache PM's standardTableaux BEFORE loading SchurFunctors (which would +-- shadow the symbol with its own Filling-returning version). +pmStdTab = value (PieriMaps#"private dictionary"#"standardTableaux"); +needsPackage "SchurFunctors"; + +-- (1) applyDualPieri agrees with the matrix product on standard input. +P = QQ[a,b,c]; +n = 3; +mu = {3,2,1}; +boxes = {1}; +lambda = {2,2,1}; +N = dualPieri(mu, boxes, P); +stdsLam = pmStdTab(n, lambda); +stdsMu = pmStdTab(n, mu); +mons = flatten entries basis(1, P); +for tLam in stdsLam do for alpha in mons do ( + img := applyDualPieri((mu, boxes), alpha, tLam, n); + -- compare with N applied to e_(tLam, alpha) + iLam := position(stdsLam, s -> s == tLam); + iAlpha := position(mons, m -> m == alpha); + col := iLam * #mons + iAlpha; + expected := flatten entries N_{col}; + observed := for s in stdsMu list ( + c := select(img, p -> p#1 == s); + if #c == 0 then 0_QQ else c#0#0 + ); + assert(observed == expected); + ); + +-- (2) applyDualPieri straightens non-standard input. +-- {{1,0},{2}} is non-standard for shape {2,1}: row 1 is decreasing. +P2 = QQ[a,b,c,d]; +nonStdRes = applyDualPieri(({3,2,1}, {3}), a, {{1,0},{2}}, 4); +strRes = applyDualPieri(({3,2,1}, {3}), a, {{0,1},{2}}, 4); +-- Straightening of {{1,0},{2}} swaps row entries: should give same as {{0,1},{2}} +-- (row Garnir gives the standard form trivially since {0,1}=sort{1,0}). +assert(set apply(nonStdRes, p -> {p#0, p#1}) === set apply(strRes, p -> {p#0, p#1})); + +-- (3) applyDualLR agrees with the matrix on a standard basis pair. +shapes = ({2,1}, {1}, {1,1}); +Q = (lrTableaux shapes)#0; +N2 = dualLR(shapes, Q, 3); +stdsLam = pmStdTab(3, shapes#0); +stdsMu = pmStdTab(3, shapes#1 | toList(#(shapes#0) - #(shapes#1) : 0)); +stdsNu = pmStdTab(3, shapes#2); +for tNu in stdsNu do for tMu in stdsMu do ( + img := applyDualLR(shapes, Q, {tNu, tMu}, 3); + iNu := position(stdsNu, s -> s == tNu); + iMu := position(stdsMu, s -> s == tMu); + col := iNu * #stdsMu + iMu; + expected := flatten entries N2_{col}; + observed := for s in stdsLam list ( + c := select(img, p -> p#1 == s); + if #c == 0 then 0_QQ else c#0#0 + ); + assert(observed == expected); + ); + +-- (4) Input-type flexibility: applyDualPieri accepts either a List (PM +-- row form) or a Filling (column form) regardless of Convention, and +-- runs without error on each. Filling inputs go through fillingToPM +-- internally (under Convention "Row" / "Weyl"), so the result is in +-- PM std-tableau basis on output. +P3 = QQ[a,b,c]; +listT = (pmStdTab(3, {2,1}))#0; +imgFromList = applyDualPieri(({3,1}, {1}), a, listT, 3); +assert(#imgFromList >= 1); +-- Same call with a Filling input runs (the conversion path is exercised). +expansion = pmToFilling listT; +filT = (keys expansion)#0; +imgFromFilling = applyDualPieri(({3,1}, {1}), a, filT, 3); +assert(#imgFromFilling >= 1); +-- Output T_mu values come from the PM std basis in either case. +assert(all(imgFromList, p -> class p#1 === List)); +assert(all(imgFromFilling, p -> class p#1 === List)); +/// + +-- Transition matrices between conventions are mutually invertible (up to +-- the documented shape-dependent scalar c_lambda for PM <-> Filling). +TEST /// +needsPackage "SchurFunctors"; +for shape in {{2,1}, {3,1}, {3,2}, {3,2,1}, {2,2,1}} do + for n in {3, 4} do ( + if n < #shape then continue; + A := pmToFillingMatrix(shape, n); + B := fillingToPMMatrix(shape, n); + -- A * B and B * A are scalar multiples of identity (the c_lambda scalar) + prodAB := A * B; + prodBA := B * A; + -- Diagonals are all the same value + diagAB := apply(min(numRows prodAB, numColumns prodAB), + i -> prodAB_(i, i)); + assert(all(diagAB, d -> d == diagAB#0)); + c := diagAB#0; + assert(c != 0); + assert(prodAB == c * id_(QQ^(numRows prodAB))); + assert(prodBA == c * id_(QQ^(numRows prodBA))); + ); +/// + +-- verifyEquivariant: all the maps we construct (pieri, lrMap, dualPieri, +-- dualLR) are GL_n-equivariant. Tested in Row and Weyl conventions +-- (matrix data is identical between the two; the test machinery uses +-- PM row-Garnir straightening for the gl_n action). +TEST /// +P = QQ[a,b,c]; + +-- pieri inclusion S_mu V -> Sym^d V (x) S_lambda V +for conv in {"Row", "Filling", "Weyl"} do ( + M := pieri({3,2,1}, {1}, P, Convention => conv); + assert(verifyEquivariant(M, {3,2,1}, {1}, P, Convention => conv)); + M2 := pieri({3,2,1}, {2}, P, Convention => conv); + assert(verifyEquivariant(M2, {3,2,1}, {2}, P, Convention => conv)); + -- multi-box + M3 := pieri({3,2,1}, {1,3}, P, Convention => conv); + assert(verifyEquivariant(M3, {3,2,1}, {1,3}, P, Convention => conv)); + ); +/// + +TEST /// +-- lrMap S_lambda V -> S_nu V (x) S_mu V +for conv in {"Row", "Filling", "Weyl"} do ( + for Q in lrTableaux({3,2,1}, {2,1}, {2,1}) do ( + M := lrMap(({3,2,1}, {2,1}, {2,1}), Q, 3, Convention => conv); + assert(verifyEquivariant(M, ({3,2,1}, {2,1}, {2,1}), 3, Convention => conv)); + ); + ); +/// + +TEST /// +-- dualPieri Sym^d V (x) S_lambda V -> S_mu V +P = QQ[a,b,c]; +for conv in {"Row", "Filling", "Weyl"} do ( + M := dualPieri({3,2,1}, {1}, P, Convention => conv); + assert(verifyEquivariant(M, {3,2,1}, {1}, P, Convention => conv, Direction => "Dual")); + M2 := dualPieri({3,2,1}, {2}, P, Convention => conv); + assert(verifyEquivariant(M2, {3,2,1}, {2}, P, Convention => conv, Direction => "Dual")); + ); +/// + +TEST /// +-- dualLR S_nu V (x) S_mu V -> S_lambda V (per Q) +for conv in {"Row", "Filling", "Weyl"} do ( + for Q in lrTableaux({3,2,1}, {2,1}, {2,1}) do ( + N := dualLR(({3,2,1}, {2,1}, {2,1}), Q, 3, Convention => conv); + assert(verifyEquivariant(N, ({3,2,1}, {2,1}, {2,1}), 3, Convention => conv, Direction => "Dual")); + ); + ); +/// + +-- pieriColumn with Convention support: matches dim and is consistent with Filling. +TEST /// +P = QQ[e_0..e_2, SkewCommutative => true]; +M_F = pieriColumn({2,1}, {1}, P, Convention => "Filling"); +M_R = pieriColumn({2,1}, {1}, P, Convention => "Row"); +M_W = pieriColumn({2,1}, {1}, P, Convention => "Weyl"); +assert(numRows M_R == numRows M_F and numColumns M_R == numColumns M_F); +assert(M_R == M_W); +/// + +-- dualPieriColumn ∘ pieriColumn = Id (column-form analogue of dualPieri). +TEST /// +P = QQ[e_0..e_2, SkewCommutative => true]; +encodeKMat = (M, monBasis, K) -> ( + dimLam := numRows M; + dimMu := numColumns M; + matrix(K, flatten for tLam from 0 to dimLam-1 list ( + for alpha in monBasis list ( + for tMu from 0 to dimMu-1 list ( + lift(coefficient(alpha, M_(tLam, tMu)), K))))) + ); +checkColId = (mu, cols, P, conv) -> ( + M := pieriColumn(mu, cols, P, Convention => conv); + N := dualPieriColumn(mu, cols, P, Convention => conv); + monBasis := flatten entries basis(#cols, P); + Mk := encodeKMat(M, monBasis, QQ); + assert(N * Mk == id_(QQ^(numColumns Mk))); + ); +for conv in {"Row", "Filling", "Weyl"} do ( + checkColId({2,1}, {1}, P, conv); + checkColId({2,1}, {2}, P, conv); + checkColId({2,1,1}, {1}, P, conv); + ); +/// + +-- applyPieri / applyPieriColumn agree with the corresponding column of the +-- matrix on a standard input tableau. +TEST /// +P = QQ[a,b,c]; +mu = {3,2,1}; +boxes = {1}; +M = pieri(mu, boxes, P); +stdsMu = standardTableaux(3, mu); +for tIdx from 0 to #stdsMu - 1 do ( + T := stdsMu#tIdx; + for r from 0 to #boxes - 1 list r; + img := applyPieri((mu, boxes), T, P); + col := flatten entries M_{tIdx}; + -- Reconstruct img as a vector indexed by lambda's std tableaux. + stdsLam := standardTableaux(3, {2,2,1}); + reconstructed := for s in stdsLam list ( + matched := select(img, p -> p#1 == s); + if #matched == 0 then 0_P else matched#0#0 + ); + assert(reconstructed == col); + ); +/// + +-- verifyWellDefined for pieri / pieriColumn / dualPieri / dualPieriColumn +-- across all conventions. Smoke test that the apply functions factor +-- through Garnir straightening relations. +TEST /// +P = QQ[a,b,c]; +Pcol = QQ[e_0..e_2, SkewCommutative => true]; +for conv in {"Row", "Filling", "Weyl"} do ( + -- pieri inclusion + assert(verifyWellDefined({3,2,1}, {1}, P, Convention => conv, Verbose => false)); + -- dualPieri projection + assert(verifyWellDefined({3,2,1}, {1}, P, Convention => conv, Direction => "Dual", Verbose => false)); + -- pieriColumn inclusion + assert(verifyWellDefined({2,1}, {1}, Pcol, Convention => conv, Verbose => false)); + -- dualPieriColumn projection + assert(verifyWellDefined({2,1}, {1}, Pcol, Convention => conv, Direction => "Dual", Verbose => false)); + ); +-- dualLR projection +for conv in {"Row", "Filling", "Weyl"} do ( + for Q in lrTableaux({2,1}, {1}, {1,1}) do + assert(verifyWellDefined(({2,1}, {1}, {1,1}), Q, 3, Convention => conv, + Direction => "Dual", Verbose => false)); + ); +/// + +-- Edge cases: empty boxes, single-cell shapes, n = 1. +TEST /// +P3 = QQ[a,b,c]; +-- Empty boxes for pieri: identity-like map (mu == lambda). pieri requires +-- at least one box currently; skip and check single-box minimal case. +assert(numRows pieri({1}, {1}, P3) == 1); -- {1} -> empty: 1x3 matrix +M = pieri({1}, {1}, P3); +assert(rank M == 1); +-- Single-cell input via applyPieri. +img = applyPieri(({1}, {1}), {{0}}, P3); +assert(#img == 1); +assert(img#0#0 == a); +-- n = 1 case: only mu = {k} valid. pieri({2}, {1}, P3) is fine; pieri({2}, {1}, QQ[x]) too. +P1 = QQ[x]; +M1 = pieri({2}, {1}, P1); +assert(numRows M1 == 1 and numColumns M1 == 1); -- dim S_(2) K^1 = 1, dim S_(1) K^1 = 1 +/// diff --git a/M2/Macaulay2/packages/Points.m2 b/M2/Macaulay2/packages/Points.m2 index 339b084b27b..b09577757a0 100644 --- a/M2/Macaulay2/packages/Points.m2 +++ b/M2/Macaulay2/packages/Points.m2 @@ -12,7 +12,7 @@ newPackage( }, Headline => "sets of points", Keywords => {"Examples and Random Objects"}, - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, PackageExports => {"LexIdeals"}, DebuggingMode => false ) diff --git a/M2/Macaulay2/packages/Polyhedra/documentation/documentation.m2 b/M2/Macaulay2/packages/Polyhedra/documentation/documentation.m2 index 196f327c07c..1da8e4e429a 100644 --- a/M2/Macaulay2/packages/Polyhedra/documentation/documentation.m2 +++ b/M2/Macaulay2/packages/Polyhedra/documentation/documentation.m2 @@ -103,6 +103,32 @@ doc /// Deprecated version of @TO "linealitySpace"@ /// +doc /// + Key + centroid + (centroid,Polyhedron) + Headline + computes the centroid or barycenter of a polyhedron + Usage + centroid P + Inputs + P:Polyhedron + a compact polyhedron + Outputs + :Matrix + Description + Text + The centroid of a polyhedron is the center of mass of the polyhedron. + This is computed by first triangulating the polyhedron, and then taking + a weighted sum of the barycenters of the simplices in the triangulation. + Example + centroid stdSimplex 3 + Example + centroid hypercube 2 + SeeAlso + barycentricTriangulation +/// + doc /// Key latticeVolume diff --git a/M2/Macaulay2/packages/Polyhedra/engine/alternatives.m2 b/M2/Macaulay2/packages/Polyhedra/engine/alternatives.m2 index 5066d9100f2..4aeeaa2e8c4 100644 --- a/M2/Macaulay2/packages/Polyhedra/engine/alternatives.m2 +++ b/M2/Macaulay2/packages/Polyhedra/engine/alternatives.m2 @@ -17,7 +17,7 @@ loadAlternative String := name -> ( ) dropAlternatives = method() -installMethod(dropAlternatives, o -> (alternative = new MutableHashTable)) +dropAlternatives() := o -> (alternative = new MutableHashTable) insertAlternatives = method() insertAlternatives MutableHashTable := newAlternatives -> ( diff --git a/M2/Macaulay2/packages/Polyhedra/exports.m2 b/M2/Macaulay2/packages/Polyhedra/exports.m2 index ff21aca381f..157c0e3d2ad 100644 --- a/M2/Macaulay2/packages/Polyhedra/exports.m2 +++ b/M2/Macaulay2/packages/Polyhedra/exports.m2 @@ -113,6 +113,7 @@ export { "barycentricTriangulation", "regularSubdivision", "minimalNonFaces", - "stanleyReisnerRing" + "stanleyReisnerRing", + "centroid", } diff --git a/M2/Macaulay2/packages/Polyhedra/extended/polyhedron/methods.m2 b/M2/Macaulay2/packages/Polyhedra/extended/polyhedron/methods.m2 index 553b6d82731..473503d5f74 100644 --- a/M2/Macaulay2/packages/Polyhedra/extended/polyhedron/methods.m2 +++ b/M2/Macaulay2/packages/Polyhedra/extended/polyhedron/methods.m2 @@ -303,6 +303,21 @@ interiorPoint Polyhedron := P -> ( Vm * ones) +-- PURPOSE : Computing the barycenter/centroid a Polyhedron +-- INPUT : 'P', a Polyhedron +-- OUTPUT : 'p', a point given as a matrix +centroid = method(TypicalValue => Matrix) +centroid Polyhedron := Matrix => P -> ( + if not isCompact P then error "the polyhedron must be compact"; + totalVolume := volume P; + sum(barycentricTriangulation P, delta -> ( + barycenter := (sum delta) / #delta; + (volume convexHull delta / totalVolume) * barycenter + ) + ) +) + + -- PURPOSE : Computing the face of a Polyhedron where a given weight attains its minimum -- INPUT : '(v,P)', a weight vector 'v' given by a one column matrix over ZZ or QQ and a -- Polyhedron 'P' diff --git a/M2/Macaulay2/packages/Polyhedra/extended/tests/polyhedron.m2 b/M2/Macaulay2/packages/Polyhedra/extended/tests/polyhedron.m2 index eae340a2fc3..9f7c2db4407 100644 --- a/M2/Macaulay2/packages/Polyhedra/extended/tests/polyhedron.m2 +++ b/M2/Macaulay2/packages/Polyhedra/extended/tests/polyhedron.m2 @@ -25,3 +25,12 @@ A = matrix{{1,0,0},{0,1,0}} Q = affineImage(A,P) assert(linealitySpace Q == promote(transpose matrix {{0,1}}, QQ)) /// + +TEST /// +-- A tetrahedron +assert(centroid stdSimplex 3 == transpose matrix {{1/4, 1/4, 1/4, 1/4}}) + +-- An associahedron +P = convexHull transpose matrix {{-1,1}, {1,1}, {1,-1}, {0,-1}, {-1,0}} +assert(centroid P == transpose matrix {{2/21, 2/21}}) +/// \ No newline at end of file diff --git a/M2/Macaulay2/packages/Posets.m2 b/M2/Macaulay2/packages/Posets.m2 index 91f8b907358..d4fabc34d1d 100644 --- a/M2/Macaulay2/packages/Posets.m2 +++ b/M2/Macaulay2/packages/Posets.m2 @@ -1050,7 +1050,7 @@ transitiveOrientation Graph := Poset => opts -> G -> ( true ); E := toList \ edges G; - E = (if opts.Random then random else identity) join(E, reverse \ E); + E = (if opts.Random then shuffle else identity) join(E, reverse \ E); orientation := new MutableHashTable from apply(E, e -> e => 0); k := 0; for e in E do if orientation#e === 0 then ( @@ -1481,19 +1481,132 @@ fPolynomial Poset := RingElement => opts -> P -> ( sum(-1..dim oP, i -> fV#(i+1) * R_0^(i + 1)) ) -greeneKleitmanPartition = method(Options => {symbol Strategy => "antichains"}) +-- This is not exported; it is a helper function for `greeneKleitmanPartition`. +frankNetwork := (P) -> ( + -- (Following the notation of Section 8 of [Britz-Fomin, 1999].) + -- Essentially, make two copies of V(P), adjoin a source s to one copy and + -- a sink t to the other copy. Then form edges (s,x_p) and (x_p,t), as well + -- as edges (x_p,y_p') if p >= p' in P. + -- + -- Since we can't compare Symbol == IndexedVariable (e.g., s == x_1), we + -- regard this network as a multipartite graph with four parts: + -- + -- Level | Part + -- ------------ + -- 1 | source = s + -- 2 | x_1 .. x_p + -- 3 | y_1 .. y_p + -- 4 | sink = t + -- + -- So, we identify each vertex with the label (level, index). For instance, + -- the vertex x_5 corresponds to the vertex (2, 5). + + -- Make the underlying (di)graph of the network. + s := (1, 0); + t := (4, 0); + + fromSourceEdges := for vertex in P_* list {s, (2, vertex)}; + comparisonEdges := apply(allRelations P, edge -> {(2, first edge), + (3, last edge)}); + toSinkEdges := for vertex in P_* list {(3, vertex), t}; + G := digraph(fromSourceEdges | comparisonEdges | toSinkEdges); + + -- Now, make all edge capacities c(e) equal to 1, with a cost function a(e) + -- defined by a(e) = 1 if e = (x_p,y_p); otherwise, a(e) = 0. + costFunction := new MutableHashTable from for edge in edges G list edge =>0; + for vertex in P_* do costFunction#({(2, vertex), (3, vertex)}) = 1; + + (G, costFunction) +) + +greeneKleitmanPartition = method(Options => {symbol Strategy => "auto"}) greeneKleitmanPartition Poset := Partition => opts -> P -> ( if P.cache.?greeneKleitmanPartition then return P.cache.greeneKleitmanPartition; - (C, f) := if opts.Strategy === "chains" then (chains P, identity) - else if opts.Strategy === "antichains" then (antichains P, conjugate) - else error "The option Strategy must either be 'chains' or 'antichains'."; + + strategy := if opts.Strategy === "auto" then ( + -- For small posets, chains or antichains is usually quicker. + -- Otherwise, Britz-Fomin drastically outperforms. + if #P.GroundSet <= 7 then "chains" else "Britz-Fomin" + ) + else if isMember(opts.Strategy, set {"chains", "antichains", "Britz-Fomin"}) then ( + opts.Strategy + ) + else ( + error "The option Strategy must be 'auto', 'chains', 'antichains, or 'Britz-Fomin'" + ); + lambda := {}; - k := 0; - while sum lambda < #P.GroundSet do ( - lk := max apply(subsets(C, k = k + 1), c -> #unique flatten c); - lambda = append(lambda, lk - sum lambda); + if isMember(strategy, set {"chains", "antichains"}) then ( + (C, f) := if strategy === "chains" then (chains P, identity) + else if strategy === "antichains" then (antichains P, conjugate); + k := 0; + while sum lambda < #P.GroundSet do ( + lk := max apply(subsets(C, k = k + 1), c -> #unique flatten c); + lambda = append(lambda, lk - sum lambda); + ); + lambda = f new Partition from lambda; + ) + else ( + -- (Following the notation of Section 7 of [Britz-Fomin, 1999].) + -- Initialize for the Ford-Fulkerson algorithm. + (G, cost) := frankNetwork P; + s := (1, 0); + t := (4, 0); + capacity := new MutableHashTable from for edge in edges G list edge => 1; + flow := new MutableHashTable from for edge in edges G list edge => 0; + potential := new MutableHashTable from for vertex in vertices G list vertex => 0; + + -- We need to build the partition step-by-step. + lambdaPart := 0; + while sum lambda != #P.GroundSet do ( + -- Ford-Fulkerson algorithm + -- Step MC1 + G' := digraph(vertices G, {}); + for edge in edges G do ( + if potential#(last edge) - potential#(first edge) == cost#edge then ( + if flow#edge < capacity#edge then ( + G' = addEdges'(G', {edge}); + ) + else if flow#edge > 0 then ( + G' = addEdges'(G', {reverse edge}); + ); + ); + ); + X := set flatten breadthFirstSearch(G', s); + if isMember(t, X) then ( + -- Step MC2a + -- Increase the flow along an s-t path in G' by 1. + -- NOTE: The choice of path here *should not* matter, but might as + -- well do it by a shortest path, since `findPaths` requires + -- a distance argument. + pathsFromStart := findPaths(G', s, distance(G', s, t)); + shortestPath := first select(pathsFromStart, somePath -> (first somePath == s) and (last somePath == t)); + shortestPathAsEdges := apply(drop(shortestPath, -1), drop(shortestPath, 1), (u, v) -> {u, v}); + for edge in shortestPathAsEdges do ( + if flow#?edge then ( + flow#edge += 1; + ) + else ( + flow#(reverse edge) -= 1; + ); + ); + -- We increased the value of the flow + if lambdaPart > 0 then ( lambda = append(lambda, lambdaPart); ); + ) + else ( + -- Step MC2b + for vertex in vertices G do ( + if not isMember(vertex, X) then ( + potential#vertex += 1; + ); + ); + -- We increased the value of the potential + lambdaPart += 1; + ); ); - P.cache.greeneKleitmanPartition = f new Partition from lambda + lambda = new Partition from reverse lambda; + ); + P.cache.greeneKleitmanPartition = lambda ) hPolynomial = method(Options => {symbol VariableName => getSymbol "q"}) @@ -5082,12 +5195,14 @@ doc /// computes the Greene-Kleitman partition of a poset Usage l = greeneKleitmanPartition P + l = greeneKleitmanPartition(P, Strategy => "auto") l = greeneKleitmanPartition(P, Strategy => "chains") l = greeneKleitmanPartition(P, Strategy => "antichains") + l = greeneKleitmanPartition(P, Strategy => "Britz-Fomin") Inputs P:Poset Strategy=>String - either "chains" or "antichains" + one of "auto", "chains", "antichains", or "Britz-Fomin" Outputs l:Partition the Greene-Kleitman partition of $P$ @@ -5113,6 +5228,17 @@ doc /// partition of $n$ with $1$ part. Example greeneKleitmanPartition chain 10 + Text + When the Strategy is "auto", for small posets, the strategies + "chains" and "antichains" are used as a brute force search is + performed. For larger posets, a max-flow computation is performed + according to [BF01]. + References + @UL { + {"[BF01] Thomas Britz and Sergey Fomin, ", + HREF("https://doi.org/10.1006/aima.2000.1966", EM "Finite Posets and Ferrer Shapes"), + ", Adv. Math., 158.1 (2001), pp. 86-127."} + }@ SeeAlso chains antichains @@ -6649,8 +6775,7 @@ assert(toString flagfPolynomial B === "6*q_0*q_1*q_2*q_3*q_4*q_5*q_6+6*q_0*q_1*q --assert(toString flaghPolynomial B === "q_1+q_2+q_3+q_4+q_5+1") assert(toString fPolynomial B === "6*q^7+37*q^6+96*q^5+135*q^4+110*q^3+51*q^2+12*q+1") assert(toString hPolynomial B === "5*q+1") ---Removed for time purposes (slow!) ---assert(greeneKleitmanPartition B === new Partition from {7,5}) +assert(greeneKleitmanPartition B === new Partition from {7,5}) assert(moebiusFunction B === new HashTable from {(1,6) => 1, (24,48) => -1, (48,24) => 0, (1,8) => 0, (1,12) => 0, (32,48) => 0, (48,32) => 0, (1,16) => 0, (1,24) => 0, (48,48) => 1, (1,32) => 0, (96,1) => 0, (96,2) => 0, (96,3) => 0, (4,96) => 0, (96,4) => 0, (96,6) => 0, (8,96) => 0, (96,8) => 0, (12,96) => 0, (96,12) => 0, (16,96) => 1, (96,16) => 0, (1,48) => 0, (24,96) => 0, (96,24) => 0, (96,32) => 0, (32,96) => -1, (2,1) => 0, (2,2) => 1, (2,3) => 0, (2,4) => -1, (6,1) => 0, (2,6) => -1, (6,2) => 0, (6,3) => 0, (2,8) => 0, (6,4) => 0, (6,6) => 1, (6,8) => 0, (2,12) => 1, (96,48) => 0, (48,96) => -1, (6,12) => -1, (2,16) => 0, (6,16) => 0, (2,24) => 0, (6,24) => 0, (1,96) => 0, (2,32) => 0, (6,32) => 0, (2,48) => 0, (6,48) => 0, (96,96) => 1, (3,1) => 0, (3,2) => 0, (3,3) => 1, (3,4) => 0, (3,6) => -1, (3,8) => 0, (3,12) => 0, (3,16) => 0, (3,24) => 0, (2,96) => 0, (3,32) => 0, (6,96) => 0, (3,48) => 0, (4,1) => 0, (4,2) => 0, (4,3) => 0, (4,4) => 1, (8,1) => 0, (8,2) => 0, (4,6) => 0, (8,3) => 0, (8,4) => 0, (4,8) => -1, (12,1) => 0, (12,2) => 0, (8,6) => 0, (12,3) => 0, (12,4) => 0, (8,8) => 1, (4,12) => -1, (16,1) => 0, (12,6) => 0, (16,2) => 0, (16,3) => 0, (12,8) => 0, (8,12) => 0, (16,4) => 0, (4,16) => 0, (16,6) => 0, (8,16) => -1, (12,12) => 1, (16,8) => 0, (24,1) => 0, (24,2) => 0, (24,3) => 0, (24,4) => 0, (12,16) => 0, (4,24) => 1, (16,12) => 0, (24,6) => 0, (24,8) => 0, (8,24) => -1, (16,16) => 1, (32,1) => 0, (32,2) => 0, (32,3) => 0, (3,96) => 0, (24,12) => 0, (32,4) => 0, (12,24) => -1, (4,32) => 0, (32,6) => 0, (24,16) => 0, (8,32) => 0, (32,8) => 0, (16,24) => 0, (32,12) => 0, (12,32) => 0, (24,24) => 1, (32,16) => 0, (16,32) => -1, (48,1) => 0, (48,2) => 0, (48,3) => 0, (48,4) => 0, (4,48) => 0, (48,6) => 0, (24,32) => 0, (32,24) => 0, (8,48) => 1, (48,8) => 0, (12,48) => 0, (48,12) => 0, (32,32) => 1, (48,16) => 0, (16,48) => -1, (1,1) => 1, (1,2) => -1, (1,3) => -1, (1,4) => 0}) assert(toString rankGeneratingFunction B === "q^6+2*q^5+2*q^4+2*q^3+2*q^2+2*q+1") assert(toString zetaPolynomial B == "(1/120)*q^6+(1/12)*q^5+(7/24)*q^4+(5/12)*q^3+(1/5)*q^2") diff --git a/M2/Macaulay2/packages/Probability.m2 b/M2/Macaulay2/packages/Probability.m2 index a3065765aca..514f21bfb3e 100644 --- a/M2/Macaulay2/packages/Probability.m2 +++ b/M2/Macaulay2/packages/Probability.m2 @@ -1,5 +1,5 @@ -- Probability package for Macaulay2 --- Copyright (C) 2022-2025 Doug Torrance +-- Copyright (C) 2022-2026 Doug Torrance -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License @@ -16,8 +16,8 @@ newPackage("Probability", Headline => "basic probability functions", - Version => "0.6", - Date => "November 10, 2025", + Version => "0.7", + Date => "January 13, 2026", Authors => {{ Name => "Doug Torrance", Email => "dtorrance@piedmont.edu", @@ -44,6 +44,9 @@ newPackage("Probability", -* +0.7 (2026-01-13, M2 1.26.05) +* use new syntactic sugar for installing nullary methods + 0.6 (2025-11-10, M2 1.25.11) * update GPL 2 text (FSF no longer has a physical address) @@ -328,7 +331,7 @@ uniformDistribution(Number, Number) := (a, b) -> ( QuantileFunction => p -> a + p * (b - a), Support => (a, b), Description => "U" | toString (a, b))) -installMethod(uniformDistribution, () -> uniformDistribution(0, 1)) +uniformDistribution() := () -> uniformDistribution(0, 1) exponentialDistribution = method() exponentialDistribution Number := lambda -> ( @@ -357,7 +360,7 @@ normalDistribution(Number, Number) := (mu, sigma) -> ( Description => "N" | toString (mu, sigma))) -- standard normal distribution -installMethod(normalDistribution, () -> normalDistribution(0, 1)) +normalDistribution() := () -> normalDistribution(0, 1) gammaDistribution = method() gammaDistribution(Number, Number) := (alpha, lambda) -> ( diff --git a/M2/Macaulay2/packages/Pullback.m2 b/M2/Macaulay2/packages/Pullback.m2 index e0a4a943b32..616dacc8acd 100644 --- a/M2/Macaulay2/packages/Pullback.m2 +++ b/M2/Macaulay2/packages/Pullback.m2 @@ -492,7 +492,11 @@ TEST /// vlist = first entries vars (l1#0); ambPull = ambient l1#0; ambId = ideal l1#0; -assert( (#(minimalPrimes (ideal l1#0)) == 3) and (#vlist == 3) and (0 == (vlist#0)*(vlist#1)) and (0 == (vlist#0)*(vlist#2)) and (0 == (vlist#2)*(vlist#1)) and ( dim(l1#0) == 1) and (pdim ((Ext^2(ambPull^1/ambId, ambPull))**(l1#0)) > 0) ) + assert( (#(minimalPrimes (ideal l1#0)) == 3) and (#vlist == 3) and (0 == (vlist#0)*(vlist#1)) + and (0 == (vlist#0)*(vlist#2)) and (0 == (vlist#2)*(vlist#1)) + and ( dim(l1#0) == 1) + --and (pdim ((Ext^2(ambPull^1/ambId, ambPull))**(l1#0)) > 0) -- this use of pdim isn't quite correct + ) /// -- **TEST3** We next construct an interesting example that should be Cohen-Macaulay (and seminormal and weakly normal, but not WN1, note we don't have a good way to check those things yet). We also check that the singular locus with reduced structure, is regular. (it should be a copy of A^1). diff --git a/M2/Macaulay2/packages/Python.m2 b/M2/Macaulay2/packages/Python.m2 index 5171591a143..b01f81995d9 100644 --- a/M2/Macaulay2/packages/Python.m2 +++ b/M2/Macaulay2/packages/Python.m2 @@ -17,7 +17,8 @@ newPackage("Python", Keywords => {"Interfaces"}, PackageImports => {"Text"}, AuxiliaryFiles => true, - OptionalComponentsPresent => Core#"private dictionary"#?"pythonTrue" + OptionalComponentsPresent => Core#"private dictionary"#?"pythonTrue", + UseCachedExampleOutput => true, ) --------------- @@ -146,13 +147,14 @@ pythonInitialize(ZZ#"Python executable" = executable) pythonHelp = Command (() -> builtins@@help()) -expression PythonObject := expression @@ pythonUnicodeAsUTF8 @@ pythonObjectStr -toString PythonObject := toString @@ expression -net PythonObject := net @@ expression -texMath PythonObject := texMath @@ expression +toString PythonObject := pythonUnicodeAsUTF8 @@ pythonObjectStr +net PythonObject := net @@ toString +texMath PythonObject := texMath @@ toString +hypertext PythonObject := x -> SAMP { toString x, "class" => "language-python" } +html PythonObject := html @@ hypertext describe PythonObject := x -> Describe FunctionApplication(pythonValue, - expression x@@"__repr__"()) + toString x@@"__repr__"()) toExternalString PythonObject := toExternalFormat @@ describe typename = x -> ( @@ -609,6 +611,14 @@ installNumPyMethods = () -> ( load "Python/doc.m2" +-- if in WebAppp mode, let's also import matplotlib and change the backend +if topLevelMode === WebApp then try ( + sys@@"path"@@append Python#"auxiliary files"; + mpl := pythonImportImportModule "matplotlib"; + mpl@@use "module://m2web_backend"; +) + + TEST /// ----------- -- value -- @@ -756,7 +766,7 @@ assert Equation(0 xor y, 2) ---------------------- assert Equation(-x, -5) assert Equation(+x, 5) -assert Equation(x~, -6) +assert Equation(~x, -6) assert Equation(not x, false) /// diff --git a/M2/Macaulay2/packages/Python/doc/bitwise.m2 b/M2/Macaulay2/packages/Python/doc/bitwise.m2 index af58f0d004b..63c4549fcc9 100644 --- a/M2/Macaulay2/packages/Python/doc/bitwise.m2 +++ b/M2/Macaulay2/packages/Python/doc/bitwise.m2 @@ -503,7 +503,7 @@ doc /// Headline bitwise negation of a python object Usage - x~ + ~x Inputs x:PythonObject Outputs @@ -511,10 +511,9 @@ doc /// Description Text This operation negates each bit. For integers, this is equivalent to - CODE "-x - 1". Unlike Python, @CODE "~"@ is a postfix unary operator - in Macaulay2. + CODE "-x - 1". Example - (toPython 5)~ + ~toPython 5 SeeAlso (symbol &, PythonObject, PythonObject) (symbol |, PythonObject, PythonObject) diff --git a/M2/Macaulay2/packages/Python/doc/string.m2 b/M2/Macaulay2/packages/Python/doc/string.m2 index bf630ad772f..30bd680db86 100644 --- a/M2/Macaulay2/packages/Python/doc/string.m2 +++ b/M2/Macaulay2/packages/Python/doc/string.m2 @@ -1,7 +1,6 @@ doc /// Key (toString, PythonObject) - (expression, PythonObject) (net, PythonObject) (texMath, PythonObject) Headline diff --git a/M2/Macaulay2/packages/Python/doc/tutorials.m2 b/M2/Macaulay2/packages/Python/doc/tutorials.m2 index 0dbdd973bb8..2fb87919cc8 100644 --- a/M2/Macaulay2/packages/Python/doc/tutorials.m2 +++ b/M2/Macaulay2/packages/Python/doc/tutorials.m2 @@ -130,28 +130,6 @@ doc /// installNumPyMethods /// --* code for canned example - -pycode = get(Python#"auxiliary files" | "doc/matplotlib-example.py") -pythonRunScript pycode - -matplotlib = PythonContext "import matplotlib.pyplot as plt" -matplotlib "import numpy as np" -matplotlib "fig = plt.figure()" -matplotlib "ax = fig.add_subplot(projection='3d')" -matplotlib "t = np.linspace(-10, 10, 100)" -matplotlib "ax.plot(t, t**2, t**3)" -matplotlib "plt.show()" - -plt = import "matplotlib.pyplot" -np = import "numpy" -fig = plt@@figure() -ax = fig@@"add_subplot"(projection => "3d") -t = np@@linspace(-10, 10, 100); -ax@@plot(t, t^2, t^3) -plt@@show() - -*- doc /// Key @@ -173,51 +151,22 @@ doc /// way is with the @TO pythonRunScript@ method. For this example, we have our code saved as a .py file. - CannedExample - i1 : pycode = get(Python#"auxiliary files" | "doc/matplotlib-example.py") - - o1 = import matplotlib.pyplot as plt - import numpy as np - - fig = plt.figure() - ax = fig.add_subplot(projection='3d') - t = np.linspace(-10, 10, 100) - ax.plot(t, t**2, t**3) - plt.show() - - - i2 : pythonRunScript pycode + Example + pycode = get(Python#"auxiliary files" | "doc/matplotlib-example.py") + pythonRunScript pycode Text @HEADER2 "Using a PythonContext"@ @TO PythonContext@ objects allow us to run Python code one line at a time, just like working in the Python REPL. - CannedExample - i3 : matplotlib = PythonContext "import matplotlib.pyplot as plt" - - o3 = matplotlib - - o3 : PythonContext - - i4 : matplotlib "import numpy as np" - - i5 : matplotlib "fig = plt.figure()" - - i6 : matplotlib "ax = fig.add_subplot(projection='3d')" - - i7 : matplotlib "t = np.linspace(-10, 10, 100)" - - i8 : matplotlib "ax.plot(t, t**2, t**3)" - - o8 = [] - - o8 : PythonObject of class list - - i9 : matplotlib "plt.show()" - - o9 = None - - o9 : PythonObject of class NoneType + Example + matplotlib = PythonContext "import matplotlib.pyplot as plt" + matplotlib "import numpy as np" + matplotlib "fig = plt.figure()" + matplotlib "ax = fig.add_subplot(projection='3d')" + matplotlib "t = np.linspace(-10, 10, 100)" + matplotlib "ax.plot(t, t**2, t**3)" + matplotlib "plt.show()" Text @HEADER2 "Using PythonObjects directly"@ @@ -227,19 +176,9 @@ doc /// First, we import the necessary modules using @TO import@. Note that we can essentially replace the Python @CODE "import foo as bar"@ with @CODE "bar = import \"foo\""@. - CannedExample - i10 : plt = import "matplotlib.pyplot" - - o10 = - - o10 : PythonObject of class module - - i11 : np = import "numpy" - - o11 = - - o11 : PythonObject of class module + Example + plt = import "matplotlib.pyplot" + np = import "numpy" Text Next, we begin to create the various Python objects needed for our plot. @@ -249,34 +188,20 @@ doc /// (see @TO (symbol \@\@, PythonObject, Thing)@). We need to be careful for attributes that include underscores. They must given as strings, i.e., delimited using quotes. - CannedExample - i12 : fig = plt@@figure() - - o12 = Figure(640x480) - - o12 : PythonObject of class matplotlib.figure.Figure - - i13 : ax = fig@@"add_subplot"(projection => "3d") - - o13 = Axes3DSubplot(0.125,0.11;0.775x0.77) - - o13 : PythonObject of class matplotlib.axes._subplots.Axes3DSubplot - - i14 : t = np@@linspace(-10, 10, 100); + Example + fig = plt@@figure() + ax = fig@@"add_subplot"(projection => "3d") + t = np@@linspace(-10, 10, 100); Text Now we construct the twisted cubic. Note that even though Python itself uses @CODE "**"@ for exponentiation, we may use @TO symbol ^@ for consistency with the rest of Macaulay2. - CannedExample - i15 : ax@@plot(t, t^2, t^3) - - o15 = [] - - o15 : PythonObject of class list + Example + ax@@plot(t, t^2, t^3) Text Finally, we show our plot. - CannedExample - i16 : plt@@show() + Example + plt@@show() Text All three of the above methods should have resulted in a window appearing containing the following image. diff --git a/M2/Macaulay2/packages/Python/examples/___Python_sptutorial_co_spplotting_spthe_sptwisted_spcubic_spwith_sp__Matplotlib.out b/M2/Macaulay2/packages/Python/examples/___Python_sptutorial_co_spplotting_spthe_sptwisted_spcubic_spwith_sp__Matplotlib.out new file mode 100644 index 00000000000..69636658a11 --- /dev/null +++ b/M2/Macaulay2/packages/Python/examples/___Python_sptutorial_co_spplotting_spthe_sptwisted_spcubic_spwith_sp__Matplotlib.out @@ -0,0 +1,139 @@ +-- -*- M2-comint -*- hash: 9426046357703022608 + +i1 : pycode = get(Python#"auxiliary files" | "doc/matplotlib-example.py") + +o1 = import matplotlib.pyplot as plt + import numpy as np + + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + t = np.linspace(-10, 10, 100) + ax.plot(t, t**2, t**3) + plt.show() + + +i2 : pythonRunScript pycode + +o2 = {'plt': , 'np': + -8.78787879, -8.58585859, + -7.97979798, -7.77777778, + -7.17171717, -6.96969697, + -6.36363636, -6.16161616, + -5.55555556, -5.35353535, + -4.74747475, -4.54545455, + -3.93939394, -3.73737374, + -3.13131313, -2.92929293, + -2.32323232, -2.12121212, + -1.51515152, -1.31313131, + -0.70707071, -0.50505051, + 0.1010101 , 0.3030303 , + 0.90909091, 1.11111111, + 1.71717172, 1.91919192, + 2.52525253, 2.72727273, + 3.33333333, 3.53535354, + 4.14141414, 4.34343434, + 4.94949495, 5.15151515, + 5.75757576, 5.95959596, + 6.56565657, 6.76767677, + 7.37373737, 7.57575758, + 8.18181818, 8.38383838, + 8.98989899, 9.19191919, + 9.7979798 , 10. ])} + , 'fig':
, 'ax': , + 't': array([-10. , -9.7979798 , -9.5959596 , -9.39393939, + +o2 : PythonObject of class dict + +i3 : matplotlib = PythonContext "import matplotlib.pyplot as plt" + +o3 = matplotlib + +o3 : PythonContext + +i4 : matplotlib "import numpy as np" + +i5 : matplotlib "fig = plt.figure()" + +i6 : matplotlib "ax = fig.add_subplot(projection='3d')" + +i7 : matplotlib "t = np.linspace(-10, 10, 100)" + +i8 : matplotlib "ax.plot(t, t**2, t**3)" + +o8 = [] + +o8 : PythonObject of class list + +i9 : matplotlib "plt.show()" + +o9 = None + +o9 : PythonObject of class NoneType + +i10 : plt = import "matplotlib.pyplot" + +o10 = + +o10 : PythonObject of class module + +i11 : np = import "numpy" + +o11 = + +o11 : PythonObject of class module + +i12 : fig = plt@@figure() + +o12 = Figure(640x480) + +o12 : PythonObject of class matplotlib.figure.Figure + +i13 : ax = fig@@"add_subplot"(projection => "3d") + +o13 = Axes3DSubplot(0.125,0.11;0.775x0.77) + +o13 : PythonObject of class matplotlib.axes._subplots.Axes3DSubplot + +i14 : t = np@@linspace(-10, 10, 100); + +i15 : ax@@plot(t, t^2, t^3) + +o15 = [] + +o15 : PythonObject of class list + +i16 : plt@@show() + +o16 = None + +o16 : PythonObject of class NoneType + +i17 : diff --git a/M2/Macaulay2/packages/Python/m2web_backend.py b/M2/Macaulay2/packages/Python/m2web_backend.py new file mode 100644 index 00000000000..490e7464584 --- /dev/null +++ b/M2/Macaulay2/packages/Python/m2web_backend.py @@ -0,0 +1,99 @@ +import io +import sys +import base64 + +# Compatibility import for different Matplotlib versions +try: + from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase +except ImportError: + from matplotlib.backends.backend_bases import FigureManagerBase, FigureCanvasBase + +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.backends.backend_svg import FigureCanvasSVG + +ASCII_START = chr(17) # DC1 +ASCII_END = chr(18) # DC2 + +# GLOBAL CONFIGURABLE OPTION +# Valid values: "png", "svg" +output_format = "svg" # default + + +def _emit_html(html): + sys.stdout.buffer.write((ASCII_START + html + ASCII_END).encode("utf-8")) + sys.stdout.buffer.flush() + + +class FigureCanvas(FigureCanvasBase): + """ + Thin wrapper that delegates to either Agg or SVG canvas depending + on the chosen output_format. + """ + + def print_m2web(self, figure): + global output_format + + buf = io.BytesIO() + + # Always create a fresh underlying renderer + if output_format == "svg": + real = FigureCanvasSVG(figure) + real.draw() + real.print_svg(buf) + svg = buf.getvalue().decode("utf-8") + html = f"
{svg}
" + _emit_html(html) + + else: # PNG + real = FigureCanvasAgg(figure) + real.draw() + real.print_png(buf) + data = base64.b64encode(buf.getvalue()).decode("ascii") + html = f'' + _emit_html(html) + + +class FigureManager(FigureManagerBase): + pass + + +def show(): + """ + Replacement for pyplot.show(): render + emit all figures. + """ + import matplotlib._pylab_helpers as helpers + for manager in helpers.Gcf.get_all_fig_managers(): + canvas = manager.canvas + canvas.draw() + canvas.print_m2web(canvas.figure) + + +# --- Backend registration hook --- +# Matplotlib expects FigureCanvas to be an actual real renderer subclass. +# We dynamically build wrapper instances here. + +def new_figure_manager(num, *args, **kwargs): + from matplotlib.figure import Figure + + # Matplotlib may pass a custom figure class + FigureClass = kwargs.pop("FigureClass", Figure) + + # Case 1: pyplot already created the Figure instance + if args and isinstance(args[0], Figure): + figure = args[0] + + else: + # Case 2: backend must construct the figure + figure = FigureClass(*args, **kwargs) + + # Choose actual underlying renderer (Agg or SVG) + if output_format == "svg": + real_canvas = FigureCanvasSVG(figure) + else: + real_canvas = FigureCanvasAgg(figure) + + # Wrapper canvas + canvas = FigureCanvas(figure) + + manager = FigureManager(canvas, num) + return manager diff --git a/M2/Macaulay2/packages/QuaternaryQuartics/Appendix2.m2 b/M2/Macaulay2/packages/QuaternaryQuartics/Appendix2.m2 index ccd3e5b2346..f330a2977ab 100644 --- a/M2/Macaulay2/packages/QuaternaryQuartics/Appendix2.m2 +++ b/M2/Macaulay2/packages/QuaternaryQuartics/Appendix2.m2 @@ -281,7 +281,7 @@ doc /// L430 = (trim minors(2, M1)) + groebnerStratum F; --elapsedTime compsL430 = minimalPrimes(L430, Verbosity=>2); Example - C = res(I, FastNonminimal => true) + C = res(I, Strategy => Nonminimal) betti C m1 = submatrixByDegrees(C.dd_2, {3}, {3}) m2 = submatrixByDegrees(C.dd_3, {4}, {4}) diff --git a/M2/Macaulay2/packages/RInterface.m2 b/M2/Macaulay2/packages/RInterface.m2 index 2b664177bb6..715a1535e1b 100644 --- a/M2/Macaulay2/packages/RInterface.m2 +++ b/M2/Macaulay2/packages/RInterface.m2 @@ -1,7 +1,23 @@ +-- RInterface package for Macaulay2 +-- Copyright (C) 2023-2026 Doug Torrance + +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License +-- as published by the Free Software Foundation; either version 2 +-- of the License, or (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. + +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, see . + newPackage("RInterface", Headline => "interface to R for statistical computing", - Version => "0.1", - Date => "January 28, 2024", + Version => "0.2", + Date => "May 7, 2026", Authors => {{ Name => "Doug Torrance", Email => "dtorrance@piedmont.edu", @@ -11,6 +27,24 @@ newPackage("RInterface", AuxiliaryFiles => true, PackageImports => {"ForeignFunctions"}) +--------------- +-- ChangeLog -- +--------------- + +-* + +0.2 (2026-05-07, M2 1.26.05) +* Release under GPL +* Add hash table <-> environment conversion +* Add RValue, RQuote, RContext +* Add support for ~ as a binary operator +* Improved documentation and tests + +0.1 (2024-01-28, M2 1.23) +* initial release + +*- + endpkg = msg -> ( document {Key => RInterface, @@ -29,12 +63,18 @@ export { -- types "RObject", "RFunction", + "RContext", -- methods + "RQuote", "RSymbol", + "RValue", -- objects - "NA" + "NA", + + -- symbols + "Environment" } ---------- @@ -91,6 +131,9 @@ PROTECT = foreignFunction(Rlib, "Rf_protect", SEXP, SEXP) UNPROTECT = foreignFunction(Rlib, "Rf_unprotect", void, int) RtryEval = foreignFunction(Rlib, "R_tryEval", SEXP, {SEXP, SEXP, voidstar}) RGlobalEnv = foreignSymbol(Rlib, "R_GlobalEnv", SEXP) +RBaseEnv = foreignSymbol(Rlib, "R_BaseEnv", SEXP) +PRINTNAME = foreignFunction(Rlib, "PRINTNAME", SEXP, SEXP) +type2char = foreignFunction(Rlib, "Rf_type2char", charstar, int) tryEval = x -> ( err := int 0; @@ -105,16 +148,36 @@ new RFunction from RObject := (T, f) -> ( result := tryEval call; UNPROTECT 1; result)) -new RFunction from String := (T, s) -> RFunction RSymbol s +new RFunction from String := (T, s) -> T RSymbol s +new RFunction from Thing := (T, x) -> T toString x install = foreignFunction(Rlib, "Rf_install", SEXP, charstar) + +RQuote = method() +RQuote String := s -> RObject install s +RQuote Thing := RQuote @@ toString + RSymbol = method() -RSymbol String := s -> RObject install s +RSymbol String := s -> tryEval install s +RSymbol Thing := RSymbol @@ toString + +toStringHelper = (f, x) -> ( + if TYPEOF x == SYMSXP then value CHAR PRINTNAME x + else f x) -net RObject := stack @@ value @@ (RFunction "capture.output") +RtoString = RFunction "toString" +environmentName = RFunction "environmentName" +captureOutput = RFunction "capture.output" -typeof = value @@ (RFunction "typeof"); -RObject.AfterPrint = x -> (RObject, " of type ", typeof x) +toString RObject := x -> value ( + if TYPEOF x == SYMSXP then CHAR PRINTNAME x + else if TYPEOF x == ENVSXP then environmentName x + else RtoString x) +net RObject := x -> ( + if TYPEOF x == SYMSXP then value CHAR PRINTNAME x + else stack value captureOutput x) + +RObject.AfterPrint = x -> (RObject, " of type ", value type2char TYPEOF x) -------------------- -- Macaulay2 -> R -- @@ -163,6 +226,17 @@ new RObject from Matrix := (RFunction "matrix") @@ ((T, x) -> ( "nrow" => numRows x, "ncol" => numColumns x)) +NewEnv = RFunction "new.env" +defineVar = foreignFunction(Rlib, "Rf_defineVar", void, {SEXP, SEXP, SEXP}) +new RObject from HashTable := (T, x) -> ( + if class x =!= HashTable then return x; + if any(keys x, k -> not instance(k, String)) + then error "expected all keys to be strings"; + env := PROTECT NewEnv(0, RBaseEnv, 0); + scanPairs(x, (k, v) -> defineVar(RQuote k, RObject v, env)); + UNPROTECT 1; + T env) + -------------------- -- R -> Macaulay2 -- -------------------- @@ -176,6 +250,7 @@ COMPLEX = foreignFunction(Rlib, "COMPLEX", voidstar, SEXP) stringElt = foreignFunction(Rlib, "STRING_ELT", SEXP, {SEXP, int}) vectorElt = foreignFunction(Rlib, "VECTOR_ELT", SEXP, {SEXP, int}) getAttrib = foreignFunction(Rlib, "Rf_getAttrib", SEXP, {SEXP, SEXP}) +lsInternal = foreignFunction(Rlib, "R_lsInternal", SEXP, {SEXP, int}) NamesSymbol = foreignSymbol(Rlib, "R_NamesSymbol", SEXP) DimSymbol = foreignSymbol(Rlib, "R_DimSymbol", SEXP) @@ -184,6 +259,7 @@ NILSXP = 0 SYMSXP = 1 LISTSXP = 2 CLOSXP = 3 +ENVSXP = 4 LANGSXP = 6 SPECIALSXP = 7 BUILTINSXP = 8 @@ -200,6 +276,9 @@ isIterable = set {LGLSXP, INTSXP, REALSXP, CPLXSXP, STRSXP} valueFunctions = hashTable { NILSXP => x -> null, SYMSXP => x -> value tryEval x, + ENVSXP => x -> ( + names := value RObject lsInternal(x, 0); + hashTable apply(names ?? {}, name -> (name, value x_name))), CHARSXP => x -> value CHAR x, LGLSXP => x -> value(int * LOGICAL x) == 1, INTSXP => x -> value(int * INTEGER x), @@ -208,6 +287,7 @@ valueFunctions = hashTable { z := value(Rcomplex * COMPLEX x); z#"r" + ii * z#"i"), STRSXP => x -> ( + if length x == 0 then return ""; y := stringElt(x, 0); T := TYPEOF y; if T == CHARSXP then value CHAR y @@ -386,7 +466,6 @@ length RObject := value @@ (RFunction "length") -- needs to return a ZZ scan({ (symbol not, "!"), (symbol !, "factorial"), - (symbol ~, "bitwNot"), (conjugate, "Conj"), (Digamma, "digamma"), (Gamma, "gamma"), @@ -423,6 +502,7 @@ scan({ (symbol ^^, "bitwXor"), (symbol <<, "bitwShiftL"), (symbol >>, "bitwShiftR"), + (symbol ~, "~"), (atan2, "atan2"), (Beta, "beta"), (binomial, "choose"), @@ -433,7 +513,62 @@ scan({ installMethod(m2f, RObject, Thing, rf); installMethod(m2f, Thing, RObject, rf))) +bitwNot = RFunction "bitwNot" +tilde = lookup(symbol ~, RObject, RObject) +~ RObject := x -> ( + if member(TYPEOF x, {INTSXP, REALSXP}) + then bitwNot x + else tilde x) + ?? RObject := x -> if TYPEOF x > 0 then x -load "./RInterface/test.m2" +----------------------- +-- evaluating R code -- +----------------------- + +Rparse = RFunction "parse" +Reval = RFunction "eval" + +RValue = method( + Dispatch => Thing, + Options => {Environment => RObject RGlobalEnv}) +RValue String := o -> s -> Reval(Rparse("text" => s), RObject o.Environment) +RValue Sequence := o -> s -> RValue(concatenate \\ toString \ s, o) + +RContext = new SelfInitializingType of MutableHashTable +RContext.synonym = "R context" +globalAssignment RContext + +protect Environment +new RContext := T -> T {Environment => RObject hashTable {}} +new RContext from String := (T, s) -> ( + env := RObject hashTable {}; + RValue(s, Environment => env); + T {Environment => env}) + +RContext String := (ctx, s) -> RValue(s, Environment => ctx.Environment) +RContext_String := (ctx, key) -> ctx.Environment_key + +importFrom(Core, {"Abbreviate", "TABLE", "TD", "TH"}) +listSymbols RObject := x -> ( + if not TYPEOF x == ENVSXP + then error "expected an environment" + else TABLE prepend( + apply({"symbol", "class", "value"}, s -> TH {s}), + apply(value RObject lsInternal(x, 0) ?? {}, + name -> ( + val := x_name; + apply({ + name, + type2char TYPEOF val, + Abbreviate {val}}, s-> TD {s}))))) +listSymbols RContext := ctx -> listSymbols ctx.Environment + +use RContext := ctx -> ( + scan(value RObject lsInternal(ctx.Environment, 0) ?? {}, key -> ( + if not match("[\\._]", toString key) + then getSymbol toString key <- ctx.Environment_key));) + +beginDocumentation() + load "./RInterface/doc.m2" diff --git a/M2/Macaulay2/packages/RInterface/doc.m2 b/M2/Macaulay2/packages/RInterface/doc.m2 index 5eaf5ab2763..bc196d95cf6 100644 --- a/M2/Macaulay2/packages/RInterface/doc.m2 +++ b/M2/Macaulay2/packages/RInterface/doc.m2 @@ -1,9 +1,3 @@ ------------------------------- --- RInterface documentation -- ------------------------------- - -beginDocumentation() - doc /// Key RInterface @@ -21,835 +15,34 @@ doc /// "row.names" => {"Democrat", "Independent", "Republican"}) chisqTest M value oo_"p.value" -/// - -doc /// - Key + Subnodes + :classes RObject - (NewFromMethod, RObject, Boolean) - (NewFromMethod, RObject, CC) - (NewFromMethod, RObject, List) - (NewFromMethod, RObject, Matrix) - (NewFromMethod, RObject, Nothing) - (NewFromMethod, RObject, Number) - (NewFromMethod, RObject, RR) - (NewFromMethod, RObject, Sequence) - (NewFromMethod, RObject, String) - (NewFromMethod, RObject, Symbol) - (NewFromMethod, RObject, ZZ) - (net, RObject) - Headline - R object - Usage - RObject x - Inputs - x:Thing -- a Macaulay2 object - Outputs - :RObject -- the R equivalent of @VAR "x"@ - Description - Text - An @SAMP "RObject"@ is a @SAMP "SEXP"@ (pointer) to an R object - in memory. An @SAMP "RObject"@ may be one of several different - types. Note that in R, most objects are actually vectors, and - scalars are just vectors of length 1. - - @SAMP "RObject"@ is a @TO SelfInitializingType@, and so it acts - as its own constructor method. When passed a Macaulay2 object - as input, the corresponding R object is returned. - - @TO Nothing@ inputs return R's @SAMP "NULL"@. - Example - RObject null - Text - @TO Boolean@ inputs return @EM "logical vectors"@. - Example - RObject true - Text - Another logical vector without a Macaulay2 equivalent is @TO "NA"@. - Example - NA - Text - @TO ZZ@ inputs return @EM "integer vectors"@. - Example - RObject 1 - Text - @TO RR@ (and @TO QQ@) inputs return @EM "double vectors"@. - Example - RObject pi - Text - @TO CC@ inputs return @EM "complex vectors"@. - Example - RObject ii - Text - @TO String@ (and @TO Symbol@) inputs return @EM "character vectors"@. - Example - RObject "Hello, world!" - RObject foo - Text - With a @TO List@ input, the type of the output depends on - the elements of the list. The R function @SAMP "c"@ is used to - combine the elements into a vector. - Example - RObject {true, false, true, false} - RObject {2, 4, 6, 8, 10} - RObject {0, 1/2, exp 1} - RObject apply(3, k -> exp(2*k*pi*ii/3)) - RObject {"foo", "bar", "baz"} - Text - When a @TO Sequence@ is given as input, a @EM "pairlist"@, a - linked list type used by R for function arguments, is returned. - Example - RObject (2, 3, 5) - Text - When a @TO List@ or @TO Sequence@ is given as input and any of its - elements are @TO Option@ objects, then the keys are used as names by R. - Example - RObject {foo => 1, bar => 2} - RObject (baz => 3, qux => 4) - Text - When a @TO Matrix@ is given as input, an R matrix is returned. - Example - A = random(ZZ^2, ZZ^3) - RObject A - Text - Each @SAMP "RObject"@ is displayed by calling R's @SAMP "capture.output"@ - function. - SeeAlso - "arithmetic operators on RObjects" - "bitwise logical operations on RObjects" - "extract or replace parts of RObjects" - "functions on RObjects representing complex numbers" - "hyperbolic functions on RObjects" - "logarithmic and exponential functions on RObjects" - "logical operators on RObjects" - "maxima and minima of RObjects" - "miscellaneous mathematical functions on RObjects" - "relational operators on RObjects" - "rounding of RObjects" - "special mathematical functions on RObjects" - "trigonometric functions on RObjects" -/// - -doc /// - Key RFunction - (NewFromMethod, RFunction, RObject) - (NewFromMethod, RFunction, String) - Headline - R function - Usage - RFunction x - Inputs - x:String -- specifying an R function - Outputs - :RFunction - Description - Text - An @SAMP "RFunction"@ is a function that wraps around an R - function specified by a string. Its arguments are converted to - @TO RObject@'s. - Example - qnorm = RFunction "qnorm" - qnorm(0.025, "lower.tail" => false) -/// - -doc /// - Key + RContext + :functions RSymbol - (RSymbol, String) - Headline - return an R object using its symbol name - Usage - RSymbol str - Inputs - str:String - Outputs - :RObject - Description - Text - Return an R object using its symbol name. For example, this can - be useful for loading datasets. - Example - RSymbol "mtcars" - Text - For R functions, use @TO RFunction@ instead. -/// - -doc /// - Key - "NA" - Headline - missing value - Description - Text - @SAMP "NA"@ is an @TO RObject@ indicating a missing value. It is a - logical vector of length 1. - Example - NA -/// - -doc /// - Key - (value, RObject) - Headline - convert R object to Macaulay2 - Usage - value x - Inputs - x:RObject - Outputs - :Thing -- the Macaulay2 equivalent of @VAR "x"@ - Description - Text - If possible, the Macaulay2 equivalent of the given @TO RObject@ - is returned. - - Note that most R objects are vectors. When a vector has length - 1, the corresponding Macaulay2 object is returned as a scalar. - - When the input is R's @SAMP "NULL"@, @TO null@ is returned. - Example - x = RObject null - value x === null - Text - When the input is a logical vector, a @TO Boolean@ is returned. - Example - x = RObject true - value x - Text - When the input is an integer vector, a @TO ZZ@ object is returned. - Example - x = RObject 5 - value x - Text - When the input is a double vector, a @TO RR@ object is returned. - Example - x = RObject (3/2) - value x - Text - When the input is a complex vector, a @TO CC@ object is returned. - Example - x = RObject ii - value x - Text - When the input is a character vector, a @TO String@ object is returned. - Example - x = RObject foo - value x - Text - When the input is a vector with more than one element, a @TO List@ - object is returned. - Example - x = RObject {1, 3, 5, 7} - value x - Text - When the input is a @EM "pairlist"@ (R's linked list type), a - @TO Sequence@ is returned. - Example - x = RObject (3, 6, 9) - value x - Text - There also exists a @EM "list"@ type in R, created by the function - @SAMP "list"@, that may contain elements of of any type, much like - Macaulay2 lists. When the input is such a list, a @TO List@ object - is returned. - Example - RList = RFunction "list" - x = RList(2, 4, 6, 8) - value x - Text - When the input is a matrix or array, a nested @TO List@ object - is returned. Note that R uses column-major order for - matrices, unlike Macaulay2, which uses row-major order. No - attempt is made to change the order, unlike @TO (NewFromMethod, - RObject, Matrix)@, which does do this conversion. For the most part, - @SAMP "value"@ and @TO RObject@ are inverses of one another, but this - is an exception. - Example - A = random(ZZ^2, ZZ^3) - x = RObject A - value x - A == transpose matrix oo - Text - When the input has any names, the elements with names are returned - as @TO Option@ objects. - Example - x = RObject {foo => 1, 2, bar => 3} - value x -/// - -doc /// - Key - "logical operators on RObjects" - (symbol not, RObject) - (symbol and, RObject, RObject) - (symbol and, RObject, Thing) - (symbol and, Thing, RObject) - (symbol or, RObject, RObject) - (symbol or, RObject, Thing) - (symbol or, Thing, RObject) - (symbol xor, RObject, RObject) - (symbol xor, RObject, Thing) - (symbol xor, Thing, RObject) - Headline - logical operators on R objects - Description - Text - The logical operators @TO symbol not@, @TO symbol and@, @TO symbol or@, - and @TO symbol xor@ are supported when working with logical vectors. - Example - not RObject true - RObject true and RObject false - RObject true or RObject false - RObject true xor RObject false - Text - When a logical vector contains multiple elements, the output will also - contain multiple elements. - Example - RObject {true, true, false, false} and RObject {true, false, true, false} - Text - For the binary operators, one of the operands may be a Macaulay2 - object. It will be converted to an @TO RObject@ before the - operation is performed. - Example - RObject {true, true, false, false} or {true, false, true, false} - {true, true, false, false} xor RObject {true, false, true, false} - Text - Note that @TO symbol and@ and @TO symbol or@ use R's @SAMP "&"@ and - @SAMP "|"@, respectively. -/// - -doc /// - Key - "arithmetic operators on RObjects" - (symbol +, RObject) - (symbol -, RObject) - (symbol +, RObject, RObject) - (symbol +, RObject, Thing) - (symbol +, Thing, RObject) - (symbol -, RObject, RObject) - (symbol -, RObject, Thing) - (symbol -, Thing, RObject) - (symbol *, RObject, RObject) - (symbol *, RObject, Thing) - (symbol *, Thing, RObject) - (symbol /, RObject, RObject) - (symbol /, RObject, Thing) - (symbol /, Thing, RObject) - (symbol ^, RObject, RObject) - (symbol ^, RObject, Thing) - (symbol ^, Thing, RObject) - (symbol %, RObject, RObject) - (symbol %, RObject, Thing) - (symbol %, Thing, RObject) - (symbol //, RObject, RObject) - (symbol //, RObject, Thing) - (symbol //, Thing, RObject) - Headline - arithmetic operators on R objects - Description - Text - The standard arithmetic operators are available when working with R - objects, and in particular, numeric and complex vectors. - - @TO symbol +@ and @TO symbol -@ are both available as unary operators. - Example - x = RObject(-5) - +x - -x - Text - @TO symbol +@, @TO symbol -@, @TO symbol *@, @TO symbol /@, and - @TO symbol ^@ are available as binary operators with the expected - behavior. - Example - x = RObject 5 - y = RObject 2 - x + y - x - y - x * y - x / y - x^y - Text - @TO symbol //@ and @TO symbol %@ have their usual Macaulay2 behaviors - of floor division and modulus, respectively, wrapping around R's - @SAMP "%/%"@ and @SAMP "%%"@. - Example - x // y - x % y - Text - For the binary operators, one of the operands may be a Macaulay2 - object. It will be converted to an @TO RObject@ before the - operation is performed. - Example - x + 2 - 5 + y -/// - -doc /// - Key - "relational operators on RObjects" - (symbol ==, RObject, RObject) - (symbol ==, RObject, Thing) - (symbol ==, Thing, RObject) - (symbol ?, RObject, RObject) - (symbol ?, RObject, Thing) - (symbol ?, Thing, RObject) - Headline - relational operators on R objects - Description - Text - The relational operators @TO symbol ==@, @TO symbol !=@, @TO symbol >@, - @TO symbol <@, @TO symbol >=@, and @TO symbol <=@ do the expected thing - when comparing two @TO RObject@'s. - Example - x = RObject 5 - y = RObject 2 - x == y - x != y - x > y - x < y - x >= y - x < y - Text - One of the operands may be a Macaulay2 object. It will be - converted to an @TO RObject@ before the - operation is performed. - Example - x > 2 - 5 < y - Text - Note that these operators return Macaulay2 @TO Boolean@ objects - and not R logical vectors. In particular, they are not useful - for comparing vectors of length greater than 1. To do this, you - may use @TO RFunction@ to get the corresponding R operator as a - function. - Example - (RFunction ">=")({1, 2, 3}, {2, 2, 2}) -/// - -doc /// - Key - (symbol :, RObject, RObject) - (symbol :, RObject, Thing) - (symbol :, Thing, RObject) - (symbol .., RObject, RObject) - (symbol .., RObject, Thing) - (symbol .., Thing, RObject) - Headline - R colon operator - Description - Text - Generate the sequence between two numbers with step size 1 (or - -1 if the first number is greater than the second). This - operator is available both as @SAMP ":"@, as in R, or @SAMP ".."@, - its Macaulay2 equivalent. - Example - x = RObject 2 - y = RObject 7 - x:y - y:x - x..y - Text - One of the operands may be a Macaulay2 object. It will be - converted to an @TO RObject@ before the operation is performed. - An exception is that when using the @SAMP ":"@ operator and the - first argument given is a Macaulay2 @TO ZZ@ object, - @TO (symbol :, ZZ, Thing)@ will be called instead. - Example - x:7 - 2:y - 2..y -/// - -doc /// - Key - "bitwise logical operations on RObjects" - (symbol ~, RObject) - (symbol &, RObject, RObject) - (symbol &, RObject, Thing) - (symbol &, Thing, RObject) - (symbol |, RObject, RObject) - (symbol |, RObject, Thing) - (symbol |, Thing, RObject) - (symbol ^^, RObject, RObject) - (symbol ^^, RObject, Thing) - (symbol ^^, Thing, RObject) - (symbol <<, RObject, RObject) - (symbol <<, RObject, Thing) - (symbol <<, Thing, RObject) - (symbol >>, RObject, RObject) - (symbol >>, RObject, Thing) - (symbol >>, Thing, RObject) - Headline - bitwise logical operations on R objects - Description - Text - These bitwise logical operations use syntax equivalent to their - Macaulay2 counterparts. - - @TO symbol ~@ is bitwise @EM "not"@, calling R's @SAMP "bitwNot"@. - Example - x = RObject 12 - y = RObject 10 - x~ - Text - @TO symbol &@ is bitwise @EM "and"@, calling R's @SAMP "bitwAnd"@. - Example - x & y - Text - @TO symbol |@ is bitwise @EM "or"@, calling R's @SAMP "bitwOr"@. - Example - x | y - Text - @TO symbol ^^@ is bitwise @EM "xor"@, calling R's @SAMP "bitwXor"@. - Example - x ^^ y - Text - @TO symbol <<@ and @TO symbol >>@ are the bitwise @EM "shift"@ - operators, calling R's @SAMP "bitwShiftL"@ and @SAMP "bitwShiftR"@, - respectively. - Example - x << y - oo >> y - Text - For the binary operators, one of the operands may be a Macaulay2 - object. It will be converted to an @TO RObject@ before the - operation is performed. - Example - x & 10 - 12 | y -/// - -doc /// - Key - "miscellaneous mathematical functions on RObjects" - (abs, RObject) - (sqrt, RObject) - Headline - miscellaneous mathematical functions on R objects - Description - Text - When passed an @TO RObject@, @TO abs@ and @TO sqrt@ do the - expected thing, calling the corresponding R functions to compute - the absolute value and square root of the given input, - respectively. - Example - x = RObject(-2) - abs x - sqrt oo -/// - -doc /// - Key - "special mathematical functions on RObjects" - (Beta, RObject, RObject) - (Beta, RObject, Thing) - (Beta, Thing, RObject) - (Digamma, RObject) - (Gamma, RObject) - (lngamma, RObject) - (binomial, RObject, RObject) - (binomial, RObject, Thing) - (binomial, Thing, RObject) - (symbol !, RObject) - Headline - special mathematical functions on R objects - Description - Text - When at least one of its arguments is an @TO RObject@, @TO Beta@ - calls R's @SAMP "beta"@ function. - Example - Beta(RObject 1, RObject 2) - Text - @TO Gamma@, @TO lngamma@, and @TO Digamma@ call R's @SAMP "gamma"@, - @SAMP "lgamma"@, and @SAMP "digamma"@, respectively, when passed an - @TO RObject@. - Example - Gamma RObject 2 - lngamma RObject 2 - Digamma RObject 2 - Text - @TO binomial@ calls R's @SAMP "choose"@ when one of its arguments is - an @TO RObject@, and the postfix operator @TO symbol !@ calls R's - @SAMP "factorial"@ when passed an @TO RObject@. - Example - binomial(RObject 4, RObject 2) - (RObject 3)! -/// - -doc /// - Key - "rounding of RObjects" - (ceiling, RObject) - (floor, RObject) - (truncate, RObject) - (round, RObject) - (round, RObject, RObject) - (round, RObject, Thing) - (round, Thing, RObject) - Headline - rounding of R objects - Description - Text - These functions round @TO RObject@'s representing numbers in - specific ways. - - @TO ceiling@ and @TO floor@ do the expected thing, calling the - corresponding R function to round up or down, respectively. - Example - x = RObject exp 1 - ceiling x - floor x - Text - @TO truncate@ calls R's @SAMP "trunc"@ function to round toward 0. - Example - truncate x - truncate(-x) - Text - @TO round@ calls the corresponding R function, which rounds to even, - just as in Macaulay2. - Example - round RObject 2.5 - round RObject 3.5 - Text - It may also be called with a second argument, or equivalently, a - @SAMP "digits"@ option, specifying the number of decimal places - to be used when rounding. - Example - round(x, 2) - round(x, digits => 3) -/// - -doc /// - Key - "trigonometric functions on RObjects" - (acos, RObject) - (asin, RObject) - (atan, RObject) - (atan2, RObject, RObject) - (atan2, RObject, Thing) - (atan2, Thing, RObject) - (cos, RObject) - (sin, RObject) - (tan, RObject) - Headline - trigonometric functions on R objects - Description - Text - These functions call R's corresponding trigonometric functions. - Example - cos RObject pi - asin RObject 1 - Text - For @SAMP "atan2"@, one of the operands may be a Macaulay2 - object. It will be converted to an @TO RObject@ before the - operation is performed. - Example - atan2(RObject 1, -1) - atan2(sqrt 3, RObject 1) -/// - -doc /// - Key - "hyperbolic functions on RObjects" - (acosh, RObject) - (asinh, RObject) - (atanh, RObject) - (cosh, RObject) - (sinh, RObject) - (tanh, RObject) - Headline - hyperbolic functions on R objects - Description - Text - These functions call R's corresponding hyperbolic functions. - Example - cosh RObject 0 - asinh RObject(3/4) -/// - -doc /// - Key - "functions on RObjects representing complex numbers" - (conjugate, RObject) - (imaginaryPart, RObject) - (realPart, RObject) - Headline - functions on R objects representing complex numbers - Description - Text - The methods @TO conjugate@, @TO imaginaryPart@, and @TO realPart@ are - overloaded to call R's @SAMP "Conj"@, @SAMP "Im"@, and @SAMP "Re"@ - functions to return the complex conjugate, imaginary part, and real - part respectively, of a given @TO RObject@ representing a complex number - Example - z = RObject(3 + 2*ii) - conjugate z - realPart z - imaginaryPart z -/// - -doc /// - Key - "logarithmic and exponential functions on RObjects" - (exp, RObject) - (expm1, RObject) - (log, RObject) - (log1p, RObject) - Headline - logarithmic and exponential functions on R objects - Description - Text - These functions do the expected thing when passed an @TO RObject@. - Example - exp RObject 1 - log RObject exp 2 -/// - -doc /// - Key - "maxima and minima of RObjects" - (max, RObject) - (min, RObject) - Headline - maxima and minima of R objects - Description - Text - These functions do the expected thing when passed an @TO RObject@. - Example - x = RObject {1, 3, 5} - max x - min x -/// - -doc /// - Key - "extract or replace parts of RObjects" - (symbol _, RObject, Thing) - ((symbol _, symbol =), RObject, Thing) - (symbol SPACE, RObject, Array) - ((symbol SPACE, symbol =), RObject, Array) - Headline - extract or replace parts of R objects - Description - Text - To extract a single element of an @TO RObject@, use an - underscore, which calls R's @SAMP "[["@ operator. Note that R - uses one-based indexing. - Example - x = RObject toList(5..15) - x_1 - Text - To extract multiple elements, use square brackets. This calls R's - @SAMP "["@ operator. - Example - x[1, 4..8] - Text - Parts of an R object may be replaced as well. This only affects the return - value. The original object is not modified. - Example - x_1 = 3 - x[1..4, 8] = {1, 2, 3, 4, 5} - x -/// -doc /// - Key - (iterator, RObject) - Headline - iterate through an R object - Usage - iterator x - Inputs - x:RObject - Outputs - :Iterator - Description - Text - This returns an @TO Iterator@ object that may be used to iterate through - @SAMP "x"@. - Example - i = iterator (RSymbol "iris")_"Petal.Length" - next i - next i - next i -/// - -doc /// - Key - (length, RObject) - Headline - the length of an R object - Usage - length x - Inputs - x:RObject - Outputs - :ZZ - Description - Text - This function returns the length of an RObject. - Example - length (RSymbol "iris")_"Petal.Length" -/// - -doc /// - Key - (product, RObject) - Headline - product of R object - Usage - product x - Inputs - x:RObject - Outputs - :RObject - Description - Text - This function returns the product of all the values present in @SAMP "x"@. - It wraps around R's @SAMP "prod"@ function. - Example - x = RObject {2, 4, 6} - product x -/// - -doc /// - Key - (sum, RObject) - Headline - sum of R object - Usage - sum x - Inputs - x:RObject - Outputs - :RObject - Description - Text - This function returns the sum of all the values present in @SAMP "x"@. - It wraps around R's @SAMP "sum"@ function. - Example - x = RObject {2, 4, 6} - sum x -/// - -doc /// - Key - "library" - Headline - load an R package - Usage - library pkg - Inputs - pkg:String - Description - Text - This loads an R package. It is an @TO RFunction@ that calls R's - @SAMP "library"@ function. - - For example, suppose we want to load the @SAMP "abbey"@ dataset - with nickel content in a Canadian syenite rock. It is available in - the @SAMP "MASS"@ package. - Example - library "MASS" - RSymbol "abbey" -/// + RQuote + RValue +/// + +load "./doc/arithmetic.m2" +load "./doc/bitwise.m2" +load "./doc/complex.m2" +load "./doc/context.m2" +load "./doc/exp-log.m2" +load "./doc/extract.m2" +load "./doc/formulas.m2" +load "./doc/functions.m2" +load "./doc/hyperbolic.m2" +load "./doc/iteration.m2" +load "./doc/logical.m2" +load "./doc/objects.m2" +load "./doc/r-value.m2" +load "./doc/relational.m2" +load "./doc/rounding.m2" +load "./doc/special.m2" +load "./doc/stats.m2" +load "./doc/symbols.m2" +load "./doc/trig.m2" +load "./doc/value.m2" diff --git a/M2/Macaulay2/packages/RInterface/doc/arithmetic.m2 b/M2/Macaulay2/packages/RInterface/doc/arithmetic.m2 new file mode 100644 index 00000000000..bc37e7b46d1 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/arithmetic.m2 @@ -0,0 +1,283 @@ +-------------------------- +-- arithmetic operators -- +-------------------------- + +doc /// + Key + (symbol +, RObject, RObject) + (symbol +, RObject, Thing) + (symbol +, Thing, RObject) + (symbol +, RObject) + Headline + add R objects + Usage + x + y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the sum of x and y + Description + Text + Add two R objects. + Example + x = RObject 5 + y = RObject 2 + x + y + Text + It may also be used as a unary operator. + Example + +x + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x + 2 + 5 + y + SeeAlso + (symbol -, RObject, RObject) + (symbol *, RObject, RObject) +/// + +doc /// + Key + (symbol -, RObject, RObject) + (symbol -, RObject, Thing) + (symbol -, Thing, RObject) + (symbol -, RObject) + Headline + subtract R objects + Usage + x - y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the difference of x and y + Description + Text + Subtract two R objects. + Example + x = RObject 5 + y = RObject 2 + x - y + Text + It may also be used as a unary operator. + Example + -x + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x - 2 + 5 - y + SeeAlso + (symbol +, RObject, RObject) + (symbol *, RObject, RObject) +/// + +doc /// + Key + (symbol *, RObject, RObject) + (symbol *, RObject, Thing) + (symbol *, Thing, RObject) + Headline + multiply R objects + Usage + x * y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the product of x and y + Description + Text + Multiply two R objects. + Example + x = RObject 5 + y = RObject 2 + x * y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x * 2 + 5 * y + SeeAlso + (symbol /, RObject, RObject) + (product, RObject) +/// + +doc /// + Key + (symbol /, RObject, RObject) + (symbol /, RObject, Thing) + (symbol /, Thing, RObject) + Headline + divide R objects + Usage + x / y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the quotient of x and y + Description + Text + Divide two R objects. + Example + x = RObject 5 + y = RObject 2 + x / y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x / 2 + 5 / y + SeeAlso + (symbol *, RObject, RObject) + (symbol //, RObject, RObject) + (symbol %, RObject, RObject) +/// + +doc /// + Key + (symbol ^, RObject, RObject) + (symbol ^, RObject, Thing) + (symbol ^, Thing, RObject) + Headline + exponentiation of R objects + Usage + x ^ y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- @VAR "x"@ raised to the @VAR "y"@ power + Description + Text + Raise one R object to the power of another. + Example + x = RObject 5 + y = RObject 2 + x^y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x^2 + 5^y + SeeAlso + (symbol *, RObject, RObject) + (sqrt, RObject) + (exp, RObject) +/// + +doc /// + Key + (symbol %, RObject, RObject) + (symbol %, RObject, Thing) + (symbol %, Thing, RObject) + Headline + modulo of R objects + Usage + x % y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the remainder when @VAR "x"@ is divided by @VAR "y"@ + Description + Text + Compute the modulo of two R objects, wrapping R's @SAMP "%%"@. + Example + x = RObject 5 + y = RObject 2 + x % y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x % 2 + 5 % y + SeeAlso + (symbol //, RObject, RObject) + (symbol /, RObject, RObject) +/// + +doc /// + Key + (symbol //, RObject, RObject) + (symbol //, RObject, Thing) + (symbol //, Thing, RObject) + Headline + floor division of R objects + Usage + x // y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the floor quotient of @VAR "x"@ divided by @VAR "y"@ + Description + Text + Perform floor division (integer division) of two R objects, wrapping + R's @SAMP "%/%"@. + Example + x = RObject 5 + y = RObject 2 + x // y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x // 2 + 5 // y + SeeAlso + (symbol %, RObject, RObject) + (symbol /, RObject, RObject) +/// + +doc /// + Key + (symbol :, RObject, RObject) + (symbol :, RObject, Thing) + (symbol :, Thing, RObject) + (symbol .., RObject, RObject) + (symbol .., RObject, Thing) + (symbol .., Thing, RObject) + Headline + R colon operator + Usage + x : y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the sequence of integers from @VAR "x"@ to @VAR "y"@ + Description + Text + Generate the sequence between two numbers with step size 1 (or + -1 if the first number is greater than the second). This + operator is available both as @SAMP ":"@, as in R, or @SAMP ".."@, + its Macaulay2 equivalent. + Example + x = RObject 2 + y = RObject 7 + x:y + y:x + x..y + Text + One of the operands may be a Macaulay2 object. It will be + converted to an @TO RObject@ before the operation is performed. + An exception is that when using the @SAMP ":"@ operator and the + first argument given is a Macaulay2 @TO ZZ@ object, + @TO (symbol :, ZZ, Thing)@ will be called instead. + Example + x:7 + 2:y + 2..y + SeeAlso + (symbol +, RObject, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/bitwise.m2 b/M2/Macaulay2/packages/RInterface/doc/bitwise.m2 new file mode 100644 index 00000000000..66f2da15eac --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/bitwise.m2 @@ -0,0 +1,201 @@ +----------------------- +-- bitwise operators -- +----------------------- + +doc /// + Key + (symbol ~, RObject) + Headline + bitwise NOT of an R object, or create a one-sided formula + Usage + ~x + Inputs + x:RObject + Outputs + :RObject -- the bitwise NOT of @VAR "x"@ (integer or real input), + or a one-sided R formula @SAMP "~x"@ (all other inputs) + Description + Text + For integer and real R vectors, @TO symbol ~@ computes bitwise NOT, + calling R's @SAMP "bitwNot"@. + Example + x = RObject 12 + ~x + Text + For all other types, @TO symbol ~@ creates a one-sided R formula, + equivalent to R's @SAMP "~x"@. This is the same operator used for + two-sided formulas; see @TO (symbol ~, RObject, RObject)@. + Example + ~ RQuote "x" + SeeAlso + (symbol ~, RObject, RObject) + (symbol &, RObject, RObject) + (symbol |, RObject, RObject) + (symbol ^^, RObject, RObject) + (symbol not, RObject) +/// + +doc /// + Key + (symbol &, RObject, RObject) + (symbol &, RObject, Thing) + (symbol &, Thing, RObject) + Headline + bitwise AND of R objects + Usage + x & y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the bitwise AND of @VAR "x"@ and @VAR "y"@ + Description + Text + Compute the bitwise AND of two R integer vectors, + calling R's @SAMP "bitwAnd"@. + Example + x = RObject 12 + y = RObject 10 + x & y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x & 10 + SeeAlso + (symbol ~, RObject) + (symbol |, RObject, RObject) + (symbol ^^, RObject, RObject) + (symbol and, RObject, RObject) +/// + +doc /// + Key + (symbol |, RObject, RObject) + (symbol |, RObject, Thing) + (symbol |, Thing, RObject) + Headline + bitwise OR of R objects + Usage + x | y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the bitwise OR of @VAR "x"@ and @VAR "y"@ + Description + Text + Compute the bitwise OR of two R integer vectors, + calling R's @SAMP "bitwOr"@. + Example + x = RObject 12 + y = RObject 10 + x | y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + 12 | y + SeeAlso + (symbol ~, RObject) + (symbol &, RObject, RObject) + (symbol ^^, RObject, RObject) + (symbol or, RObject, RObject) +/// + +doc /// + Key + (symbol ^^, RObject, RObject) + (symbol ^^, RObject, Thing) + (symbol ^^, Thing, RObject) + Headline + bitwise XOR of R objects + Usage + x ^^ y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the bitwise XOR of @VAR "x"@ and @VAR "y"@ + Description + Text + Compute the bitwise XOR of two R integer vectors, + calling R's @SAMP "bitwXor"@. + Example + x = RObject 12 + y = RObject 10 + x ^^ y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + 12 ^^ y + SeeAlso + (symbol ~, RObject) + (symbol &, RObject, RObject) + (symbol |, RObject, RObject) + (symbol xor, RObject, RObject) +/// + +doc /// + Key + (symbol <<, RObject, RObject) + (symbol <<, RObject, Thing) + (symbol <<, Thing, RObject) + Headline + bitwise left shift of an R object + Usage + x << y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- @VAR "x"@ shifted left by @VAR "y"@ bits + Description + Text + Compute the bitwise left shift of an R integer vector, + calling R's @SAMP "bitwShiftL"@. + Example + x = RObject 12 + y = RObject 2 + x << y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x << 2 + SeeAlso + (symbol >>, RObject, RObject) +/// + +doc /// + Key + (symbol >>, RObject, RObject) + (symbol >>, RObject, Thing) + (symbol >>, Thing, RObject) + Headline + bitwise right shift of an R object + Usage + x >> y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- @VAR "x"@ shifted right by @VAR "y"@ bits + Description + Text + Compute the bitwise right shift of an R integer vector, + calling R's @SAMP "bitwShiftR"@. + Example + x = RObject 12 + y = RObject 2 + x << y + oo >> y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x >> 2 + SeeAlso + (symbol <<, RObject, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/complex.m2 b/M2/Macaulay2/packages/RInterface/doc/complex.m2 new file mode 100644 index 00000000000..80ce9318cfe --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/complex.m2 @@ -0,0 +1,72 @@ +-------------------------- +-- complex number functions -- +-------------------------- + +doc /// + Key + (conjugate, RObject) + Headline + complex conjugate of an R object + Usage + conjugate z + Inputs + z:RObject + Outputs + :RObject -- the element-wise complex conjugate of @VAR "z"@ + Description + Text + Compute the complex conjugate of each element of an R complex vector, + calling R's @SAMP "Conj"@. + Example + z = RObject(3 + 2*ii) + conjugate z + SeeAlso + (realPart, RObject) + (imaginaryPart, RObject) +/// + +doc /// + Key + (realPart, RObject) + Headline + real part of an R object + Usage + realPart z + Inputs + z:RObject + Outputs + :RObject -- the element-wise real part of @VAR "z"@ + Description + Text + Extract the real part of each element of an R complex vector, + calling R's @SAMP "Re"@. + Example + z = RObject(3 + 2*ii) + realPart z + SeeAlso + (imaginaryPart, RObject) + (conjugate, RObject) +/// + +doc /// + Key + (imaginaryPart, RObject) + Headline + imaginary part of an R object + Usage + imaginaryPart z + Inputs + z:RObject + Outputs + :RObject -- the element-wise imaginary part of @VAR "z"@ + Description + Text + Extract the imaginary part of each element of an R complex vector, + calling R's @SAMP "Im"@. + Example + z = RObject(3 + 2*ii) + imaginaryPart z + SeeAlso + (realPart, RObject) + (conjugate, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/context.m2 b/M2/Macaulay2/packages/RInterface/doc/context.m2 new file mode 100644 index 00000000000..55764979f56 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/context.m2 @@ -0,0 +1,140 @@ +doc /// + Key + RContext + (NewMethod, RContext) + Headline + R context + Description + Text + An @SAMP "RContext"@ is a wrapper around an R environment that keeps + track of variable bindings across a series of R computations. + + Create an empty context with @SAMP "new RContext"@: + Example + ctx = new RContext + Text + Alternatively, pass an R code string to initialize the context with + variable bindings. See @TO (NewFromMethod, RContext, String)@. + Subnodes + (NewFromMethod, RContext, String) + (symbol SPACE, RContext, String) + (symbol _, RContext, String) + (listSymbols, RContext) + (use, RContext) + SeeAlso + RValue +/// + +doc /// + Key + (NewFromMethod, RContext, String) + Headline + construct an R context + Usage + RContext s + Inputs + s:String -- R code to initialize the context with + Outputs + :RContext + Description + Text + Pass an R code string to initialize the context with + variable bindings: + Example + ctx = RContext "x <- 5L; y <- x^2L" + ctx_"x" + ctx_"y" +/// + +doc /// + Key + (symbol SPACE, RContext, String) + Headline + evaluate R code in a context + Usage + ctx s + Inputs + ctx:RContext + s:String -- R code to evaluate + Outputs + :RObject -- result of evaluating @VAR "s"@ + Description + Text + Evaluates R code in the R environment associated with the context. + Variable assignments persist across calls. + Example + ctx = new RContext + ctx "x <- 5L" + ctx "y <- x + 1L" + ctx "y" +/// + +doc /// + Key + (symbol _, RContext, String) + Headline + get a variable from an R context + Usage + ctx_s + Inputs + ctx:RContext + s:String -- variable name + Outputs + :RObject -- the value of variable @VAR "s"@ in the context + Description + Text + Returns the value of the named variable from the R environment + associated with the context. + Example + ctx = RContext "x <- 42L" + ctx_"x" +/// + +doc /// + Key + (listSymbols, RObject) + (listSymbols, RContext) + Headline + list symbols in an R environment + Usage + listSymbols x + Inputs + x:{RObject, RContext} -- an R environment (type @SAMP "environment"@) or context + Outputs + :TABLE -- an HTML table showing each symbol's name, R type, and value + Description + Text + Returns a table of the symbols defined in an R environment. When given an + @TO RContext@, the symbols in its associated environment are listed. + Example + ctx = RContext "x <- 5L; y <- \"hello\"" + listSymbols ctx + SeeAlso + (use, RContext) +/// + +doc /// + Key + (use, RContext) + Headline + import R context variables into Macaulay2 + Usage + use ctx + Inputs + ctx:RContext + Consequences + Item + Variables in @VAR "ctx"@ whose names do not contain @SAMP "_"@ or + @SAMP "."@ are assigned to Macaulay2 symbols of the same name + Description + Text + Imports the variables from a context's R environment into Macaulay2's + global symbol table. + Example + ctx = RContext "a <- 5L; b <- 10L" + use ctx + a + b + SeeAlso + (listSymbols, RContext) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/exp-log.m2 b/M2/Macaulay2/packages/RInterface/doc/exp-log.m2 new file mode 100644 index 00000000000..d580e0b8b3d --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/exp-log.m2 @@ -0,0 +1,139 @@ +-------------------------------------------- +-- power, exponential, and logarithmic -- +-------------------------------------------- + +doc /// + Key + (abs, RObject) + Headline + absolute value of an R object + Usage + abs x + Inputs + x:RObject + Outputs + :RObject -- the element-wise absolute value of @VAR "x"@ + Description + Text + Compute the absolute value of each element of an R numeric vector, + calling R's @SAMP "abs"@. + Example + x = RObject(-2) + abs x + SeeAlso + (sqrt, RObject) +/// + +doc /// + Key + (sqrt, RObject) + Headline + square root of an R object + Usage + sqrt x + Inputs + x:RObject + Outputs + :RObject -- the element-wise square root of @VAR "x"@ + Description + Text + Compute the square root of each element of an R numeric vector, + calling R's @SAMP "sqrt"@. + Example + x = RObject 2 + sqrt x + SeeAlso + (abs, RObject) + (symbol ^, RObject, RObject) + (exp, RObject) +/// + +doc /// + Key + (exp, RObject) + Headline + exponential of an R object + Usage + exp x + Inputs + x:RObject + Outputs + :RObject -- the element-wise exponential of @VAR "x"@ + Description + Text + Compute @EM "e"@ raised to the power of each element of an R numeric + vector, calling R's @SAMP "exp"@. + Example + exp RObject 1 + SeeAlso + (expm1, RObject) + (log, RObject) +/// + +doc /// + Key + (expm1, RObject) + Headline + exponential minus 1 of an R object + Usage + expm1 x + Inputs + x:RObject + Outputs + :RObject -- the element-wise value of @SAMP "e^x - 1"@ + Description + Text + Compute @SAMP "e^x - 1"@ for each element of an R numeric vector, + calling R's @SAMP "expm1"@. This is more accurate than + @SAMP "exp(x) - 1"@ for small values of @VAR "x"@. + Example + expm1 RObject 0.001 + SeeAlso + (exp, RObject) + (log1p, RObject) +/// + +doc /// + Key + (log, RObject) + Headline + natural logarithm of an R object + Usage + log x + Inputs + x:RObject + Outputs + :RObject -- the element-wise natural logarithm of @VAR "x"@ + Description + Text + Compute the natural logarithm of each element of an R numeric vector, + calling R's @SAMP "log"@. + Example + log RObject exp 2 + SeeAlso + (log1p, RObject) + (exp, RObject) +/// + +doc /// + Key + (log1p, RObject) + Headline + natural logarithm of 1 plus an R object + Usage + log1p x + Inputs + x:RObject + Outputs + :RObject -- the element-wise value of @SAMP "log(1 + x)"@ + Description + Text + Compute @SAMP "log(1 + x)"@ for each element of an R numeric vector, + calling R's @SAMP "log1p"@. This is more accurate than + @SAMP "log(1 + x)"@ for small values of @VAR "x"@. + Example + log1p RObject 0.001 + SeeAlso + (log, RObject) + (expm1, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/extract.m2 b/M2/Macaulay2/packages/RInterface/doc/extract.m2 new file mode 100644 index 00000000000..95325a88aa9 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/extract.m2 @@ -0,0 +1,101 @@ +doc /// + Key + (symbol _, RObject, Thing) + Headline + extract a single element of an R object + Usage + x_i + Inputs + x:RObject + i:Thing -- the index (one-based) + Outputs + :RObject -- the @VAR "i"@-th element of @VAR "x"@ + Description + Text + Extract a single element of an R vector or list using R's @SAMP "[["@ + operator. Note that R uses one-based indexing. + Example + x = RObject toList(5..15) + x_1 + SeeAlso + (symbol SPACE, RObject, Array) + Subnodes + ((symbol _, symbol =), RObject, Thing) +/// + +doc /// + Key + ((symbol _, symbol =), RObject, Thing) + Headline + replace a single element of an R object + Usage + x_i = e + Inputs + x:RObject + i:Thing -- the index (one-based) + e:Thing -- the replacement value + Outputs + :RObject -- a copy of @VAR "x"@ with its @VAR "i"@-th element replaced by @VAR "e"@ + Description + Text + Replace a single element of an R vector or list. Note that this only + affects the return value; the original object @VAR "x"@ is not modified. + Example + x = RObject toList(5..15) + x_1 = 3 + x + SeeAlso + (symbol _, RObject, Thing) + ((symbol SPACE, symbol =), RObject, Array) +/// + +doc /// + Key + (symbol SPACE, RObject, Array) + Headline + extract multiple elements of an R object + Usage + x[i, j, ...] + Inputs + x:RObject + :Array -- indices @VAR "i"@, @VAR "j"@, and so on (one-based) + Outputs + :RObject -- the elements of @VAR "x"@ at the given indices + Description + Text + Extract multiple elements of an R vector or list using R's @SAMP "["@ + operator. + Example + x = RObject toList(5..15) + x[1, 4..8] + SeeAlso + (symbol _, RObject, Thing) + Subnodes + ((symbol SPACE, symbol =), RObject, Array) +/// + +doc /// + Key + ((symbol SPACE, symbol =), RObject, Array) + Headline + replace multiple elements of an R object + Usage + x[i, j, ...] = e + Inputs + x:RObject + :Array -- indices @VAR "i"@, @VAR "j"@, and so on (one-based) + e:Thing -- the replacement values + Outputs + :RObject -- a copy of @VAR "x"@ with elements at the given indices replaced + Description + Text + Replace multiple elements of an R vector or list. Note that this only + affects the return value; the original object @VAR "x"@ is not modified. + Example + x = RObject toList(5..15) + x[1..4, 8] = {1, 2, 3, 4, 5} + x + SeeAlso + (symbol SPACE, RObject, Array) + ((symbol _, symbol =), RObject, Thing) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/formulas.m2 b/M2/Macaulay2/packages/RInterface/doc/formulas.m2 new file mode 100644 index 00000000000..e46784d4dd2 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/formulas.m2 @@ -0,0 +1,27 @@ +doc /// + Key + (symbol ~, RObject, RObject) + (symbol ~, RObject, Thing) + (symbol ~, Thing, RObject) + Headline + create a two-sided R formula + Usage + y ~ x + Inputs + y:RObject + x:RObject + Outputs + :RObject -- a two-sided formula @SAMP "y ~ x"@ + Description + Text + Create a two-sided R formula using R's @SAMP "~"@ operator. + Formulas are used throughout R's statistical modeling functions + to specify relationships between variables. + Example + RQuote mpg ~ RQuote wt + Text + To create a one-sided formula (right-hand side only), use the + unary form; see @TO (symbol ~, RObject)@. + SeeAlso + (symbol ~, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/functions.m2 b/M2/Macaulay2/packages/RInterface/doc/functions.m2 new file mode 100644 index 00000000000..e51aa25cb46 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/functions.m2 @@ -0,0 +1,53 @@ +doc /// + Key + RFunction + (NewFromMethod, RFunction, RObject) + (NewFromMethod, RFunction, String) + (NewFromMethod, RFunction, Thing) + Headline + R function + Usage + RFunction x + Inputs + x:{String, Thing} -- specifying an R function + Outputs + :RFunction + Description + Text + An @SAMP "RFunction"@ is a function that wraps around an R + function specified by a string. Its arguments are converted to + @TO RObject@'s. + Example + qnorm = RFunction "qnorm" + qnorm(0.025, "lower.tail" => false) + Text + Any Macaulay2 object may also be used; it is converted to a string with + @TO toString@. + Example + RFunction sin + oo(pi/4) + Subnodes + "library" +/// + +doc /// + Key + "library" + Headline + load an R package + Usage + library pkg + Inputs + pkg:String + Description + Text + This loads an R package. It is an @TO RFunction@ that calls R's + @SAMP "library"@ function. + + For example, suppose we want to load the @SAMP "abbey"@ dataset + with nickel content in a Canadian syenite rock. It is available in + the @SAMP "MASS"@ package. + Example + library "MASS" + RSymbol "abbey" +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/hyperbolic.m2 b/M2/Macaulay2/packages/RInterface/doc/hyperbolic.m2 new file mode 100644 index 00000000000..03493a472ee --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/hyperbolic.m2 @@ -0,0 +1,141 @@ +------------------------- +-- hyperbolic functions -- +------------------------- + +doc /// + Key + (acosh, RObject) + Headline + hyperbolic arccosine of an R object + Usage + acosh x + Inputs + x:RObject + Outputs + :RObject -- the element-wise hyperbolic arccosine of @VAR "x"@ + Description + Text + Compute the hyperbolic arccosine of each element of an R numeric + vector, calling R's @SAMP "acosh"@. + Example + acosh RObject 1 + SeeAlso + (cosh, RObject) + (asinh, RObject) + (atanh, RObject) +/// + +doc /// + Key + (asinh, RObject) + Headline + hyperbolic arcsine of an R object + Usage + asinh x + Inputs + x:RObject + Outputs + :RObject -- the element-wise hyperbolic arcsine of @VAR "x"@ + Description + Text + Compute the hyperbolic arcsine of each element of an R numeric + vector, calling R's @SAMP "asinh"@. + Example + asinh RObject(3/4) + SeeAlso + (sinh, RObject) + (acosh, RObject) + (atanh, RObject) +/// + +doc /// + Key + (atanh, RObject) + Headline + hyperbolic arctangent of an R object + Usage + atanh x + Inputs + x:RObject + Outputs + :RObject -- the element-wise hyperbolic arctangent of @VAR "x"@ + Description + Text + Compute the hyperbolic arctangent of each element of an R numeric + vector, calling R's @SAMP "atanh"@. + Example + atanh RObject(1/2) + SeeAlso + (tanh, RObject) + (acosh, RObject) + (asinh, RObject) +/// + +doc /// + Key + (cosh, RObject) + Headline + hyperbolic cosine of an R object + Usage + cosh x + Inputs + x:RObject + Outputs + :RObject -- the element-wise hyperbolic cosine of @VAR "x"@ + Description + Text + Compute the hyperbolic cosine of each element of an R numeric + vector, calling R's @SAMP "cosh"@. + Example + cosh RObject 0 + SeeAlso + (acosh, RObject) + (sinh, RObject) + (tanh, RObject) +/// + +doc /// + Key + (sinh, RObject) + Headline + hyperbolic sine of an R object + Usage + sinh x + Inputs + x:RObject + Outputs + :RObject -- the element-wise hyperbolic sine of @VAR "x"@ + Description + Text + Compute the hyperbolic sine of each element of an R numeric + vector, calling R's @SAMP "sinh"@. + Example + sinh RObject 0 + SeeAlso + (asinh, RObject) + (cosh, RObject) + (tanh, RObject) +/// + +doc /// + Key + (tanh, RObject) + Headline + hyperbolic tangent of an R object + Usage + tanh x + Inputs + x:RObject + Outputs + :RObject -- the element-wise hyperbolic tangent of @VAR "x"@ + Description + Text + Compute the hyperbolic tangent of each element of an R numeric + vector, calling R's @SAMP "tanh"@. + Example + tanh RObject 0 + SeeAlso + (atanh, RObject) + (cosh, RObject) + (sinh, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/iteration.m2 b/M2/Macaulay2/packages/RInterface/doc/iteration.m2 new file mode 100644 index 00000000000..4c08a52d2c9 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/iteration.m2 @@ -0,0 +1,39 @@ +doc /// + Key + (iterator, RObject) + Headline + iterate through an R object + Usage + iterator x + Inputs + x:RObject + Outputs + :Iterator + Description + Text + This returns an @TO Iterator@ object that may be used to iterate through + @SAMP "x"@. + Example + i = iterator (RSymbol "iris")_"Petal.Length" + next i + next i + next i +/// + +doc /// + Key + (length, RObject) + Headline + the length of an R object + Usage + length x + Inputs + x:RObject + Outputs + :ZZ + Description + Text + This function returns the length of an RObject. + Example + length (RSymbol "iris")_"Petal.Length" +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/logical.m2 b/M2/Macaulay2/packages/RInterface/doc/logical.m2 new file mode 100644 index 00000000000..a8be7955b5c --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/logical.m2 @@ -0,0 +1,142 @@ +----------------------- +-- logical operators -- +----------------------- + +doc /// + Key + (symbol not, RObject) + Headline + logical negation of an R object + Usage + not x + Inputs + x:RObject + Outputs + :RObject -- the element-wise logical negation of @VAR "x"@ + Description + Text + Compute the logical negation of an R logical vector. + Example + not RObject true + Text + When the input contains multiple elements, the output will also + contain multiple elements. + Example + not RObject {true, false, true, false} + SeeAlso + (symbol and, RObject, RObject) + (symbol or, RObject, RObject) + (symbol xor, RObject, RObject) +/// + +doc /// + Key + (symbol and, RObject, RObject) + (symbol and, RObject, Thing) + (symbol and, Thing, RObject) + Headline + logical conjunction of R objects + Usage + x and y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the element-wise logical conjunction of @VAR "x"@ and @VAR "y"@ + Description + Text + Compute the element-wise logical conjunction of two R logical vectors, + calling R's @SAMP "&"@. + Example + RObject true and RObject false + Text + When the inputs contain multiple elements, the output will also + contain multiple elements. + Example + RObject {true, true, false, false} and RObject {true, false, true, false} + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + RObject {true, true, false, false} and {true, false, true, false} + SeeAlso + (symbol not, RObject) + (symbol or, RObject, RObject) + (symbol xor, RObject, RObject) + (symbol &, RObject, RObject) +/// + +doc /// + Key + (symbol or, RObject, RObject) + (symbol or, RObject, Thing) + (symbol or, Thing, RObject) + Headline + logical disjunction of R objects + Usage + x or y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the element-wise logical disjunction of @VAR "x"@ and @VAR "y"@ + Description + Text + Compute the element-wise logical disjunction of two R logical vectors, + calling R's @SAMP "|"@. + Example + RObject true or RObject false + Text + When the inputs contain multiple elements, the output will also + contain multiple elements. + Example + RObject {true, true, false, false} or RObject {true, false, true, false} + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + {true, true, false, false} or RObject {true, false, true, false} + SeeAlso + (symbol not, RObject) + (symbol and, RObject, RObject) + (symbol xor, RObject, RObject) + (symbol |, RObject, RObject) +/// + +doc /// + Key + (symbol xor, RObject, RObject) + (symbol xor, RObject, Thing) + (symbol xor, Thing, RObject) + Headline + logical exclusive disjunction of R objects + Usage + x xor y + Inputs + x:RObject + y:RObject + Outputs + :RObject -- the element-wise exclusive disjunction of @VAR "x"@ and @VAR "y"@ + Description + Text + Compute the element-wise exclusive disjunction of two R logical vectors, + calling R's @SAMP "xor"@. + Example + RObject true xor RObject false + RObject true xor RObject true + Text + When the inputs contain multiple elements, the output will also + contain multiple elements. + Example + RObject {true, true, false, false} xor RObject {true, false, true, false} + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + RObject {true, true, false, false} xor {true, false, true, false} + SeeAlso + (symbol not, RObject) + (symbol and, RObject, RObject) + (symbol or, RObject, RObject) + (symbol ^^, RObject, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/objects.m2 b/M2/Macaulay2/packages/RInterface/doc/objects.m2 new file mode 100644 index 00000000000..7dadae5cb99 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/objects.m2 @@ -0,0 +1,416 @@ +doc /// + Key + RObject + (net, RObject) + (toString, RObject) + Headline + R object + Usage + RObject x + Inputs + x:Thing -- a Macaulay2 object + Outputs + :RObject -- the R equivalent of @VAR "x"@ + Description + Text + An @SAMP "RObject"@ is a @SAMP "SEXP"@ (pointer) to an R object + in memory. Note that in R, most objects are actually vectors, and + scalars are just vectors of length 1. + + @SAMP "RObject"@ is a @TO SelfInitializingType@, and so it acts + as its own constructor method. When passed a Macaulay2 object + as input, the corresponding R object is returned. See the + @SAMP "converting to R objects"@ section below for the supported + input types. + + Each @SAMP "RObject"@ is displayed by calling R's + @SAMP "capture.output"@ function. + Example + RObject {2, 4, 6, 8} + RObject pi + Text + It is converted to a string using R's @SAMP "toString"@ function. + Example + toString RObject {2, 4, 6, 8} + Subnodes + :converting to R objects + (NewFromMethod, RObject, Boolean) + (NewFromMethod, RObject, CC) + (NewFromMethod, RObject, HashTable) + (NewFromMethod, RObject, List) + (NewFromMethod, RObject, Matrix) + (NewFromMethod, RObject, Nothing) + (NewFromMethod, RObject, RR) + (NewFromMethod, RObject, Sequence) + (NewFromMethod, RObject, String) + (NewFromMethod, RObject, Symbol) + (NewFromMethod, RObject, ZZ) + "NA" + :converting from R objects + (value, RObject) + :extracting or replacing parts + (symbol _, RObject, Thing) + (symbol SPACE, RObject, Array) + :iteration + (iterator, RObject) + (length, RObject) + :arithmetic operators + (symbol +, RObject, RObject) + (symbol -, RObject, RObject) + (symbol *, RObject, RObject) + (symbol /, RObject, RObject) + (symbol ^, RObject, RObject) + (symbol %, RObject, RObject) + (symbol //, RObject, RObject) + (symbol :, RObject, RObject) + :relational operators + (symbol ==, RObject, RObject) + (symbol ?, RObject, RObject) + :logical operators + (symbol not, RObject) + (symbol and, RObject, RObject) + (symbol or, RObject, RObject) + (symbol xor, RObject, RObject) + :bitwise operators + (symbol ~, RObject) + (symbol &, RObject, RObject) + (symbol |, RObject, RObject) + (symbol ^^, RObject, RObject) + (symbol <<, RObject, RObject) + (symbol >>, RObject, RObject) + :rounding + (ceiling, RObject) + (floor, RObject) + (truncate, RObject) + (round, RObject, RObject) + :maxima, minima, sums, and products + (max, RObject) + (min, RObject) + (sum, RObject) + (product, RObject) + :power, exponential, and logarithmic functions + (abs, RObject) + (sqrt, RObject) + (exp, RObject) + (expm1, RObject) + (log, RObject) + (log1p, RObject) + :trigonometric functions + (acos, RObject) + (asin, RObject) + (atan, RObject) + (atan2, RObject, RObject) + (cos, RObject) + (sin, RObject) + (tan, RObject) + :hyperbolic functions + (acosh, RObject) + (asinh, RObject) + (atanh, RObject) + (cosh, RObject) + (sinh, RObject) + (tanh, RObject) + :complex number functions + (conjugate, RObject) + (imaginaryPart, RObject) + (realPart, RObject) + :special mathematical functions + (Beta, RObject, RObject) + (Digamma, RObject) + (Gamma, RObject) + (lngamma, RObject) + (binomial, RObject, RObject) + (symbol !, RObject) + :formulas + (symbol ~, RObject, RObject) +/// + +doc /// + Key + (NewFromMethod, RObject, Nothing) + (symbol ??, RObject) + Headline + create an R NULL object + Usage + RObject null + Inputs + :Nothing + Outputs + :RObject -- R's @SAMP "NULL"@ + Description + Text + Converts Macaulay2's @TO null@ to R's @SAMP "NULL"@. + Example + RObject null + Text + Note that the null coalescing operator @TO symbol ??@ treats + R's @SAMP "NULL"@ as null. + Example + (?? RObject null) === null + RObject null ?? RObject 5 + SeeAlso + RObject + (value, RObject) +/// + +doc /// + Key + (NewFromMethod, RObject, Boolean) + Headline + create an R logical vector from a Boolean + Usage + RObject x + Inputs + x:Boolean + Outputs + :RObject -- a logical vector of length 1 + Description + Text + Converts a Macaulay2 @TO Boolean@ to an R @EM "logical vector"@ of + length 1. + Example + RObject true + RObject false + Text + Another logical vector without a Macaulay2 equivalent is @TO "NA"@. + SeeAlso + "NA" + (NewFromMethod, RObject, List) + (symbol not, RObject) + (symbol and, RObject, RObject) +/// + +doc /// + Key + (NewFromMethod, RObject, ZZ) + Headline + create an R integer vector from an integer + Usage + RObject n + Inputs + n:ZZ + Outputs + :RObject -- an integer vector of length 1 + Description + Text + Converts a Macaulay2 @TO ZZ@ to an R @EM "integer vector"@ of length 1. + Example + RObject 1 + SeeAlso + (NewFromMethod, RObject, RR) +/// + +doc /// + Key + (NewFromMethod, RObject, RR) + (NewFromMethod, RObject, Number) + Headline + create an R double vector from a real or rational number + Usage + RObject x + Inputs + x:RR + Outputs + :RObject -- a double vector of length 1 + Description + Text + Converts a Macaulay2 @TO RR@ (floating-point real number) to an R + @EM "double vector"@ of length 1. + Example + RObject pi + Text + @TO QQ@ and other @TO Number@ subtypes are also converted to R double + vectors. + Example + RObject (3/2) + SeeAlso + (NewFromMethod, RObject, ZZ) + (NewFromMethod, RObject, CC) +/// + +doc /// + Key + (NewFromMethod, RObject, CC) + Headline + create an R complex vector from a complex number + Usage + RObject z + Inputs + z:CC + Outputs + :RObject -- a complex vector of length 1 + Description + Text + Converts a Macaulay2 @TO CC@ (complex number) to an R @EM "complex + vector"@ of length 1. + Example + RObject ii + RObject(3 + 2*ii) + SeeAlso + (NewFromMethod, RObject, RR) + (realPart, RObject) + (imaginaryPart, RObject) + (conjugate, RObject) +/// + +doc /// + Key + (NewFromMethod, RObject, String) + Headline + create an R character vector from a string + Usage + RObject s + Inputs + s:String + Outputs + :RObject -- a character vector of length 1 + Description + Text + Converts a Macaulay2 @TO String@ to an R @EM "character vector"@ of + length 1. + Example + RObject "Hello, world!" + SeeAlso + (NewFromMethod, RObject, Symbol) +/// + +doc /// + Key + (NewFromMethod, RObject, Symbol) + Headline + create an R character vector from a symbol + Usage + RObject s + Inputs + s:Symbol + Outputs + :RObject -- a character vector of length 1 + Description + Text + Converts a Macaulay2 @TO Symbol@ to an R @EM "character vector"@ of + length 1. + Example + RObject foo + SeeAlso + (NewFromMethod, RObject, String) +/// + +doc /// + Key + (NewFromMethod, RObject, List) + Headline + create an R vector from a list + Usage + RObject L + Inputs + L:List + Outputs + :RObject -- an R vector whose type depends on the elements of @VAR "L"@ + Description + Text + Converts a Macaulay2 @TO List@ to an R vector using R's @SAMP "c"@ + function. The type of the resulting vector is determined by the + elements: + Example + RObject {true, false, true, false} + RObject {2, 4, 6, 8, 10} + RObject {0, 1/2, exp 1} + RObject apply(3, k -> exp(2*k*pi*ii/3)) + RObject {"foo", "bar", "baz"} + Text + When any elements are @TO Option@ objects, the keys become names in R. + Example + RObject {foo => 1, bar => 2} + RObject {foo => 1, 2, bar => 3} + SeeAlso + (NewFromMethod, RObject, Sequence) +/// + +doc /// + Key + (NewFromMethod, RObject, Sequence) + Headline + create an R pairlist from a sequence + Usage + RObject s + Inputs + s:Sequence + Outputs + :RObject -- a pairlist + Description + Text + Converts a Macaulay2 @TO Sequence@ to an R @EM "pairlist"@, the + linked-list type used internally by R for function argument lists. + Example + RObject (2, 3, 5) + Text + When any elements are @TO Option@ objects, the keys become names. + Example + RObject (baz => 3, qux => 4) + SeeAlso + (NewFromMethod, RObject, List) +/// + +doc /// + Key + (NewFromMethod, RObject, HashTable) + Headline + create an R environment from a hash table + Usage + RObject t + Inputs + t:HashTable -- with @TO String@ keys + Outputs + :RObject -- an R environment + Description + Text + Converts a Macaulay2 @TO HashTable@ with @TO String@ keys to an R + @EM "environment"@, with each key-value pair becoming a variable binding. + Example + env = RObject hashTable {"x" => 5_ZZ, "y" => 2_ZZ} + RValue("x^y", Environment => env) + value oo +/// + +doc /// + Key + (NewFromMethod, RObject, Matrix) + Headline + create an R matrix from a Macaulay2 matrix + Usage + RObject A + Inputs + A:Matrix + Outputs + :RObject -- an R matrix + Description + Text + Converts a Macaulay2 @TO Matrix@ to an R matrix. Since Macaulay2 + uses row-major order and R uses column-major order, the matrix is + transposed during conversion so that the displayed values match. + Example + A = random(ZZ^2, ZZ^3) + RObject A + Text + Note that @TO (value, RObject)@ does not undo this transposition, + so @SAMP "value"@ and @SAMP "RObject"@ are not exact inverses when + applied to matrices. + Example + value oo + A == transpose matrix oo + SeeAlso + (value, RObject) +/// + +doc /// + Key + "NA" + Headline + missing value + Description + Text + @SAMP "NA"@ is an @TO RObject@ indicating a missing value. It is a + logical vector of length 1. + Example + NA +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/r-value.m2 b/M2/Macaulay2/packages/RInterface/doc/r-value.m2 new file mode 100644 index 00000000000..49f1487a92e --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/r-value.m2 @@ -0,0 +1,45 @@ +doc /// + Key + RValue + (RValue, String) + (RValue, Sequence) + Environment + [RValue, Environment] + Headline + evaluate R code + Usage + RValue s + Inputs + s:{String,Sequence} -- R code to evaluate + Environment => {RObject, HashTable} -- R environment for evaluation + Outputs + :RObject -- result of evaluating @VAR "s"@ + Description + Text + @SAMP "RValue"@ parses and evaluates R code given as a string and returns + the result as an @TO RObject@. + Example + RValue "choose(10, 3)" + Text + When given a @TO Sequence@, the elements are converted to strings with + @TO toString@ and concatenated before evaluation. This is useful for + programmatically constructing R code. + Example + n = 5 + RValue("factorial(", n, ")") + Text + The @CODE "Environment"@ option specifies the R environment in which to + evaluate the code. + Example + env = RObject hashTable {"n" => 10_ZZ, "k" => 3_ZZ} + RValue("choose(n, k)", Environment => env) + Text + A Macaulay2 hash table specifying values of variables with the variable + names given as strings may also be passed to the @CODE "Environment"@ + option. + Example + env2 = hashTable {"x" => 3} + RValue("x + 5", Environment => env2) + SeeAlso + RContext +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/relational.m2 b/M2/Macaulay2/packages/RInterface/doc/relational.m2 new file mode 100644 index 00000000000..1af5586a051 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/relational.m2 @@ -0,0 +1,92 @@ +-------------------------- +-- relational operators -- +-------------------------- + +doc /// + Key + (symbol ==, RObject, RObject) + (symbol ==, RObject, Thing) + (symbol ==, Thing, RObject) + Headline + equality of R objects + Usage + x == y + Inputs + x:RObject + y:RObject + Outputs + :Boolean + Description + Text + Test whether two R objects are equal. + Example + x = RObject 5 + y = RObject 2 + x == y + x == x + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x == 5 + 5 == y + Text + Note that these operators return Macaulay2 @TO Boolean@ objects + and not R logical vectors. In particular, they are not useful + for comparing vectors of length greater than 1. To do this, you + may use @TO RFunction@ to get the corresponding R operator as a + function. + Example + (RFunction "==")({1, 2, 3}, {2, 2, 2}) + SeeAlso + (symbol ?, RObject, RObject) +/// + +doc /// + Key + (symbol ?, RObject, RObject) + (symbol ?, RObject, Thing) + (symbol ?, Thing, RObject) + Headline + comparison of R objects + Usage + x < y + x > y + x <= y + x >= y + Inputs + x:RObject + y:RObject + Outputs + :Boolean + Description + Text + The standard comparison operators work on R objects. + Example + x = RObject 5 + y = RObject 2 + x > y + x < y + x >= y + x <= y + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + Example + x > 2 + 5 < y + Text + Note that these operators return Macaulay2 @TO Boolean@ objects + and not R logical vectors. In particular, they are not useful + for comparing vectors of length greater than 1. To do this, you + may use @TO RFunction@ to get the corresponding R operator as a + function. + Example + (RFunction ">=")({1, 2, 3}, {2, 2, 2}) + Text + Internally, these operators are implemented via Macaulay2's + @TO symbol ?@ method, which returns @CODE "<"@, @CODE ">"@, + @CODE "=="@, or @TO symbol incomparable@. + SeeAlso + (symbol ==, RObject, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/rounding.m2 b/M2/Macaulay2/packages/RInterface/doc/rounding.m2 new file mode 100644 index 00000000000..28cc8538ee3 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/rounding.m2 @@ -0,0 +1,111 @@ +------------- +-- rounding -- +------------- + +doc /// + Key + (ceiling, RObject) + Headline + ceiling of an R object + Usage + ceiling x + Inputs + x:RObject + Outputs + :RObject -- the element-wise ceiling of @VAR "x"@ + Description + Text + Round each element of @VAR "x"@ up to the nearest integer, + calling R's @SAMP "ceiling"@. + Example + x = RObject exp 1 + ceiling x + SeeAlso + (floor, RObject) + (truncate, RObject) + (round, RObject) +/// + +doc /// + Key + (floor, RObject) + Headline + floor of an R object + Usage + floor x + Inputs + x:RObject + Outputs + :RObject -- the element-wise floor of @VAR "x"@ + Description + Text + Round each element of @VAR "x"@ down to the nearest integer, + calling R's @SAMP "floor"@. + Example + x = RObject exp 1 + floor x + SeeAlso + (ceiling, RObject) + (truncate, RObject) + (round, RObject) +/// + +doc /// + Key + (truncate, RObject) + Headline + truncation of an R object + Usage + truncate x + Inputs + x:RObject + Outputs + :RObject -- the element-wise truncation of @VAR "x"@ toward zero + Description + Text + Round each element of @VAR "x"@ toward zero, + calling R's @SAMP "trunc"@. + Example + x = RObject exp 1 + truncate x + truncate(-x) + SeeAlso + (ceiling, RObject) + (floor, RObject) + (round, RObject) +/// + +doc /// + Key + (round, RObject, RObject) + (round, RObject, Thing) + (round, Thing, RObject) + (round, RObject) + Headline + rounding of an R object + Usage + round x + round(x, n) + Inputs + x:RObject + n:RObject -- number of decimal places (default 0) + Description + Text + Round each element of @VAR "x"@ to the nearest integer, + calling R's @SAMP "round"@, which rounds to even (banker's rounding), + just as in Macaulay2. + Example + round RObject 2.5 + round RObject 3.5 + Text + An optional second argument, or equivalently a @SAMP "digits"@ option, + specifies the number of decimal places to use when rounding. + Example + x = RObject exp 1 + round(x, 2) + round(x, digits => 3) + SeeAlso + (ceiling, RObject) + (floor, RObject) + (truncate, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/special.m2 b/M2/Macaulay2/packages/RInterface/doc/special.m2 new file mode 100644 index 00000000000..72a9c87adc8 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/special.m2 @@ -0,0 +1,146 @@ +------------------------------ +-- special mathematical functions -- +------------------------------ + +doc /// + Key + (Beta, RObject, RObject) + (Beta, RObject, Thing) + (Beta, Thing, RObject) + Headline + beta function of R objects + Usage + Beta(a, b) + Inputs + a:RObject + b:RObject + Outputs + :RObject -- the beta function @SAMP "B(a, b)"@ + Description + Text + Compute the beta function of two R numeric vectors, + calling R's @SAMP "beta"@. + Example + Beta(RObject 1, RObject 2) + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + SeeAlso + (Gamma, RObject) + (binomial, RObject, RObject) +/// + +doc /// + Key + (Digamma, RObject) + Headline + digamma function of an R object + Usage + Digamma x + Inputs + x:RObject + Outputs + :RObject -- the element-wise digamma function of @VAR "x"@ + Description + Text + Compute the digamma function (the derivative of the log-gamma function) + of each element of an R numeric vector, calling R's @SAMP "digamma"@. + Example + Digamma RObject 2 + SeeAlso + (Gamma, RObject) + (lngamma, RObject) +/// + +doc /// + Key + (Gamma, RObject) + Headline + gamma function of an R object + Usage + Gamma x + Inputs + x:RObject + Outputs + :RObject -- the element-wise gamma function of @VAR "x"@ + Description + Text + Compute the gamma function of each element of an R numeric vector, + calling R's @SAMP "gamma"@. + Example + Gamma RObject 2 + SeeAlso + (lngamma, RObject) + (Digamma, RObject) + (Beta, RObject, RObject) +/// + +doc /// + Key + (lngamma, RObject) + Headline + log-gamma function of an R object + Usage + lngamma x + Inputs + x:RObject + Outputs + :RObject -- the element-wise log of the gamma function of @VAR "x"@ + Description + Text + Compute the natural logarithm of the gamma function of each element + of an R numeric vector, calling R's @SAMP "lgamma"@. + Example + lngamma RObject 2 + SeeAlso + (Gamma, RObject) + (Digamma, RObject) +/// + +doc /// + Key + (binomial, RObject, RObject) + (binomial, RObject, Thing) + (binomial, Thing, RObject) + Headline + binomial coefficient of R objects + Usage + binomial(n, k) + Inputs + n:RObject + k:RObject + Description + Text + Compute the binomial coefficient of two R numeric vectors, + calling R's @SAMP "choose"@. + Example + binomial(RObject 4, RObject 2) + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + SeeAlso + (symbol !, RObject) + (Beta, RObject, RObject) +/// + +doc /// + Key + (symbol !, RObject) + Headline + factorial of an R object + Usage + x! + Inputs + x:RObject + Outputs + :RObject -- the element-wise factorial of @VAR "x"@ + Description + Text + Compute the factorial of each element of an R numeric vector, + calling R's @SAMP "factorial"@. + Example + (RObject 3)! + SeeAlso + (binomial, RObject, RObject) + (Gamma, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/stats.m2 b/M2/Macaulay2/packages/RInterface/doc/stats.m2 new file mode 100644 index 00000000000..a18635fe8bd --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/stats.m2 @@ -0,0 +1,92 @@ +------------------------------------ +-- maxima, minima, sums, products -- +------------------------------------ + +doc /// + Key + (max, RObject) + Headline + maximum of an R object + Usage + max x + Inputs + x:RObject + Outputs + :RObject -- the maximum value in @VAR "x"@ + Description + Text + Return the maximum value of an R vector. + Example + x = RObject {1, 3, 5} + max x + SeeAlso + (min, RObject) +/// + +doc /// + Key + (min, RObject) + Headline + minimum of an R object + Usage + min x + Inputs + x:RObject + Outputs + :RObject -- the minimum value in @VAR "x"@ + Description + Text + Return the minimum value of an R vector. + Example + x = RObject {1, 3, 5} + min x + SeeAlso + (max, RObject) +/// + +doc /// + Key + (sum, RObject) + Headline + sum of elements of an R object + Usage + sum x + Inputs + x:RObject + Outputs + :RObject -- the sum of all values in @VAR "x"@ + Description + Text + Return the sum of all values in an R vector. + This wraps R's @SAMP "sum"@ function. + Example + x = RObject {2, 4, 6} + sum x + SeeAlso + (product, RObject) + (max, RObject) + (min, RObject) +/// + +doc /// + Key + (product, RObject) + Headline + product of elements of an R object + Usage + product x + Inputs + x:RObject + Outputs + :RObject -- the product of all values in @VAR "x"@ + Description + Text + Return the product of all values in an R vector. + This wraps R's @SAMP "prod"@ function. + Example + x = RObject {2, 4, 6} + product x + SeeAlso + (sum, RObject) + (symbol *, RObject, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/symbols.m2 b/M2/Macaulay2/packages/RInterface/doc/symbols.m2 new file mode 100644 index 00000000000..a43e2b60f02 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/symbols.m2 @@ -0,0 +1,62 @@ +doc /// + Key + RSymbol + (RSymbol, String) + (RSymbol, Thing) + Headline + look up an R object by name + Usage + RSymbol str + Inputs + str:{String, Thing} + Outputs + :RObject -- the value of the R object named @VAR "str"@ + Description + Text + Look up an R object by name, returning its value. For example, + this can be useful for loading datasets. + Example + RSymbol "mtcars" + Text + A Macaulay2 @TO Symbol@ may also be used. + Example + RSymbol mtcars + Text + To obtain an unevaluated R symbol rather than its value, use + @TO RQuote@ instead. For R functions, use @TO RFunction@ instead. + SeeAlso + RQuote + RFunction +/// + +doc /// + Key + RQuote + (RQuote, String) + (RQuote, Thing) + Headline + create an unevaluated R symbol + Usage + RQuote str + Inputs + str:{String, Thing} + Outputs + :RObject -- an R symbol (type @SAMP "symbol"@) + Description + Text + Create an unevaluated R symbol. This is analogous to R's + @SAMP "quote"@ function, and is useful when a symbol itself + (rather than its value) needs to be passed to an R function. + + Compare the behavior of @TO RSymbol@, which evaluates the symbol + and returns its value: + Example + RSymbol "pi" + RQuote "pi" + Text + A Macaulay2 @TO Symbol@ may also be used. + Example + RQuote pi + SeeAlso + RSymbol +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/trig.m2 b/M2/Macaulay2/packages/RInterface/doc/trig.m2 new file mode 100644 index 00000000000..c0a4f083537 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/trig.m2 @@ -0,0 +1,171 @@ +---------------------------- +-- trigonometric functions -- +---------------------------- + +doc /// + Key + (acos, RObject) + Headline + arccosine of an R object + Usage + acos x + Inputs + x:RObject + Outputs + :RObject -- the element-wise arccosine of @VAR "x"@, in radians + Description + Text + Compute the arccosine of each element of an R numeric vector, + calling R's @SAMP "acos"@. + Example + acos RObject 1 + SeeAlso + (cos, RObject) + (asin, RObject) + (atan, RObject) +/// + +doc /// + Key + (asin, RObject) + Headline + arcsine of an R object + Usage + asin x + Inputs + x:RObject + Outputs + :RObject -- the element-wise arcsine of @VAR "x"@, in radians + Description + Text + Compute the arcsine of each element of an R numeric vector, + calling R's @SAMP "asin"@. + Example + asin RObject 1 + SeeAlso + (sin, RObject) + (acos, RObject) + (atan, RObject) +/// + +doc /// + Key + (atan, RObject) + Headline + arctangent of an R object + Usage + atan x + Inputs + x:RObject + Outputs + :RObject -- the element-wise arctangent of @VAR "x"@, in radians + Description + Text + Compute the arctangent of each element of an R numeric vector, + calling R's @SAMP "atan"@. + Example + atan RObject 1 + SeeAlso + (tan, RObject) + (acos, RObject) + (asin, RObject) + (atan2, RObject, RObject) +/// + +doc /// + Key + (atan2, RObject, RObject) + (atan2, RObject, Thing) + (atan2, Thing, RObject) + Headline + two-argument arctangent of R objects + Usage + atan2(y, x) + Inputs + y:RObject + x:RObject + Outputs + :RObject -- the element-wise arctangent of @SAMP "y/x"@, in radians + Description + Text + Compute the two-argument arctangent (i.e., the angle in radians + between the positive x-axis and the point @SAMP "(x, y)"@), + calling R's @SAMP "atan2"@. + Example + atan2(RObject 1, -1) + atan2(sqrt 3, RObject 1) + Text + One of the operands may be a Macaulay2 object. It will be converted + to an @TO RObject@ before the operation is performed. + SeeAlso + (atan, RObject) +/// + +doc /// + Key + (cos, RObject) + Headline + cosine of an R object + Usage + cos x + Inputs + x:RObject + Outputs + :RObject -- the element-wise cosine of @VAR "x"@ (in radians) + Description + Text + Compute the cosine of each element of an R numeric vector, + calling R's @SAMP "cos"@. + Example + cos RObject pi + SeeAlso + (acos, RObject) + (sin, RObject) + (tan, RObject) +/// + +doc /// + Key + (sin, RObject) + Headline + sine of an R object + Usage + sin x + Inputs + x:RObject + Outputs + :RObject -- the element-wise sine of @VAR "x"@ (in radians) + Description + Text + Compute the sine of each element of an R numeric vector, + calling R's @SAMP "sin"@. + Example + sin RObject pi + SeeAlso + (asin, RObject) + (cos, RObject) + (tan, RObject) +/// + +doc /// + Key + (tan, RObject) + Headline + tangent of an R object + Usage + tan x + Inputs + x:RObject + Outputs + :RObject -- the element-wise tangent of @VAR "x"@ (in radians) + Description + Text + Compute the tangent of each element of an R numeric vector, + calling R's @SAMP "tan"@. + Example + tan RObject pi + SeeAlso + (atan, RObject) + (cos, RObject) + (sin, RObject) +/// diff --git a/M2/Macaulay2/packages/RInterface/doc/value.m2 b/M2/Macaulay2/packages/RInterface/doc/value.m2 new file mode 100644 index 00000000000..e4ade09fa38 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/doc/value.m2 @@ -0,0 +1,89 @@ +doc /// + Key + (value, RObject) + Headline + convert R object to Macaulay2 + Usage + value x + Inputs + x:RObject + Outputs + :Thing -- the Macaulay2 equivalent of @VAR "x"@ + Description + Text + If possible, the Macaulay2 equivalent of the given @TO RObject@ + is returned. + + Note that most R objects are vectors. When a vector has length + 1, the corresponding Macaulay2 object is returned as a scalar. + + When the input is R's @SAMP "NULL"@, @TO null@ is returned. + Example + x = RObject null + value x === null + Text + When the input is a logical vector, a @TO Boolean@ is returned. + Example + x = RObject true + value x + Text + When the input is an integer vector, a @TO ZZ@ object is returned. + Example + x = RObject 5 + value x + Text + When the input is a double vector, a @TO RR@ object is returned. + Example + x = RObject (3/2) + value x + Text + When the input is a complex vector, a @TO CC@ object is returned. + Example + x = RObject ii + value x + Text + When the input is a character vector, a @TO String@ object is returned. + Example + x = RObject foo + value x + Text + When the input is a vector with more than one element, a @TO List@ + object is returned. + Example + x = RObject {1, 3, 5, 7} + value x + Text + When the input is a @EM "pairlist"@ (R's linked list type), a + @TO Sequence@ is returned. + Example + x = RObject (3, 6, 9) + value x + Text + There also exists a @EM "list"@ type in R, created by the function + @SAMP "list"@, that may contain elements of of any type, much like + Macaulay2 lists. When the input is such a list, a @TO List@ object + is returned. + Example + RList = RFunction "list" + x = RList(2, 4, 6, 8) + value x + Text + When the input is a matrix or array, a nested @TO List@ object + is returned. Note that R uses column-major order for + matrices, unlike Macaulay2, which uses row-major order. No + attempt is made to change the order, unlike @TO (NewFromMethod, + RObject, Matrix)@, which does do this conversion. For the most part, + @SAMP "value"@ and @TO RObject@ are inverses of one another, but this + is an exception. + Example + A = random(ZZ^2, ZZ^3) + x = RObject A + value x + A == transpose matrix oo + Text + When the input has any names, the elements with names are returned + as @TO Option@ objects. + Example + x = RObject {foo => 1, 2, bar => 3} + value x +/// diff --git a/M2/Macaulay2/packages/RInterface/test.m2 b/M2/Macaulay2/packages/RInterface/test.m2 deleted file mode 100644 index 7d3002c92ce..00000000000 --- a/M2/Macaulay2/packages/RInterface/test.m2 +++ /dev/null @@ -1,199 +0,0 @@ ----------------------- --- RInterface tests -- ----------------------- - ------------ --- tests -- ------------ - -TEST /// --- basic conversions -assert BinaryOperation(symbol ===, value RObject null, null) -assert Equation(value RObject (1, 2, 3), (1, 2, 3)) -assert BinaryOperation(symbol ===, - value RObject ("foo" => 1, "bar" => 2), ("foo" => 1, "bar" => 2)) -assert Equation(value RObject true, true) -assert Equation(value RObject false, false) -assert Equation(value RObject 5, 5) -assert Equation(value RObject 0.2, 0.2) -assert Equation(value RObject(1/2), 0.5) -assert Equation(value RObject pi, numeric pi) -assert Equation(value RObject(2 + 3*ii), 2 + 3*ii) -assert Equation(value RObject "foo", "foo") -assert Equation(value RObject {1, 2, 3}, {1, 2, 3}) -assert BinaryOperation(symbol ===, - value RObject {"foo" => 1, "bar" => 2}, {"foo" => 1, "bar" => 2}) -/// - - -TEST /// --- iterators -assert Equation(toList RObject null, {}) -c = RFunction "c" -assert Equation(value c(true, false), {true, false}) -assert Equation(value c(1, 2, 3), {1, 2, 3}) -assert Equation(value c(1.5, 2.5), {1.5, 2.5}) -assert Equation(value c(ii, 2*ii), {ii, 2*ii}) -assert Equation(value c("foo", "bar"), {"foo", "bar"}) -list' = RFunction "list" -assert Equation(value list'(true, 2, pi), {true, 2, numeric pi}) -/// - -TEST /// --- subscripting -x = RObject {2, 4, 6, 8, 10} -assert Equation(x_1, RObject 2) -assert Equation(x_2, RObject 4) -assert Equation(x_3, RObject 6) -assert Equation(x_4, RObject 8) -assert Equation(x_5, RObject 10) -assert Equation(x[1], RObject 2) -assert Equation(x[1, 3, 5], RObject {2, 6, 10}) -assert Equation(x_1 = 3, RObject {3, 4, 6, 8, 10}) -assert Equation(x[1, 3, 5] = {3, 7, 11}, RObject {3, 4, 7, 8, 11}) -/// - -TEST /// --- binary operators -assert Equation(RObject 5 + RObject 2, RObject 7) -assert Equation(RObject 5 + 2, RObject 7) -assert Equation(5 + RObject 2, RObject 7) -assert Equation(RObject 5 - RObject 2, RObject 3) -assert Equation(RObject 5 - 2, RObject 3) -assert Equation(5 - RObject 2, RObject 3) -assert Equation(RObject 5 * RObject 2, RObject 10) -assert Equation(RObject 5 * 2, RObject 10) -assert Equation(5 * RObject 2, RObject 10) -assert Equation(RObject 5 / RObject 2, RObject 2.5) -assert Equation(RObject 5 / 2, RObject 2.5) -assert Equation(5 / RObject 2, RObject 2.5) -assert Equation(RObject 5^(RObject 2), RObject 25) -assert Equation(RObject 5^2, RObject 25) -assert Equation(5^(RObject 2), RObject 25) -assert Equation(RObject 5 % RObject 2, RObject 1) -assert Equation(RObject 5 % 2, RObject 1) -assert Equation(5 % RObject 2, RObject 1) -assert Equation(RObject 5 // RObject 2, RObject 2) -assert Equation(RObject 5 // 2, RObject 2) -assert Equation(5 // RObject 2, RObject 2) -assert Equation(RObject 5 ?? RObject 2, RObject 5) -assert Equation(RObject null ?? RObject 2, RObject 2) -/// - -TEST /// --- logic operators -assert Equation(RObject true and RObject true, RObject true) -assert Equation(RObject true and true, RObject true) -assert Equation(true and RObject true, RObject true) -assert Equation(RObject true or RObject true, RObject true) -assert Equation(RObject true or true, RObject true) -assert Equation(true or RObject true, RObject true) -assert Equation(RObject true xor RObject true, RObject false) -assert Equation(RObject true xor true, RObject false) -assert Equation(true xor RObject true, RObject false) -assert Equation(not RObject true, RObject false) -/// - -TEST /// --- comparison operators -assert Equation(RObject 2, RObject 2) -assert Equation(RObject 2, 2) -assert Equation(2, RObject 2) -assert BinaryOperation(symbol <, RObject 2, RObject 3) -assert BinaryOperation(symbol <, RObject 2, 3) -assert BinaryOperation(symbol <, 2, RObject 3) -assert BinaryOperation(symbol <=, RObject 2, RObject 3) -assert BinaryOperation(symbol <=, RObject 2, 3) -assert BinaryOperation(symbol <=, 2, RObject 3) -assert BinaryOperation(symbol >, RObject 3, RObject 2) -assert BinaryOperation(symbol >, RObject 3, 2) -assert BinaryOperation(symbol >, 3, RObject 2) -assert BinaryOperation(symbol >=, RObject 3, RObject 2) -assert BinaryOperation(symbol >=, RObject 3, 2) -assert BinaryOperation(symbol >=, 3, RObject 2) -assert BinaryOperation(symbol !=, RObject 2, RObject 3) -assert BinaryOperation(symbol !=, RObject 2, 3) -assert BinaryOperation(symbol !=, 2, RObject 3) -/// - -TEST /// --- matrices/arrays -c = RFunction "c" -matrix' = RFunction "matrix" -array = RFunction "array" -assert Equation(value matrix'(c(1, 2, 3, 11, 12, 13), "nrow" => 2, "ncol" => 3), - {{1, 2}, {3, 11}, {12, 13}}) -assert Equation(value array(c splice(5, 9, 3, 10..15), "dim" => c(3, 3, 2)), { - {{5, 9, 3}, {10, 11, 12}, {13, 14, 15}}, - {{5, 9, 3}, {10, 11, 12}, {13, 14, 15}}}) -/// - -TEST /// --- functions -assert Equation(+RObject 1, RObject 1) -assert Equation(-RObject 1, RObject(-1)) -assert Equation(abs RObject(-1), RObject 1) -assert Equation(acos RObject 1, RObject 0) -assert Equation(acosh RObject 1, RObject 0) -assert Equation(asin RObject 0, RObject 0) -assert Equation(asinh RObject 0, RObject 0) -assert Equation(atan RObject 0, RObject 0) -assert Equation(atanh RObject 0, RObject 0) -assert Equation(ceiling RObject 2.5, RObject 3) -assert Equation(cos RObject 0, RObject 1) -assert Equation(cosh RObject 0, RObject 1) -assert Equation(exp RObject 0, RObject 1) -assert Equation(expm1 RObject 0, RObject 0) -assert Equation(floor RObject 2.5, RObject 2) -assert Equation(log RObject 1, RObject 0) -assert Equation(log1p RObject 0, RObject 0) -assert Equation(max RObject {1, 3, 5}, RObject 5) -assert Equation(min RObject {1, 3, 5}, RObject 1) -assert Equation(round RObject 2.6, RObject 3) -assert Equation(sin RObject 0, RObject 0) -assert Equation(sinh RObject 0, RObject 0) -assert Equation(sqrt RObject 1, RObject 1) -assert Equation(sum RObject {1, 3, 5}, RObject 9) -assert Equation(tan RObject 0, RObject 0) -assert Equation(tanh RObject 0, RObject 0) -assert Equation((RObject 1)!, RObject 1) -assert Equation((RObject 1)~, RObject(-2)) -assert Equation(conjugate RObject ii, RObject(-ii)) -assert(abs(Digamma RObject 1 + RObject EulerConstant) < - RObject(1e-15)) -assert Equation(Gamma RObject 1, RObject 1) -assert Equation(imaginaryPart RObject ii, RObject 1) -assert Equation(lngamma RObject 1, RObject 0) -assert Equation(product RObject {1, 3, 5}, RObject 15) -assert Equation(realPart RObject ii, RObject 0) -assert Equation(truncate RObject 2.5, RObject 2) -assert Equation(value((RObject 1):(RObject 5)), {1, 2, 3, 4, 5}) -assert Equation(value((RObject 1):5), {1, 2, 3, 4, 5}) -assert Equation(value((RObject 1)..(RObject 5)), {1, 2, 3, 4, 5}) -assert Equation(value((RObject 1)..5), {1, 2, 3, 4, 5}) -assert Equation(value(1..(RObject 5)), {1, 2, 3, 4, 5}) -assert Equation(RObject 3 & RObject 5, RObject 1) -assert Equation(RObject 3 & 5, RObject 1) -assert Equation(3 & RObject 5, RObject 1) -assert Equation(RObject 3 | RObject 5, RObject 7) -assert Equation(RObject 3 | 5, RObject 7) -assert Equation(3 | RObject 5, RObject 7) -assert Equation(RObject 3 ^^ RObject 5, RObject 6) -assert Equation(RObject 3 ^^ 5, RObject 6) -assert Equation(3 ^^ RObject 5, RObject 6) -assert Equation(RObject 3 << RObject 5, RObject 96) -assert Equation(RObject 3 << 5, RObject 96) -assert Equation(3 << RObject 5, RObject 96) -assert Equation(RObject 10 >> RObject 1, RObject 5) -assert Equation(RObject 10 >> 1, RObject 5) -assert Equation(10 >> RObject 1, RObject 5) -assert Equation(atan2(RObject 0, RObject 1), RObject 0) -assert Equation(atan2(RObject 0, 1), RObject 0) -assert Equation(atan2(0, RObject 1), RObject 0) -assert Equation(Beta(RObject 1, RObject 1), RObject 1) -assert Equation(Beta(RObject 1, 1), RObject 1) -assert Equation(Beta(1, RObject 1), RObject 1) -assert Equation(binomial(RObject 1, RObject 1), RObject 1) -assert Equation(binomial(RObject 1, 1), RObject 1) -assert Equation(binomial(1, RObject 1), RObject 1) -/// diff --git a/M2/Macaulay2/packages/RInterface/tests/basic-conversions.m2 b/M2/Macaulay2/packages/RInterface/tests/basic-conversions.m2 new file mode 100644 index 00000000000..a554d334da7 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/basic-conversions.m2 @@ -0,0 +1,16 @@ +assert BinaryOperation(symbol ===, value RObject null, null) +assert Equation(value RObject (1, 2, 3), (1, 2, 3)) +assert BinaryOperation(symbol ===, + value RObject ("foo" => 1, "bar" => 2), ("foo" => 1, "bar" => 2)) +assert Equation(value RObject true, true) +assert Equation(value RObject false, false) +assert Equation(value RObject 5, 5) +assert Equation(value RObject 0.2, 0.2) +assert Equation(value RObject(1/2), 0.5) +assert Equation(value RObject pi, numeric pi) +assert Equation(value RObject(2 + 3*ii), 2 + 3*ii) +assert Equation(value RObject "foo", "foo") +assert Equation(value RObject foo, "foo") +assert Equation(value RObject {1, 2, 3}, {1, 2, 3}) +assert BinaryOperation(symbol ===, + value RObject {"foo" => 1, "bar" => 2}, {"foo" => 1, "bar" => 2}) diff --git a/M2/Macaulay2/packages/RInterface/tests/binary-operators.m2 b/M2/Macaulay2/packages/RInterface/tests/binary-operators.m2 new file mode 100644 index 00000000000..04504e18baf --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/binary-operators.m2 @@ -0,0 +1,23 @@ +assert Equation(RObject 5 + RObject 2, RObject 7) +assert Equation(RObject 5 + 2, RObject 7) +assert Equation(5 + RObject 2, RObject 7) +assert Equation(RObject 5 - RObject 2, RObject 3) +assert Equation(RObject 5 - 2, RObject 3) +assert Equation(5 - RObject 2, RObject 3) +assert Equation(RObject 5 * RObject 2, RObject 10) +assert Equation(RObject 5 * 2, RObject 10) +assert Equation(5 * RObject 2, RObject 10) +assert Equation(RObject 5 / RObject 2, RObject 2.5) +assert Equation(RObject 5 / 2, RObject 2.5) +assert Equation(5 / RObject 2, RObject 2.5) +assert Equation(RObject 5^(RObject 2), RObject 25) +assert Equation(RObject 5^2, RObject 25) +assert Equation(5^(RObject 2), RObject 25) +assert Equation(RObject 5 % RObject 2, RObject 1) +assert Equation(RObject 5 % 2, RObject 1) +assert Equation(5 % RObject 2, RObject 1) +assert Equation(RObject 5 // RObject 2, RObject 2) +assert Equation(RObject 5 // 2, RObject 2) +assert Equation(5 // RObject 2, RObject 2) +assert Equation(RObject 5 ?? RObject 2, RObject 5) +assert Equation(RObject null ?? RObject 2, RObject 2) diff --git a/M2/Macaulay2/packages/RInterface/tests/comparison-operators.m2 b/M2/Macaulay2/packages/RInterface/tests/comparison-operators.m2 new file mode 100644 index 00000000000..50056063bf1 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/comparison-operators.m2 @@ -0,0 +1,18 @@ +assert Equation(RObject 2, RObject 2) +assert Equation(RObject 2, 2) +assert Equation(2, RObject 2) +assert BinaryOperation(symbol <, RObject 2, RObject 3) +assert BinaryOperation(symbol <, RObject 2, 3) +assert BinaryOperation(symbol <, 2, RObject 3) +assert BinaryOperation(symbol <=, RObject 2, RObject 3) +assert BinaryOperation(symbol <=, RObject 2, 3) +assert BinaryOperation(symbol <=, 2, RObject 3) +assert BinaryOperation(symbol >, RObject 3, RObject 2) +assert BinaryOperation(symbol >, RObject 3, 2) +assert BinaryOperation(symbol >, 3, RObject 2) +assert BinaryOperation(symbol >=, RObject 3, RObject 2) +assert BinaryOperation(symbol >=, RObject 3, 2) +assert BinaryOperation(symbol >=, 3, RObject 2) +assert BinaryOperation(symbol !=, RObject 2, RObject 3) +assert BinaryOperation(symbol !=, RObject 2, 3) +assert BinaryOperation(symbol !=, 2, RObject 3) diff --git a/M2/Macaulay2/packages/RInterface/tests/formulas.m2 b/M2/Macaulay2/packages/RInterface/tests/formulas.m2 new file mode 100644 index 00000000000..d128d1b48ec --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/formulas.m2 @@ -0,0 +1,9 @@ +isLanguage = RFunction "is.language" +-- unary ~: integer input uses bitwNot +assert Equation(~ RObject 12, RObject(-13)) +-- unary ~: non-integer/real input creates a one-sided formula +assert Equation(value isLanguage (~ RObject "x"), true) +-- binary ~: creates a two-sided formula +assert Equation(value isLanguage (RObject "y" ~ RObject "x"), true) +assert Equation(value isLanguage ("y" ~ RObject "x"), true) +assert Equation(value isLanguage (RObject "y" ~ "x"), true) diff --git a/M2/Macaulay2/packages/RInterface/tests/functions.m2 b/M2/Macaulay2/packages/RInterface/tests/functions.m2 new file mode 100644 index 00000000000..1e2efc44ad7 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/functions.m2 @@ -0,0 +1,68 @@ +assert Equation(+RObject 1, RObject 1) +assert Equation(-RObject 1, RObject(-1)) +assert Equation(abs RObject(-1), RObject 1) +assert Equation(acos RObject 1, RObject 0) +assert Equation(acosh RObject 1, RObject 0) +assert Equation(asin RObject 0, RObject 0) +assert Equation(asinh RObject 0, RObject 0) +assert Equation(atan RObject 0, RObject 0) +assert Equation(atanh RObject 0, RObject 0) +assert Equation(ceiling RObject 2.5, RObject 3) +assert Equation(cos RObject 0, RObject 1) +assert Equation(cosh RObject 0, RObject 1) +assert Equation(exp RObject 0, RObject 1) +assert Equation(expm1 RObject 0, RObject 0) +assert Equation(floor RObject 2.5, RObject 2) +assert Equation(log RObject 1, RObject 0) +assert Equation(log1p RObject 0, RObject 0) +assert Equation(max RObject {1, 3, 5}, RObject 5) +assert Equation(min RObject {1, 3, 5}, RObject 1) +assert Equation(round RObject 2.6, RObject 3) +assert Equation(sin RObject 0, RObject 0) +assert Equation(sinh RObject 0, RObject 0) +assert Equation(sqrt RObject 1, RObject 1) +assert Equation(sum RObject {1, 3, 5}, RObject 9) +assert Equation(tan RObject 0, RObject 0) +assert Equation(tanh RObject 0, RObject 0) +assert Equation((RObject 1)!, RObject 1) +assert Equation(~RObject 1, RObject(-2)) +assert Equation(conjugate RObject ii, RObject(-ii)) +assert(abs(Digamma RObject 1 + RObject EulerConstant) < + RObject(1e-15)) +assert Equation(Gamma RObject 1, RObject 1) +assert Equation(imaginaryPart RObject ii, RObject 1) +assert Equation(lngamma RObject 1, RObject 0) +assert Equation(product RObject {1, 3, 5}, RObject 15) +assert Equation(realPart RObject ii, RObject 0) +assert Equation(truncate RObject 2.5, RObject 2) +assert Equation(value((RObject 1):(RObject 5)), {1, 2, 3, 4, 5}) +assert Equation(value((RObject 1):5), {1, 2, 3, 4, 5}) +assert Equation(value((RObject 1)..(RObject 5)), {1, 2, 3, 4, 5}) +assert Equation(value((RObject 1)..5), {1, 2, 3, 4, 5}) +assert Equation(value(1..(RObject 5)), {1, 2, 3, 4, 5}) +assert Equation(RObject 3 & RObject 5, RObject 1) +assert Equation(RObject 3 & 5, RObject 1) +assert Equation(3 & RObject 5, RObject 1) +assert Equation(RObject 3 | RObject 5, RObject 7) +assert Equation(RObject 3 | 5, RObject 7) +assert Equation(3 | RObject 5, RObject 7) +assert Equation(RObject 3 ^^ RObject 5, RObject 6) +assert Equation(RObject 3 ^^ 5, RObject 6) +assert Equation(3 ^^ RObject 5, RObject 6) +assert Equation(RObject 3 << RObject 5, RObject 96) +assert Equation(RObject 3 << 5, RObject 96) +assert Equation(3 << RObject 5, RObject 96) +assert Equation(RObject 10 >> RObject 1, RObject 5) +assert Equation(RObject 10 >> 1, RObject 5) +assert Equation(10 >> RObject 1, RObject 5) +assert Equation(atan2(RObject 0, RObject 1), RObject 0) +assert Equation(atan2(RObject 0, 1), RObject 0) +assert Equation(atan2(0, RObject 1), RObject 0) +assert Equation(Beta(RObject 1, RObject 1), RObject 1) +assert Equation(Beta(RObject 1, 1), RObject 1) +assert Equation(Beta(1, RObject 1), RObject 1) +assert Equation(binomial(RObject 1, RObject 1), RObject 1) +assert Equation(binomial(RObject 1, 1), RObject 1) +assert Equation(binomial(1, RObject 1), RObject 1) +assert Equation(round(RObject 2.567, 2), RObject 2.57) +assert Equation(round(RObject 2.567, digits => 2), RObject 2.57) diff --git a/M2/Macaulay2/packages/RInterface/tests/iterators.m2 b/M2/Macaulay2/packages/RInterface/tests/iterators.m2 new file mode 100644 index 00000000000..501e2680344 --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/iterators.m2 @@ -0,0 +1,16 @@ +assert Equation(toList RObject null, {}) +c = RFunction "c" +assert Equation(value c(true, false), {true, false}) +assert Equation(value c(1, 2, 3), {1, 2, 3}) +assert Equation(value c(1.5, 2.5), {1.5, 2.5}) +assert Equation(value c(ii, 2*ii), {ii, 2*ii}) +assert Equation(value c("foo", "bar"), {"foo", "bar"}) +list' = RFunction "list" +assert Equation(value list'(true, 2, pi), {true, 2, numeric pi}) +x = RObject {10, 20, 30} +i = iterator x +assert Equation(next i, RObject 10) +assert Equation(next i, RObject 20) +assert Equation(next i, RObject 30) +assert Equation(length RSymbol "letters", 26) +assert Equation(length RSymbol letters, 26) diff --git a/M2/Macaulay2/packages/RInterface/tests/library.m2 b/M2/Macaulay2/packages/RInterface/tests/library.m2 new file mode 100644 index 00000000000..b713187121c --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/library.m2 @@ -0,0 +1,2 @@ +library "MASS" +assert(instance(RSymbol "abbey", RObject)) diff --git a/M2/Macaulay2/packages/RInterface/tests/logic-operators.m2 b/M2/Macaulay2/packages/RInterface/tests/logic-operators.m2 new file mode 100644 index 00000000000..16eea76cb1f --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/logic-operators.m2 @@ -0,0 +1,12 @@ +assert Equation(RObject true and RObject true, RObject true) +assert Equation(RObject true and true, RObject true) +assert Equation(true and RObject true, RObject true) +assert Equation(RObject true or RObject true, RObject true) +assert Equation(RObject true or true, RObject true) +assert Equation(true or RObject true, RObject true) +assert Equation(RObject true xor RObject true, RObject false) +assert Equation(RObject true xor true, RObject false) +assert Equation(true xor RObject true, RObject false) +assert Equation(not RObject true, RObject false) +assert Equation(RObject false and NA, RObject false) +assert Equation(RObject true or NA, RObject true) diff --git a/M2/Macaulay2/packages/RInterface/tests/matrices-arrays.m2 b/M2/Macaulay2/packages/RInterface/tests/matrices-arrays.m2 new file mode 100644 index 00000000000..74e46fee0fc --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/matrices-arrays.m2 @@ -0,0 +1,10 @@ +c = RFunction "c" +matrix' = RFunction "matrix" +array = RFunction "array" +assert Equation(value matrix'(c(1, 2, 3, 11, 12, 13), "nrow" => 2, "ncol" => 3), + {{1, 2}, {3, 11}, {12, 13}}) +assert Equation(value array(c splice(5, 9, 3, 10..15), "dim" => c(3, 3, 2)), { + {{5, 9, 3}, {10, 11, 12}, {13, 14, 15}}, + {{5, 9, 3}, {10, 11, 12}, {13, 14, 15}}}) +A = matrix {{1, 2, 3}, {4, 5, 6}} +assert Equation(A, transpose matrix value RObject A) diff --git a/M2/Macaulay2/packages/RInterface/tests/quote.m2 b/M2/Macaulay2/packages/RInterface/tests/quote.m2 new file mode 100644 index 00000000000..03b27344b8e --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/quote.m2 @@ -0,0 +1,4 @@ +assert Equation(toString RQuote "x", "x") +assert Equation(toString RQuote x, "x") +assert Equation(toString RQuote log, "log") +assert Equation((RFunction log) RObject 1, RObject 0) diff --git a/M2/Macaulay2/packages/RInterface/tests/rvalue.m2 b/M2/Macaulay2/packages/RInterface/tests/rvalue.m2 new file mode 100644 index 00000000000..320b16795db --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/rvalue.m2 @@ -0,0 +1,49 @@ +-- RValue: basic evaluation +assert Equation(value RValue "TRUE", true) +assert Equation(value RValue "1L", 1) +assert Equation(value RValue "\"hello\"", "hello") + +-- RValue: Sequence input (elements are concatenated as strings) +n = 5 +assert Equation(value RValue("sum(1:", n, "L)"), 15) + +-- RValue: Environment option +env = RObject hashTable {"x" => 3, "y" => 4} +assert Equation(value RValue("x + y", Environment => env), 7) +assert Equation(value RValue("x * y", Environment => env), 12) + +-- RObject from HashTable: creates an R environment +env2 = RObject hashTable {"a" => 2_ZZ} +assert Equation(value RValue("a^3", Environment => env2), 8) + +-- RContext: empty constructor +ctx = new RContext + +-- RContext: constructor from string +ctx = RContext "p <- 7L" +assert Equation(value ctx_"p", 7) + +-- RContext: evaluate more code in the same context +ctx "q <- p + 1L" +assert Equation(value ctx_"q", 8) + +-- RContext: (RContext, String) returns result of evaluation +result = ctx "p * q" +assert Equation(value result, 56) + +-- RContext: subscript operator +ctx2 = RContext "val <- 42L" +assert Equation(value ctx2_"val", 42) + +-- listSymbols: check it runs without error on RContext +ctx3 = RContext "m <- 1L; n <- 2L" +listSymbols ctx3 + +-- listSymbols: check it runs without error on RObject (environment) +listSymbols ctx3.Environment + +-- use RContext: imports variables into M2 symbol table +ctxUse = RContext "rval1 <- 5L; rval2 <- 10L" +use ctxUse +assert Equation(value rval1, 5) +assert Equation(value rval2, 10) diff --git a/M2/Macaulay2/packages/RInterface/tests/subscripting.m2 b/M2/Macaulay2/packages/RInterface/tests/subscripting.m2 new file mode 100644 index 00000000000..61ad9c5809f --- /dev/null +++ b/M2/Macaulay2/packages/RInterface/tests/subscripting.m2 @@ -0,0 +1,11 @@ +x = RObject {2, 4, 6, 8, 10} +assert Equation(x_1, RObject 2) +assert Equation(x_2, RObject 4) +assert Equation(x_3, RObject 6) +assert Equation(x_4, RObject 8) +assert Equation(x_5, RObject 10) +assert Equation(x[1], RObject 2) +assert Equation(x[1, 3, 5], RObject {2, 6, 10}) +assert Equation(x_1 = 3, RObject {3, 4, 6, 8, 10}) +assert Equation(x[1, 3, 5] = {3, 7, 11}, RObject {3, 4, 7, 8, 11}) +assert Equation(length x, 5) diff --git a/M2/Macaulay2/packages/RandomComplexes.m2 b/M2/Macaulay2/packages/RandomComplexes.m2 index 2e29f74596f..35c9377d86f 100644 --- a/M2/Macaulay2/packages/RandomComplexes.m2 +++ b/M2/Macaulay2/packages/RandomComplexes.m2 @@ -21,7 +21,7 @@ newPackage( }, Headline => "random complexes over fields or the integers", Keywords => {"Examples and Random Objects"}, - PackageExports => {"OldChainComplexes", "SimplicialComplexes"}, + PackageExports => {"Complexes", "SimplicialComplexes"}, PackageImports => {"LLLBases"} ) @@ -71,15 +71,15 @@ maximalEntry=method() maximalEntry(Matrix) := m -> ( max(flatten entries m/abs)+0.0) -maximalEntry(ChainComplex) := C -> ( +maximalEntry(Complex) := C -> ( R:= ring C; if not( R === ZZ or R === QQ or R === RR_53 ) then - error "expect a ChainComplex over ZZ ,QQ or RR_53"; + error "expect a Complex over ZZ ,QQ or RR_53"; for i from min C+1 to max C list maximalEntry C.dd_i) disturb = method(Options => {Strategy => Discrete}) -disturb(ChainComplex,RR) := opts -> (C,epsilon) -> ( - chainComplex for i from 1 to length C list ( +disturb(Complex,RR) := opts -> (C,epsilon) -> ( + complex for i from 1 to length C list ( c := rank C_(i-1); d := rank C_i; e := maximalEntry C.dd_i; @@ -92,10 +92,10 @@ disturb(ChainComplex,RR) := opts -> (C,epsilon) -> ( entry=C.dd_i_(k,l)*(1+epsilon*(2*random(RR)-1))))) -- C.dd_i +e*epsilon*(2*random(RR^c,RR^d)-oneMatrix(c,d)) )) -disturb(ChainComplex,RR) := ChainComplex => opts -> (C,epsilon) -> ( +disturb(Complex,RR) := Complex => opts -> (C,epsilon) -> ( if ring C =!= ZZ and ring C =!= QQ and not instance(ring C, RealField) then error "expected a chain complex over ZZ, QQ, or RR"; - chainComplex for i from 1 to length C list ( + complex for i from 1 to length C list ( c := rank C_(i-1); d := rank C_i; elems := entries C.dd_i; @@ -112,7 +112,7 @@ testTimeForLLLonSyzygies(ZZ,ZZ):= opts->(r,n)->( t1:=timing (B:=syz A**QQ); B=lift(B,ZZ); t2:=timing(C:=LLL B); - (append(maximalEntry chainComplex(A,B),maximalEntry C),t1#0,t2#0) + (append(maximalEntry complex{A,B},maximalEntry C),t1#0,t2#0) ) TEST/// @@ -149,7 +149,7 @@ randomChainComplex(List,List):= opts -> (h,r)-> ( if opts.ZeroMean then C=C-mean*oneMatrix(rr_i,c_i); L=append(L,A*B*C); ); - return chainComplex L) + return complex L) TEST /// needsPackage("SVDComplexes") @@ -194,8 +194,8 @@ randomSimplicialComplex(ZZ,ZZ):= (k,n) -> ( N:=#sets-k-2; I:=monomialIdeal apply(apply(n,i->sets_(random(N)+k+2)),s->product(s,i->x_i)); c:=simplicialComplex I; - CQ:=chainComplex complex c; - C:=(chainComplex apply(length CQ-1,i->lift(CQ.dd_(i+1),ZZ))) + CQ:=complex complex c; + C:=(complex apply(length CQ-1,i->lift(CQ.dd_(i+1),ZZ))) ) TEST /// @@ -211,7 +211,7 @@ TEST /// /// normalize = method() -normalize ChainComplex := C-> ( +normalize Complex := C-> ( minC := min C; maxC := max C; D := if ring C === ZZ then C**QQ else C; @@ -222,7 +222,7 @@ normalize ChainComplex := C-> ( ); -- this next line is not correct: it might negate some differentials. -- if the complex has minC odd... - chainComplex C'[-minC] + (complex C')[-minC] ) beginDocumentation() @@ -234,7 +234,7 @@ doc /// support for creating random complexes over the integers Description Text - We implement two methods to create a random @TO "ChainComplex"@ over the integers. + We implement two methods to create a random @TO "Complex"@ over the integers. The first method (@TO randomChainComplex@) builds the complex from products of randomly chosen matrices of desired rank. The limitation of this method to produce large complexes over the integers with moderate Height is the use of the LLL algorithm to improve the presentation of @@ -270,7 +270,7 @@ doc /// ZeroMean => Boolean whether to balance the random numbers around zero Outputs - C:ChainComplex + C:Complex a random chain complex over the integers whose homology ranks match $h$, and whose matrices have ranks given by $r$ Description @@ -314,7 +314,7 @@ doc /// k:ZZ n:ZZ Outputs - C:ChainComplex + C:Complex the chainComplex of the Stanley-Reisner simplicial complex of a random square free monomial ideal in k+1 variables and n generators Description @@ -333,17 +333,17 @@ doc /// doc /// Key normalize - (normalize,ChainComplex) + (normalize,Complex) Headline - normalize a ChainComplex over QQ or RR + normalize a Complex over QQ or RR Usage B = normalize C Inputs - C:ChainComplex + C:Complex over RR or QQ Outputs - B:ChainComplex - an isomorphic ChainComplex over QQ or RR + B:Complex + an isomorphic Complex over QQ or RR Description Text We divide each matrix by its entry of maximal absolute value, to obtain a complex with entries of absolute size $\le 1$. @@ -358,14 +358,14 @@ doc /// doc /// Key maximalEntry - (maximalEntry,ChainComplex) + (maximalEntry,Complex) (maximalEntry,Matrix) Headline maximal absolute value of the entries of the matrix or matrices Usage m = maximalEntries C Inputs - C:ChainComplex + C:Complex or a @TO "Matrix"@, over ZZ, QQ, or RR Outputs m:List @@ -386,21 +386,21 @@ doc /// doc /// Key disturb - (disturb,ChainComplex,RR) + (disturb,Complex,RR) [disturb, Strategy] Headline disturb the matrices of a chain complex over RR Usage B = disturb(C,epsilon) Inputs - C:ChainComplex + C:Complex over RR or QQ epsilon:RR Strategy => Symbol either Discrete or Continuous, whether the disturbed values should be drawn from a discrete distribution or a continuous distribution Outputs - B:ChainComplex + B:Complex a sequence of matrices over RR Description Text @@ -592,7 +592,7 @@ TEST /// BR = normalize CR BR.dd - D = chainComplex{map(RR^1, RR^3, 0), map(RR^3, RR^1, {{1.0},{3.0},{5.0}})} + D = complex{map(RR^1, RR^3, 0), map(RR^3, RR^1, {{1.0},{3.0},{5.0}})} D.dd^2 normalize D D.dd @@ -712,7 +712,7 @@ elapsedTime (h,U)=SVDComplex CR; Sigma = source U e=1e-10 -nearlyZero=elapsedTime chainComplex apply(length C,i->(U_i*Sigma.dd_(i+1)*transpose U_(i+1)-C.dd_(i+1))) +nearlyZero=elapsedTime complex apply(length C,i->(U_i*Sigma.dd_(i+1)*transpose U_(i+1)-C.dd_(i+1))) -- needs speed up for multiplication maximalEntry nearlyZero /// @@ -897,8 +897,8 @@ Ud#0 *(source Ud).dd_1 *transpose Ud#1 - D.dd_1 Ud#1 *(source Ud).dd_2 *transpose Ud#2 - D.dd_2 Ud#2 *(source Ud).dd_3 *transpose Ud#3 - D.dd_3 -F=chainComplex apply(3,i->U#i *(source U).dd_(i+1) *transpose U#(i+1)) -E=chainComplex apply(3,i->Ud#i *(source Ud).dd_(i+1) *transpose Ud#(i+1)) +F=complex apply(3,i->U#i *(source U).dd_(i+1) *transpose U#(i+1)) +E=complex apply(3,i->Ud#i *(source Ud).dd_(i+1) *transpose Ud#(i+1)) D.dd^2 E.dd^2 (h,Ue)=SVDComplex(E,Strategy=>Laplacian,Threshold=>1e-8); @@ -945,7 +945,7 @@ tex Cplus.dd_0 tex CplusQ.dd_0 -B=chainComplex CplusQ +B=complex CplusQ B.dd^2 C.dd^2 B[3] diff --git a/M2/Macaulay2/packages/RandomCurvesOverVerySmallFiniteFields.m2 b/M2/Macaulay2/packages/RandomCurvesOverVerySmallFiniteFields.m2 index 5563936ac8c..f5693e91ca5 100644 --- a/M2/Macaulay2/packages/RandomCurvesOverVerySmallFiniteFields.m2 +++ b/M2/Macaulay2/packages/RandomCurvesOverVerySmallFiniteFields.m2 @@ -10,7 +10,7 @@ newPackage( HomePage =>"https://www.math.uni-sb.de/ag/schreyer"}}, Headline=> "general canonical curves of genus <= 15 over fields with small characteristic", Keywords => {"Examples and Random Objects"}, - PackageImports => {"OldChainComplexes", "Elimination","Truncations","Complexes"} + PackageImports => {"Elimination","Truncations","Complexes"} ) export{"isSmoothCurve", @@ -104,7 +104,7 @@ isSmoothCurve (Ideal) := C -> ( --This function puts everything together smoothCanonicalCurve = method(Options => {Details => false, Printing => false}) smoothCanonicalCurve (ZZ,ZZ) := opt -> (g,p) -> ( - if p == 57 then error "57 is the Grotehdieck prime number"; + if p == 57 then error "57 is the Grothendieck prime number"; if isPrime(p) == false then error "p is not prime"; if (g > 15) then error"not implemented yet"; if (g < 11) then ( diff --git a/M2/Macaulay2/packages/RandomGenus14Curves.m2 b/M2/Macaulay2/packages/RandomGenus14Curves.m2 index 098565c44ef..f1e2b2234fd 100644 --- a/M2/Macaulay2/packages/RandomGenus14Curves.m2 +++ b/M2/Macaulay2/packages/RandomGenus14Curves.m2 @@ -12,7 +12,7 @@ newPackage( Headline => "random smooth curves of genus 14", Keywords => {"Examples and Random Objects"}, PackageExports => {"RandomObjects"}, - PackageImports => {"OldChainComplexes", "Truncations"}, + PackageImports => {"Complexes", "Truncations"}, DebuggingMode => false ) diff --git a/M2/Macaulay2/packages/RandomIdeals.m2 b/M2/Macaulay2/packages/RandomIdeals.m2 index a03bcdeec1a..d9a802bb921 100644 --- a/M2/Macaulay2/packages/RandomIdeals.m2 +++ b/M2/Macaulay2/packages/RandomIdeals.m2 @@ -73,7 +73,7 @@ randomMonomialIdeal(List, Ring) := Ideal => (L,S)->( Ls := apply(Lu, t -> #positions(L, s->(s==t))); --handle the initial degree M := flatten entries basis(Lu#0, S^1); - if Ls#0 < #M then I := ideal((random M)_{0..Ls#0-1}) + if Ls#0 < #M then I := ideal shuffle(M, Ls#0) else ( I = ideal(M); <<"***** there are only " @@ -86,7 +86,7 @@ randomMonomialIdeal(List, Ring) := Ideal => (L,S)->( --now the rest of the degrees, if any. for t from 1 to #Lu-1 do ( M = flatten entries compress (basis(Lu#t,S^1) % I); - if Ls#t < #M then I = I+ideal((random M)_{0..Ls#t-1}) + if Ls#t < #M then I = I+ideal shuffle(M, Ls#t-1) else ( if #M=!=0 then I = I+ideal(M); print("low degree gens generated everything"); @@ -112,7 +112,7 @@ randomSquareFreeMonomialIdeal(List, Ring) := Ideal => (L,S)->( Lu := sort unique L; Ls := apply(Lu, t -> #positions(L, s->(s==t))); M := flatten entries gens squareFree(Lu#0, S); - if Ls#0 < #M then I := ideal((random M)_{0..Ls#0-1}) + if Ls#0 < #M then I := ideal shuffle(M, Ls#0) else (I = ideal(M); <<"***** there are only " < (L,S)->( ); for t from 1 to #Lu-1 do ( M=flatten entries compress (gens squareFree(Lu#t,S) % I); - if Ls#t < #M then I = I+ideal((random M)_{0..Ls#t-1}) + if Ls#t < #M then I = I+ideal shuffle(M, Ls#t) else (if #M=!=0 then I = I+ideal(M); print("low degree gens generated everything"); break @@ -1460,9 +1460,9 @@ S=ZZ/101[a..e] setRandomSeed 123456 assert (randomMonomial(7,S)==a*b^3*c^3) setRandomSeed 123456 -assert(randomMonomialIdeal({3,4,5}, S)==ideal(d*e^2,a*b*d^2,b*c^3*e)) +assert(randomMonomialIdeal({3,4,5}, S)==ideal(b^2*d)) setRandomSeed 123456 -assert(randomSquareFreeMonomialIdeal({6,4,4},S)==ideal(a*b*c*e,a*c*d*e)) +assert(randomSquareFreeMonomialIdeal({6,4,4},S)==ideal(a*b*d*e,a*c*d*e)) setRandomSeed 123456 assert(ideal(8*a^2+5*a*b+4*b^2+35*b*c+3*b*d+36*b*e,29*a^2+22*a*b+32*b^2-44*b*c-6*b*d+40*b*e) == randomIdeal({2,2},matrix{{a^2,b}})) setRandomSeed 123456 diff --git a/M2/Macaulay2/packages/RandomMonomialIdeals.m2 b/M2/Macaulay2/packages/RandomMonomialIdeals.m2 index 7cb3403ab31..8ae9e5446f1 100644 --- a/M2/Macaulay2/packages/RandomMonomialIdeals.m2 +++ b/M2/Macaulay2/packages/RandomMonomialIdeals.m2 @@ -49,7 +49,7 @@ newPackage( }, Headline => "Erdos-Renyi-type random monomial ideals", Keywords => {"Examples and Random Objects"}, - PackageImports => { "OldChainComplexes", "Depth", "BoijSoederberg", "Serialization" }, + PackageImports => { "Complexes", "Depth", "BoijSoederberg", "Serialization" }, DebuggingMode => false, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry", @@ -217,7 +217,7 @@ writeSample (Sample, String) := (s, dirname) -> ( if fileExists dirname then ( stderr << "warning: directory or file with this name already exists." << endl; if not isDirectory dirname then ( - stderr << "warning: overwrting file." << endl; + stderr << "warning: overwriting file." << endl; removeFile dirname; mkdir dirname; ); @@ -355,7 +355,8 @@ randomMonomialSet (PolynomialRing,ZZ,ZZ) := List => o -> (R,D,M) -> ( if M<0 then stderr << "warning: M expected to be a nonnegative integer" << endl; if o.Strategy === "Minimal" then error "Minimal not implemented for fixed size ER model"; allMonomials := flatten flatten apply(toList(1..D),d->entries basis(d,R)); - C := take(random(allMonomials), M); + print(#allMonomials); + C := shuffle(allMonomials, min(M, #allMonomials)); if C==={} then {0_R} else C ) @@ -376,12 +377,13 @@ randomMonomialSet (PolynomialRing,ZZ,List) := List => o -> (R,D,pOrM) -> ( if o.Strategy === "Minimal" then ( currentRingM := R; apply(D, d->( - chosen := take(random(flatten entries basis(d+1, currentRingM)), pOrM_d); + mons := flatten entries basis(d+1, currentRingM); + chosen := shuffle(mons, min(pOrM_d, #mons)); B = flatten append(B, chosen/(i->sub(i, R))); currentRingM = currentRingM/promote(ideal(chosen), currentRingM) ) ) - ) else B = flatten apply(toList(1..D), d->take(random(flatten entries basis(d,R)), pOrM_(d-1))); + ) else B = flatten apply(toList(1..D), d->shuffle(flatten entries basis(d,R), pOrM_(d-1))); ) else if all(pOrM,q->instance(q,RR)) then ( if any(pOrM,q-> q<0.0 or 1.0 (R1, L1) ->( if (d <= 0) then ( ); - tempList := random genList; + tempList := shuffle genList; if (opts.Verify) then ( if (#tempList < monomialForms + trueMonomialForms + binomialForms) then (tempList = tempList | apply(monomialForms + trueMonomialForms + binomialForms - #tempList, i->(genList)#(random d))); ); @@ -471,7 +471,7 @@ getRandomLinearForms(Ring, List) := opts -> (R1, L1) ->( ); formList = formList | apply(constForms, i -> random(0, R1)); - return random formList; + return shuffle formList; ); @@ -845,7 +845,7 @@ linearIntersectionNew(ZZ, Ideal) := opts -> (n1, I1) -> ( if ((not homogFlag) and ((fastDim0(workingIdeal) == true))) then (--if we are using decompose if opts.Verbose or debugLevel > 0 then print("linearIntersectionNew: We found at least one point"); - ptList = random decompose trim (workingIdeal); + ptList = shuffle decompose trim (workingIdeal); if opts.Verbose or debugLevel > 0 then print("linearIntersectionNew: We found " | toString(#ptList) | " points."); j=0; sortedPtList = sort apply(#ptList, t -> {0, degree (ptList#t), t}); @@ -862,7 +862,7 @@ linearIntersectionNew(ZZ, Ideal) := opts -> (n1, I1) -> ( I3 = psi(I2); newS2 = target psi; m2 = psi(ptList#j); - newPtList = random decompose(m2); --make sure we are picking points randomly from this decomposition + newPtList = shuffle decompose(m2); --make sure we are picking points randomly from this decomposition --since these points are going to be conjugate, we only pick 1. if (#newPtList > 0) then ( finalPoint = idealToPoint(newPtList#0); @@ -1391,7 +1391,7 @@ findANonZeroMinor(ZZ, Matrix, Ideal) := opts -> (n,M,I)->( Mcolumnextract = M_N1; M11 := mutableMatrix phi(Mcolumnextract); N2 = (rowRankProfile(M11)); - N1rand := random(N1); + N1rand := shuffle(N1); N1new = {}; for i from 0 to n-1 do( N1new = join(N1new, {N1rand#i}); @@ -1401,7 +1401,7 @@ findANonZeroMinor(ZZ, Matrix, Ideal) := opts -> (n,M,I)->( if (rank(M3) "random smooth space curves", Keywords => {"Examples and Random Objects"}, - PackageImports => {"OldChainComplexes", "Complexes"}, + PackageImports => {"Complexes"}, PackageExports => {"RandomObjects"}, DebuggingMode => false ) diff --git a/M2/Macaulay2/packages/RationalPoints2.m2 b/M2/Macaulay2/packages/RationalPoints2.m2 index 2213b0fd8d5..a50929b91bc 100644 --- a/M2/Macaulay2/packages/RationalPoints2.m2 +++ b/M2/Macaulay2/packages/RationalPoints2.m2 @@ -73,7 +73,7 @@ pow = (n, lst) -> ( -- Utility function that shuffles two list u and v according to a list ind -- of indices in 0 (for u) and 1 (for v) -- -shuffle = (ind, u, v) -> ( +shuffle' = (ind, u, v) -> ( (i,j) := (-1,-1); return for k in (0..<#ind) list if ind_k==0 then (i=i+1; u_i) else (j=j+1; v_j); ); @@ -759,7 +759,7 @@ rationalPoints(Ideal) := opts -> I -> ( unusedR := k(monoid[x_{0.. I -> ( if AMOUNT then result = result*(#ELS)^unused+((#ELS)^unused-1)//(#ELS-1) else ( -- shuffle and homogenize so that the first non-zero coordinate is 1 - result = flatten table(pow_unused ELS, result, homogCoord @@ shuffle_ind); + result = flatten table(pow_unused ELS, result, homogCoord @@ shuffle'_ind); -- extra points lying in a projective subspace PP := findProjPoints(ideal 0_unusedR); - result = result | PP / (x->homogCoord shuffle(ind, x, ((n-unused):0_k))); + result = result | PP / (x->homogCoord shuffle'(ind, x, ((n-unused):0_k))); ); ); ) else ( -- affine case result = findPoints I; if unused > 0 then ( -- reconstruction if AMOUNT then result = result*(#ELS)^unused - else result = flatten table(pow_unused ELS, result, shuffle_ind); + else result = flatten table(pow_unused ELS, result, shuffle'_ind); ); ); ) else ( -- number field diff --git a/M2/Macaulay2/packages/Regularity.m2 b/M2/Macaulay2/packages/Regularity.m2 index 1a46dcb7bab..d17250bc7b9 100644 --- a/M2/Macaulay2/packages/Regularity.m2 +++ b/M2/Macaulay2/packages/Regularity.m2 @@ -13,7 +13,7 @@ newPackage( --=========================================================================-- -- This package is based on --- [BG1] Bermejo, Gimenez "Saturation and Castelnuovo-mumford Regularity", +-- [BG1] Bermejo, Gimenez "Saturation and Castelnuovo-Mumford Regularity", -- Journal of Algebra 303/2006 -- [BG2] Bermejo, Gimenez "Computing the Castelnuovo-Mumford Regularity of some -- subschemes of P^n using quotients of monomial ideals", @@ -223,7 +223,7 @@ document { PARA {TT "Regularity", " is a package for computing the Castelnuovo-Mumford regularity of homogeneous ideals in a polynomial ring without having to compute a minimal free resolution of the homogeneous ideal"}, - PARA {"This package is based on two articles by Bermejo and Gimenez: ", TT"Saturation and Castelnuovo-mumford Regularity", ", Journal of Algebra 303/2006 + PARA {"This package is based on two articles by Bermejo and Gimenez: ", TT"Saturation and Castelnuovo-Mumford Regularity", ", Journal of Algebra 303/2006 and ", TT"Computing the Castelnuovo-Mumford Regularity of some subschemes of P^n using quotients of monomial ideals", ", Journal of Pure and Applied Algebra 164/2001."} } @@ -236,7 +236,7 @@ document { MonCurve => Boolean =>{ " parameter that should be set to true if I is the ideal of a monomial curve"} }, Outputs =>{ "the Castelnuovo-Mumford regularity of the given ideal, if it is homogeneous, and -1 otherwise"}, - PARA {"This package is based on two articles by Bermejo and Gimenez: ", TT"Saturation and Castelnuovo-mumford Regularity", ", Journal of Algebra 303/2006 + PARA {"This package is based on two articles by Bermejo and Gimenez: ", TT"Saturation and Castelnuovo-Mumford Regularity", ", Journal of Algebra 303/2006 and ", TT"Computing the Castelnuovo-Mumford Regularity of some subschemes of P^n using quotients of monomial ideals", ", Journal of Pure and Applied Algebra 164/2001."}, PARA {"computing the regularity of the defining ideal of the second Veronesean of P3"}, EXAMPLE lines /// diff --git a/M2/Macaulay2/packages/RelativeCanonicalResolution.m2 b/M2/Macaulay2/packages/RelativeCanonicalResolution.m2 index 0ccd6210a1f..76f05ea7235 100644 --- a/M2/Macaulay2/packages/RelativeCanonicalResolution.m2 +++ b/M2/Macaulay2/packages/RelativeCanonicalResolution.m2 @@ -10,7 +10,7 @@ newPackage( HomePage => "http://www.math.uni-sb.de/ag-schreyer/index.php/people/researchers/74-michael-hahn"}}, Headline=> "the relative canonical resolution for g-nodal canonical curves with a fixed g^1_k", Keywords => {"Commutative Algebra"}, - PackageExports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry", "journal URI" => "https://msp.org/jsag/", @@ -181,7 +181,7 @@ balancedPartition (ZZ,ZZ) := (n,m) -> ( -- m items list of length n (L,L1) := ({},{}); PHI := matrix map(S^1,S^1,0_S); while (#flatten entries PHI < g) do ( - pts := random(apply(p,i -> matrix{{i,1}})|{matrix{{1,0}}}); + pts := shuffle(apply(p,i -> matrix{{i,1}})|{matrix{{1,0}}}); while (#L < g or #(unique flatten L) < #(flatten L)) do ( (L,L1) = ({},{}); f := random(S^1, S^{2:-k}); @@ -267,7 +267,7 @@ resCurveOnScroll (Ideal,ZZ,ZZ) := (Jcan,g,lengthRes) -> ( M = M0_(select(cols,j -> ((degrees source M0)_j)_0 == degsH_(i-1))); if (rank source M < rkSyzModules(1+i,k) ) then error("DegreeLimit too low"); M)); - chainComplex resX + complex resX ); resCurveOnScroll (Ideal,ZZ) := (Jcan,g) -> ( @@ -289,7 +289,7 @@ resCurveOnScroll (Ideal,ZZ) := (Jcan,g) -> ( M = M0_(select(cols,j -> ((degrees source M0)_j)_0 == degsH_(i-1))); if (rank source M < rkSyzModules(1+i,k) ) then error("DegreeLimit too low"); M)); - chainComplex resX + complex resX ); --------------------------------------------------------------- @@ -326,7 +326,7 @@ bettiENtype (ZZ,ZZ,ZZ,ZZ) := (b,a,f,mult) -> ( -- Computes the Eagon-Northcott type resolution. -- The function is based on the Eagon-Northcott function by Greg Smith. -- Input: twist b of the Ruling and matrix Phi defining the scroll, thus defining the differentials --- Output: a chain complex, the Eagon-Northcott type complex +-- Output: a Complex, the Eagon-Northcott type complex eagonNorthcottType = method() eagonNorthcottType (Matrix,ZZ) := (Phi,b) -> ( @@ -362,7 +362,7 @@ eagonNorthcottType (Matrix,ZZ) := (Phi,b) -> ( t := first select(toList(0..g-1), l -> vec#l == 1); (-1)^(s+1)*Phi_(t,q#0#s)))) ); - chainComplex d) + complex d) -- lifts a monomial to the Eagon-Nortcott type resolution @@ -436,7 +436,7 @@ matrix apply(rank target A, i -> -- Output: the iterated mapping cone iteratedCone = method() -iteratedCone (ChainComplex,List) := (resX,e) -> ( +iteratedCone (Complex,List) := (resX,e) -> ( k := length(e)+1; g := sum(e)+k-1; kk := coefficientRing ring resX_0; @@ -585,7 +585,7 @@ doc /// l: ZZ the length limit of the resolution Outputs - resX: ChainComplex + resX: Complex the relative canonical resolution Description Text @@ -642,7 +642,7 @@ doc /// b: ZZ the twist Outputs - ENresolution: ChainComplex + ENresolution: Complex the Eagon-Northcott type resolution Description Text @@ -698,18 +698,18 @@ doc /// doc /// Key iteratedCone - (iteratedCone,ChainComplex,List) + (iteratedCone,Complex,List) Headline Computes a (possibly non-minimal) resolution of C in P^{g-1} starting from the relative canonical resolution of C in P(E) Usage resC=iteratedCone(resX,e) Inputs - resX: ChainComplex + resX: Complex the relative canonical resolution e: List the type of the scroll $P(E)$ Outputs - resC: ChainComplex + resC: Complex the resolution of C obtained by an iterated mapping cone Description Text diff --git a/M2/Macaulay2/packages/ResidualIntersections.m2 b/M2/Macaulay2/packages/ResidualIntersections.m2 index 9d1fc07ba18..1c2b5e324a9 100644 --- a/M2/Macaulay2/packages/ResidualIntersections.m2 +++ b/M2/Macaulay2/packages/ResidualIntersections.m2 @@ -752,7 +752,7 @@ doc /// the homological index to compute Outputs L:List - a list of the depths of Kozul homology + a list of the depths of Koszul homology d:ZZ the depth of the k-th Koszul homology Description diff --git a/M2/Macaulay2/packages/ResolutionsOfStanleyReisnerRings.m2 b/M2/Macaulay2/packages/ResolutionsOfStanleyReisnerRings.m2 index 3c3e4ae975f..35a41598358 100644 --- a/M2/Macaulay2/packages/ResolutionsOfStanleyReisnerRings.m2 +++ b/M2/Macaulay2/packages/ResolutionsOfStanleyReisnerRings.m2 @@ -13,7 +13,7 @@ newPackage( Date => "July 15, 2020", Authors => {{Name => "Ashleigh Adams", Email => "adams869@umn.edu", HomePage => "http://www.ashleigh-adams.com"}}, Headline => "Comparing resolutions of Stanley-Reisner rings and computing various systems of parameters", - PackageImports => { "OldChainComplexes" }, + PackageImports => { "Complexes" }, PackageExports => {"SimplicialComplexes", "Posets", "SimplicialDecomposability"}, Keywords => { "Combinatorial Commutative Algebra" }, DebuggingMode => false diff --git a/M2/Macaulay2/packages/Resultants.m2 b/M2/Macaulay2/packages/Resultants.m2 index 7e10b30a444..654d2559fed 100644 --- a/M2/Macaulay2/packages/Resultants.m2 +++ b/M2/Macaulay2/packages/Resultants.m2 @@ -427,7 +427,7 @@ tangentialChowForm (Ideal,ZZ,ZZ) := o -> (I,s,l) -> ( r := if useDuality then n-l-1 else l; if l >= n or l <=-1 then return 1_(Grass(l,n,K,Variable=>p)); mnr := o.AffineChartGrass; - if mnr === true then mnr = (random toList(0..n))_{0..r}; + if mnr === true then mnr = shuffle(toList(0..n), r + 1); if mnr =!= false then (try assert(ring matrix{mnr} === ZZ and min mnr >=0 and max mnr <=n and # unique mnr == r+1 and # mnr == r+1) else error("bad value for option AffineChartGrass: expected either boolean value or list of "|toString(r+1)|" distinct integers between 0 and "|toString(n))); if mnr =!= false then mnr = sort mnr; if (class o.AssumeOrdinary =!= Boolean and o.AssumeOrdinary =!= null) then error "expected true or false for option AssumeOrdinary"; @@ -686,7 +686,7 @@ projectionMap (Ring,Boolean) := o -> (G,B) -> ( psi := map(R,G,gens minors(k+1,M)); mnr := o.AffineChartGrass; if mnr === false then return (psi,M); - if mnr === true then mnr = (random toList(0..n))_{0..k}; + if mnr === true then mnr = shuffle(toList(0..n), k + 1); try assert(ring matrix{mnr} === ZZ and min mnr >=0 and max mnr <=n and # unique mnr == k+1 and # mnr == k+1) else error("bad value for option AffineChartGrass: expected either boolean value or list of "|toString(k+1)|" distinct integers between 0 and "|toString(n)); mnr = sort mnr; R = KK[flatten entries submatrix'(transpose M,mnr)]; @@ -826,7 +826,7 @@ fanoVariety (Ideal,ZZ) := o -> (I,k) -> ( p := if o.Variable === null then getVariable ring I else getVariable o.Variable; G := Grass(k,n,K,Variable=>p); mnr := o.AffineChartGrass; - if mnr === true then mnr = (random toList(0..n))_{0..k}; + if mnr === true then mnr = shuffle(toList(0..n), k + 1); (f,M) := projectionMap(G,false,Variable=>"fano",AffineChartGrass=>mnr); t := local t; R := (target f)[t_0..t_k]; diff --git a/M2/Macaulay2/packages/SCSCP/docinput/Makefile.in b/M2/Macaulay2/packages/SCSCP/docinput/Makefile.in index 597be4ab666..0a3fef8e681 100644 --- a/M2/Macaulay2/packages/SCSCP/docinput/Makefile.in +++ b/M2/Macaulay2/packages/SCSCP/docinput/Makefile.in @@ -23,4 +23,3 @@ checkmagma: %.out: %.m2; $(M2) -q --stop -e 'loadPackage "SCSCP"' < $^ > out.tmp mv out.tmp $@ -.NOTPARALLEL: diff --git a/M2/Macaulay2/packages/SVDComplexes.m2 b/M2/Macaulay2/packages/SVDComplexes.m2 index c398edcd148..38f8201fa64 100644 --- a/M2/Macaulay2/packages/SVDComplexes.m2 +++ b/M2/Macaulay2/packages/SVDComplexes.m2 @@ -22,7 +22,7 @@ newPackage( }, Headline => "SVD (singular value decomposition) of a complex over the reals and related functions", Keywords => {"Homological Algebra", "Commutative Algebra"}, - PackageExports => { "LLLBases", "OldChainComplexes" }, + PackageExports => {"LLLBases", "Complexes"}, DebuggingMode => false ) @@ -49,56 +49,17 @@ export { -- Some basic functions that should not be here -- -- Move these to the core? ----------------------- -------------------------------------------------- -importFrom_OldChainComplexes "spots" +--debug Core -concentration = method() -concentration ChainComplex := C -> ( - goodspots := select(spots C, i -> C_i != 0); - if #goodspots == 0 then (0,0) else (min goodspots, max goodspots) +clean(RR, Complex) := (epsilon, C) -> ( + (loC, hiC) := concentration C; + if loC === hiC then return C; + complex hashTable for i from loC + 1 to hiC list i => clean(epsilon, C.dd_i) ) -chainComplex(HashTable) := (maps) -> ( - -- maps should be a HashTable with keys integers. values are maps at that spot. - rgs := (values maps)/ring//unique; - if #rgs != 1 then error "expected matrices over the same ring"; - R := rgs#0; - C := new ChainComplex; - C.ring = R; - for i in keys maps do ( - f := maps#i; - F := source f; - G := target f; - if C#?i then (if C#i =!= F then error("different modules at index "|i)) - else C#i = F; - if C#?(i-1) then (if C#(i-1) =!= G then error("different modules at index "|i-1)) - else C#(i-1) = G; - ); - C.dd.cache = new CacheTable; - lo := min keys maps - 1; - hi := max keys maps; - for i from lo+1 to hi do C.dd#i = if maps#?i then maps#i else map(C_i, C_(i-1), 0); - C - ) - -newChainComplexMap = method() -newChainComplexMap(ChainComplex, ChainComplex, HashTable) := (tar,src,maps) -> ( - f := new ChainComplexMap; - f.cache = new CacheTable; - f.source = src; - f.target = tar; - f.degree = 0; - goodspots := select(spots src, i -> src_i != 0); - scan(goodspots, i -> f#i = if maps#?i then maps#i else map(tar_i, src_i, 0)); - f - ) - -clean(RR, ChainComplex) := (epsilon, C) -> ( - chainComplex hashTable for i from min C + 1 to max C list i => clean(epsilon, C.dd_i) - ) - -clean(RR, ChainComplexMap) := (epsilon, f) -> ( +clean(RR, ComplexMap) := (epsilon, f) -> ( H := hashTable for k in keys f list if instance(k,ZZ) then k => clean(epsilon, f_k) else continue; - newChainComplexMap(clean(epsilon, target f), clean(epsilon, source f), H) + map(clean(epsilon, target f), clean(epsilon, source f), H) ) numericRank = method() @@ -140,7 +101,7 @@ randomSL(ZZ) := opts -> n -> randomUpper(n,opts) * transpose randomUpper(n,opts) ----------------------------------------------- laplacians = method() -laplacians ChainComplex := (L) -> ( +laplacians Complex := (L) -> ( laps := new MutableHashTable; for i from min L to max L do ( laps#i=((transpose L.dd_(i))*L.dd_(i) + (L.dd_(i+1) * (transpose L.dd_(i+1))))); @@ -170,7 +131,7 @@ SVDComplex = method(Options => { } ) -SVDComplex ChainComplex := opts -> (C) -> ( +SVDComplex Complex := opts -> (C) -> ( if ring C =!= RR_53 then error "excepted chain complex over the reals RR_53"; (lo, hi) := concentration C; if lo === hi then ( @@ -221,10 +182,10 @@ SVDComplex ChainComplex := opts -> (C) -> ( for i from 0 to rks#ell-1 do m_(rks#(ell-1)+i, i) = Sigmas#ell#i; matrix m -- TODO: make this via diagonal matrices and block matrices. ); - sourceComplex := (chainComplex SigmaMatrices); + sourceComplex := (complex SigmaMatrices); -- transpose all ortho matrices to get the map in the right direction for i from lo to hi do Orthos#i=transpose Orthos#i; - result := newChainComplexMap(C, sourceComplex, new HashTable from Orthos); + result := map(C, sourceComplex, new HashTable from Orthos); return ( new HashTable from hs,result);--, new HashTable from hs, new HashTable from smallestSing; ); @@ -273,17 +234,17 @@ SVDComplex ChainComplex := opts -> (C) -> ( Orthos#ell = Orthos#ell * matrix d; matrix m ); - sourceComplex = (chainComplex SigmaMatrices); + sourceComplex = (complex SigmaMatrices); -- do not transpose all ortho matrices to get the map in the right direction -- for i from lo to hi do Orthos#i=transpose Orthos#i; - U = newChainComplexMap(C, sourceComplex, new HashTable from Orthos); + U = map(C, sourceComplex, new HashTable from Orthos); h := new HashTable from hs; return(h,U); ); error "expected Strategy=>Projection or Strategy=>Laplacian" ) -SVDComplex(ChainComplex,ChainComplex) := opts -> (C,C') -> ( +SVDComplex(Complex,Complex) := opts -> (C,C') -> ( -- returns a hash table of the ranks of the homology of C if ring C =!= RR_53 then error "excepted chain complex over the reals RR_53"; (lo, hi) := concentration C; @@ -299,6 +260,8 @@ SVDComplex(ChainComplex,ChainComplex) := opts -> (C,C') -> ( sigma1 := null; sigma1' := null; U := null; U' := null; Vt := null;Vt' := null; + if opts.Strategy == symbol Laplacian then + error "not implemented for complexes in two precisions"; if opts.Strategy == symbol Projection then ( P0 := mutableIdentity(ring C, rank C_lo); -- last projector matrix constructed P0' := mutableIdentity(ring C, rank C_lo); @@ -334,19 +297,17 @@ SVDComplex(ChainComplex,ChainComplex) := opts -> (C,C') -> ( for i from 0 to rks#ell-1 do m_(rks#(ell-1)+i, i) = Sigmas#ell#i; matrix m -- TODO: make this via diagonal matrices and block matrices. ); - sourceComplex := (chainComplex SigmaMatrices); + sourceComplex := (complex SigmaMatrices); -- transpose all ortho matrices to get the map in the right direction for i from lo to hi do Orthos#i=transpose Orthos#i; - result := newChainComplexMap(C, sourceComplex, new HashTable from Orthos); - return ( new HashTable from hs,result);--, - -- new HashTable from hs, new HashTable from smallestSing; - ); - if opts.Strategy == symbol Laplacian then error "not implemented for complexes in two precisions"; + result := map(C, sourceComplex, new HashTable from Orthos); + (new HashTable from hs, result) + ) ) SVDHomology = method (Options => options SVDComplex) -SVDHomology ChainComplex := opts -> (C) -> ( +SVDHomology Complex := opts -> (C) -> ( -- returns a hash table of the ranks of the homology of C if ring C =!= RR_53 then error "excepted chain complex over the reals RR_53"; (lo, hi) := concentration C; @@ -421,7 +382,7 @@ SVDHomology ChainComplex := opts -> (C) -> ( ) -SVDHomology(ChainComplex,ChainComplex) := opts -> (C,C') -> ( +SVDHomology(Complex,Complex) := opts -> (C,C') -> ( -- returns a hash table of the ranks of the homology of C if ring C =!= RR_53 then error "excepted chain complex over the reals RR_53"; (lo, hi) := concentration C; @@ -482,7 +443,7 @@ h Sigma =source U Sigma.dd_0 errors=apply(toList(min CR+1..max CR),ell->C.dd_ell-U_(ell-1)*Sigma.dd_ell*transpose U_ell); -maximalEntry chainComplex errors +maximalEntry complex errors elapsedTime (h,U)=SVDComplex(CR,Strategy=>Laplacian); @@ -490,12 +451,12 @@ h SigmaL =source U maximalEntry(SigmaL.dd_1 -Sigma.dd_1) errors=apply(toList(min C+1..max C),ell->C.dd_ell-U_(ell-1)*SigmaL.dd_ell*transpose U_ell); -maximalEntry chainComplex errors +maximalEntry complex errors /// projectToComplex=method() -projectToComplex(ChainComplex,HashTable) := (B,hs) -> ( +projectToComplex(Complex,HashTable) := (B,hs) -> ( -- returns a hash table of the ranks of the homology of C if ring B =!= RR_53 then error "excepted chain complex over the reals RR_53"; (lo, hi) := concentration B; @@ -538,10 +499,10 @@ projectToComplex(ChainComplex,HashTable) := (B,hs) -> ( for i from lo to hi do Orthos#i=transpose Orthos#i; As := hashTable for ell from lo+1 to hi list ell => ( Orthos#(ell-1)*SigmaMatrices#ell* transpose Orthos#ell); - return chainComplex As) + return complex As) euclideanDistance=method() -euclideanDistance(ChainComplex,ChainComplex) := (A,B) -> ( +euclideanDistance(Complex,Complex) := (A,B) -> ( (lo,hi) := (min A, max A); if (lo,hi) != (min B,max B) then error "expect complexes of the same range"; for i from lo to hi do if A_i =!= B_i then error "expected complexe with free modules of the same ranks"; @@ -615,23 +576,27 @@ for i from 1 to 3 list maximalEntry(B.dd_i-A.dd_i) maxEntry = method() maxEntry(Matrix) := (m) -> (flatten entries m)/abs//max -maxEntry(ChainComplexMap) := (F) -> max for m in spots F list maxEntry(F_m) +maxEntry(ComplexMap) := (F) -> ( + (lo, hi) := concentration F; + max for m from lo to hi list maxEntry(F_m) + ) checkSVDComplex = (C, Fhs) -> ( -- routine to find the smallest errors which occur. -- where here (F,hs) = SVDComplex C, C is a complex over RR_53. (F,hs, minsing) := Fhs; - debug Core; + (loF, hiF) := concentration F; + --debug Core; tar2 := (target F).dd^2; src2 := (source F).dd^2; val1 := maxEntry tar2; val2 := maxEntry src2; - vals3 := for m in spots F list (flatten entries (((transpose F_m) * F_m) - id_(source F_m)))/abs//max; - vals4 := for i in spots F list ( + vals3 := for m from loF to hiF list (flatten entries (((transpose F_m) * F_m) - id_(source F_m)))/abs//max; + vals4 := for i from loF to hiF list ( m := (target F).dd_i * F_i - F_(i-1) * (source F).dd_i; (flatten entries m)/abs//max ); - vals5 := for i in spots F list ( + vals5 := for i from loF to hiF list ( m := (C.dd_i - ((transpose F_(i-1)) * (target F).dd_i * F_i)); (flatten entries m)/abs//max ); @@ -639,7 +604,7 @@ checkSVDComplex = (C, Fhs) -> ( ) pseudoInverse=method(Options=> options SVDComplex) -pseudoInverse ChainComplex := opts -> C -> ( -- old version, to be removed. +pseudoInverse Complex := opts -> C -> ( -- old version, to be removed. if not ring C === RR_53 then pseudoInverse1 C else ( U := last SVDComplex(C,Strategy=>opts.Strategy); SigmaComplex := source U; @@ -651,12 +616,13 @@ pseudoInverse ChainComplex := opts -> C -> ( -- old version, to be removed. if A_(i,j)==0 then 0 else 1/(A_(i,j)))))); CplusMats := apply(#SigmaPlus,i-> U_(minC+i+1)*SigmaPlus_i* transpose U_(minC+i)); - Cplus := (chainComplex reverse CplusMats); + Cplus := (complex reverse CplusMats); Cplus )) -pseudoInverse ChainComplex := opts -> C -> ( - if not ring C === RR_53 then pseudoInverse1 C else ( +pseudoInverse Complex := opts -> C -> ( + if not ring C === RR_53 then + return pseudoInverse1 C; U := last SVDComplex(C,Strategy=>opts.Strategy); SigmaComplex := source U; (loC,hiC) := concentration C; @@ -667,15 +633,15 @@ pseudoInverse ChainComplex := opts -> C -> ( sigmaPlus := matrix for r in eA list for a in r list if a == 0 then 0 else 1/a; (-i+1) => U_i * sigmaPlus * transpose U_(i-1) ); - chainComplex CPlusMats - )) + complex CPlusMats + ) pseudoInverse1=method() -pseudoInverse1 ChainComplex := C -> ( +pseudoInverse1 Complex := C -> ( if not isField ring C then error " expected a chain complex defined over a field"; (lo,hi) := concentration C; - chainComplex hashTable for i from lo to hi list (-i+1) => pseudoInverse1 C.dd_i + complex hashTable for i from lo+1 to hi list (-i+1) => pseudoInverse1 C.dd_i ) pseudoInverse1(Matrix) := M -> ( @@ -764,7 +730,7 @@ arePseudoInverses(Matrix,Matrix) := opts -> (A,B) -> ( true ) -arePseudoInverses(ChainComplex, ChainComplex) := opts -> (A,B) -> ( +arePseudoInverses(Complex, Complex) := opts -> (A,B) -> ( (loA,hiA) := concentration A; (loB,hiB) := concentration B; if loA != -hiB or loB != -hiA then ( @@ -786,8 +752,8 @@ TEST /// needsPackage "RandomComplexes" -- Simple boundary cases for pseudoInverse. m = matrix id_(QQ^2) - C = chainComplex {m} - CRR = chainComplex {m ** RR_53} + C = complex {m} + CRR = complex {m ** RR_53} C3 = C[-3] iC = pseudoInverse C iC3 = pseudoInverse C3 @@ -895,7 +861,7 @@ Pm,Pn *- conjugateComplex=method(Options=>{Height=>10}) -conjugateComplex ChainComplex := opts -> C -> ( +conjugateComplex Complex := opts -> C -> ( minC:= min C; maxC:= max C; U:=for i from minC to maxC list ( @@ -903,7 +869,7 @@ conjugateComplex ChainComplex := opts -> C -> ( randomSL(r,opts)); C':=for i from minC+1 to maxC list ( U_(i-1)*C.dd_i*inverse U_i); - (chainComplex C')[-minC]) + (complex C')[-minC]) beginDocumentation() @@ -1042,19 +1008,19 @@ doc /// doc /// Key SVDComplex - (SVDComplex,ChainComplex) - (SVDComplex,ChainComplex,ChainComplex) + (SVDComplex,Complex) + (SVDComplex,Complex,Complex) [SVDComplex,Strategy] [SVDComplex,Threshold] Headline - Compute the SVD decomposition of a chainComplex over RR + Compute the SVD decomposition of a chain complex over RR Usage (h,U)=SVDComplex C or (h,U)=SVDComplex(C,C') Inputs - C:ChainComplex + C:Complex over RR_{53} - C':ChainComplex + C':Complex in a lower precision Strategy => Symbol Laplacian or Projection for the method used @@ -1063,14 +1029,14 @@ doc /// Outputs h:HashTable the dimensions of the homology groups HH C - U:ChainComplexMap + U:ComplexMap a map C <- Sigma - where the source is the chainComplex of the singular value matrices + where the source is the chain complex of the singular value matrices and U is given by orthogonal matrices Description Text We compute the singular value decomposition either by the iterated Projections or by the - Laplacian method. In case the input consists of two chainComplexes we use the iterated + Laplacian method. In case the input consists of two chain complexes we use the iterated Projection method, and identify the stable singular values. Example needsPackage "RandomComplexes" @@ -1084,14 +1050,14 @@ doc /// Sigma =source U Sigma.dd_0 errors=apply(toList(min CR+1..max CR),ell->CR.dd_ell-U_(ell-1)*Sigma.dd_ell*transpose U_ell); - maximalEntry chainComplex errors + maximalEntry complex errors elapsedTime (hL,U)=SVDComplex(CR,Strategy=>Laplacian); hL === h SigmaL =source U; for i from min CR+1 to max CR list maximalEntry(SigmaL.dd_i -Sigma.dd_i) errors=apply(toList(min C+1..max C),ell->CR.dd_ell-U_(ell-1)*SigmaL.dd_ell*transpose U_ell); - maximalEntry chainComplex errors + maximalEntry complex errors Text The optional argument Caveat @@ -1103,19 +1069,19 @@ doc /// doc /// Key SVDHomology - (SVDHomology,ChainComplex) - (SVDHomology,ChainComplex,ChainComplex) + (SVDHomology,Complex) + (SVDHomology,Complex,Complex) [SVDHomology, Strategy] [SVDHomology,Threshold] Headline - Estimate the homology of a chainComplex over RR with the SVD decomposition + Estimate the homology of a chain complex over RR with the SVD decomposition Usage (h,h1)=SVDHomology C or (h,h1)=SVDHomology(C,C') Inputs - C:ChainComplex + C:Complex over RR_{53} - C':ChainComplex + C':Complex in a lower precision Strategy => Symbol Laplacian or Projection for the method used @@ -1135,7 +1101,7 @@ doc /// In case of the Laplacian method we record in h1 the smallest common Eigenvalues of the neighboring Laplacians, and the first Eigenvalue expected to be zero. - In case the input consists of two chainComplexes we use the iterated Projection method, and identify the stable + In case the input consists of two chain complexes we use the iterated Projection method, and identify the stable singular values. Example needsPackage "RandomComplexes" @@ -1167,22 +1133,22 @@ doc /// doc /// Key projectToComplex - (projectToComplex,ChainComplex,HashTable) + (projectToComplex,Complex,HashTable) Headline compute a nearby complex with the projection method Usage C = projectToComplex(D,h) or Inputs - D:ChainComplex + D:Complex an approximate complex over over RR_{53} h:HashTable the desired homology groups Outputs - C:ChainComplex - a nearby chainComplex + C:Complex + a nearby chain complex Description Text - Using the iterated projection method we compute a nearby chainComplex C with + Using the iterated projection method we compute a nearby chain complex C with homology h. Example needsPackage "RandomComplexes" @@ -1221,7 +1187,7 @@ doc /// Key arePseudoInverses (arePseudoInverses,Matrix,Matrix) - (arePseudoInverses,ChainComplex,ChainComplex) + (arePseudoInverses,Complex,Complex) Headline check the Penrose relations for the pseudo inverse Usage @@ -1231,8 +1197,8 @@ doc /// A:Matrix B:Matrix or - C:ChainComplex - Cplus:ChainComplex + C:Complex + Cplus:Complex Threshold => RR an absolute error up to which the identities should hold Outputs @@ -1277,20 +1243,20 @@ doc /// Key pseudoInverse --(pseudoInverse,Matrix) - (pseudoInverse,ChainComplex) + (pseudoInverse,Complex) Headline - compute the pseudoInverse of a chainComplex + compute the pseudoInverse of a chain complex Usage Cplus = pseudoInverse C Inputs - C:ChainComplex + C:Complex an approximate complex over an field Strategy => Symbol Laplacian or Projection for the method used Threshold => RR the relative threshold used to detect the zero singular values Outputs - Cplus:ChainComplex + Cplus:Complex the pseudo inverse complex Description Text @@ -1331,14 +1297,14 @@ doc /// doc /// Key euclideanDistance - (euclideanDistance,ChainComplex,ChainComplex) + (euclideanDistance,Complex,Complex) Headline compute the euclidean distance of two chain complexes Usage euclideanDistance(C,D) Inputs - C:ChainComplex - D:ChainComplex + C:Complex + D:Complex two chain complexes over RR or QQ Outputs :RR @@ -1414,13 +1380,13 @@ doc /// doc /// Key laplacians - (laplacians,ChainComplex) + (laplacians,Complex) Headline compute the laplacians of a chain complex Usage delta=laplacians C Inputs - C:ChainComplex + C:Complex defined over RR Outputs delta:HashTable diff --git a/M2/Macaulay2/packages/SchurComplexes.m2 b/M2/Macaulay2/packages/SchurComplexes.m2 index 906b3fe3880..20518dbac23 100644 --- a/M2/Macaulay2/packages/SchurComplexes.m2 +++ b/M2/Macaulay2/packages/SchurComplexes.m2 @@ -1,7 +1,7 @@ newPackage( "SchurComplexes", - Version => "1.1", - Date => "June 1, 2019", + Version => "1.2", + Date => "November 12, 2025", Authors => { {Name => "Michael K. Brown", Email => "mkbrown5@wisc.edu", @@ -164,66 +164,83 @@ tableauxDiff(ZZ, ZZ, List, HashTable) := (m,n,lT,D) -> ( --Output: --a ChainComplex, the Schur Complex of F associated to the given partition. When F = 0, the output is a --new ChainComplex. -schurComplex= (Lambda,F) -> +schurComplex = (Lambda,F) -> ( lambda:=new Partition from Lambda; - Size:=sum Lambda; - Min:=min{min(F),0}; - F=F[2*Min];----moves F into non-negative homological degree + --Size:=sum Lambda; + --Min:=min{min(F),0}; + --F=F[2*Min];----moves F into non-negative homological degree R := ring(F); - l := max(F); - evengen := flatten for i from 0 to l//2 list ( + m0 := min(F); + m1 := max(F); + evengen := flatten for i from m0 to m1//2 list ( d := numgens F_(2*i); for j from 1 to d list (2*i,j) ); n := #evengen; --number of even variables - oddgen := flatten for i from 0 to (l-1)//2 list ( + oddgen := flatten for i from m0 to (m1-1)//2 list ( d := numgens F_(2*i+1); for j from 1 to d list(2*i+1,j) ); m := #oddgen; --number of odd variables + evenInternalDegrees := flatten for i from m0 to m1//2 list (degrees F_(2*i)); -- list of internal degrees of the basis elements of F in even homological degree + oddInternalDegrees := flatten for i from m0 to m1//2 list (degrees F_(2*i+1)); -- list of internal degrees of the basis elements of F in odd homological degree inversehash := new HashTable from --computes the map that associates a label to a generator of F (for i from 1 to n list evengen#(i-1) => i) | (for i from 1 to m list oddgen#(i-1) => -i); - D := new HashTable from flatten for i from 1 to l list --encodes the differentials of F + D := new HashTable from flatten for i from m0 + 1 to m1 list --encodes the differentials of F for j from 1 to numgens F_i list inversehash#(i,j) => for r from 1 to numgens F_(i-1) list (inversehash#(i-1,r),(F.dd_i)_(r-1,j-1)); - tabs := standardZ2Tableaux(lambda,m,n); --computes tableaux indexing the generators in S_lambda(F) + tabs := standardZ2Tableaux(lambda,m,n); --computes tableaux indexing the generators in S_lambda(F) + tabsInternalDegrees := apply(tabs, T -> tableauInternalDegree(T, evenInternalDegrees, oddInternalDegrees)); if #tabs > 0 then--checks if F is the zero complex. ( - degreeList := for T in tabs list homologicalDegree(T, evengen, oddgen); - shift := min(degreeList);--keeps track of first nonzero homological degree of the Schur complex + --degreeList := for T in tabs list homologicalDegree(T, evengen, oddgen); + --shift := min(degreeList);--keeps track of first nonzero homological degree of the Schur complex differential := new HashTable from for T in tabs list T => tableauxDiff(m,n,{lambda,T},D);--the differential in the Schur complex - mutG := new MutableHashTable from {}; + nonzeroTabsByDegreeHT := new MutableHashTable from {}; for T in tabs do ( deg := homologicalDegree(T,evengen,oddgen); - if mutG#?deg then + if nonzeroTabsByDegreeHT#?deg then ( - l := #(mutG#deg); - (mutG#deg)#l = T; + l := #(nonzeroTabsByDegreeHT#deg); + (nonzeroTabsByDegreeHT#deg)#l = T; ) - else mutG#deg = new MutableList from {T}; - ); - tabsByDegree := for i in sort(keys mutG) list toList(mutG#i); + else nonzeroTabsByDegreeHT#deg = new MutableList from {T}; + ); + n0 := min keys nonzeroTabsByDegreeHT; + n1 := max keys nonzeroTabsByDegreeHT; + for j from n0 to n1 do ( + if not nonzeroTabsByDegreeHT#?j then nonzeroTabsByDegreeHT#j = new MutableList from {}; + ); + tabsByDegree := for i in sort(keys nonzeroTabsByDegreeHT) list toList(nonzeroTabsByDegreeHT#i); --tabsByDegree is a List of Lists: the i-th entry is a List of standard tableaux of --homological degree i. - tabsInEachDegree := for l in tabsByDegree list #l; - --the i-th entry in the list tabsInEachDegree is the number of tableaux in the Schur complex of - --homological degree i. - componentList := for i in tabsInEachDegree list R^i; - r := #componentList-1; - matrixList := for i from 0 to r-1 list( - transpose matrix for u in tabsByDegree#(i+1) list - for v in tabsByDegree#i list try((differential#u)#v) else 0 - ); - --The i-th entry of matrixList is the matrix giving the map between the (i+1)-st and i-th components - --of the Schur complex. - C:= complex for i from 0 to (#matrixList - 1) list map(componentList_i, componentList_(i + 1), matrixList_i); - --removed: C.ring = R; - C[-shift-2*Min*Size]---moves the Schur complex based on the shift of F at the beginning - ) + internalTwistList := apply(tabsByDegree, L -> apply(L, T -> -tableauInternalDegree(T, evenInternalDegrees, oddInternalDegrees))); + --internalTwistList is a list of the internal grading twists of the basis elements corresponding to the tableaux in tabsByDegree + componentList := apply(internalTwistList, i -> R^i); + r:=#componentList-1; + if r > 0 then ----checks if the Schur complex will have length greater than 1 + ( + matrixList := for i from 0 to r-1 list( + transpose matrix for u in tabsByDegree#(i+1) list + for v in tabsByDegree#i list try((differential#u)#v) else 0 + ); + --The i-th entry of matrixList is the matrix giving the map between the (i+1)-st and i-th components + --of the Schur complex. + C:= complex for i from 0 to (#matrixList - 1) list ( + if source(matrixList_i) == 0 or target(matrixList_i) == 0 then ( + map(componentList_i, componentList_(i + 1), 0) + ) + else ( + map(componentList_i, componentList_(i + 1), matrixList_i)) + ); + C[-n0] + ) + else (complex componentList_0)[-n0] + ) else complex R^0 --changed: else new ChainComplex ) @@ -249,6 +266,20 @@ homologicalDegree(HashTable,List,List) := (T,evengen,oddgen) -> sum for b in key if x>0 then (evengen#(x-1))_0 else (oddgen#(-x-1))_0 ) +--Input: +--a HashTable T encoding a Z/2-graded tableau, and list evenDegs (resp. oddDegs) that gives the internal degrees of the +--basis elements of a free complex F in even (resp. odd) homological degree +-- +--Output: +--an integer, the degree of the basis element of the schurComplex of F corresponding to the tableau T. +tableauInternalDegree = method(); +tableauInternalDegree(HashTable, List, List) := (T, evenDegs, oddDegs) -> ( + L := apply(keys T, i -> + if (T#i) > 0 then evenDegs_(T#i - 1) else oddDegs_(-(T#i) - 1) + ); + sum L + ) + ------Straightening algorithm --Inputs: U= List, V= permutation represented as a list. Outputs: sign of the induced permutation on the unmarked elements of U. @@ -281,7 +312,7 @@ permutedTableau= (T,X,row1, row2, col1,lengthcol1,L)-> ( -- Inputs: T=Tableau, vio = pair (column, row) where violation occurs, downCol2 is the number of times the entry in the second column repeats. ---Outputs: Linear combination represented as a hashtable with tableau keys. -shuffle = (T, vio, downCol2, lambdaprime) -> ( +shuffle' = (T, vio, downCol2, lambdaprime) -> ( lengthcol1 := lambdaprime#(vio_0-1); truncatedcol1:=apply(toList (vio_1..lengthcol1),i -> T#(vio_0,i)); truncatedcol2:=apply(toList(1..vio_1+downCol2), i-> T#(vio_0 + 1,i)); @@ -403,7 +434,7 @@ recursiveStraighten = (H,lambda) -> ( if vio === null then hashlist = merge(hashlist,new HashTable from {T => H#T},plus) else ( repeat = true; - hashlist = merge(hashlist,scalarMultiply(H#T,shuffle(T,vio,downCol2,lambdaprime)),plus); + hashlist = merge(hashlist,scalarMultiply(H#T,shuffle'(T,vio,downCol2,lambdaprime)),plus); ); ); ); @@ -442,6 +473,7 @@ scalarMultiply = (s,H) -> ( + beginDocumentation() doc /// Key @@ -536,7 +568,7 @@ doc /// apply(1+length G,i->reduceHilbert hilbertSeries HH_i(G)) Text - We compute a third example. + We compute a third example. Example R=ZZ/7[x,y,z,w]; @@ -545,8 +577,19 @@ doc /// lambda={2,1}; G=schurComplex(lambda,F) G.dd + + Text + If the input complex F has a (multi)grading, the resulting Schur complex does as well. - + Example + needsPackage "NormalToricVarieties" + R = ring hirzebruchSurface 3; + F = koszulComplex {x_0^4*x_1 - x_2*x_3, x_0^2}; + lambda = {2,2}; + G = schurComplex(lambda, F); + G.dd + assert isHomogeneous G + SeeAlso straightenTableau @@ -559,6 +602,7 @@ TEST /// --F = new ChainComplex; F.ring = R; F#0=target M; F#1=source M; F.dd#1=M; lambda={3} G=schurComplex(lambda,F) + assert(isHomogeneous G) H=reduceHilbert hilbertSeries HH_1(G) H1=lift(numerator(H),ZZ) assert (H1 === 0) @@ -572,6 +616,7 @@ TEST ///-------this Schur complex is exact by Proposition 2.4.7(a) Weyman "Cohom --F= new ChainComplex; F.ring = R; F#-7=target M; F#-6=source M; F.dd#-6=M; lambda={3,1} G=schurComplex(lambda,F) + assert isHomogeneous G H={} for i from -28 to -24 do H=H|{reduceHilbert hilbertSeries HH_i(G)} H1=unique H @@ -582,38 +627,54 @@ TEST ///-------this Schur complex is exact by Proposition 2.4.7(a) Weyman "Cohom TEST /// - R=ZZ[x,y] - F=freeResolution ideal (x,y) - lambda={1,1} - S=schurComplex(lambda,F) - N2=S.dd_2 - M2=matrix{{y,x,0,x},{0,y,x,-y}} + R=ZZ[x,y]; + F=freeResolution ideal (x,y); + lambda={1,1}; + S=schurComplex(lambda,F); + assert(isHomogeneous S); + N2=S.dd_2; + M2=matrix{{y,x,0,x},{0,y,x,-y}}; assert((N2-M2==0)) /// TEST /// - T = new HashTable from {(1,1) => 2, (1,2) => 1} - lambda = {1,1} - S=straightenTableau(T,lambda) - T2=new HashTable from{(1,1)=> 1, (1,2)=>2} - output=new HashTable from{T2=> -1} + needsPackage "NormalToricVarieties" + S = ring hirzebruchSurface 3; + I= ideal vars S; + F= res I; + Lambda={1,1}; + G=schurComplex(Lambda,F); + assert(isHomogeneous G) +/// + + +TEST /// + T = new HashTable from {(1,1) => 2, (1,2) => 1}; + lambda = {1,1}; + S=straightenTableau(T,lambda); + T2=new HashTable from{(1,1)=> 1, (1,2)=>2}; + output=new HashTable from{T2=> -1}; assert (S===output) /// TEST /// - T = new HashTable from {(1,1) => -3, (1,2) => -2, (1,3) => -2, (2,1) => 1, (2,2) => 2, (2,3) => 3, (3,1) => -1, (3,2) => -1} - lambda = new Partition from {3,3,2} - S=straightenTableau(T,lambda) - T1= new HashTable from {(1,1)=> -3, (1,2)=>-2, (1,3)=> -2, (2,1)=> -1, (2,2)=> -1, (2,3)=> 1, (3,1)=> 2, (3,2)=> 3} - T2= new HashTable from {(1,1)=> -3, (1,2)=>-2, (1,3)=> -2, (2,1)=> -1, (2,2)=> -1, (2,3)=> 2, (3,1)=> 1, (3,2)=> 3} - T3= new HashTable from {(1,1)=> -3, (1,2)=>-2, (1,3)=> -2, (2,1)=> -1, (2,2)=> -1, (2,3)=> 3, (3,1)=> 1, (3,2)=> 2} - Output= new HashTable from {T1=> 1, T2=> -1, T3=> 1} + T = new HashTable from {(1,1) => -3, (1,2) => -2, (1,3) => -2, (2,1) => 1, (2,2) => 2, (2,3) => 3, (3,1) => -1, (3,2) => -1}; + lambda = new Partition from {3,3,2}; + S=straightenTableau(T,lambda); + T1= new HashTable from {(1,1)=> -3, (1,2)=>-2, (1,3)=> -2, (2,1)=> -1, (2,2)=> -1, (2,3)=> 1, (3,1)=> 2, (3,2)=> 3}; + T2= new HashTable from {(1,1)=> -3, (1,2)=>-2, (1,3)=> -2, (2,1)=> -1, (2,2)=> -1, (2,3)=> 2, (3,1)=> 1, (3,2)=> 3}; + T3= new HashTable from {(1,1)=> -3, (1,2)=>-2, (1,3)=> -2, (2,1)=> -1, (2,2)=> -1, (2,3)=> 3, (3,1)=> 1, (3,2)=> 2}; + Output= new HashTable from {T1=> 1, T2=> -1, T3=> 1}; assert(S===Output) /// +end; + +restart +uninstallPackage "SchurComplexes" +installPackage "SchurComplexes" -end-- diff --git a/M2/Macaulay2/packages/SchurFunctors.m2 b/M2/Macaulay2/packages/SchurFunctors.m2 index 91e719e2c3b..7475ed938c1 100644 --- a/M2/Macaulay2/packages/SchurFunctors.m2 +++ b/M2/Macaulay2/packages/SchurFunctors.m2 @@ -1,13 +1,16 @@ newPackage( "SchurFunctors", - Version => "0.1", - Date => "March 5, 2008", + Version => "1.0", + Date => "April 18, 2026", Authors => { - {Name => "Michael E. Stillman", - Email => "mike@math.cornell.edu", + {Name => "Michael E. Stillman", + Email => "mike@math.cornell.edu", HomePage => "http://www.math.cornell.edu/~mike"}, {Name => "Anton Leykin"}, - {Name => "Mauricio Velasco"} + {Name => "Mauricio Velasco"}, + {Name => "Keller VandeBogert", + Email => "keller.v@uky.edu", + HomePage => "https://sites.google.com/view/kellervandebogert/home"} }, Headline => "Schur modules and maps between them", Keywords => {"Homological Algebra", "Representation Theory"}, @@ -15,9 +18,20 @@ newPackage( AuxiliaryFiles=>true ) -export{ "schur", "schurModule", "Filling", - "straighten", "printSchurModuleElement", "schurModulesMap", "augmentFilling", - "character", "splitCharacter", "characterRep", "decomposeRep"} +export{ "schur", "schurModule", "Filling", + "straighten", "printSchurModuleElement", "schurModulesMap", "augmentFilling", + "isStandard", "standardTableaux", "normalize", + "character", "splitCharacter", "characterRep", "decomposeRep", + -- Weyl module base functionality (divided-power analogue of Filling) + "WeylFilling", "weyl", + "weylStraighten", "weylNormalize", + "isWeylStandard", "towardWeylStandard", + "standardWeylTableaux", + "divComult", "divMult", + -- Weyl module / functor (divided-power analogue of Schur) + "weylModule", "dividedPower", + "printWeylModuleElement", "weylModulesMap", "maxWeylFilling", + "augmentWeylFilling", "weylCharacter"} exteriorPower(List, Module) := opts -> (L,M) -> ( if #L == 0 then exteriorPower(0,M) @@ -29,66 +43,52 @@ exteriorPower(List, Matrix) := opts -> (L,f) -> ( else exteriorPower(L#0, f) ** exteriorPower(drop(L,1), f) ) +-- A Filling is a tableau stored column-wise: T#j is the j-th column, a +-- list of entries read top-to-bottom. The Schur side. Filling = new Type of BasicList - -conjugate Filling := (T) -> ( +-- Partition-transpose at the tableau level: columns of T become rows of +-- conjugate T (truncated when a column runs out). +conjugate Filling := T -> ( a := #T#0; - new Filling from apply(0..a-1, i -> ( - -- the i th element of each list (until length is too big) - for j from 0 to #T-1 when #T#j > i list T#j#i - )) + new Filling from apply(0..a-1, i -> + for j from 0 to #T-1 when #T#j > i list T#j#i) ) --- compares two tableaux -Filling ? Filling := (T,U) -> ( - -- T and U should have the same shape - if #T == 0 then symbol== - else ( - a := T#-1; - b := U#-1; - i := #a-1; - while i >= 0 do ( - if a#i > b#i then return symbol>; - if a#i < b#i then return symbol<; - i = i-1; +-- Lex ordering on Fillings of the same shape. We compare columns right-to- +-- left (outermost first) and, within a column, entries top-to-bottom. +-- Iterative form: avoids the per-call `drop` allocation that would otherwise +-- dominate rsort in schurModule when |A| is large. +Filling ? Filling := (T, U) -> ( + k := #T - 1; + while k >= 0 do ( + a := T#k; b := U#k; + j := #a - 1; + while j >= 0 do ( + if a#j > b#j then return symbol>; + if a#j < b#j then return symbol<; + j = j - 1; ); - drop(T,-1) ? drop(U,-1)) + k = k - 1; + ); + symbol== ) --- return subset of rows -Filling _ List := (T,L) -> (toList T)_L +-- Subset of columns, indexed by a list of positions. +Filling _ List := (T, L) -> (toList T)_L - -normalize = method() -normalize Filling := (T) -> ( - -- returns (c,T'), where c is 0,1 or -1. - -- T' is T with rows sorted - -- c=0 for repeats in rows, else {1,-1} is sign of permutation needed to sort - coeff := 0; - coeffzero := false; - T' := apply(T, t -> ( - (c,t') := sortLen t; - if c < 0 then coeffzero = true; - coeff = coeff + c; - t')); - if coeffzero - then (0,null) - else - (if coeff % 2 == 0 then 1 else -1, new Filling from T') - ) - -sortLen = (L) -> ( - -- L is a list of integers - -- returned: (s, L') - -- s is the length of the permutation to place L into order - -- s will be -1 if L contains duplicate entries +-- Sort a column into strictly-increasing order, tracking the permutation +-- sign. Returns (-1, L) as a sentinel if the column has a repeated entry +-- (in which case the Filling represents 0 in the ambient exterior power). +-- Bubble sort is used because columns are short (~|lambda'_j|) and this +-- form makes the sign bookkeeping trivial. +sortLen = L -> ( len := 0; s := new MutableList from L; n := #s; for i from 0 to n-2 do for j from 0 to n-i-2 do ( - if s#j === s#(j+1) then return (-1,L); + if s#j === s#(j+1) then return (-1, L); if s#j > s#(j+1) then ( tmp := s#(j+1); s#(j+1) = s#j; @@ -96,13 +96,35 @@ sortLen = (L) -> ( len = len+1; ) ); - (len, toList s)) + (len, toList s) + ) + +-- As sortLen, but returns (sign, L') with sign in {1, -1} (or null if the +-- column is degenerate). +sortSign = L -> ( + (len, L1) := sortLen L; + (if len =!= -1 then (if len % 2 === 0 then 1 else -1), L1) + ) -sortSign = (L) -> ( - (len,L1) := sortLen L; - (if len =!= -1 then (if len % 2 === 0 then 1 else -1), L1)) +-- Sort every column of T in place. Returns (c, T'), where +-- c = 0 if any column has a repeated entry (T represents 0), +-- c = 1 if the total permutation has even sign, +-- c = -1 if it has odd sign. +normalize = method(Options => true) +normalize Filling := {} >> opts -> T -> ( + coeff := 0; + degenerate := false; + T' := apply(T, t -> ( + (c, t') := sortLen t; + if c < 0 then degenerate = true; + coeff = coeff + c; + t')); + if degenerate then (0, null) + else (if coeff % 2 == 0 then 1 else -1, new Filling from T') + ) -isStandard = (T) -> ( +isStandard = method() +isStandard Filling := T -> ( i := #T-2; while i >= 0 do ( a := T#i; @@ -115,38 +137,67 @@ isStandard = (T) -> ( null ) -exchange(Filling, ZZ, ZZ, List) := (T, col1, col2, s) -> ( - -- s should be a list of positions of T#col1, that will be placed into col2 - -- The returned value is {(coeff, T')} - -- coeff is 1 or -1. The length of the list is 0 or 1. +-- Exchange step: swap the entries at positions in `s` of column col1 with +-- the first #s entries of column col2, then re-sort both columns and track +-- the sign of the permutation. Returns null if either resulting column has +-- a repeated entry; otherwise (newColumns, coeff) with coeff in {1,-1}. +schurExchange = (T, col1, col2, s) -> ( b := T#col2; M := new MutableList from T#col1; b = join(apply(#s, i -> (j := s#i; a := M#j; M#j = b#i; a)), drop(b,#s)); (sgn, M1) := sortSign M; (sgnb, b1) := sortSign b; - if sgn === null or sgnb === null then null else - (for i from 0 to #T-1 list ( - if i == col1 then M1 else if i == col2 then b1 else T#i - ), sgn*sgnb) + if sgn === null or sgnb === null then null + else (for i from 0 to #T-1 list ( + if i == col1 then M1 else if i == col2 then b1 else T#i + ), sgn*sgnb) ) -shuffle = (T, nrows, col1, col2) -> ( - -- replace the first nrows elems of col2 with all the possibles in col1. - a := T#col1; - b := T#col2; - I := subsets(0..#a-1, nrows); - select(apply(I,x -> exchange(T,col1,col2,toList x)), y -> y =!= null) +-- Garnir shuffle between columns col1, col2: enumerate all ways to move +-- nrows entries between the columns. Returns the list of non-degenerate +-- (newFilling, sign) outcomes. +shuffle' = (T, nrows, col1, col2) -> ( + I := subsets(0..#(T#col1)-1, nrows); + select(apply(I, x -> schurExchange(T, col1, col2, toList x)), y -> y =!= null) ) --- writes T as a linear combination of other tableaux T' s.t. T' ( +-- Raw single-step: assumes T is already a normalized Filling (columns +-- sorted, no repeats). Used by trusted callers (the main schurModule fill +-- loop, the worklist-based straighten) that handle normalization up front. +-- Same collision-safe accumulator as towardStandard. +towardStandardRaw = T -> ( x := isStandard T; - if x === null - then new HashTable from {T=>1} - else ( - new HashTable from shuffle(T, x#1+1, x#0, x#0+1) - ) + acc := new MutableHashTable; + if x === null then acc#T = 1 + else ( + for p in shuffle'(T, x#1+1, x#0, x#0+1) do ( + U := new Filling from p#0; + s := p#1; + if acc#?U then ( + v := acc#U + s; + if v == 0 then remove(acc, U) else acc#U = v; + ) + else acc#U = s; + ); + ); + new HashTable from acc + ) + +-- Single Garnir step for Schur: writes T as a Z-linear combination of +-- strictly-larger (in rsort order) Fillings if T is non-standard. +-- Normalizes on entry (so input with un-sorted columns is handled), wraps +-- output keys as Filling (so downstream lookups work directly), and is +-- collision-safe (if two shuffle branches produce the same Filling their +-- coefficients are summed rather than the second overwriting the first). +towardStandard = T -> ( + (c, Tn) := normalize T; + if c == 0 then return new HashTable from {}; + if c == 1 then towardStandardRaw Tn + else ( + -- c == -1: scale every coefficient in the raw expansion by -1. + H := towardStandardRaw Tn; + applyValues(H, v -> -v) + ) ) alltab = (dim,mu) -> ( @@ -158,7 +209,10 @@ alltab = (dim,mu) -> ( ) ) -standardTableaux = (dim,mu) -> select(alltab(dim, mu), T -> isStandard T === null) +standardTableaux = method() +standardTableaux (ZZ, List) := (dim, mu) -> + select(apply(alltab(dim, mu), T -> new Filling from T), + T -> isStandard T === null) schurModule = method() schurModule(List,Module) := (lambda,E) -> ( @@ -169,11 +223,11 @@ schurModule(List,Module) := (lambda,E) -> ( -- A is the list of all of these tableaux. A := alltab(rank E, mu); A = apply(A, T -> new Filling from T); - AT := hashTable toList apply(#A, i -> A#i => i); + AT := hashTable apply(#A, i -> A#i => i); -- now we create the hash table ST of all standard tableaux: T => i -- where the index now is that in the resulting module M B := positions(A, T -> isStandard T === null); - ST := hashTable toList apply(#B, i -> A#(B#i) => i); + ST := hashTable apply(#B, i -> A#(B#i) => i); -- Make the two modules of interest: exteriorE := exteriorPower(mu,E); M := source exteriorE_B; @@ -181,7 +235,16 @@ schurModule(List,Module) := (lambda,E) -> ( -- canonical lifting finv := map(exteriorE, M, (id_exteriorE)_B); m := mutableMatrix(R, numgens M, numgens exteriorE, Dense=>false); - sortedT := rsort A; + -- rsort the ambient basis via a precomputed flat lex key. Our Filling ? + -- Filling compares columns right-to-left and entries within a column + -- top-to-bottom, so the corresponding flat key is + -- reverse T#(#T-1) | reverse T#(#T-2) | ... | reverse T#0 + -- i.e. flatten apply(reverse toList T, reverse). + -- Dispatching to M2's built-in list ? inside compiled code gives an + -- ~10x speedup over the interpreted Filling ? method. + sortedT := apply( + rsort apply(A, T -> (flatten apply(reverse toList T, reverse), T)), + p -> p#1); scan(sortedT, T -> ( col := AT#T; if ST#?T then ( @@ -189,10 +252,12 @@ schurModule(List,Module) := (lambda,E) -> ( m_(ST#T,col) = 1_R; ) else ( - -- this column is a combination of others - a := towardStandard T; + -- T is a column of alltab, so it's already column-sorted and + -- has no repeats (otherwise it wouldn't be in A). Skip the + -- normalize+sign check inside towardStandard. + a := towardStandardRaw T; scan(pairs a, (U,s) -> ( - newcol := AT#(new Filling from U); + newcol := AT#U; columnAdd(m, col, s * 1_R, newcol); )); ))); @@ -200,239 +265,2580 @@ schurModule(List,Module) := (lambda,E) -> ( M.cache#"Schur" = {f, finv, AT, ST}; M) +-- Functorial action: a map f : M -> N of free modules induces +-- S_lambda(f) : S_lambda(M) -> S_lambda(N). We realize it by lifting to +-- the ambient tensor-of-exteriors, applying the exterior functor there, +-- and projecting back down using the cached splittings / projections. schur = method() -schur(List,Matrix) := (lambda,f) -> ( - M := source f; - N := target f; - SM := schurModule(lambda,M); - SN := schurModule(lambda,N); +schur(List, Matrix) := (lambda, f) -> ( + SM := schurModule(lambda, source f); + SN := schurModule(lambda, target f); mu := toList conjugate new Partition from lambda; - F := exteriorPower(mu,f); - gM := SM.cache#"Schur"_1; - gN := SN.cache#"Schur"_0; + F := exteriorPower(mu, f); + gM := SM.cache#"Schur"#1; -- splitting: S_lambda(M) -> ambient(M) + gN := SN.cache#"Schur"#0; -- projection: ambient(N) -> S_lambda(N) schurNM := gN * F * gM; (source schurNM).cache#"Schur" = SM.cache#"Schur"; (target schurNM).cache#"Schur" = SN.cache#"Schur"; schurNM ) ----- MAURICIO and ANTON's additions --------------------------------- -net Filling := T -> netList {apply(toList T, c->stack apply(c, e->net e))}; +-- Pretty-print a Filling as a single row of stacked columns. +net Filling := T -> netList {apply(toList T, c -> stack apply(c, e -> net e))} -augmentFilling=method() -augmentFilling (Filling,ZZ,ZZ):=(T,c,e)->( - if c>=#T then join(T,{{e}}) - else new Filling from apply(#T,j->if j!=c then T#j else T#j|{e}) - ) +-- Append the entry `e` to column `c` of T; if c is past the last column, +-- create a new singleton column {e}. +augmentFilling = method() +augmentFilling (Filling, ZZ, ZZ) := (T, c, e) -> ( + if c >= #T then join(T, {{e}}) + else new Filling from apply(#T, j -> if j != c then T#j else T#j | {e}) + ) -straighten = method(TypicalValue=>Vector) -straighten (Filling, Module) := (T, M) -> ( +----------------------------------------------------------------------- +-- Shared full-straightening worklist. +-- Used by both Schur (straighten) and Weyl (weylStraighten). +-- H : HashTable of BasicList-shaped keys (Filling / WeylFilling / +-- plain List) with coefficients. +-- cast : wraps a raw key as the correct Filling/WeylFilling type. +-- isStd : predicate; returns null on standard keys, violation tuple else. +-- advance : single Garnir step, returning a HashTable (must normalize on +-- entry and produce keys strictly larger than its input in the +-- rsort ordering, so termination is guaranteed). +-- Returns a HashTable whose keys are all standard (w.r.t. isStd) and whose +-- values are the coefficients of the canonical standard-tableau expansion. +-- Every tableau encountered is visited at most once; coefficient collisions +-- are summed (rather than overwritten). +----------------------------------------------------------------------- +straightenWorklist := (H, cast, isStd, advance) -> ( + std := new MutableHashTable; + work := new MutableHashTable; + bump := (t, K, c) -> ( + if t#?K then ( + v := t#K + c; + if v == 0 then remove(t, K) else t#K = v; + ) + else t#K = c; + ); + for K in keys H do ( + c := H#K; + if c != 0 then ( + Kc := cast K; + if isStd Kc === null then bump(std, Kc, c) else bump(work, Kc, c); + ); + ); + while #work > 0 do ( + K := (keys work)#0; + c := work#K; + remove(work, K); + exp := advance K; + for U in keys exp do ( + cc := c * exp#U; + if cc != 0 then ( + Uc := cast U; + if isStd Uc === null then bump(std, Uc, cc) else bump(work, Uc, cc); + ); + ); + ); + new HashTable from std + ) + +----------------------------------------------------------------------- +-- Schur straightening (parallel to weylStraighten below). +----------------------------------------------------------------------- +-- Fast path used by the 2-arg versions: the module-building phase has +-- already baked the full Garnir expansion of every ambient basis element +-- into the cached matrix f, so straighten(T, M) just looks up the column +-- of f indexed by the normalized T. Full symbolic straightening (no +-- module required) goes through the worklist helper. + +schurCast := K -> if instance(K, Filling) then K else new Filling from toList K + +straighten = method() + +-- Symbolic: writes T as a Z-linear combination of standard Fillings. +straighten Filling := T -> ( + (c, Tn) := normalize T; + if c == 0 then new HashTable from {} + else straightenWorklist( + new HashTable from {Tn => c}, + schurCast, isStandard, towardStandard) + ) + +-- Symbolic on a HashTable of (Filling => ZZ) terms. +straighten HashTable := H -> straightenWorklist( + H, schurCast, isStandard, towardStandard) + +-- Module-valued: T => Vector in the Schur module M. Uses the cached +-- change-of-basis matrix f (which already encodes the full Garnir +-- expansion of every ambient basis element), so this is a single +-- sparse-column extraction. +straighten (Filling, Module) := Vector => (T, M) -> ( + if not M.cache#?"Schur" then error "module has no Schur cache"; (c, S) := normalize T; if c == 0 then 0_M else ( - if not M.cache#"Schur"#2#?S then error "tableau and Schur module incompatible" - else ( - i := M.cache#"Schur"#2#S; - f := M.cache#"Schur"#0; - c*f*(source f)_i - ) - ) + AT := M.cache#"Schur"#2; + if not AT#?S then error "tableau and Schur module incompatible"; + f := M.cache#"Schur"#0; + c * f * (source f)_(AT#S) + ) + ) + +-- Module-valued on a HashTable of terms. +straighten (HashTable, Module) := Vector => (H, M) -> ( + if not M.cache#?"Schur" then error "module has no Schur cache"; + AT := M.cache#"Schur"#2; + f := M.cache#"Schur"#0; + amb := source f; + result := 0_M; + for K in keys H do ( + (c, S) := normalize K; + if c != 0 then ( + if not AT#?S then error "tableau and Schur module incompatible"; + result = result + (c * H#K) * f * amb_(AT#S); + ); + ); + result ) printSchurModuleElement = method() -printSchurModuleElement (Vector, Module) := (v,M) -> ( - l := applyPairs(M.cache#"Schur"#3, (T,i)->(i,T)); - scanKeys(l, i-> - if v_i != 0 then - << v_i << "*" << l#i << " " ); - << endl; +printSchurModuleElement (Vector, Module) := (v, M) -> ( + l := applyPairs(M.cache#"Schur"#3, (T, i) -> (i, T)); + scanKeys(l, i -> + if v_i != 0 then << v_i << "*" << l#i << " "); + << endl ) -schurModulesMap = method() -schurModulesMap (Module, Module, Function) := (N,M,F) -> ( - l := applyPairs(M.cache#"Schur"#3, (T,i)->(i,T)); - matrix apply(#l, j->sum(F(l#j), a->a#0*straighten(a#1,N))) +schurModulesMap = method() +schurModulesMap (Module, Module, Function) := (N, M, F) -> ( + l := applyPairs(M.cache#"Schur"#3, (T, i) -> (i, T)); + matrix apply(#l, j -> sum(F(l#j), a -> a#0 * straighten(a#1, N))) ) -maxFilling = method(TypicalValue=>Filling) -maxFilling (List,ZZ) := (p,d)->( --- makes a maximal semistandard tableau filled with 0..d-1 for a given partition p +-- Maximal semistandard Filling for shape p with entries in {0,...,d-1}. +-- Column c has height h = #{i : p_i > c} and is filled with d-h, d-h+1, ..., d-1. +maxFilling = method(TypicalValue => Filling) +maxFilling (List, ZZ) := (p, d) -> ( nCols := max p; - new Filling from apply(nCols, c->( - h := #select(p,j->j>c); -- the length of the column - toList ((d-h)..(d-1)) - )) + new Filling from apply(nCols, c -> ( + h := #select(p, j -> j > c); + toList ((d-h)..(d-1)))) ) -/// -restart -loadPackage "SchurFunctors" -debug SchurFunctors -maxFilling({5,3,3,2}, 6) -/// + +----------------------------------------------------------------------- +-- Characters of (composed) Schur functors. +-- character(L, d) returns the character of S_{L_0} S_{L_1} ... (R^d) +-- as a symmetric polynomial in QQ[x_0..x_{d-1}], where L is a list of +-- partitions applied outside-in. +----------------------------------------------------------------------- character = method() -character (List, ZZ) := (L,d)->( +character (List, ZZ) := (L, d) -> ( L = reverse L; - m:=#L; x := local x; - R:=QQ[x_0..x_(d-1)]; - M:=map(R^d,R^d,matrix(apply(d,j->(apply(d,s->(if j==s then x_j else 0)))))); - apply(m,j->M=schur(L_j,M)); - return trace M + R := QQ[x_0..x_(d-1)]; + M := diagonalMatrix gens R; + scan(L, p -> M = schur(p, M)); + trace M ) ------------Decomposition of representations into irreducibles - -Specialization=(M,F)->( - EV:=map(ring M, ring F, matrix{flatten entries M}), - return EV(F)) +----------------------------------------------------------------------- +-- Decomposition of (polynomial) GL_d representations into irreducibles. +-- +-- F : an m x m matrix whose entries are polynomials in the d^2 generators +-- w_{ij} of R = QQ[w_1..w_{d^2}]; F represents the image of the generic +-- d x d matrix under some polynomial GL_d-representation rho. +-- +-- specialize(M, F) evaluates F at the entries of M (so F is pulled back +-- to rho(M)). transvections(d) produces the generators of the unipotent +-- radical, used to carve out highest-weight vectors. +----------------------------------------------------------------------- +-- Evaluate a matrix-valued polynomial F at the numerical matrix M, i.e. +-- return rho(M) where F = rho(generic matrix). +specialize = (M, F) -> ( + (map(ring M, ring F, matrix {flatten entries M})) F + ) -Identity=(d)->( -matrix(apply(d,i->apply(d,j->if i==j then 1_QQ else 0_QQ)))) +-- d x d transvection I + E_{i,j} (i > j). +transvection = (i, j, d) -> id_(QQ^d) + matrix( + apply(d, r -> apply(d, c -> if r == i and c == j then 1_QQ else 0_QQ))) -Transvection=(param1,param2,d)->( - M1:=matrix(apply(d,i->apply(d,j->if i==j then 1_QQ else 0_QQ))); - M2:=matrix(apply(d,i->apply(d,j->if i==param1 and j==param2 then 1_QQ else 0_QQ))); - M1+M2 - ) +-- The d*(d-1)/2 lower-triangular transvections generating the unipotent +-- radical of upper-triangular GL_d. +transvections = d -> flatten apply(d, i -> + apply(i, j -> transvection(i, j, d))) -Transvections=(d)->( - L:=flatten apply(d,j->(apply(d,s->if j>s then Transvection(s,j,d)))); - select(L,a->if a=!=null then true) - ) - -diagonal=(L)->( - d:=#L; - matrix(apply(d,j->(apply(d,i->if i==j then L_i else 0)))) - ) +-- Weight of a filling with respect to a torus character D = (d_0,...,d_{n-1}): +-- weight(T, D) = prod_{cell} D#(entry). For D = (1,2,...,d) this is the +-- numeric image of the maximal Weyl weight. +weight = method() +weight (Filling, List) := (T, D) -> product(flatten toList T, i -> D#i) +-- Find a highest-weight vector for the S_p-isotypic component of rho. findSubRep = method() -findSubRep (List,Matrix) := (p,F)->( +findSubRep (List, Matrix) := (p, F) -> ( d := round sqrt numgens ring F; - T := maxFilling(p,d); - Trans:=Transvections(d); - TransEval:=apply(Trans,M->Specialization(M,F)); - TE:=matrix apply(TransEval,k->{k-Specialization(Identity(d),F)}); - D:=apply(d,j->(j+1)_QQ); - M:=Specialization(diagonal(D),F)-weight(T,D)*Specialization(Identity(d),F); - return syz(TE||M) + T := maxFilling(p, d); + TransEval := apply(transvections d, M -> specialize(M, F)); + idF := specialize(id_(QQ^d), F); + -- each transvection must act as the identity on highest-weight vectors + TE := matrix apply(TransEval, k -> {k - idF}); + -- the diagonal torus must act by the maximal weight of S_p + D := apply(d, j -> (j+1)_QQ); + M := specialize(diagonalMatrix D, F) - weight(T, D) * idF; + syz(TE || M) ) characterRep = method() -characterRep Matrix := F->( +characterRep Matrix := F -> ( d := round sqrt numgens ring F; x := symbol x; R := QQ[x_0..x_(d-1)]; - D := diagonalMatrix gens R; - trace Specialization(D, F) - ) + trace specialize(diagonalMatrix gens R, F) + ) + decomposeRep = method() decomposeRep Matrix := F -> ( - -- extremely readable code to follow... - ir := flatten@@exponents \flatten entries first coefficients splitCharacter characterRep F; - new HashTable from apply(ir, p->p=>findSubRep(p,F)) - ); + -- splitCharacter produces an element of a SchurRing; + -- listForm returns a list of (partition, multiplicity) pairs. + ir := apply(listForm splitCharacter characterRep F, first); + new HashTable from apply(ir, p -> p => findSubRep(p, F)) + ) + +needsPackage "SymmetricPolynomials" +needsPackage "SchurRings" + +-- splitCharacter: given a symmetric polynomial in x_0..x_{n-1} (produced +-- e.g. by characterRep), rewrite it as a linear combination of Schur +-- polynomials s_lambda in the SchurRing. Uses SymmetricPolynomials to +-- pass through elementary symmetric functions, then toS to land in a +-- SchurRing of rank n. +splitCharacter = method() +splitCharacter RingElement := ce -> ( + pe := elementarySymmetric ce; + -- elementarySymmetric returns a polynomial in 2n variables: the first + -- n are the original x_i, the last n are the elementary symmetric + -- functions e_1..e_n. + n := numgens source vars ring ce; + R2 := symmetricRing(coefficientRing ring pe, n); + es := (vars R2)_{0..n-1}; + toS substitute(pe, es | es) + ) + +----------------------------------------------------------------------- +-- Weyl modules: divided-power analogue of Filling / Schur straightening +-- Ported from Keller VandeBogert's Ext-hooks.m2 +-- The straightening algorithm here enforces strictly-increasing rows +-- (the defining relation for W_lambda in characteristic-free form) and +-- uses divided-power comultiplication/multiplication, so no signs are +-- tracked when rows are re-sorted. +----------------------------------------------------------------------- + +WeylFilling = new Type of BasicList + +weyl = method() +weyl List := L -> new WeylFilling from L + +net WeylFilling := T -> (net conjugate new Filling from toList T) + +-- Compare two WeylFillings. Mirrors Filling ? Filling, with rows playing the +-- role that columns do for Filling: the LAST row is compared first, and +-- within a row we compare from the last entry toward the first. +-- Iterative version — avoids the per-comparison `drop` allocation that +-- would otherwise dominate rsort in weylModule. +WeylFilling ? WeylFilling := (T,U) -> ( + k := #T - 1; + while k >= 0 do ( + a := T#k; + b := U#k; + j := #a - 1; + while j >= 0 do ( + if a#j > b#j then return symbol>; + if a#j < b#j then return symbol<; + j = j - 1; + ); + k = k - 1; + ); + symbol== + ) + +conjugate WeylFilling := T -> ( + a := #T#0; + new WeylFilling from apply(0..a-1, i -> + for j from 0 to #T-1 when #T#j > i list T#j#i) + ) + +WeylFilling == WeylFilling := (T,P) -> toList T == toList P + +-- row selection / single-row access +WeylFilling _ List := (T,L) -> (toList T)_L +WeylFilling _ ZZ := (T,n) -> (toList T)_n + +-- convert a tuple (weakly-increasing list representing a multiset) to its +-- exponent vector of length n. Single pass over L, O(#L + n) rather than +-- the O(n*#L) scan-n-times implementation that was dominant in profiling. +tupleToExp = method() +tupleToExp(List,ZZ) := (L,n) -> ( + e := new MutableList from toList(n:0); + for x in L do e#x = e#x + 1; + toList e + ) +tupleToExp(List) := L -> ( + if #L == 0 then {} + else tupleToExp(L, max L + 1) + ) + +-- inverse of tupleToExp: exponent vector -> weakly-increasing tuple. +-- Single-pass with a MutableList — faster than the `splice ... (L_i):i` version +-- because it avoids allocating #L intermediate Sequences. +expToTuple = L -> ( + out := new MutableList; + for i from 0 to #L-1 do for k from 1 to L#i do out#(#out) = i; + toList out + ) + +-- componentwise majorization test, used by divComult +totalBd = (L1,L2) -> ( + n := max(#L1, #L2); + L1 = L1 | toList((n-#L1):0); + L2 = L2 | toList((n-#L2):0); + all(L2-L1, i -> i >= 0) + ) + +boundedPower = (n,d,L) -> select(compositions(n,d), i -> totalBd(i,L)) + +------------------------------------------------------------------------- +-- Fast exponent-native divided-power helpers. +-- These work directly on exponent vectors (lists of length n whose entries +-- sum to the total degree), avoiding the tuple<->exp round-trips that +-- dominated profiling of weylShuffle. +------------------------------------------------------------------------- + +-- Enumerate length-n exponent vectors summing to p, bounded componentwise +-- by Lexp. Direct backtracking: no compositions(n,p) allocation, no filter. +boundedExpAux := (Lexp, p, idx, n, acc, out) -> ( + if idx == n-1 then ( + if p <= Lexp#idx then ( + acc#idx = p; + out#(#out) = toList acc; + ); + ) else ( + upper := min(p, Lexp#idx); + for v from 0 to upper do ( + acc#idx = v; + boundedExpAux(Lexp, p-v, idx+1, n, acc, out); + ); + ); + ) + +boundedExp = (Lexp, p) -> ( + n := #Lexp; + if n == 0 then (if p == 0 then {{}} else {}) + else ( + acc := new MutableList from toList(n:0); + out := new MutableList; + boundedExpAux(Lexp, p, 0, n, acc, out); + toList out + ) + ) + +-- Divided-power comultiplication on an exponent vector, returning a list +-- of (left-exp, right-exp) pairs with left + right = Lexp and sum right = p. +divComultExp = (Lexp, p) -> ( + for i in boundedExp(Lexp, p) list (Lexp - i, i) + ) + +-- Divided-power multiplication on exponent vectors of the same length n. +-- Returns (c, L1+L2) where c = prod binomial(L1_k + L2_k, L1_k). +divMultExp = (L1, L2) -> ( + c := 1; + for k from 0 to #L1-1 do c = c * binomial(L1#k + L2#k, L1#k); + (c, L1 + L2) + ) + +-- divided-power comultiplication D_{p+q} F -> D_p F ** D_q F +-- applied to the basis element encoded by the given exponent vector. +divComult = method() +divComult(Sequence,ZZ) := (L,p) -> ( + t := boundedPower(#L, p, toList L); + for i in t list {toList L - i, i} + ) +divComult(List,ZZ,ZZ) := (L,p,n) -> ( + Lexp := tupleToExp(L, n); + for pair in divComultExp(Lexp, p) list {expToTuple pair#0, expToTuple pair#1} + ) +divComult(List,ZZ) := (L,p) -> ( + if #L == 0 then divComult(L, p, 0) + else divComult(L, p, max L + 1) + ) + +-- divided-power multiplication D_p F ** D_q F -> D_{p+q} F on a basis pair +divMult = method() +divMult(Sequence,Sequence) := (S1,S2) -> ( + Lk := for i from 0 to #S1-1 list binomial(S1_i + S2_i, S1_i); + {product Lk, toList S1 + toList S2} + ) +divMult(List,List,ZZ) := (L1,L2,n) -> ( + (c, sumExp) := divMultExp(tupleToExp(L1,n), tupleToExp(L2,n)); + {c, expToTuple sumExp} + ) +divMult(List,List) := (L1,L2) -> ( + m1 := if #L1 == 0 then -1 else max L1; + m2 := if #L2 == 0 then -1 else max L2; + divMult(L1, L2, max(m1, m2) + 1) + ) + +-- rows-only sort. Since divided powers are symmetric, no sign is tracked. +weylNormalize = method() +weylNormalize WeylFilling := T -> new WeylFilling from (toList T)/sort + +-- Returns null if T is Weyl-standard (strictly increasing down each column), +-- otherwise the (row,column) of the first violation from the bottom up. +isWeylStandard = method() +isWeylStandard WeylFilling := T -> ( + i := #T - 2; + while i >= 0 do ( + a := T#i; b := T#(i+1); + n := #b; + for j from 0 to n-1 do if a#j >= b#j then return (i,j); + i = i - 1; + ); + null + ) + +-- Exchange step between two columns. Divided-power setting, so coefficient is -1. +weylExchange = method() +weylExchange(WeylFilling,ZZ,ZZ,List) := (T,col1,col2,s) -> ( + b := reverse T#col2; + M := new MutableList from reverse T#col1; + b = join(apply(#s, i -> (j := s#i; a := M#j; M#j = b#i; a)), drop(b,#s)); + M1 := sort toList M; + b1 := sort b; + (for i from 0 to #T-1 list ( + if i == col1 then M1 + else if i == col2 then b1 + else T#i + ), -1) + ) + +-- Single Garnir-style shuffle using divided-power comultiplication +-- across rows (row1, row2) of T, at column ncols. Assumes T is already +-- weylNormalize'd (rows weakly increasing). +-- +-- All divided-power arithmetic is inlined: +-- * the comultiplication enumerates split vectors i (bounded by Jexp) +-- via a direct recursive backtracker — no intermediate list of splits +-- is ever allocated; +-- * for each non-identity split, the two divided-power multiplications +-- (r1head * (Jexp-i) and i * r2tail) are computed in a single pass +-- over the alphabet: the resulting exponent vectors d1, d2, and the +-- binomial coefficients c1, c2 are accumulated together. +-- This eliminates the per-split function-call overhead of divComultExp / +-- divMultExp (which together contributed ~80% of this routine's cost). +weylShuffle = (T,ncols,row1,row2) -> ( + r1 := T#row1; + r2 := T#row2; + -- T#row2 is already sorted, so "last j with r2#j == r2#(ncols-1)" + -- is a rightward scan from ncols-1. + pivot := r2#(ncols-1); + newLast := ncols-1; + while newLast+1 < #r2 and r2#(newLast+1) == pivot do newLast = newLast+1; + + r1head := r1_{0..ncols-2}; + r1tail := r1_{ncols-1..#r1-1}; + r2head := r2_{0..newLast}; + r2tail := r2_{newLast+1..#r2-1}; + + -- Uniform alphabet size covering every slice. + maxOf := L -> if #L == 0 then -1 else max L; + n := 1 + max {maxOf r1head, maxOf r1tail, maxOf r2head, maxOf r2tail}; + p := newLast + 1; + + -- Exponent-form slices. r1tail's exponent vector is captured inside + -- Jexp (= r1tExp + r2hExp); we never materialize r1tExp separately. + r1hExp := tupleToExp(r1head, n); + r2hExp := tupleToExp(r2head, n); + r2tExp := tupleToExp(r2tail, n); + Jexp := tupleToExp(r1tail | r2head, n); + + Tlist := toList T; + result := new MutableList; + accI := new MutableList from toList(n:0); + + -- The newly-built rows have fixed lengths equal to #r1 and #r2. + r1len := #r1; + r2len := #r2; + + -- Backtracking enumerator of i in boundedExp(Jexp, p), inlined: + -- at each leaf we write newR1 and newR2 directly as weakly-increasing + -- tuples (by iterating k = 0..n-1 and emitting k-copies into pre-sized + -- MutableLists), accumulating c1, c2 in the same pass. This skips + -- materialization of intermediate exponent vectors d1, d2 and any call + -- to expToTuple. The identity split (i == r2hExp) is skipped. + emit := null; + emit = (idx, remaining) -> ( + if idx == n-1 then ( + if remaining > Jexp#idx then return; + accI#idx = remaining; + -- identity check: accI == r2hExp ? + isIdentity := true; + for k from 0 to n-1 do + if accI#k =!= r2hExp#k then (isIdentity = false; break); + if isIdentity then return; + + -- Single pass: write newR1, newR2 directly into pre-sized + -- MutableLists and accumulate c1, c2 at the same time. + c1 := 1; c2 := 1; + newR1Mut := new MutableList from toList(r1len:0); + newR2Mut := new MutableList from toList(r2len:0); + i1 := 0; i2 := 0; + for k from 0 to n-1 do ( + ik := accI#k; + a := Jexp#k - ik; -- split#0#k + r1hk := r1hExp#k; + r2tk := r2tExp#k; + d1k := r1hk + a; + d2k := ik + r2tk; + for q from 1 to d1k do (newR1Mut#i1 = k; i1 = i1+1); + for q from 1 to d2k do (newR2Mut#i2 = k; i2 = i2+1); + if r1hk > 0 and a > 0 then c1 = c1 * binomial(d1k, r1hk); + if ik > 0 and r2tk > 0 then c2 = c2 * binomial(d2k, ik); + ); + newR1 := toList newR1Mut; + newR2 := toList newR2Mut; + newFilling := new WeylFilling from for j from 0 to #T-1 list ( + if j == row1 then newR1 + else if j == row2 then newR2 + else Tlist#j); + result#(#result) = (newFilling, -c1*c2); + return; + ); + upper := min(remaining, Jexp#idx); + for v from 0 to upper do ( + accI#idx = v; + emit(idx+1, remaining-v); + ); + ); + emit(0, p); + toList result + ) + +-- Raw single-step: assumes T is already weylNormalize'd (rows weakly +-- increasing). Used by trusted callers (the main weylModule fill loop, +-- and the worklist-based weylStraighten) that handle normalization up front. +-- +-- Note: weylShuffle's output keys are pairwise distinct -- different splits +-- i in boundedExp(Jexp, p) give different d2 = i + r2tExp, hence different +-- output WeylFillings -- so no collision accumulator is needed here; we can +-- construct the HashTable in one shot. +towardWeylStandardRaw = T -> ( + x := isWeylStandard T; + if x === null then new HashTable from {T => 1} + else hashTable weylShuffle(T, x#1+1, x#0, x#0+1)) + +-- One straightening step: writes T as a Z-linear combination of +-- WeylFillings T' that are strictly larger than T in the rsort ordering, +-- if T is not already standard. +-- Collision-safe: two shuffle branches that happen to land on the same +-- normalized filling have their coefficients summed (rather than the second +-- silently overwriting the first, as would happen with `new HashTable from`). +towardWeylStandard = T -> towardWeylStandardRaw weylNormalize T + +----------------------------------------------------------------------- +-- Weyl straightening (parallel to straighten above). +----------------------------------------------------------------------- +-- Fast path used by the 2-arg versions: the module-building phase has +-- already baked the full Garnir expansion of every ambient basis element +-- into the cached matrix f, so weylStraighten(T, M) just looks up the +-- column of f indexed by the normalized T. Full symbolic straightening +-- (no module required) goes through the shared worklist helper. + +weylCast := K -> if instance(K, WeylFilling) then K else new WeylFilling from toList K + +weylStraighten = method() + +-- Symbolic: writes T as a Z-linear combination of standard WeylFillings. +weylStraighten WeylFilling := T -> straightenWorklist( + new HashTable from {weylNormalize T => 1}, + weylCast, isWeylStandard, towardWeylStandard) + +-- Symbolic on a HashTable of (WeylFilling => ZZ) terms. +weylStraighten HashTable := H -> straightenWorklist( + H, weylCast, isWeylStandard, towardWeylStandard) + +----------------------------------------------------------------------- +-- Divided-power functor D^d : Mod(R) -> Mod(R) +-- Parallel to exteriorPower(ZZ|List, Module|Matrix) +-- On a free module of rank n, D^d has rank binomial(n+d-1, d), with basis +-- indexed by compositions(n,d) (so that dividedPower(lambda,E) as an +-- iterated tensor agrees with M2's ** order: first factor varies slowest). +----------------------------------------------------------------------- + +dividedPower = method() + +dividedPower(ZZ, Module) := (d,E) -> ( + R := ring E; + n := rank E; + R^(binomial(n+d-1, d)) + ) + +dividedPower(List, Module) := (L,E) -> ( + if #L == 0 then dividedPower(0,E) + else dividedPower(L#0, E) ** dividedPower(drop(L,1), E) + ) + +-- D^d applied to f: R^n -> R^m, on the basis indexed by compositions(n,d). +-- Formula: if f = [f_1 | ... | f_n] then D^d(f)(d_alpha) = prod_i (f_i)^{(alpha_i)} +-- computed in the divided-power algebra of the target, where +-- (sum_j v_j e_j)^{(k)} = sum_{|beta|=k} (prod_j v_j^{beta_j}) e^{(beta)} +-- e^{(beta^1)} * e^{(beta^2)} = (prod_j binomial(beta^1_j+beta^2_j, beta^1_j)) e^{(beta^1+beta^2)} +dividedPower(ZZ, Matrix) := (d,f) -> ( + R := ring f; + src := source f; + tgt := target f; + n := rank src; + m := rank tgt; + srcBasis := compositions(n, d); + tgtBasis := compositions(m, d); + tgtIdx := hashTable apply(#tgtBasis, i -> tgtBasis#i => i); + cols := if n == 0 then {} else entries transpose f; + -- Precompute cols#i#j^k for 0 <= k <= d, once per matrix entry. + -- Without this, the inner loop re-exponentiates every ring element on + -- every (alpha, beta) pair. colPow#i#j#k = (cols#i#j)^k. + one := 1_R; + colPow := apply(n, i -> apply(m, j -> ( + v := cols#i#j; + pw := new MutableList from (d+1):one; + for k from 1 to d do pw#k = (pw#(k-1)) * v; + toList pw + ))); + -- Precompute compositions(m, k) once per k <= d; reused for every alpha. + betasOf := apply(d+1, k -> compositions(m, k)); + zeroG := toList(m:0); + DF := mutableMatrix(R, #tgtBasis, #srcBasis); + scan(#srcBasis, aIdx -> ( + alpha := srcBasis#aIdx; + acc := new MutableHashTable; + acc#zeroG = one; + for i from 0 to n-1 do ( + ai := alpha#i; + if ai > 0 then ( + newAcc := new MutableHashTable; + colPowI := colPow#i; + betas := betasOf#ai; + for g in keys acc do ( + coef := acc#g; + for beta in betas do ( + cc := coef; + for j from 0 to m-1 do ( + bj := beta#j; + if bj > 0 then cc = cc * colPowI#j#bj; + cc = cc * binomial(g#j + bj, bj); + ); + newG := g + beta; + if newAcc#?newG then newAcc#newG = newAcc#newG + cc + else newAcc#newG = cc; + ); + ); + acc = newAcc; + ); + ); + for g in keys acc do ( + DF_(tgtIdx#g, aIdx) = acc#g; + ); + )); + map(dividedPower(d, tgt), dividedPower(d, src), matrix DF) + ) + +dividedPower(List, Matrix) := (L,f) -> ( + if #L == 0 then dividedPower(0,f) + else dividedPower(L#0, f) ** dividedPower(drop(L,1), f) + ) + +----------------------------------------------------------------------- +-- Enumeration of WeylFillings +-- allWeylTab(dim, lambda) mirrors alltab +-- standardWeylTableaux(...) mirrors standardTableaux +-- Row 0 is enumerated slowest, matching the ** convention so that the +-- i-th WeylFilling in allWeylTab corresponds to the i-th basis element +-- of dividedPower(lambda, E). +----------------------------------------------------------------------- + +allWeylTab = (dim, lambda) -> ( + if #lambda == 0 then return {{}}; + rows := apply(compositions(dim, lambda#0), e -> toList expToTuple e); + if #lambda == 1 then apply(rows, r -> {r}) + else ( + rest := allWeylTab(dim, drop(lambda, 1)); + flatten apply(rows, r -> apply(rest, y -> prepend(r, y))) + ) + ) + +standardWeylTableaux = method() +standardWeylTableaux (ZZ, List) := (dim, lambda) -> + select(apply(allWeylTab(dim, lambda), T -> new WeylFilling from T), + T -> isWeylStandard T === null) + +----------------------------------------------------------------------- +-- weylModule : exact parallel of schurModule. +-- M = image of the canonical projection D^{lambda_0}(E) ** ... ** D^{lambda_{k-1}}(E) +-- ---> W_lambda(E) +-- The cache key "Weyl" stores {f, finv, AT, ST}, analogous to "Schur". +----------------------------------------------------------------------- + +weylModule = method() +weylModule(List, Module) := (lambda, E) -> ( + R := ring E; + A := allWeylTab(rank E, lambda); + A = apply(A, T -> new WeylFilling from T); + AT := hashTable apply(#A, i -> A#i => i); + B := positions(A, T -> isWeylStandard T === null); + ST := hashTable apply(#B, i -> A#(B#i) => i); + ambD := dividedPower(lambda, E); + M := source ambD_B; + finv := map(ambD, M, (id_ambD)_B); + m := mutableMatrix(R, numgens M, numgens ambD, Dense=>false); + -- rsort via a precomputed flat lex key (last row slowest, entries within + -- a row last-to-first). Built-in list ? runs in compiled code; on the + -- WeylFilling ambient this is ~10x faster than sorting on the interpreted + -- WeylFilling ? method directly. + sortedT := apply( + rsort apply(A, T -> (flatten apply(reverse toList T, reverse), T)), + p -> p#1); + scan(sortedT, T -> ( + col := AT#T; + if ST#?T then ( + m_(ST#T, col) = 1_R; + ) + else ( + -- T comes from allWeylTab so its rows are already weakly + -- increasing — skip the weylNormalize inside towardWeylStandard. + a := towardWeylStandardRaw T; + scan(pairs a, (U, s) -> ( + -- towardWeylStandardRaw already returns WeylFilling keys. + columnAdd(m, col, s * 1_R, AT#U); + )); + ))); + f := map(M, ambD, matrix m); + M.cache#"Weyl" = {f, finv, AT, ST}; + M + ) + +-- Functorial action on morphisms: parallel of schur(List, Matrix). A map +-- f : M -> N induces W_lambda(f) : W_lambda(M) -> W_lambda(N); we lift to +-- the tensor-of-divided-powers, apply D^lambda(f), and project back down. +weyl(List, Matrix) := (lambda, f) -> ( + WM := weylModule(lambda, source f); + WN := weylModule(lambda, target f); + F := dividedPower(lambda, f); + gM := WM.cache#"Weyl"#1; -- splitting: W_lambda(M) -> ambient(M) + gN := WN.cache#"Weyl"#0; -- projection: ambient(N) -> W_lambda(N) + weylNM := gN * F * gM; + (source weylNM).cache#"Weyl" = WM.cache#"Weyl"; + (target weylNM).cache#"Weyl" = WN.cache#"Weyl"; + weylNM + ) + +-- Module-valued: T => Vector in the Weyl module M. Uses the cached +-- change-of-basis matrix f (which already encodes the full Garnir +-- expansion of every ambient basis element), so this is a single +-- sparse-column extraction. +weylStraighten(WeylFilling, Module) := Vector => (T, M) -> ( + if not M.cache#?"Weyl" then error "module has no Weyl cache"; + Tn := weylNormalize T; + AT := M.cache#"Weyl"#2; + if not AT#?Tn then error "tableau and Weyl module incompatible"; + f := M.cache#"Weyl"#0; + f * (source f)_(AT#Tn) + ) + +-- Module-valued on a HashTable of terms. +weylStraighten(HashTable, Module) := Vector => (H, M) -> ( + if not M.cache#?"Weyl" then error "module has no Weyl cache"; + AT := M.cache#"Weyl"#2; + f := M.cache#"Weyl"#0; + amb := source f; + result := 0_M; + for K in keys H do ( + c := H#K; + if c != 0 then ( + S := weylNormalize weylCast K; + if not AT#?S then error "tableau and Weyl module incompatible"; + result = result + c * f * amb_(AT#S); + ); + ); + result + ) + +printWeylModuleElement = method() +printWeylModuleElement (Vector, Module) := (v, M) -> ( + l := applyPairs(M.cache#"Weyl"#3, (T,i) -> (i,T)); + scanKeys(l, i -> + if v_i != 0 then + << v_i << "*" << l#i << " "); + << endl; + ) + +weylModulesMap = method() +weylModulesMap (Module, Module, Function) := (N, M, F) -> ( + l := applyPairs(M.cache#"Weyl"#3, (T,i) -> (i,T)); + matrix apply(#l, j -> sum(F(l#j), a -> a#0 * weylStraighten(a#1, N))) + ) + +-- Maximal Weyl-standard tableau for shape p with entries 0..d-1, filled +-- with d - (conj p)_j + i at position (i,j). Same formula as maxFilling, +-- but stored row-wise. +maxWeylFilling = method(TypicalValue => WeylFilling) +maxWeylFilling (List, ZZ) := (p, d) -> ( + cp := toList conjugate new Partition from p; + new WeylFilling from apply(#p, i -> + apply(p#i, j -> d - cp#j + i)) + ) + +-- Row-wise analogue of augmentFilling. WeylFillings are stored row-wise +-- (#T is the number of rows), so augmentWeylFilling(T, r, e) extends row r +-- by the entry e, or appends a new singleton row {e} if r >= #T. +augmentWeylFilling = method() +augmentWeylFilling (WeylFilling, ZZ, ZZ) := (T, r, e) -> ( + if r >= #T then new WeylFilling from join(toList T, {{e}}) + else new WeylFilling from apply(#T, j -> if j != r then T#j else T#j | {e}) + ) + +-- Character of a (nested composition of) Weyl functor(s), parallel to +-- character(List, ZZ) on the Schur side. L is a list of partitions; this +-- computes the trace of the composed Weyl-functor image of the diagonal +-- GL_d operator. In characteristic 0 W_λ and S_λ are canonically +-- isomorphic, so weylCharacter(L, d) == character(L, d); we provide this +-- parallel wrapper for uniformity and in case one wants to see that the +-- diagonal-character computation goes through the Weyl side end-to-end. +weylCharacter = method() +weylCharacter (List, ZZ) := (L, d) -> ( + L = reverse L; + m := #L; + x := local x; + R := QQ[x_0..x_(d-1)]; + M := map(R^d, R^d, + matrix(apply(d, j -> apply(d, s -> if j == s then x_j else 0)))); + apply(m, j -> M = weyl(L_j, M)); + trace M + ) + + +beginDocumentation() + +----------------------------------------------------------------------- +-- Overview +----------------------------------------------------------------------- +doc /// + Key + SchurFunctors + Headline + Schur and Weyl functors from partitions and Young tableaux + Description + Text + A {\em Schur functor} $S_\lambda$ is a polynomial functor on the + category of free modules, indexed by a partition + $\lambda = (\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_r)$. + Classical special cases include + Text + @UL { + {TEX "$S_{(d)}(E) = \\mathrm{Sym}^d(E)$, the $d$-th symmetric power"}, + {TEX "$S_{(1^d)}(E) = \\wedge^d(E)$, the $d$-th exterior power"}, + {TEX "$S_{(d,\\dots,d)}(E) = (\\det E)^{\\otimes k}$ on a rank-$d$ factor (repeated $k$ times)"}, + {TEX "$S_{(2,1)}(E) \\subseteq E \\otimes \\wedge^2 E$, the first genuinely mixed case"} + }@ + Text + The {\em Weyl functor} $W_\lambda$ is the divided-power analogue: + in characteristic zero $W_\lambda(E) \cong S_\lambda(E)$, but + over $\mathbb{Z}$ or in positive characteristic they are + genuinely different functors. They are related by duality and + appear symmetrically in Schur-Weyl duality and in the definition + of Weyl modules of the general linear group. + Text + @STRONG "Construction."@ Both functors are presented by + Garnir / straightening relations. For $S_\lambda$ one has the + presentation + $$\textstyle\bigotimes_{i \ge 1}\wedge^{\lambda'_i}(E) + \;\twoheadrightarrow\; S_\lambda(E),$$ + where $\lambda'$ is the conjugate (transpose) partition: $S_\lambda(E)$ + is the quotient of this tensor product of exterior powers by the + Garnir relations across adjacent columns. Dually, $W_\lambda$ is + a quotient of a tensor product of divided powers, + $$\textstyle\bigotimes_{i \ge 1} D^{\lambda_i}(E) + \;\twoheadrightarrow\; W_\lambda(E),$$ + by (divided-power) Garnir relations across adjacent rows. + Text + @STRONG "Tableaux and the semistandard basis."@ A filling of a + Young diagram of shape $\lambda$ by integers $\{0,\dots,n-1\}$ + (representing a basis of $E = R^n$) is {\em semistandard} if its + entries weakly increase along rows and strictly increase down + columns. Over $\mathbb{Z}$ the semistandard fillings index a + free basis of $S_\lambda(E)$ and of $W_\lambda(E)$. Arbitrary + fillings are reduced to combinations of semistandard ones by the + @EM "straightening algorithm"@; see @TO straighten@ and + @TO weylStraighten@. + Text + In this package Schur tableaux are stored {\em column-wise} as + objects of type @TO Filling@, while Weyl tableaux are stored + {\em row-wise} as objects of type @TO WeylFilling@. Conjugation + of partitions swaps the two conventions; see + @TO (conjugate, Filling)@. + Text + @STRONG "Representation theory."@ Over a field of characteristic + zero the Schur functors $S_\lambda(V)$, as $\lambda$ ranges over + partitions with at most $\dim V$ parts, give all the polynomial + irreducible representations of $GL(V)$. The character of + $S_\lambda(V)$ is the Schur function $s_\lambda$ in the + eigenvalues of the diagonal torus; computing and decomposing + characters is handled by @TO character@, @TO weylCharacter@, + @TO splitCharacter@, @TO characterRep@, and @TO decomposeRep@. + Text + @STRONG "Package layout."@ The two worlds (Schur / Weyl) expose + parallel sets of functions: + Text + @UL { + {"Modules: ", TO schurModule, " / ", TO weylModule, "."}, + {"Functoriality: ", TO schur, " / ", TO weyl, " take a map ", + TEX "$f : E \\to F$", " to the induced ", + TEX "$S_\\lambda(f)$", " / ", TEX "$W_\\lambda(f)$", "."}, + {"Straightening: ", TO straighten, " / ", TO weylStraighten, + " rewrite an arbitrary tableau in the semistandard basis."}, + {"Module maps: ", TO schurModulesMap, " / ", TO weylModulesMap, + " build a homomorphism from its action on a tableau basis."}, + {"Tableau combinatorics: ", TO Filling, " / ", TO WeylFilling, + ", with ", TO normalize, " / ", TO weylNormalize, ", ", + TO isStandard, " / ", TO isWeylStandard, ", ", + TO towardWeylStandard, ", ", + TO standardTableaux, " / ", TO standardWeylTableaux, ", ", + TO augmentFilling, " / ", TO augmentWeylFilling, ", ", + TO maxWeylFilling, "."}, + {"Divided powers: ", TO dividedPower, ", ", TO divMult, ", ", + TO divComult, "."}, + {"Characters: ", TO character, " / ", TO weylCharacter, ", ", + TO splitCharacter, ", ", TO characterRep, ", ", + TO decomposeRep, "."}, + {"Pretty-printing: ", TO printSchurModuleElement, " / ", + TO printWeylModuleElement, "."} + }@ + Text + @STRONG "A first example."@ Take the smallest non-hook, non-trivial + shape $(2,1)$: + Example + E = QQ^3; + M = schurModule({2,1}, E) + rank M + W = weylModule({2,1}, E) + rank W + Text + Both have rank 8, which is $\dim S_{(2,1)}(\mathbb{Q}^3) = 8$ by + the Weyl character formula. They are canonically isomorphic in + characteristic 0, but are presented as quotients of different + ambients: + $\wedge^2(E)\otimes E \twoheadrightarrow \mathrm{schurModule}((2,1), \mathbb{Q}^3)$ + while + $D^2(E)\otimes D^1(E) \twoheadrightarrow \mathrm{weylModule}((2,1), \mathbb{Q}^3)$. + Text + @STRONG "References."@ For the Schur side see W. Fulton, + {\em Young Tableaux}, LMS Student Texts 35 (1997), Chapter 8. + For the Weyl-module / divided-power construction see + K. Akin, D. Buchsbaum, and J. Weyman, + {\em Schur functors and Schur complexes}, Adv. Math. {\bf 44} + (1982), 207-278; and the book J. Weyman, + {\em Cohomology of Vector Bundles and Syzygies}, Cambridge Tracts + 149 (2003). + SeeAlso + Filling + WeylFilling + schurModule + weylModule + schur + weyl + straighten + weylStraighten + character + weylCharacter /// -restart -loadPackage "SchurFunctors" -debug SchurFunctors -R=QQ[w_1..w_9] -F=genericMatrix(R,3,3) -G=schur({4},schur({2},F)); -splitCharacter characterRep G -decomposeRep G +----------------------------------------------------------------------- +-- Filling type and tableau combinatorics (Schur side) +----------------------------------------------------------------------- +doc /// + Key + Filling + (symbol ?, Filling, Filling) + (symbol _, Filling, List) + (net, Filling) + Headline + a Young tableau stored column-wise + Description + Text + A @TT "Filling"@ is a Young tableau of partition shape, stored as + a list of its columns. A filling of shape + $\mu = (\mu_1 \ge \mu_2 \ge \cdots \ge \mu_r)$ with entries from + an alphabet $\{0, 1, \ldots, d-1\}$ is represented by + @TT "new Filling from {c_1, c_2, ..., c_{mu_1}}"@, + where $c_j$ is the list of entries in the $j$-th column read from + top to bottom. The length of $c_j$ equals $\mu'_j$, the length of + the $j$-th column (equivalently, the $j$-th part of the conjugate + partition $\mu'$). + Text + Because Schur modules are built from + $\wedge^{\mu'_1}(E) \otimes \cdots \otimes \wedge^{\mu'_k}(E)$, + the column-wise storage makes each column directly correspond to + one wedge-power tensor factor. This is the dual convention to + @TO WeylFilling@, which stores rows. + Example + T = new Filling from {{0,1}, {1,2}} + toList T + T#0 + #T + Text + The tableau pretty-prints via its @TT "net"@ method: + Example + T + Text + Conjugation of a @TT "Filling"@ produces the tableau of conjugate + (transposed) shape: + Example + conjugate T + toList conjugate T + Text + A filling is @EM "standard"@ (in this package; equivalently, + @EM "semistandard"@ in the standard combinatorial sense) if its + entries strictly increase down each column and weakly increase + along each row. Over $\mathbb{Z}$ the standard fillings of shape + $\mu$ with entries in $\{0,\ldots,d-1\}$ index a free basis of + $S_\mu(R^d)$. + Example + S = standardTableaux(3, {2,1}); + #S == rank schurModule({2,1}, QQ^3) + SeeAlso + WeylFilling + isStandard + standardTableaux + normalize + augmentFilling + (conjugate, Filling) + schurModule + straighten /// -weight = method() -weight (Filling, List) := (T,D) -> product(flatten toList T, i->D#i); +doc /// + Key + (conjugate, Filling) + Headline + transpose of a Young tableau + Usage + conjugate T + Inputs + T:Filling + Outputs + :Filling + the tableau of conjugate (transposed) shape whose columns are + the rows of @TT "T"@ + Description + Text + The conjugate partition $\mu'$ of $\mu$ has + $\mu'_j = \#\{i : \mu_i \ge j\}$. As a Young diagram, $\mu'$ is + the reflection of $\mu$ across its main diagonal. Since Schur + tableaux are stored column-wise (each @TT "T#j"@ is a column), + conjugation swaps rows and columns. + Example + T = new Filling from {{0,1}, {1,2}} + T + conjugate T + Text + Conjugation is an involution and intertwines the two + tableau-storage conventions: + Example + new WeylFilling from toList conjugate T + SeeAlso + Filling + WeylFilling + (conjugate, WeylFilling) +/// +doc /// + Key + normalize + (normalize, Filling) + Headline + sort each column of a Filling, with Koszul sign + Usage + (c, T') = normalize T + Inputs + T:Filling + Outputs + :Sequence + a pair $(c, T')$ with $c \in \{-1, 0, 1\}$ and $T'$ either a + @TT "Filling"@ or @TT "null"@ + Description + Text + Each column of a @TT "Filling"@ represents an element of an + exterior power, so repeated entries in a column collapse the + corresponding wedge to zero, and reordering a column changes the + sign by the parity of the permutation. @TT "normalize T"@ + returns + Text + @UL { + TEX "$(0, \\texttt{null})$ if any column has a repeated entry;", + TEX "$(1, T')$ if every column sorts into strictly increasing + order under an even permutation;", + TEX "$(-1, T')$ if the total permutation is odd." + }@ + Text + This is the first step of the straightening algorithm; after it, + $T'$ lives in the standard ambient + $\wedge^{\mu'_1}(E) \otimes \cdots \otimes \wedge^{\mu'_k}(E)$ + with a known sign. + Example + normalize new Filling from {{1,0}, {2,1}} + normalize new Filling from {{0,0}, {1,2}} + normalize new Filling from {{2,1,0}, {1,0}} + SeeAlso + Filling + isStandard + straighten /// -restart -loadPackage "SchurFunctors" -debug SchurFunctors -T = maxFilling({4,3,3,2}, 6) -weight(T, {1,1,1,1,1,2}) + +doc /// + Key + isStandard + (isStandard, Filling) + Headline + test whether a Filling is semistandard + Usage + isStandard T + Inputs + T:Filling + assumed to have strictly increasing columns (for example, the + output of @TO normalize@); the routine scans adjacent columns + right to left for the first row-ascent violation + Outputs + : + @TT "null"@ if @TT "T"@ is semistandard (entries weakly increase + along every row), otherwise a pair @TT "(i, j)"@ such that + @TT "T#i#j > T#(i+1)#j"@ + Description + Text + In the combinatorial literature a {\em semistandard Young tableau} + is a filling with entries strictly increasing down columns and + weakly increasing along rows. This package calls such a filling + @EM "standard"@; @TT "isStandard"@ is its predicate. + + The return value on failure is the Garnir-violation location used + by the straightening algorithm @TO straighten@ and by the main + Schur-module construction @TO schurModule@. + Example + isStandard new Filling from {{0,1}, {1,2}} + isStandard new Filling from {{1,2}, {0,1}} + Text + To enumerate all semistandard fillings of a given shape with + entries in $\{0,\ldots,d-1\}$, use @TO standardTableaux@. + SeeAlso + Filling + normalize + standardTableaux + straighten + schurModule /// -simpleFind=(d,F)->( - Trans:=Transvections(d); - TransEval:=apply(Trans,M->Specialization(M,F)); - T:=matrix apply(TransEval,k->{k-Specialization(Identity(d),F)}); - return syz(T) - ) +doc /// + Key + standardTableaux + (standardTableaux, ZZ, List) + Headline + all semistandard Young tableaux of a given shape + Usage + standardTableaux(d, mu) + Inputs + d:ZZ + the size of the alphabet $\{0,\ldots,d-1\}$ + mu:List + a partition giving the column lengths of the @TO Filling@ + (equivalently, the conjugate of the row shape $\lambda$; + see @TO schurModule@) + Outputs + :List + all semistandard @TO Filling@s with column lengths $\mu$ + and entries in $\{0,\ldots,d-1\}$ + Description + Text + The output is a $\mathbb{Z}$-basis of @TO schurModule@@TT "(mu, R^d)"@; + its cardinality equals the Weyl dimension $\dim S_\mu(\mathbb{Q}^d)$, + given by the hook-content formula + $$\dim S_\mu(\mathbb{Q}^d) = \prod_{(i,j)\in\mu} \frac{d - i + j}{h(i,j)},$$ + where $h(i,j)$ is the hook length at cell $(i,j)$. + Example + S = standardTableaux(3, {2,1}); + #S + rank schurModule({2,1}, QQ^3) + Text + For $\mu = (2,1)$ and $d = 3$ the hook-content formula gives + $(3 \cdot 4 \cdot 2)/(3 \cdot 1 \cdot 1) = 8$. + Example + standardTableaux(2, {1,1}) + standardTableaux(4, {3}) + SeeAlso + Filling + isStandard + schurModule + standardWeylTableaux +/// +doc /// + Key + augmentFilling + (augmentFilling, Filling, ZZ, ZZ) + Headline + append an entry to a column of a Filling + Usage + augmentFilling(T, c, e) + Inputs + T:Filling + c:ZZ + column index + e:ZZ + entry to append + Outputs + :Filling + a new filling where @TT "e"@ has been appended to the bottom of + column @TT "c"@, or (if @TT "c >= #T"@) a new rightmost column + @TT "{e}"@ has been created + Description + Text + This is the natural cell-adding operation for Schur-functor + combinatorics: extending a filling by one cell at the bottom of + column $c$ (or creating a new rightmost column if $c$ is past + the current rightmost column). It is the building block used to + describe Schur-module maps by their action on tableaux; see for + example the Koszul-style differential built in the example of + @TO schurModulesMap@. + Example + T = new Filling from {{0,1}, {1}} + T + augmentFilling(T, 0, 2) + augmentFilling(T, 1, 2) + augmentFilling(T, 5, 3) + SeeAlso + Filling + augmentWeylFilling + schurModulesMap +/// +----------------------------------------------------------------------- +-- WeylFilling type and tableau combinatorics (Weyl side) +----------------------------------------------------------------------- +doc /// + Key + WeylFilling + (symbol ?, WeylFilling, WeylFilling) + (symbol ==, WeylFilling, WeylFilling) + (symbol _, WeylFilling, List) + (symbol _, WeylFilling, ZZ) + (net, WeylFilling) + Headline + a Young tableau stored row-wise for the Weyl-module basis + Description + Text + A @TT "WeylFilling"@ is a Young tableau of partition shape + $\lambda$, stored as the list of its rows. A filling is + represented by + @TT "new WeylFilling from {r_1, r_2, ..., r_k}"@, + where @TT "r_i"@ is the list of entries in the $i$-th row + (top-to-bottom, left-to-right within each row), of length + $\lambda_i$. + Text + The row-wise storage matches the ambient decomposition of the + Weyl module + $$W_\lambda(E) \;\subseteq\; \textstyle\bigotimes_{i=1}^{r} + D^{\lambda_i}(E),$$ + so each row directly corresponds to one divided-power tensor + factor. Entries in a row are stored in sorted (weakly-increasing) + order since divided powers of a free module have a basis indexed + by multisets; see @TO dividedPower@. + Example + T = weyl {{0,0,1}, {1,2}} + T + toList T + #T + T#0 + Text + The constructor @TO "weyl"@ with a single @TT "List"@ argument + wraps a list of rows into a @TT "WeylFilling"@; the two-argument + form @TT "weyl(lambda, f)"@ builds the induced module map, see + @TO (weyl, List, Matrix)@. + Text + A @TT "WeylFilling"@ is @EM "Weyl-semistandard"@ (= + Weyl-standard in the terminology of this package) if its rows are + weakly increasing and its columns are strictly increasing. Over + $\mathbb{Z}$ the Weyl-standard fillings of shape $\lambda$ with + entries in $\{0,\ldots,d-1\}$ index a free basis of + $W_\lambda(R^d)$. + Example + SW = standardWeylTableaux(3, {2,1}); + #SW == rank weylModule({2,1}, QQ^3) + Text + Comparison @TT "?"@, equality @TT "=="@, and row access + @TT "T_i"@ / @TT "T_{i_0, i_1, ...}"@ are all defined and mirror + the corresponding operations on @TO Filling@ (with rows in the + role of columns). + SeeAlso + Filling + weyl + weylNormalize + isWeylStandard + towardWeylStandard + standardWeylTableaux + maxWeylFilling + augmentWeylFilling + (conjugate, WeylFilling) + weylModule + weylStraighten +/// -needsPackage "SymmetricPolynomials" -needsPackage "SchurRings" --- schurRings interface has changed at version 0.5 -schurVersion = value SchurRings.Options.Version +doc /// + Key + (conjugate, WeylFilling) + Headline + transpose of a Weyl-stored Young tableau + Usage + conjugate T + Inputs + T:WeylFilling + Outputs + :WeylFilling + the tableau of conjugate shape, stored row-wise (so its rows are + the columns of @TT "T"@) + Description + Text + Conjugation of a @TT "WeylFilling"@ is the combinatorial transpose + of the underlying Young tableau, preserving the row-wise storage + convention. + Example + T = weyl {{0,2,4},{1,3,5}} + conjugate T + Text + The conjugate stores the columns of $T$ as its rows; equivalently, + @TT "toList conjugate T"@ equals the list of columns of + @TT "T"@, each sorted in its natural top-to-bottom order. + SeeAlso + WeylFilling + (conjugate, Filling) +/// -splitCharacter = method() -splitCharacter RingElement := ce -> ( - pe:=elementarySymmetric(ce); - -- Assumption: ring of pe: vars 0..n-1 - -- are orig vars, n..2n-1 are elem symm fcns - n:=numgens source vars ring ce; - R2 := if schurVersion < .5 then ( - error "need SchurRings, version > 0.5"; - --symmRing n -- vars 0..n-1 are elem symm fcns - ) - else - symmetricRing(coefficientRing ring pe, n); -- vars 0..n-1 are elem symm fcns - es := (vars R2)_{0..n-1}; - toS substitute(pe,es|es) - ) +doc /// + Key + weylNormalize + (weylNormalize, WeylFilling) + Headline + sort each row of a WeylFilling into weakly-increasing order + Usage + weylNormalize T + Inputs + T:WeylFilling + Outputs + :WeylFilling + the WeylFilling obtained by sorting each row of @TT "T"@ + Description + Text + Each row of a @TT "WeylFilling"@ represents a basis element + $x^{(a_1)} x^{(a_2)} \cdots \in D^\ell(E)$ of a divided power. + Divided powers of a free module have a basis indexed by + {\em unordered} multisets of basis indices (or, equivalently, + exponent vectors), so two @TT "WeylFilling"@s with the same + multiset of entries in each row represent the same element of + the ambient. @TT "weylNormalize"@ puts every row into its + canonical sorted representative. + Text + Unlike @TO normalize@ on the Schur side, no sign is produced: + divided powers are cocommutative / symmetric, not alternating. + Example + U = new WeylFilling from {{2,0,1}, {3,1}} + weylNormalize U + SeeAlso + WeylFilling + isWeylStandard + weylStraighten +/// -beginDocumentation() -document { - Key => SchurFunctors, - Headline => "for computing Schur functors", - "This package provides methods for computing Schur functors." - } - -doc get (currentFileDirectory | "SchurFunctors/schurModule.txt") -doc get (currentFileDirectory | "SchurFunctors/schur.txt") -doc get (currentFileDirectory | "SchurFunctors/straightenSchur.txt") -doc get (currentFileDirectory | "SchurFunctors/schurModulesMap.txt") -doc get (currentFileDirectory | "SchurFunctors/character.txt") -doc get (currentFileDirectory | "SchurFunctors/splitCharacter.txt") +doc /// + Key + isWeylStandard + (isWeylStandard, WeylFilling) + Headline + test whether a WeylFilling is Weyl-semistandard + Usage + isWeylStandard T + Inputs + T:WeylFilling + assumed to have rows in sorted (weakly-increasing) order, for + example the output of @TO weylNormalize@ + Outputs + : + @TT "null"@ if @TT "T"@ is Weyl-standard (columns strictly + increasing top-to-bottom), otherwise a pair @TT "(i, j)"@ giving + the first column violation: @TT "T#i#j >= T#(i+1)#j"@, scanning + rows from bottom to top + Description + Text + A @TT "WeylFilling"@ is {\em Weyl-standard} when, in addition to + having sorted rows, every column is strictly increasing + top-to-bottom. This is exactly the semistandard condition, + phrased for row-wise storage. + Example + isWeylStandard weyl {{0,1}, {1,2}} + isWeylStandard weyl {{1,2}, {1,3}} -- column 0: 1 >= 1 violates + Text + To straighten a non-standard @TT "WeylFilling"@ into a linear + combination of standard ones, use @TO towardWeylStandard@ (single + step) or @TO weylStraighten@ (full reduction). + SeeAlso + WeylFilling + weylNormalize + towardWeylStandard + weylStraighten + standardWeylTableaux +/// + +doc /// + Key + towardWeylStandard + Headline + a single Garnir-style straightening step for Weyl fillings + Usage + towardWeylStandard T + Inputs + T:WeylFilling + Outputs + H:HashTable + mapping @TT "WeylFilling"@s @TT "T'"@ to integer coefficients, + with $\sum_{T'} H(T') \cdot T' = T$ in the Weyl module and every + key @TT "T'"@ strictly larger than @TT "T"@ in the term order + used by @TO weylStraighten@ + Description + Text + If @TT "T"@ is already Weyl-standard the output is the singleton + @TT "{T => 1}"@. Otherwise @TT "towardWeylStandard"@ performs + one divided-power Garnir shuffle across the first violation + (located by @TO isWeylStandard@) and returns the resulting linear + combination. Repeated application terminates at a + $\mathbb{Z}$-linear combination of Weyl-standard fillings; this + is exactly what @TO weylStraighten@ computes. + Text + The replacements are generated by divided-power comultiplication + (see @TO divComult@) followed by divided-power multiplication + (see @TO divMult@); the coefficients are products of binomial + coefficients, so in general @TT "towardWeylStandard"@ produces + many terms unlike the sign-flip shuffle on the Schur side. + Example + T = weyl {{1,2},{0,0}} + isWeylStandard T + a = towardWeylStandard T + scan(pairs a, (U, c) -> << " " << c << " * " << toList U << endl) + SeeAlso + WeylFilling + isWeylStandard + weylStraighten + divComult + divMult +/// + +doc /// + Key + standardWeylTableaux + (standardWeylTableaux, ZZ, List) + Headline + all Weyl-semistandard tableaux of a given shape + Usage + standardWeylTableaux(d, lambda) + Inputs + d:ZZ + the size of the alphabet $\{0,\ldots,d-1\}$ + lambda:List + a partition (the shape of the tableau) + Outputs + :List + all Weyl-semistandard @TO WeylFilling@s of shape $\lambda$ with + entries in $\{0,\ldots,d-1\}$ + Description + Text + The output is a $\mathbb{Z}$-basis of + @TO weylModule@@TT "(lambda, R^d)"@. Its cardinality equals + $\dim S_\lambda(\mathbb{Q}^d)$, the same count given by + @TO standardTableaux@ for the conjugate shape: $S_\lambda$ and + $W_\lambda$ have the same rank in every characteristic. + Example + SW = standardWeylTableaux(3, {2,1}); + #SW + rank weylModule({2,1}, QQ^3) + Example + standardWeylTableaux(2, {2}) + standardWeylTableaux(3, {1,1,1}) + SeeAlso + WeylFilling + isWeylStandard + weylModule + standardTableaux +/// + +doc /// + Key + augmentWeylFilling + (augmentWeylFilling, WeylFilling, ZZ, ZZ) + Headline + append an entry to a row of a WeylFilling + Usage + augmentWeylFilling(T, r, e) + Inputs + T:WeylFilling + r:ZZ + row index + e:ZZ + entry to append + Outputs + :WeylFilling + a new filling where @TT "e"@ has been appended to the end of + row @TT "r"@, or (if @TT "r >= #T"@) a new final row + @TT "{e}"@ has been created + Description + Text + The Weyl analogue of @TO augmentFilling@. Where @TO Filling@ is + column-indexed, @TT "WeylFilling"@ is row-indexed; this routine + grows a tableau by one cell in the natural row-wise direction and + is the basic building block for describing Weyl-module maps + tableau-by-tableau (see the example of @TO weylModulesMap@). + Example + T = weyl {{0,1},{2}} + augmentWeylFilling(T, 0, 2) + augmentWeylFilling(T, 1, 3) + augmentWeylFilling(T, 2, 4) + SeeAlso + WeylFilling + augmentFilling + weylModulesMap +/// + +doc /// + Key + maxWeylFilling + (maxWeylFilling, List, ZZ) + Headline + the lex-largest Weyl-standard tableau of given shape + Usage + maxWeylFilling(p, d) + Inputs + p:List + a partition + d:ZZ + the rank of the underlying module + Outputs + :WeylFilling + the Weyl-standard filling of shape $p$ with entries in + $\{0,\ldots,d-1\}$ which is lex-largest in the row-reversed order + used to sort the ambient basis in @TO weylModule@ + Description + Text + With $p' = (p'_1, \ldots, p'_{p_1})$ the conjugate of $p$, the + cell $(i, j)$ (0-indexed) is filled with $d - p'_j + i$. This + coincides numerically with the formula for the lex-largest + semistandard tableau on the Schur side (the internal helper + @TT "maxFilling"@), but stored row-wise. + Example + maxWeylFilling({3, 2}, 4) + isWeylStandard oo + Text + Useful as a starting point for constructing extreme elements of + highest-weight subrepresentations; see @TO characterRep@ and + @TO decomposeRep@. + SeeAlso + WeylFilling + isWeylStandard + standardWeylTableaux +/// + +----------------------------------------------------------------------- +-- Schur functor and module +----------------------------------------------------------------------- +doc /// + Key + schurModule + (schurModule, List, Module) + Headline + Schur module of a partition applied to a free module + Usage + schurModule(lambda, E) + Inputs + lambda:List + a partition $\lambda = (\lambda_1 \ge \cdots \ge \lambda_r)$ + E:Module + a free module over some ring $R$ + Outputs + :Module + the Schur module $S_\lambda(E)$, a free $R$-module whose rank + equals the number of semistandard Young tableaux of shape + $\lambda$ with entries in $\{0,\ldots,\mathrm{rank}(E) - 1\}$ + Description + Text + The Schur functor $S_\lambda$ is a polynomial functor on free + modules, realized here as the quotient of + $\wedge^{\lambda'_1}(E) \otimes \cdots \otimes \wedge^{\lambda'_k}(E)$ + by the Garnir relations across adjacent columns of the Young + diagram. See Fulton, {\em Young Tableaux}, Chapter 8. + + The partition $\lambda$ is specified as a (non-strictly) decreasing + list of positive integers. The rank of the output module matches + the Weyl dimension formula + $$\dim S_\lambda(R^d) = \#\{\text{SSYT of shape } \lambda + \text{ with entries in } 1,\ldots,d\}.$$ + Text + @STRONG "Cached data."@ The returned module @TT "M"@ stores in + @TT "M.cache#\"Schur\""@ the 4-tuple @TT "(f, finv, AT, ST)"@: + Text + @UL { + {TT "f", " : the quotient projection ", + TEX "$\\wedge^{\\lambda'_1}(E) \\otimes \\cdots \\otimes \\wedge^{\\lambda'_k}(E) + \\twoheadrightarrow M$", + " (the straightening map, realized in the SSYT basis of ", TT "M", ");"}, + {TT "finv", " : a splitting ", TT "M", " ", + TEX "$\\to \\wedge^{\\lambda'_1}(E) \\otimes \\cdots$", " + sending each standard-basis vector to its canonical lift in the + ambient tensor product (so ", TT "f*finv == id_M", ");"}, + {TT "AT", " : a HashTable keyed by all column-standard Fillings + of shape ", TEX "$\\lambda$", " with values their positions in the + ambient tensor product;"}, + {TT "ST", " : the sub-HashTable keyed only by the SSYT + (the standard basis of ", TT "M", ")."} + }@ + Text + These are consumed by @TO straighten@ (for a fast-path module + evaluation) and by @TO schurModulesMap@ (for building homomorphisms). + Text + @STRONG "Examples."@ Symmetric and exterior powers recover the + familiar functors; with a partition of shape $(k)$ we get + $\mathrm{Sym}^k(E)$ and with shape $(1^k)$ we get + $\wedge^k(E)$: + Example + E = QQ^3; + schurModule({3}, E) + schurModule({1,1,1}, E) -- top exterior power: rank 1 + Text + The first genuinely mixed shape: + Example + M = schurModule({2,1}, E); + rank M + standardTableaux(3, {2,1}) + M.cache#"Schur" + Text + The cached quotient projection / splitting satisfy + @TT "f * finv == id_M"@ by construction: + Example + (f, finv, AT, ST) = toSequence M.cache#"Schur"; + f * finv == id_M + Caveat + The partition @TT "lambda"@ should be nonempty and weakly + decreasing. Entries of @TT "lambda"@ should not exceed + @TT "rank E"@, or else the output module has rank 0. + SeeAlso + schur + straighten + schurModulesMap + standardTableaux + weylModule +/// + +doc /// + Key + schur + (schur, List, Matrix) + Headline + the Schur functor applied to a matrix + Usage + schur(lambda, f) + Inputs + lambda:List + a partition + f:Matrix + a map $f : E \to F$ between free modules over a ring $R$ + Outputs + :Matrix + the induced map $S_\lambda(f) : S_\lambda(E) \to S_\lambda(F)$ + Description + Text + For each partition $\lambda$, $S_\lambda$ is a polynomial functor. + Applied to a morphism $f : E \to F$ of free modules it yields a + morphism $S_\lambda(f) : S_\lambda(E) \to S_\lambda(F)$, and the + construction is functorial: + $S_\lambda(f \circ g) = S_\lambda(f) \circ S_\lambda(g)$ and + $S_\lambda(\mathrm{id}_E) = \mathrm{id}_{S_\lambda(E)}$. + Text + The source and target matrices have modules that carry the same + cached data as returned by @TO schurModule@. + Example + R = QQ[x_1,x_2,x_3]; + F = map(R^1, R^3, vars R); + schur({2}, F) -- 2nd Veronese embedding + Text + Classical specializations. Top exterior power = determinant: + Example + F = matrix{{1_QQ,2,4},{3,9,27},{4,16,64}}; + det F + schur({1,1,1}, F) + Text + Second exterior power = matrix of $2\times2$ minors: + Example + entries schur({1,1}, F) + entries gens minors(2, F) + Example + S = schur({3,1}, id_(QQ^3)); -- identity maps to the identity + S == id_(source S) + Text + Composition is compatible with matrix composition: + Example + A = random(QQ^3, QQ^3); + B = random(QQ^3, QQ^3); + schur({2,1}, A * B) == schur({2,1}, A) * schur({2,1}, B) + Caveat + The partition @TT "lambda"@ should be nonempty and weakly + decreasing. + SeeAlso + schurModule + weyl +/// + +doc /// + Key + straighten + (straighten, Filling) + (straighten, HashTable) + (straighten, Filling, Module) + (straighten, HashTable, Module) + Headline + straighten a filling into a linear combination of semistandard tableaux + Usage + straighten T + straighten H + straighten(T, M) + straighten(H, M) + Inputs + T:Filling + a Young tableau of shape $\mu$ + H:HashTable + a formal $\mathbb{Z}$-linear combination of @TT "Filling"@s + (keys) with integer coefficients (values); all keys must have the + same shape + M:Module + (optional) a Schur module $S_\mu(R^d)$ produced by + @TO schurModule@; when supplied, the output is assembled as a + vector in @TT "M"@ + Outputs + : + either a @TT "HashTable"@ keyed by semistandard fillings, with + $\sum_{T'} H(T')\,T' = T$ in $S_\mu$, or (when @TT "M"@ is + supplied) the corresponding @TT "Vector"@ in @TT "M"@ + Description + Text + The straightening algorithm writes an arbitrary tableau as a + unique $\mathbb{Z}$-linear combination of semistandard tableaux of + the same shape, modulo the Garnir relations. This is the standard + way to reduce to the SSYT basis of $S_\lambda(E)$; see Fulton, + {\em Young Tableaux}, §8.1. + + The implementation repeatedly locates a violation with + @TO isStandard@ and resolves it by a Garnir-style column shuffle, + keeping track of the intermediate tableaux that still need to be + rewritten; the process terminates when every tableau is + semistandard. The final output does not depend on the order in + which violations are chosen. + Text + @STRONG "Symbolic straightening."@ Without a module argument the + output is a @TT "HashTable"@ of @TT "Filling"@ keys and integer + coefficients: + Example + T = new Filling from {{2,1,0}} + straighten T + Example + T = new Filling from {{1,0}, {0}} + straighten T + Text + @STRONG "Linearity."@ @TT "straighten"@ is $\mathbb{Z}$-linear + in its input, so it extends to formal linear combinations + presented as a @TT "HashTable"@ of (Filling => ZZ) pairs: + Example + H = hashTable { + (new Filling from {{2,1}, {0}}, 1), + (new Filling from {{1,0}, {2}}, -1)} + straighten H + Text + @STRONG "Module evaluation."@ When a Schur module @TT "M"@ is + supplied, @TT "straighten"@ returns the element of @TT "M"@ + corresponding to the straightened combination. This is the usual + way to lift a combinatorially-specified element to its actual + vector representation: + Example + M = schurModule({1,1,1}, QQ^4); + v = straighten(new Filling from {{3,2,1}}, M) + printSchurModuleElement(v, M) + SeeAlso + Filling + isStandard + normalize + schurModule + weylStraighten +/// + +doc /// + Key + schurModulesMap + (schurModulesMap, Module, Module, Function) + Headline + build a map between Schur modules from its action on tableaux + Usage + schurModulesMap(N, M, F) + Inputs + N:Module + a Schur module from @TO schurModule@ + M:Module + a Schur module from @TO schurModule@ + F:Function + a function describing the map on tableaux: for every SSYT + @TT "T"@ in the basis of @TT "M"@, @TT "F(T)"@ should produce a + list of pairs @TT "(c, T')"@ where @TT "c"@ is a coefficient in + @TT "ring N"@ and @TT "T'"@ is a (not-necessarily-semistandard) + filling of the shape of @TT "N"@ + Outputs + :Matrix + the $R$-linear map @TT "M -> N"@ given, on each basis tableau + @TT "T"@ of @TT "M"@, by the $R$-linear combination + $\sum (c, T') \in F(T)$, implicitly straightened into $N$ + Description + Text + This constructor is the main tool for producing Schur-module + homomorphisms from combinatorial data. It amounts to specifying + where each tableau in a basis of the source goes, in a + possibly-non-standard form; the straightening algorithm is invoked + internally on each output to assemble the resulting vector in + @TT "N"@. + Text + @STRONG "Example: a piece of a Koszul-type differential."@ For + $\lambda = (1^j)$ and $\lambda' = (1^{j+1})$ the natural map + $\wedge^j(E) \to \wedge^{j+1}(E) \otimes E^*$ sends a basis + tableau to $\sum_k x_k \otimes (T \cup \{k\})$. Lifting this via + @TT "schurModulesMap"@ gives (a piece of) the Koszul differential: + Example + n = 4; + j = 2; + R = QQ[x_1..x_n]; + M = schurModule(toList(j : 1), R^n); + N = schurModule(toList(j+1 : 1), R^n); + F = T -> apply(numgens R, k -> (R_k, augmentFilling(T, 0, k))); + schurModulesMap(N, M, F) + SeeAlso + schurModule + augmentFilling + straighten + weylModulesMap +/// + +----------------------------------------------------------------------- +-- Weyl functor and module +----------------------------------------------------------------------- +doc /// + Key + weylModule + (weylModule, List, Module) + Headline + Weyl module of a partition applied to a free module + Usage + weylModule(lambda, E) + Inputs + lambda:List + a partition $\lambda = (\lambda_1 \ge \cdots \ge \lambda_r)$ + E:Module + a free module over some ring $R$ + Outputs + :Module + the Weyl module $W_\lambda(E)$, a free $R$-module whose rank + equals the number of Weyl-semistandard fillings of shape + $\lambda$ with entries in $\{0,\ldots,\mathrm{rank}(E) - 1\}$ + Description + Text + The Weyl functor $W_\lambda$ is the divided-power analogue of + $S_\lambda$. It is realized here as the quotient + $$D^{\lambda_1}(E) \otimes \cdots \otimes D^{\lambda_r}(E) + \;\twoheadrightarrow\; W_\lambda(E)$$ + by the divided-power Garnir relations across adjacent rows; + see Akin-Buchsbaum-Weyman, {\em Schur functors and Schur + complexes}, Adv. Math. {\bf 44} (1982). + + In characteristic zero $W_\lambda \cong S_\lambda$ canonically, + but over $\mathbb{Z}$ (or in positive characteristic) $W_\lambda$ + and $S_\lambda$ are distinct functors related by duality. + Text + The rank matches the usual Weyl-dimension count: + Example + E = QQ^3; + weylModule({2}, E) -- divided square: rank C(3+1, 2) = 6 + rank oo + weylModule({1,1}, E) -- W_{(1,1)} = ∧^2, rank 3 + rank oo + Text + For a genuinely mixed shape: + Example + W = weylModule({2,1}, E); + rank W + standardWeylTableaux(3, {2,1}) + Text + @STRONG "Cached data."@ The returned module @TT "W"@ stores in + @TT "W.cache#\"Weyl\""@ the 4-tuple @TT "(f, finv, AT, ST)"@, + exactly parallel to @TO schurModule@: + Example + W.cache#"Weyl" + (f, finv, AT, ST) = toSequence W.cache#"Weyl"; + f * finv == id_W + Caveat + The partition @TT "lambda"@ should be nonempty and weakly + decreasing. Entries of @TT "lambda"@ need not bound the rank of + @TT "E"@, but the output module may of course have small rank. + SeeAlso + weyl + weylStraighten + weylModulesMap + standardWeylTableaux + schurModule + dividedPower +/// + +doc /// + Key + weyl + (weyl, List) + (weyl, List, Matrix) + Headline + construct a WeylFilling or apply the Weyl functor to a map + Usage + weyl L + weyl(lambda, f) + Inputs + L:List + a list of rows, each row a list of integers (weakly increasing); + interpreted as the rows of a @TT "WeylFilling"@ + lambda:List + a partition + f:Matrix + a map $f : E \to F$ between free modules + Outputs + : + in the one-argument form, a @TT "WeylFilling"@; + in the two-argument form, a @TT "Matrix"@ giving the induced map + $W_\lambda(f) : W_\lambda(E) \to W_\lambda(F)$ + Description + Text + The one-argument form is a constructor for @TO WeylFilling@: + Example + T = weyl {{0,0,1}, {1,2}} + class T + isWeylStandard T + Text + The two-argument form is the Weyl functor applied to a morphism + of free modules. Functoriality: $W_\lambda(f \circ g) = + W_\lambda(f) \circ W_\lambda(g)$ and + $W_\lambda(\mathrm{id}) = \mathrm{id}$: + Example + F = matrix{{1_QQ,2},{3,4}}; + weyl({2}, F) + Example + A = random(QQ^3, QQ^3); + B = random(QQ^3, QQ^3); + weyl({2,1}, A * B) == weyl({2,1}, A) * weyl({2,1}, B) + Text + For special shapes $W_\lambda$ coincides with familiar functors: + $W_{(d)} = D^d$ (divided power), $W_{(1^d)} = \wedge^d$ + (exterior power, also equal to $S_{(1^d)}$). + Example + weyl({2}, F) == dividedPower(2, F) + SeeAlso + WeylFilling + weylModule + dividedPower + schur +/// + +doc /// + Key + weylStraighten + (weylStraighten, WeylFilling) + (weylStraighten, HashTable) + (weylStraighten, WeylFilling, Module) + (weylStraighten, HashTable, Module) + Headline + straighten a Weyl filling into a linear combination of Weyl-semistandard tableaux + Usage + weylStraighten T + weylStraighten H + weylStraighten(T, W) + weylStraighten(H, W) + Inputs + T:WeylFilling + H:HashTable + a formal $\mathbb{Z}$-linear combination of @TT "WeylFilling"@s + (all of the same shape) + W:Module + (optional) a Weyl module produced by @TO weylModule@ + Outputs + : + a @TT "HashTable"@ keyed by Weyl-semistandard fillings, or (when + a module is supplied) the corresponding @TT "Vector"@ in @TT "W"@ + Description + Text + The divided-power analogue of @TO straighten@. Reduces an + arbitrary @TT "WeylFilling"@ (or a formal combination of them) + to a unique linear combination of Weyl-standard tableaux modulo + the divided-power Garnir relations. Unlike on the Schur side, + a single straightening step generally produces many terms with + binomial-coefficient multiplicities (via @TO divComult@ / + @TO divMult@) rather than a single sign flip. + Example + U = weyl {{1,2},{0,0}} + weylStraighten U + Text + @STRONG "Linearity."@ + Example + H = hashTable { + (weyl {{1,2},{0,0}}, 1), + (weyl {{0,2},{1,0}}, -1)}; + weylStraighten H + Text + @STRONG "Module evaluation."@ With a Weyl module @TT "W"@ + supplied, the combinatorial result is assembled as a vector: + Example + W = weylModule({2,2}, QQ^3); + v = weylStraighten(weyl {{0,2},{1,0}}, W) + printWeylModuleElement(v, W) + SeeAlso + WeylFilling + isWeylStandard + towardWeylStandard + weylModule + straighten +/// + +doc /// + Key + weylModulesMap + (weylModulesMap, Module, Module, Function) + Headline + build a map between Weyl modules from its action on tableaux + Usage + weylModulesMap(N, M, F) + Inputs + N:Module + a Weyl module from @TO weylModule@ + M:Module + a Weyl module from @TO weylModule@ + F:Function + a function such that for every Weyl-semistandard filling @TT "T"@ + in the basis of @TT "M"@, @TT "F(T)"@ produces a list of pairs + @TT "(c, T')"@ with @TT "c"@ in @TT "ring N"@ and @TT "T'"@ a + (not-necessarily-standard) filling of the shape of @TT "N"@ + Outputs + :Matrix + the $R$-linear map @TT "M -> N"@ whose value on each basis + tableau is straightened into @TT "N"@ + Description + Text + The Weyl analogue of @TO schurModulesMap@. The shape of @TT "N"@ + is the target shape of the tableaux produced by @TT "F"@; the + straightening into the Weyl-standard basis is performed + internally using @TO weylStraighten@. + Text + @STRONG "Example: a multiplication-style map + $W_{(2)}(E) \\to W_{(2,1)}(E) \\otimes E^*$."@ + The analogue of the Koszul differential, with divided-power + multiplication: + Example + n = 3; + R = QQ[x_1..x_n]; + M = weylModule({2}, R^n); + N = weylModule({2,1}, R^n); + F = T -> apply(numgens R, k -> (R_k, augmentWeylFilling(T, 1, k))); + weylModulesMap(N, M, F) + SeeAlso + weylModule + augmentWeylFilling + weylStraighten + schurModulesMap +/// + +----------------------------------------------------------------------- +-- Divided-power utilities +----------------------------------------------------------------------- +doc /// + Key + dividedPower + (dividedPower, ZZ, Module) + (dividedPower, List, Module) + (dividedPower, ZZ, Matrix) + (dividedPower, List, Matrix) + Headline + divided-power functor on free modules and matrices + Usage + dividedPower(d, E) + dividedPower(lambda, E) + dividedPower(d, f) + dividedPower(lambda, f) + Inputs + d:ZZ + lambda:List + a list of nonnegative integers; the output is the tensor product + of divided powers $D^{\lambda_1}(\cdot) \otimes \cdots \otimes D^{\lambda_k}(\cdot)$ + E:Module + a free module + f:Matrix + a map between free modules + Outputs + : + the divided power $D^d(E)$ or the induced map $D^d(f)$, and for + list input the tensor product of divided powers + Description + Text + The {\em $d$-th divided power} $D^d(E)$ of a free module $E$ of + rank $n$ is the degree-$d$ part of the divided-power algebra on + $E$. It is free of rank $\binom{n+d-1}{d}$, with a basis indexed + by weakly-increasing tuples of length $d$ in $\{0,\ldots,n-1\}$ + (equivalently, compositions of $d$ into $n$ parts, or + multisets of size $d$ from $\{0,\ldots,n-1\}$). + Text + Over a field of characteristic zero $D^d(E) \cong \mathrm{Sym}^d(E)$ + canonically (the isomorphism rescales basis vectors by factorials); + over $\mathbb{Z}$ the two functors differ. + Example + E = QQ^3; + dividedPower(2, E) -- rank C(4,2) = 6 + dividedPower({2,1,0,1}, E) -- D^2 ⊗ D^1 ⊗ D^0 ⊗ D^1 + f = matrix{{1_QQ,2},{3,4}}; + dividedPower(2, f) -- induced map on D^2 + Text + The single-row (non-list) form with a @TT "List"@ partition + builds the ambient tensor product used by @TO weylModule@: + Example + dividedPower({2,1}, E) == dividedPower(2, E) ** dividedPower(1, E) + Text + @TT "dividedPower"@ is used as the ambient object in the + construction of @TO weylModule@, playing the role that + @TT "exteriorPower"@ plays for @TO schurModule@. + SeeAlso + weyl + weylModule + divMult + divComult +/// + +doc /// + Key + divMult + (divMult, List, List) + (divMult, List, List, ZZ) + (divMult, Sequence, Sequence) + divComult + (divComult, List, ZZ) + (divComult, List, ZZ, ZZ) + (divComult, Sequence, ZZ) + Headline + multiplication and comultiplication of divided powers + Description + Text + The divided-power algebra $D^*(E) = \bigoplus_{d \ge 0} D^d(E)$ + on a free module $E$ of rank $n$ is a graded bi-algebra. Its + basis elements are indexed by weakly-increasing tuples of + integers, or equivalently by their exponent vectors. This + package provides both the multiplication + $D^p \otimes D^q \to D^{p+q}$ and the comultiplication + $D^{p+q} \to D^p \otimes D^q$ on those bases. + Text + @STRONG "Multiplication."@ @TT "divMult(L1, L2)"@ takes two + weakly-increasing lists @TT "L1"@, @TT "L2"@ (basis elements of + $D^{|L_1|}$ and $D^{|L_2|}$) and returns the pair @TT "(c, L)"@ + where @TT "L"@ is the sorted concatenation and @TT "c"@ is the + binomial coefficient encoding the divided-power product rule + $x^{(p)} \cdot x^{(q)} = \binom{p+q}{p}\, x^{(p+q)}$. + Example + divMult({0,0,1}, {0,1}) + -- coefficient C(3,2) * C(2,1) accounting for the two '0's meeting + -- the single '0' and the two '1's meeting another '1' + Text + @STRONG "Comultiplication."@ @TT "divComult(L, p)"@ takes a + basis element @TT "L"@ of $D^{|L|}$ and returns the set of ways + to split it as a pair @TT "(A, B)"@ with @TT "|A| = p"@, + @TT "|B| = |L| - p"@, and $A + B = L$ as multisets. This is the + comultiplication + $D^{|L|}(E) \to D^{p}(E) \otimes D^{|L| - p}(E)$. + Example + divComult({0,0,1,2}, 2) + Text + Internally, @TT "divComult"@ drives the Weyl-side straightening: + a single Garnir shuffle across adjacent rows proceeds by + comultiplying the concatenated row tails and remultiplying into + the partner row; see @TO towardWeylStandard@. A basis element + can be stored either as a weakly-increasing tuple or as an + exponent vector recording the multiplicity of each letter; for + efficiency, much of the straightening uses the exponent-vector + form, and both forms are accepted as input below. + Text + @STRONG "Variants."@ Both operations come in three forms: + Text + @UL { + {TT "divMult(L1, L2)", " and ", TT "divComult(L, p)", ": tuple + (weakly-increasing list) input; the alphabet size is inferred + from the maximum entry appearing in the input."}, + {TT "divMult(L1, L2, n)", " and ", TT "divComult(L, p, n)", + ": tuple input with an explicit alphabet size ", TT "n", + ", useful when the tuples are padded or when the caller + needs all output tuples to have a uniform length."}, + {TT "divMult(S1, S2)", " and ", TT "divComult(S, p)", ": + ", EM "sequence", " input, interpreted as an exponent vector + (that is, ", TT "S_i", " is the multiplicity of the letter + ", TT "i", "). Working directly with exponent vectors avoids + the conversion to and from tuples, and is the form used + inside the straightening loop."} + }@ + SeeAlso + dividedPower + weylModule + towardWeylStandard +/// + +----------------------------------------------------------------------- +-- Pretty-printers +----------------------------------------------------------------------- +doc /// + Key + printSchurModuleElement + (printSchurModuleElement, Vector, Module) + Headline + display a Schur-module element as a linear combination of tableaux + Usage + printSchurModuleElement(v, M) + Inputs + v:Vector + a vector in a Schur module @TT "M"@ + M:Module + a Schur module returned by @TO schurModule@ + Description + Text + Interprets @TT "v"@ as an element of @TT "M"@ and prints it as a + sum $\sum_T c_T \cdot T$ over semistandard fillings $T$ in the + cached basis of @TT "M"@, with each @TT "T"@ drawn via its + @TT "net"@ method. + Example + M = schurModule({2,1}, QQ^3); + v = sum(numgens M, i -> (i+1) * M_i); + printSchurModuleElement(v, M) + SeeAlso + schurModule + printWeylModuleElement +/// + +doc /// + Key + printWeylModuleElement + (printWeylModuleElement, Vector, Module) + Headline + display a Weyl-module element as a linear combination of tableaux + Usage + printWeylModuleElement(v, W) + Inputs + v:Vector + a vector in a Weyl module @TT "W"@ + W:Module + a Weyl module returned by @TO weylModule@ + Description + Text + The Weyl analogue of @TO printSchurModuleElement@: prints @TT "v"@ + as $\sum_T c_T \cdot T$ over Weyl-semistandard @TT "WeylFilling"@s + $T$ in the cached basis of @TT "W"@. + Example + W = weylModule({2,1}, QQ^3); + v = sum(numgens W, i -> (i+1) * W_i); + printWeylModuleElement(v, W) + SeeAlso + weylModule + printSchurModuleElement +/// + +----------------------------------------------------------------------- +-- Characters and GL-representation decomposition +----------------------------------------------------------------------- +doc /// + Key + character + (character, List, ZZ) + Headline + the character of a composition of Schur functors on GL(V) + Usage + character(L, d) + Inputs + L:List + a list of partitions $[\lambda^{(1)}, \lambda^{(2)}, \ldots, \lambda^{(m)}]$ + d:ZZ + the rank of the underlying module + Outputs + :RingElement + the character, as a symmetric polynomial in $x_0, \ldots, x_{d-1}$, + of the composed Schur functor + $S_{\lambda^{(1)}}(S_{\lambda^{(2)}}(\cdots S_{\lambda^{(m)}}(V)))$ + on the defining representation $V = \mathbb{Q}^d$ of $GL_d$ + Description + Text + The character of a polynomial $GL_d$-representation $W$ is the + trace of the diagonal matrix $\mathrm{diag}(x_0, \ldots, x_{d-1})$ + acting on $W$. For an irreducible $S_\lambda(V)$ this is the + Schur polynomial $s_\lambda(x_0, \ldots, x_{d-1})$; for an + arbitrary polynomial representation it is a non-negative integer + combination of Schur polynomials, making @TT "character"@ the + primary input to @TO splitCharacter@ and @TO decomposeRep@. + Example + character({{2,1}}, 3) + Text + Nested compositions are supported; for example the GL_4 character + of $\wedge^3(S^2 V) = S_{(1,1,1)}(S_{(2)}(V))$: + Example + character({{1,1,1}, {2}}, 4) + Text + To split such a character into irreducibles, apply + @TO splitCharacter@: + Example + splitCharacter character({{1,1,1}, {2}}, 4) + SeeAlso + weylCharacter + splitCharacter + characterRep + decomposeRep + schur +/// + +doc /// + Key + weylCharacter + (weylCharacter, List, ZZ) + Headline + the character of a composition of Weyl functors on GL(V) + Usage + weylCharacter(L, d) + Inputs + L:List + a list of partitions + d:ZZ + the rank of the underlying module + Outputs + :RingElement + the character, as a symmetric polynomial in $x_0, \ldots, x_{d-1}$, + of the composed Weyl functor on the defining representation + Description + Text + The Weyl analogue of @TO character@, using @TO weyl@ instead of + @TO schur@. Over a field of characteristic zero + $W_\lambda(V) \cong S_\lambda(V)$ canonically, so @TT "weylCharacter"@ + returns the same polynomial as @TT "character"@; this routine is + provided for uniformity and for verifying that the diagonal-character + computation goes through the Weyl-side construction end-to-end. + Example + toString character({{2,1}}, 3) == toString weylCharacter({{2,1}}, 3) + splitCharacter weylCharacter({{1,1,1}, {2}}, 4) + SeeAlso + character + splitCharacter + weyl +/// + +doc /// + Key + splitCharacter + (splitCharacter, RingElement) + Headline + decompose a symmetric polynomial into Schur functions + Usage + splitCharacter(p) + Inputs + p:RingElement + a symmetric polynomial in the eigenvalues of the diagonal torus + Outputs + :RingElement + the expansion of @TT "p"@ as a $\mathbb{Z}_{\ge 0}$-linear + combination of Schur functions $s_\lambda$ + Description + Text + Together with @TO character@ this gives the isotypic + decomposition of a polynomial $GL_d$-representation $W$ whose + character is @TT "p"@: + $$\chi_W = \sum_\lambda m_\lambda \cdot s_\lambda + \;\Longleftrightarrow\; + W \cong \bigoplus_\lambda S_\lambda(V)^{\oplus m_\lambda}.$$ + The coefficients $m_\lambda$ are the multiplicities of the + irreducible polynomial $GL_d$-representations in $W$. + Example + c = character({{1,1,1}, {2}}, 4); + splitCharacter c + Text + Verifies the Hermite-reciprocity / plethysm identity + $\wedge^3(S^2 V_4) = S_{(4,1,1)}(V_4) \oplus S_{(3,3)}(V_4)$. + SeeAlso + character + weylCharacter + decomposeRep +/// + +doc /// + Key + characterRep + (characterRep, Matrix) + Headline + extract the character of a polynomial GL-representation from its matrix + Usage + characterRep F + Inputs + F:Matrix + a square matrix over a polynomial ring $R[w_1, \ldots, w_{d^2}]$ + representing a polynomial $GL_d$-action on some module; typically + the output of @TO schur@ applied to a generic matrix + Outputs + :RingElement + the trace of the diagonal specialization of @TT "F"@ in a fresh + polynomial ring $\mathbb{Q}[x_0, \ldots, x_{d-1}]$ + Description + Text + Given a matrix $F$ whose entries are polynomials in the + $d^2$ variables of a generic matrix on $V = \mathbb{Q}^d$, + @TT "characterRep"@ substitutes a diagonal matrix + $\mathrm{diag}(x_0, \ldots, x_{d-1})$ for that generic matrix and + reads off the trace. The result is the character of the + $GL_d$-representation $F$ encodes. + Example + R = QQ[w_1..w_9]; + G = genericMatrix(R, 3, 3); + F = schur({2,1}, G); + characterRep F + splitCharacter characterRep F + SeeAlso + character + splitCharacter + decomposeRep + schur +/// + +doc /// + Key + decomposeRep + (decomposeRep, Matrix) + Headline + decompose a polynomial GL-representation into irreducible subspaces + Usage + decomposeRep F + Inputs + F:Matrix + a square matrix representing a polynomial $GL_d$-action, as in + @TO characterRep@ + Outputs + :HashTable + keyed by the partitions $\lambda$ appearing in the decomposition + of @TT "F"@ (as returned by @TO splitCharacter@), with each value + a matrix whose columns span the $\lambda$-isotypic subspace + Description + Text + Given a polynomial $GL_d$-representation $W$ whose matrix is + @TT "F"@, @TT "decomposeRep"@ computes the character via + @TO characterRep@, decomposes it via @TO splitCharacter@, and for + each irreducible $S_\lambda$ appearing produces a basis of the + $S_\lambda$-isotypic subspace as the syzygy space of two + operators: + Text + @UL { + "the transvections (upper-triangular unipotent generators) + must act trivially on a highest-weight vector;", + "the diagonal torus must act by the maximal-weight character + of the $\\lambda$-isotypic piece." + }@ + Text + The partitions appearing as keys, and their multiplicities (as + the number of columns of the corresponding basis matrix), agree + with the output of @TT "splitCharacter characterRep F"@. + Example + R = QQ[w_1..w_9]; + G = genericMatrix(R, 3, 3); + H = decomposeRep schur({2}, schur({2}, G)); + keys H + SeeAlso + character + splitCharacter + characterRep + schur +/// + +----------------------------------------------------------------------- +-- Regression tests +----------------------------------------------------------------------- TEST /// M = schurModule({2,2,2}, QQ^4) assert(rank M == 10) (f, finv, AT, ST) = toSequence M.cache#"Schur"; assert(f*finv == map(QQ^10)) - -- straighten M = schurModule({1,1,1}, QQ^4); v = straighten(new Filling from {{3,2,1}}, M) - assert(v == vector{0_QQ,0,0,-1}) -/// + assert(v == vector{0_QQ,0,0,-1}) +/// + +TEST /// + c = character({{1,1,1},{2}}, 4) + assert(splitCharacter(c) == s_(4,1,1) + s_(3,3)) +/// + +TEST /// + -- Weyl module rank sanity + assert(rank weylModule({2,1}, QQ^3) == 8) + assert(rank weylModule({1,1,1}, QQ^4) == 4) + assert(rank weylModule({3}, QQ^2) == 4) +/// + +TEST /// + -- Schur / Weyl character agreement (in char 0) + assert(toString character({{2,1}}, 3) == toString weylCharacter({{2,1}}, 3)) + assert(toString character({{3,1}}, 3) == toString weylCharacter({{3,1}}, 3)) +/// + +TEST /// + -- standardTableaux dimensions (mu = column lengths) + assert(#standardTableaux(3, {2,1}) == 8) + assert(#standardWeylTableaux(3, {2,1}) == 8) + assert(#standardTableaux(4, {3}) == 4) +/// + +TEST /// + -- isStandard / isWeylStandard basic sanity + assert(isStandard(new Filling from {{0,1},{1,2}}) === null) + assert(isStandard(new Filling from {{1,2},{0,1}}) =!= null) + assert(isWeylStandard(weyl {{0,1},{1,2}}) === null) + assert(isWeylStandard(weyl {{1,2},{1,3}}) =!= null) +/// TEST /// -c=character({{1,1,1},{2}},4) -assert ( splitCharacter(c) == s_(4,1,1)+s_(3,3) ) + -- augmentWeylFilling basic sanity + T = weyl {{0,1},{2}} + assert(toList augmentWeylFilling(T, 0, 2) == {{0,1,2},{2}}) + assert(toList augmentWeylFilling(T, 2, 5) == {{0,1},{2},{5}}) /// end restart loadPackage "SchurFunctors" debug SchurFunctors -help schurModule -help schur +help SchurFunctors installPackage "SchurFunctors" --- Discussion with Mike, Anton, Mauricio 3/26/09 about what needs to be done for this package. -1. write mathematical documentation for schur and schurModule, explaining our conventions - Mauricio will do this by Sunday, April 5. -2. document the type Filling, and implement some functions for it: - -- check whether a filling is standard, semi-standard - -- transpose - -- generate all (as a list) standard, semi-standard - -- to/from lists - -- pretty printing (done already). - -- example in doc: should show how this related to a basis for schurModules -3. other functions need doc or examples: - character, splitCharacter (maybe the name should change) -4. decomposeRep, characterRep, augmentFilling -- need documentation +-- Historical note: the 2009 TODO list (Mike, Anton, Mauricio -- 3/26/09) +-- has been addressed by the current overhaul: +-- 1. mathematical documentation for schur / schurModule : DONE +-- 2. Filling type: standard/semistandard check, transpose, listing, +-- to/from lists, pretty printing, example of basis correspondence : DONE +-- 3. documentation + examples for character, splitCharacter : DONE +-- 4. documentation for decomposeRep, characterRep, augmentFilling : DONE diff --git a/M2/Macaulay2/packages/SchurFunctors/character.txt b/M2/Macaulay2/packages/SchurFunctors/character.txt deleted file mode 100644 index 35686cf5bc5..00000000000 --- a/M2/Macaulay2/packages/SchurFunctors/character.txt +++ /dev/null @@ -1,23 +0,0 @@ - Key - character - (character,List,ZZ) - Headline - Determines the character of a composition of Schur functors applied to the representation of GL(V) on V - Usage - character(L,d) - Inputs - L: List - A nested list whose entries are partitions - d: ZZ - An integer - Outputs - p:RingElement - A symmetric polynomial - Description - Text - Given a list L of partitions {L1,...,Ln} computes the character of the composition of Schur functors - SL1(SL2(...(SLn(V)))) applied to the canonical representation of GL(V) where dim(V)=d - Example - character({{1,1,1},{2}},4)--The GL(4) action on the Grassmannian of 3-dimensional subspaces of quadrics in four variables - SeeAlso - schur diff --git a/M2/Macaulay2/packages/SchurFunctors/schur.txt b/M2/Macaulay2/packages/SchurFunctors/schur.txt deleted file mode 100644 index 8b0014890bf..00000000000 --- a/M2/Macaulay2/packages/SchurFunctors/schur.txt +++ /dev/null @@ -1,35 +0,0 @@ - Key - schur - (schur, List, Matrix) - Headline - creates a map between Schur modules - Usage - schur(lambda,f) - Inputs - lambda:List - a list of numbers representing a partition - f:Module - a map between two free modules - Outputs - F:Matrix - the result of applying the Schur functor associated to lambda to f - Description - Text - Applies the Schur functor associated to lambda to the map f between free modules. - The modules @TT "source F"@ and @TT "target F"@ are Schur modules containing - certain data in cache (see @TO schurModule@). - Example - R=QQ[x_1,x_2,x_3] - F=map(R^1,R^3,vars R) - L=schur({2},F) -- 2nd veronese embedding - F=matrix{{1_QQ,2,4},{3,9,27},{4,16,64}} - schur({1,1},F) - minors(2,F) - schur({1,1,1},F) == det F - Caveat - The partition lambda should be a valid nonempty partition. - SeeAlso - schurModule - - - diff --git a/M2/Macaulay2/packages/SchurFunctors/schurModule.txt b/M2/Macaulay2/packages/SchurFunctors/schurModule.txt deleted file mode 100644 index d8f7eef9c09..00000000000 --- a/M2/Macaulay2/packages/SchurFunctors/schurModule.txt +++ /dev/null @@ -1,40 +0,0 @@ - Key - schurModule - (schurModule, List, Module) - Headline - creates Schur module from a partition and free module - Usage - schurModule(lambda,E) - Inputs - lambda:List - a list of numbers representing a partition; e.g. {3,1} stands for 2 rows of length 3 and 1. - E:Module - a free module - Outputs - M:Module - The result of application of the Schur functor associated to lambda to E. - Description - Text - Applies the Schur functor associated to lambda to the free module E. - For a detailed definition of the Schur module see p.106 of Fulton "Young Tableaux". - - The resulting M comes with cached data M.cache.Schur = {f, finv, AT, ST} where - - {"f is a map from exterior_mu E to M;", - "finv is a map from M to exterior_mu E;", - "AT is a hash table of all tableaux, whose entries increase in every column;", - "ST is a hash table of all standard tableaux (tableaux in AT, whose entries nondecrease in every row)." - } - - Tableaux are represented with objects of class Filling, - which is a double list whose entries are lists giving the fillings of the corresponding columns. - Example - M=QQ^3; - scan(4, i-> << i+1 << "-th symmetric power of M = " << schurModule({i+1},M) << endl) - S = schurModule({3,2,1}, M); - v = sum(numgens S, i-> (i+1)*S_i) -- an element of S represented by a vector - printSchurModuleElement(v, S); - Caveat - The partition lambda should be a valid nonempty partition. - SeeAlso - schur diff --git a/M2/Macaulay2/packages/SchurFunctors/schurModulesMap.txt b/M2/Macaulay2/packages/SchurFunctors/schurModulesMap.txt deleted file mode 100644 index 490af996d78..00000000000 --- a/M2/Macaulay2/packages/SchurFunctors/schurModulesMap.txt +++ /dev/null @@ -1,37 +0,0 @@ - Key - schurModulesMap - (schurModulesMap, Module, Module, Function) - Headline - creates a map between two Schur modules via the specified function. - Usage - schurModulesMap(N,M,F) - Inputs - N:Module - Schur module - M:Module - Schur module - F:Function - The function should specify, for every SST in the basis of M, - a linear combination of tableaux of the shape specified by N. - The output of F should be a sequence of pairs (c,T) where c is a coefficient - in ring(N) and T a tableau (not necessarily semistandard). - Outputs - G:Matrix - A map between M and N. - Description - Text - Constructs the map between M and N specified by the function F. - Example - n = 4; --j-th differential of the Koszul Complex on the variables of R - j = 2; - mu1=apply(j,j->1) - mu2=apply(j+1,j->1) - R = QQ[x_1..x_n]; - M=schurModule(mu1,R^n); - N=schurModule(mu2,R^n); - F = T -> apply(numgens R, j -> (R_j, augmentFilling(T,0,j))) - schurModulesMap(N,M,F) - Caveat - The function F should output lists of pairs where the second component is a filling of the partition corresponding to N. - SeeAlso - schur diff --git a/M2/Macaulay2/packages/SchurFunctors/splitCharacter.txt b/M2/Macaulay2/packages/SchurFunctors/splitCharacter.txt deleted file mode 100644 index cd3a40711a6..00000000000 --- a/M2/Macaulay2/packages/SchurFunctors/splitCharacter.txt +++ /dev/null @@ -1,19 +0,0 @@ - Key - splitCharacter - (splitCharacter,RingElement) - Headline - Decomposes a symmetric polynomial as a sum of Schur functions - Usage - splitCharacter(c) - Inputs - c: RingElement - A Symmetric polynomial - Outputs - p: RingElement - Description - Text - Expresses a symmetric polynomial c as a linear combination of Schur functions - Example - c=character({{1,1,1},{2}},4) - splitCharacter(c) - diff --git a/M2/Macaulay2/packages/SchurFunctors/straightenSchur.txt b/M2/Macaulay2/packages/SchurFunctors/straightenSchur.txt deleted file mode 100644 index 12a7aefccec..00000000000 --- a/M2/Macaulay2/packages/SchurFunctors/straightenSchur.txt +++ /dev/null @@ -1,23 +0,0 @@ - Key - straighten - (straighten, Filling, Module) - Headline - Given a tableau of shape lambda express it as a linear combination of SST in the given Schur module - Usage - straighten(T,M) - Inputs - T: Filling - Any filling with shape mu and entries between 0 and rank(E)-1. - M: Module - A Schur module of the form S_{mu}(E) for some partition mu. - Outputs - T2: - Description - Text - A linear combination of semistandard tableaux representing T in M. - Example - bla - Caveat - The function F should output lists of pairs where the second component is a filling of the partition corresponding to N. - SeeAlso - schur diff --git a/M2/Macaulay2/packages/SchurRings.m2 b/M2/Macaulay2/packages/SchurRings.m2 index 1710eccce74..f5466bdbe87 100644 --- a/M2/Macaulay2/packages/SchurRings.m2 +++ b/M2/Macaulay2/packages/SchurRings.m2 @@ -18,29 +18,35 @@ newPackage( "SchurRings", - Version => "1.1", - Date => "August 24, 2011", + Version => "2.0", + Date => "April 17, 2026", Authors => { {Name => "Michael Stillman", Email => "mike@math.cornell.edu", HomePage => "http://www.math.cornell.edu/~mike/"}, {Name => "Hal Schenck"}, - {Name => "Claudiu Raicu", Email => "claudiu@math.berkeley.edu", HomePage => "http://math.berkeley.edu/~claudiu/"} + {Name => "Claudiu Raicu", Email => "craicu@nd.edu", HomePage => "http://math.berkeley.edu/~claudiu/"}, + {Name => "Keller VandeBogert", Email => "keller.v@uky.edu", HomePage => "https://sites.google.com/view/kellervandebogert/home"} }, Keywords => {"Representation Theory"}, - Headline => "representation rings of general linear groups and of symmetric groups" --- AuxiliaryFiles => true + Headline => "representation rings of general linear groups and of symmetric groups", + DebuggingMode => false ) export {"schurRing", "SchurRing", "symmetricRing", - "toS", "toE", "toP", "toH", + "toS", "toE", "toP", "toH", "toM", "toSp", "toO", "specialize", + "kostkaNumber", "jacobiTrudi", "plethysm", - "centralizerSize", "classFunction", "symmetricFunction", + "centralizerSize", "classFunction", "symmetricFunction", "scalarProduct", "internalProduct", "SchurRingIndexedVariableTable", "EHPVariables", "SVariable", "ClassFunction", "schurLevel", "schurResolution", - "SchurRingElement", - "Memoize", "Schur", "EorH", "GroupActing", - "eVariable", "pVariable", "hVariable" + "SchurRingElement", + "Memoize", "Schur", "EorH", "GroupActing", "Basis", "OddOrEven", + "eVariable", "pVariable", "hVariable", + "modificationRule", + "branch", + "toRatGL", + "toSymm", "toGL", "toSn", "convert" } debug Core @@ -62,6 +68,31 @@ protect symbol HtoPTable --protect symbol plethysmMaps protect symbol mapFromE protect symbol sFunction +-- dispatch attributes for variant support (GL/Sn/Sp/O, Monomial, etc.) +protect symbol multiplySchurLevel1 +protect symbol highLevelCombine +protect symbol plethysmFcn +protect symbol recTransOp +protect symbol kostkaCache +protect symbol monomialBasisRing +protect symbol symplecticBasisRing +protect symbol orthogonalBasisRing +protect symbol schurBasisRing +protect symbol stableSchurHelper +protect symbol plethysmHelpers +protect symbol specializedSpRings +protect symbol specializedORings +protect symbol specializedGLRings +protect symbol specializedSLRings +protect symbol specializedRatGLRings +protect symbol specializedSnRings +protect symbol associatedRatGLRing +protect symbol ratNegRing +protect symbol ratNegSym +protect symbol ratPosSym +protect symbol ratOuterRing +protect symbol ratRank +protect symbol ratStableHelper SchurRing = new Type of EngineRing @@ -78,20 +109,49 @@ expression SchurRing := S -> ( else new FunctionApplication from unhold describe S) undocumented (expression, SchurRing) +----------------------------------------------------------------------------- +-- Engine boundary: SchurRing plumbing. +-- This section wraps the C++-side `rawSchurRing1` engine (which implements +-- Littlewood-Richardson multiplication on Schur-partition monomials) as an +-- M2 `SchurRing` type. The engine stores elements as polynomials in +-- monomials-that-are-partitions; the helpers below convert to/from that +-- representation and install element-access sugar (s_lambda, etc.) on top +-- of the raw engine ring. +----------------------------------------------------------------------------- + rawmonom2partition = (m) -> ( + -- Decode an engine Schur monomial (= a raw monomial representing s_lambda) + -- into its partition. `rawSparseListFormMonomial m` returns pairs + -- (x,e) = (row-length, multiplicity); `e:x` builds an e-tuple of x's, + -- `splice` flattens the list of tuples into the multiset of row lengths, + -- and `reverse` puts them in weakly-decreasing (partition) order. reverse splice apply(rawSparseListFormMonomial m, (x,e) -> e:x) ) +-- Strip trailing zero parts from a (weakly-decreasing) list representing a +-- partition. Used to normalize partition keys for memoization and for +-- returning partitions without padding. +stripTrailingZeros = lst -> ( + k := #lst; + while k > 0 and lst#(k-1) == 0 do k = k - 1; + take(lst, k) + ) + --various ways of addressing elements of a Schur ring +-- s_{lambda_1, lambda_2, ...} from an explicit partition list SchurRing _ List := (SR, L) -> new SR from rawSchurFromPartition(raw SR, L) +-- s_(lambda_1, lambda_2, ...) from a partition given as a sequence SchurRing _ Sequence := (SR, L) -> new SR from rawSchurFromPartition(raw SR, L) +-- s_L for a single-row partition L; `1:L` is M2 syntax for the length-1 tuple (L) SchurRing _ ZZ := (SR, L) -> new SR from rawSchurFromPartition(raw SR, 1:L) -- coefficientRing SchurRing := Ring => R -> last R.baseRings numgens SchurRing := Ring => R -> R.numgens ---n = schurLevel R is the number of iterations of the schurRing/symmetricRing function ---used in the construction of R +-- `schurLevel R` = nesting depth: the number of times schurRing/symmetricRing +-- was applied to build R. Plain rings (e.g. QQ) have level 0; a single +-- schurRing(QQ, s, n) has level 1; schurRing(schurRing(QQ, t, m), s, n) has +-- level 2; each wrap increments the stored value by 1 (see newSchur2 below). schurLevel = method() schurLevel (Ring) := R -> if R.?schurLevel then R.schurLevel else 0 @@ -99,27 +159,44 @@ schurLevel (Ring) := R -> if R.?schurLevel then R.schurLevel else 0 newSchur2 = method() newSchur2(Ring,Symbol) := (A,p) -> newSchur2(A,p,-1) +-- Concrete element type returned by `newSchurEngineRing`, used so that M2's +-- method dispatch can distinguish SchurRing elements from generic RingElement. SchurRingElement = new Type of RingElement newSchurEngineRing = R -> ( + -- Wrap the raw engine ring R as an M2 SchurRing of SchurRingElement, + -- bind the RawRing pointer, and cache the identity/zero shortcuts. S := new SchurRing of SchurRingElement; S.RawRing = R; S#1 = 1_S; S#0 = 0_S; S) - + newSchur2(Ring,Symbol,ZZ) := (A,p,n) -> ( - if not (A.?Engine and A.Engine) + if not (A.?Engine and A.Engine) then error "expected coefficient ring handled by the engine"; + -- Build a SchurRing over coefficient ring A with rank n + -- (n = -1 is the engine's sentinel for "infinite rank / stable GL"). + -- Steps: + -- (1) build the engine ring via rawSchurRing1 and wrap it; + -- (2) attach M2-side metadata (Symbol, baseRings, numgens, ...); + -- (3) install generic engine-backed overloads (+, *, ...); + -- (4) override expression / listForm to present elements as + -- partition-indexed s_lambda terms; + -- (5) propagate .char and bump .schurLevel. + -- (1) engine ring + M2 wrapper SR := newSchurEngineRing rawSchurRing1(raw A,n); + -- (2) M2-side metadata SR.Symbol = p; SR.baseRings = append(A.baseRings,A); SR.generators = {}; SR.numgens = if n < 0 then infinity else n; SR.degreeLength = 0; ---the basic features of SR are coded at the engine level + -- (3) the basic features of SR (arithmetic, etc.) are coded at the engine level commonEngineRingInitializations SR; ONE := SR#1; if A.?char then SR.char = A.char; + -- (4) pretty-printing: walk (coeff, monomial) pairs from the engine and + -- render each monomial as s_lambda via rawmonom2partition. toExternalString SR := r -> toString expression r; expression SR := f -> ( (coeffs,monoms) -> sum( @@ -130,16 +207,45 @@ newSchur2(Ring,Symbol,ZZ) := (A,p,n) -> ( if #t1 === 1 then t1#0 else t1 )}) ) rawPairs(raw A, raw f); + -- structured enumeration: return [(partition, coefficient)] pairs listForm SR := (f) -> ( n := numgens SR; (cc,mm) := rawPairs(raw A, raw f); toList apply(cc, mm, (c,m) -> (rawmonom2partition m, new A from c))); + -- (5) nesting-depth bookkeeping if (A.?schurLevel) then SR.schurLevel = A.schurLevel + 1 else SR.schurLevel = 1; SR ) -schurRing = method(Options => {EHPVariables => (getSymbol"e",getSymbol"h",getSymbol"p"), SVariable => getSymbol"s", GroupActing => "GL"}) +-- SL(n) canonicalization: for finite SL(n), irreducibles are indexed by +-- partitions lambda with lambda_n = 0. Any partition with n rows of +-- length k at the bottom (i.e. lambda_n = k > 0) equals +-- det^k tensor s_{lambda_1-k, ..., lambda_{n-1}-k} +-- which in SL collapses to s_{lambda_1-k, ..., lambda_{n-1}-k}. +-- Apply this row-by-row: if #lambda == n, strip lambda_n from every part. +-- Rings with numgens = infinity are treated as "stable SL" == GL (no +-- determinant to collapse since there's no top row to pin down). +slCanonicalize = (f, S) -> ( + n := numgens S; + if n === infinity then return f; + rawRes := raw(0_S); + for term in listForm f do ( + lam := term#0; + c := term#1; + if #lam > n then continue; -- engine usually prevents this; be safe + lamList := toList lam | toList((n - #lam) : 0); + k := lamList#(n-1); + lamNew := ( + if k == 0 then toList lam + else stripTrailingZeros(for i from 0 to n-1 list lamList#i - k) + ); + rawRes = rawRes + raw promote(c, S) * raw (S_lamNew); + ); + new S from rawRes + ); + +schurRing = method(Options => {EHPVariables => (getSymbol"e",getSymbol"h",getSymbol"p"), SVariable => getSymbol"s", GroupActing => "GL", Basis => "Schur", OddOrEven => null}) schurRing(Ring,Thing,ZZ) := SchurRing => opts -> (A,p,n) -> ( try p = baseName p else error "schurRing: can't use provided thing as variable"; if class p === Symbol then schurRing(A,p,n,opts) @@ -156,78 +262,420 @@ dim(List,SchurRingElement) := (lis,s) -> dimSchur(lis, s); dim(Thing,SchurRingElement) := (n,s) -> dimSchur(n, s); +--------------------------------------------------------------- +---- Rational (type A rational) SchurRings (Koike-Terada) ------ +--------------------------------------------------------------- +-- A rational irreducible representation of GL(n) is indexed by a pair +-- (alpha, beta) of partitions with ell(alpha) + ell(beta) <= n, giving the +-- dominant integral weight +-- (alpha_1, ..., alpha_p, 0, ..., 0, -beta_q, ..., -beta_1). +-- The stable universal rational character ring (Koike-Terada) has basis +-- {r_{alpha, beta}} and is isomorphic to S (x) S (two disjoint copies of the +-- ring of symmetric functions). Multiplication is componentwise: +-- r_{alpha, beta} * r_{gamma, delta} +-- = sum_{mu, nu} c^mu_{alpha, gamma} c^nu_{beta, delta} r_{mu, nu}. +-- We therefore implement RatGL as a thin re-tagging of a level-2 GL/GL +-- SchurRing: the outer layer tracks alpha, the inner (auto-generated) layer +-- tracks beta. All existing level-2 GL/GL multiplication infrastructure +-- applies unchanged. +-- +-- Specialization of the stable universal character to a finite rank n is the +-- Koike-Terada modification rule, which is computed here by applying the +-- Weyl reflection formula to the composite weight above. +--------------------------------------------------------------- + +-- Koike-Terada / Sam-Snowden-Weyman modification rule for rational GL(n). +-- +-- Given a stable bipartition label (alpha, beta), specialize +-- chi^{stable}_{alpha, beta} |_{GL(n)} +-- to a finite rational GL(n) character following [SSW] Sec. 5.4 in the +-- border-strip form. A pair (alpha, beta) is *admissible* when +-- ell(alpha) + ell(beta) <= n; for admissible pairs the specialization is +-- chi^{GL(n)}_{alpha, beta} with sign +1. +-- +-- When (alpha, beta) is not admissible, remove a border strip of length +-- L = ell(alpha) + ell(beta) - n - 1 +-- from BOTH alpha and beta, starting at the first box of the final row +-- (equivalently the hook at (k,1) with alpha_k + ell(alpha) - k = L and +-- likewise for beta). The sign contribution of the step is +-- (-1)^(c(R_alpha) + c(R_beta) - 1) +-- where c(R) is the number of columns the strip occupies. Recurse until +-- admissible, or return 0 if at some step no valid strip of the required +-- length exists. (L = 0 also forces vanishing: the strip must be non-empty.) +-- +-- Returns a list of triples (alpha', beta', coef): either empty (character +-- vanishes) or a singleton [(alpha', beta', sign)] with sign in {+1, -1}. +ratGLModify = (alpha, beta, n) -> ( + a := stripTrailingZeros toList alpha; + b := stripTrailingZeros toList beta; + if #a + #b <= n then return {(a, b, 1)}; + sign := 1; + while #a + #b > n do ( + L := #a + #b - n - 1; + if L == 0 then return {}; + kA := findBorderStripRow(a, L); + if kA === null then return {}; + kB := findBorderStripRow(b, L); + if kB === null then return {}; + (newA, cA) := removeBorderStripAtFirstColumn(a, kA); + (newB, cB) := removeBorderStripAtFirstColumn(b, kB); + if odd (cA + cB - 1) then sign = -sign; + a = newA; + b = newB; + ); + {(a, b, sign)} + ) + +-- Enumerate all partitions contained componentwise in the given bound. +-- Result includes the empty partition and the bound itself; trailing zeros +-- are stripped. +allSubpartitionsBoundedBy = (bound) -> ( + b := stripTrailingZeros toList bound; + if #b == 0 then return {{}}; + aux := (idx, prevMax) -> ( + if idx >= #b then return {{}}; + upper := min(b#idx, prevMax); + flatten for v from 0 to upper list + for t in aux(idx+1, v) list prepend(v, t) + ); + apply(aux(0, infinity), stripTrailingZeros) + ) + +-- Iterate all (alpha, beta, scalar) triples of a RatGL element. +-- Calls f(alpha, beta, scalar) for each triple; scalar lives in the ultimate +-- base (coefficient) ring of the inner layer. +iterateRatGLTerms = (elt, f) -> ( + for outerTerm in listForm elt do ( + alpha := toList outerTerm#0; + bElt := outerTerm#1; + for innerTerm in listForm bElt do ( + beta := toList innerTerm#0; + scalar := innerTerm#1; + f(alpha, beta, scalar); + ); + ); + ) + +-- Stable (infinity-rank) RatGL helper ring attached to a finite RatGL ring S. +-- Lazily constructed and cached on the ring. Used by finite RatGL +-- multiplication (stable componentwise LR, then Koike-Terada modification). +stableRatGLHelperOf = (S) -> ( + if S.?ratStableHelper then S.ratStableHelper + else ( + baseR := coefficientRing (S.ratNegRing); + helperSym := getSymbol("ratHelper" | toString hash S); + H := schurRing(baseR, helperSym, infinity, GroupActing => "RatGL"); + S.ratStableHelper = H; + H + ) + ) + +buildRatGLRing = (R, p, n, opts) -> ( + ------------------------------------------------------------------ + -- (1) Inner / outer ring construction + -- Build a two-layer GL SchurRing tower: the inner layer B + -- (beta / "negative" variables) sits over R, and the outer + -- layer S (alpha / "positive" variables) sits over B. The + -- bipartition basis element r_{alpha,beta} will later be + -- realized as (S_alpha) * (lift of B_beta into S). + ------------------------------------------------------------------ + negSym := getSymbol(toString p | "Neg"); + innerOpts := opts ++ {GroupActing => "GL", OddOrEven => null}; + B := schurRing(R, negSym, n, innerOpts); + outerOpts := opts ++ {GroupActing => "GL", OddOrEven => null}; + S := schurRing(B, p, n, outerOpts); + + ------------------------------------------------------------------ + -- (2) Metadata tagging + -- Re-tag S as the RatGL ring and remember both layers. + ------------------------------------------------------------------ + S.GroupActing = "RatGL"; + S.ratNegRing = B; + S.ratNegSym = negSym; + S.ratPosSym = p; + S.ratRank = n; + B.ratOuterRing = S; + + ------------------------------------------------------------------ + -- (3) Multiplication override + -- Multiplication on universal rational characters is the + -- Koike product, NOT the componentwise Littlewood-Richardson + -- product on the auxiliary Schur-polynomial basis. + -- * Stable rank (n = infinity): install the raw Koike + -- product directly. + -- * Finite rank n: lift both factors into the stable + -- helper ring, multiply there via Koike, then apply the + -- Koike-Terada (Sam-Snowden-Weyman) modification rule + -- to specialize back down to rank n. + ------------------------------------------------------------------ + isFiniteN := not (class n === InfiniteNumber or n < 0); + if isFiniteN then ( + S * S := (f1, f2) -> ( + H := stableRatGLHelperOf S; + h1 := toRatGL(f1, H); + h2 := toRatGL(f2, H); + hProd := h1 * h2; + specializeRatGLInto(hProd, n, S) + ); + ) + else ( + -- Stable RatGL: override componentwise LR with the Koike product. + S * S := (f1, f2) -> new S from ratGLKoikeProductRaw(S, f1, f2); + ); + + ------------------------------------------------------------------ + -- (4) Bipartition-syntax override via IndexedVariableTable + -- Override so that user-facing bipartition syntax + -- r_{{a1,a2,...}, {b1,b2,...}} -> r_{(alpha, beta)} + -- builds the basis element directly. At finite rank, if + -- #alpha + #beta > n the character reduces via Koike-Terada; + -- we apply that modification rule here. + ------------------------------------------------------------------ + t := value p; + t#symbol _ = a -> ( + isListish := x -> instance(x, VisibleList); + if isListish a and #a == 2 and isListish (a#0) and isListish (a#1) then ( + alpha := toList a#0; + beta := toList a#1; + isFin := not (class n === InfiniteNumber or n < 0); + if isFin and (#alpha + #beta) > n then ( + return specializeRatGLInto( + new S from ratGLBasisRaw(S, alpha, beta), + n, S); + ); + return new S from ratGLBasisRaw(S, alpha, beta); + ); + S _ a + ); + S.use = So -> (globalAssign(p, t); So); + S.use S; + + ------------------------------------------------------------------ + -- (5) Pretty-printing override + -- Display r_(alpha, beta) instead of the internal product + -- a_alpha * b_beta that actually represents it. + ------------------------------------------------------------------ + expression S := f -> ( + acc := null; + (outerCoeffs, outerMonoms) := rawPairs(raw B, raw f); + for i from 0 to #outerMonoms - 1 do ( + alphaLst := toList rawmonom2partition (outerMonoms#i); + bElt := new B from (outerCoeffs#i); + (innerCoeffs, innerMonoms) := rawPairs(raw R, raw bElt); + for j from 0 to #innerMonoms - 1 do ( + betaLst := toList rawmonom2partition (innerMonoms#j); + sc := new R from (innerCoeffs#j); + sub := new Subscript from {p, {alphaLst, betaLst}}; + term := if sc == 1 then expression sub + else (expression sc) * (expression sub); + acc = if acc === null then term else acc + term; + ); + ); + if acc === null then expression 0 else acc + ); + S + ) + schurRing(Ring,Symbol) := opts -> (R,p) -> schurRing(R,p,infinity,opts) -schurRing(Ring,Symbol,InfiniteNumber) := +schurRing(Ring,Symbol,InfiniteNumber) := schurRing(Ring,Symbol,ZZ) := SchurRing => opts -> (R,p,n) -> ( + ------------------------------------------------------------------ + -- (1) RatGL short-circuit + -- Rational GL (Koike-Terada bipartition) rings have their + -- own builder; dispatch there and return immediately. + ------------------------------------------------------------------ + if opts.GroupActing == "RatGL" then return buildRatGLRing(R, p, n, opts); + + ------------------------------------------------------------------ + -- (2) newSchur2 construction + field copies + -- Create the raw engine-level Schur ring S and copy across + -- option data (EHPVariables, GroupActing, Basis). Also: + -- * handle the OddOrEven tag for orthogonal rings, + -- * initialize the kostkaCache, + -- * install plethysm-related operator overloads (@, ^, + -- symmetricPower, exteriorPower) that are common to all + -- GroupActing variants. + ------------------------------------------------------------------ S := local S; if n == infinity then S = newSchur2(R,p,-1) else S = newSchur2(R,p,n); S.EHPVariables = opts.EHPVariables; --S.SVariable = opts.SVariable; S.GroupActing = opts.GroupActing; + S.Basis = opts.Basis; + + -- For GroupActing "O", distinguish O(2n+1) (type B_n, "Odd") from O(2n) + -- (type D_n, "Even") at finite rank. Stable rings (n = infinity) ignore + -- the tag (both limits coincide) but we still store a default for + -- downstream code that wants to read it. + if opts.GroupActing == "O" then ( + if opts.OddOrEven === null then S.OddOrEven = "Odd" + else if opts.OddOrEven === "Odd" or opts.OddOrEven === "Even" then + S.OddOrEven = opts.OddOrEven + else error("schurRing: OddOrEven must be \"Odd\" or \"Even\"; got " | toString opts.OddOrEven); + ) + else if opts.OddOrEven =!= null then + error "schurRing: OddOrEven is only meaningful with GroupActing => \"O\""; + + S.kostkaCache = new MutableHashTable; S @ RingElement := RingElement @ S := (f1,f2) -> plethysm(f1,f2); S^ZZ := (f,n) -> product apply(n,i->f); symmetricPower(ZZ,S) := (n,s) -> plethysm({n},s); exteriorPower(ZZ,S) := opts -> (n,s) -> plethysm(splice{n:1},s); - - --define the multiplication on S - --in the case when the group acting is a general linear group - if opts.GroupActing == "GL" then - ( - oldmult := method(); - oldmult(S,S) := (f1,f2) -> new S from raw f1 * raw f2; - oldmult(RingElement, S) := (f1,f2) -> if member(ring f1,S.baseRings | {S}) then oldmult(promote(f1,S),f2); - oldmult(S, RingElement) := (f1,f2) -> if member(ring f2,S.baseRings | {S}) then oldmult(f1,promote(f2,S)); - oldmult(Number, S) := (f1,f2) -> if member(ring f1,S.baseRings | {S}) then oldmult(promote(f1,S),f2); - oldmult(S, Number) := (f1,f2) -> if member(ring f2,S.baseRings | {S}) then oldmult(f1,promote(f2,S)); - - S * S := (f1,f2) -> - if schurLevel S == 1 then oldmult(f1,f2) - else - ( - lF1 := listForm f1; - lF2 := listForm f2; - sum flatten for p1 in lF1 list - for p2 in lF2 list - ( - oldmult((last p1) * (last p2), oldmult(S_(first p1),S_(first p2))) - ) - ); - ) + --define the multiplication on S - --in the case when the group acting is a symmetric group - else if opts.GroupActing == "Sn" then - ( - S ** S := (f1,f2) -> new S from raw f1 * raw f2; --- RingElement ** S := (f,g) -> if member(ring f,S.baseRings | {S}) then promote(f,S) ** g; --- S ** RingElement := (f,g) -> if member(ring g,S.baseRings | {S}) then f ** promote(g,S); + -- Raw (engine-level) multiplication — available for all variants + rawMult := (f1,f2) -> new S from raw f1 * raw f2; + + -- For Sn: install ** operator (needed for recTrans and higher-level combine) + if opts.GroupActing == "Sn" then ( + S ** S := (f1,f2) -> rawMult(f1,f2); RingElement ** S := (f,g) -> if member(ring f,S.baseRings | {S}) then promote(f,S) ** g else if member(S,(ring f).baseRings | {ring f}) then f ** promote(g,S); Number ** S := (f,g) -> if member(ring f,S.baseRings | {S}) then promote(f,S) ** g; S ** Number := (f,g) -> if member(ring g,S.baseRings | {S}) then f ** promote(g,S); - - S * S := (f1,f2) -> - if schurLevel S == 1 then - ( - cS := coefficientRing S; - if liftable(f1,cS) or liftable(f2,cS) then f1 ** f2 else - if f1 == 0 or f2 == 0 then 0_S else - internalProduct(f1,f2) - ) - else - ( - lF1 := listForm f1; - lF2 := listForm f2; - sum flatten for p1 in lF1 list - for p2 in lF2 list - ( - ((last p1) * (last p2)) ** internalProduct(S_(first p1),S_(first p2)) - ) - ); + ); + + ------------------------------------------------------------------ + -- (3) Dispatch table + -- Each GroupActing variant installs three callbacks on S + -- that are consumed by the unified S*S routine below: + -- * S.multiplySchurLevel1(f1,f2) -- product at schurLevel 1 + -- * S.highLevelCombine(c, bProd) -- how to stitch a + -- coefficient-ring + -- product with a + -- level-1 basis product + -- at higher schurLevel + -- * S.plethysmFcn -- plethysm algorithm + -- * S.recTransOp -- operator used inside + -- recTrans for + -- recursive basis + -- conversion + ------------------------------------------------------------------ + -- Register dispatch functions based on GroupActing + if opts.GroupActing == "GL" then ( + -- -- GL: ordinary general linear group + -- Level-1 product is the raw engine Littlewood-Richardson + -- product; higher-level combine is ordinary * ; plethysm + -- and recTrans both multiplicative. + S.multiplySchurLevel1 = (f1,f2) -> rawMult(f1,f2); + S.highLevelCombine = (coeff, basisProd) -> rawMult(promote(coeff,S), basisProd); + S.plethysmFcn = plethysmGL; + S.recTransOp = (a,b) -> a*b; + ) else if opts.GroupActing == "SL" then ( + -- -- SL: special linear (collapse determinant) + -- Same multiplication as GL, then strip a full column of n + -- rows (determinant character is trivial in SL). Stable + -- SL (numgens = infinity) coincides with stable GL. + S.multiplySchurLevel1 = (f1,f2) -> slCanonicalize(rawMult(f1,f2), S); + S.highLevelCombine = (coeff, basisProd) -> + rawMult(promote(coeff,S), basisProd); + S.plethysmFcn = plethysmGL; + S.recTransOp = (a,b) -> a*b; + ) else if opts.GroupActing == "Sn" then ( + -- -- Sn: symmetric group (internal product / Kronecker) + -- Level-1 product is the internal (Kronecker) product of + -- Sn characters; both high-level combine and recTransOp + -- also use **, since the Sn ring is "internal" at every + -- schurLevel. Liftable / zero shortcut avoids engine + -- edge cases on scalar factors. + S.multiplySchurLevel1 = (f1,f2) -> ( + cS := coefficientRing S; + if liftable(f1,cS) or liftable(f2,cS) then f1 ** f2 else + if f1 == 0 or f2 == 0 then 0_S else + internalProduct(f1,f2) + ); + S.highLevelCombine = (coeff, basisProd) -> coeff ** basisProd; + S.plethysmFcn = plethysmSn; + S.recTransOp = (a,b) -> a**b; + ) else if opts.GroupActing == "Sp" then ( + -- -- Sp: symplectic (Newell-Littlewood via stable helper) + -- Multiplication goes via convert-to-Schur, LR-multiply, + -- convert-back (== Newell-Littlewood rule). We compute + -- the GL product in a STABLE Schur helper ring so that + -- partitions with more than numgens S rows survive to be + -- collapsed by the modification rule in schurToSpRE (the + -- finite-n Sp ring's own engine would otherwise truncate + -- them too early). + S.multiplySchurLevel1 = (f1,f2) -> ( + H := stableSchurHelperOf S; + sf1 := spToSchurRE(f1, H); + sf2 := spToSchurRE(f2, H); + prod := new H from raw sf1 * raw sf2; + schurToSpRE(prod, S) + ); + S.highLevelCombine = (coeff, basisProd) -> rawMult(promote(coeff,S), basisProd); + S.plethysmFcn = plethysmSp; + S.recTransOp = (a,b) -> a*b; + ) else if opts.GroupActing == "O" then ( + -- -- O: orthogonal (Newell-Littlewood via stable helper) + -- Same stable-helper trick as Sp, but with the orthogonal + -- conversion maps oToSchurRE / schurToORE. + S.multiplySchurLevel1 = (f1,f2) -> ( + H := stableSchurHelperOf S; + sf1 := oToSchurRE(f1, H); + sf2 := oToSchurRE(f2, H); + prod := new H from raw sf1 * raw sf2; + schurToORE(prod, S) + ); + S.highLevelCombine = (coeff, basisProd) -> rawMult(promote(coeff,S), basisProd); + S.plethysmFcn = plethysmO; + S.recTransOp = (a,b) -> a*b; + ) else error("Unknown GroupActing: " | toString opts.GroupActing); + + ------------------------------------------------------------------ + -- (4) Monomial-basis override + -- If the user selected the monomial-symmetric basis, we + -- overwrite multiplySchurLevel1 post-hoc: convert both + -- factors to the Schur basis, multiply via engine LR, and + -- convert back. All other dispatch callbacks are inherited + -- from the GroupActing branch above. + ------------------------------------------------------------------ + -- Override multiplication for Monomial basis + if opts.Basis == "Monomial" then ( + S.multiplySchurLevel1 = (f1,f2) -> ( + -- Convert from monomial to Schur + sf1 := monomialToSchurRE(f1, S); + sf2 := monomialToSchurRE(f2, S); + -- Multiply via engine (Littlewood-Richardson) + prod := new S from raw sf1 * raw sf2; + -- Convert back to monomial + schurToMonomialRE(prod, S) + ); + ); + + ------------------------------------------------------------------ + -- (5) Unified S * S dispatch + -- At schurLevel 1 we call multiplySchurLevel1 directly. At + -- higher schurLevel we expand each factor via listForm into + -- (partition, coefficient) pairs, multiply bases pairwise + -- via multiplySchurLevel1, and stitch the coefficient + -- product back in via highLevelCombine. + ------------------------------------------------------------------ + -- Unified multiplication dispatch + S * S := (f1,f2) -> + if schurLevel S == 1 then S.multiplySchurLevel1(f1,f2) + else ( + lF1 := listForm f1; + lF2 := listForm f2; + sum flatten for p1 in lF1 list + for p2 in lF2 list + S.highLevelCombine( + (last p1) * (last p2), + S.multiplySchurLevel1(S_(first p1), S_(first p2)) + ) ); + ------------------------------------------------------------------ + -- (6) IndexedVariableTable setup + S.use + -- Wire up the subscript syntax S_lambda. For SL rings we + -- canonicalize the result (strip determinant columns). + ------------------------------------------------------------------ t := new SchurRingIndexedVariableTable from p; t.SchurRing = S; - t#symbol _ = a -> ( S _ a); + if opts.GroupActing == "SL" then + t#symbol _ = a -> slCanonicalize(S _ a, S) + else + t#symbol _ = a -> ( S _ a); S.use = S -> (globalAssign(p,t); S); S.use S; S) @@ -244,8 +692,8 @@ schurRing (Ring) := opts -> R -> ( if instance(R, SchurRing) then R else ( s := R.SVariable; - if schurLevel R == 1 then R.Schur = schurRing(coefficientRing R,s,R.dim,EHPVariables => R.EHPVariables, GroupActing => R.GroupActing) - else R.Schur = schurRing(schurRing coefficientRing R,s,R.dim,EHPVariables => R.EHPVariables, GroupActing => R.GroupActing); --symmetricRing is wrong, right? + if schurLevel R == 1 then R.Schur = schurRing(coefficientRing R,s,R.dim,EHPVariables => R.EHPVariables, GroupActing => R.GroupActing, Basis => if R.?Basis then R.Basis else "Schur") + else R.Schur = schurRing(schurRing coefficientRing R,s,R.dim,EHPVariables => R.EHPVariables, GroupActing => R.GroupActing, Basis => if R.?Basis then R.Basis else "Schur"); --symmetricRing is wrong, right? R.Schur.symmetricRing = R; R.Schur ) @@ -267,59 +715,111 @@ SchurRingIndexedVariableTable _ Thing := (x,i) -> x#symbol _ i --construction of symmetric rings symmetricRing = method(Options => options schurRing) symmetricRing (Ring,ZZ) := opts -> (A,n) -> ( + -- =================================================================== + -- Build R = A[e_1..e_n, p_1..p_n, h_1..h_n]: a polynomial ring over A + -- with 3n generators in the fixed block order + -- R_0 .. R_{n-1} = e_1..e_n (elementary) + -- R_n .. R_{2n-1} = p_1..p_n (power sum) + -- R_{2n} .. R_{3n-1} = h_1..h_n (complete homogeneous) + -- deg(e_i) = deg(p_i) = deg(h_i) = i. + -- + -- symRingForE is the SAME ring with the variable order [h | p | e]; + -- under GRevLex this makes e smallest, so a GB killing + -- { h_i - H_i(e), p_i - P_i(e) } + -- reduces any polynomial to e-only. symRingForP is analogous with + -- e and p swapped, making p smallest. For H-reduction we just reuse + -- R itself (default GRevLex places h last, so h is already smallest). + -- =================================================================== + + -- ==== Construct R and register E/P/H variable accessors ==== (e,h,p) := opts.EHPVariables; R := A[e_1..e_n,p_1..p_n,h_1..h_n, Degrees => toList(1..n,1..n,1..n), MonomialSize => 8]; R.EHPVariables = opts.EHPVariables; R.SVariable = opts.SVariable; + -- Index arithmetic follows the block layout above: + -- e_i = R_(i-1), p_i = R_(n+i-1), h_i = R_(2n+i-1). R.eVariable = (i) -> if 1 <= i and i <= n then R_(i-1) else error"Invalid index"; R.pVariable = (i) -> if 1 <= i and i <= n then R_(n+i-1) else error"Invalid index"; R.hVariable = (i) -> if 1 <= i and i <= n then R_(2*n+i-1) else error"Invalid index"; R.GroupActing = opts.GroupActing; + R.Basis = opts.Basis; R.dim = n; + + -- ==== Propagate dispatch function for plethysm ==== + if opts.GroupActing == "GL" or opts.GroupActing == "SL" then R.plethysmFcn = plethysmGL + else if opts.GroupActing == "Sn" then R.plethysmFcn = plethysmSn; + + -- ==== Operator overloads on R ==== R ** R := (f1,f2) -> internalProduct(f1,f2); --internal product of symmetric functions R @ RingElement := RingElement @ R := (f1,f2) -> plethysm(f1,f2); symmetricPower(ZZ,R) := (n,r) -> plethysm({n},r); exteriorPower(ZZ,R) := opts -> (n,r) -> plethysm(splice{n:1},r); ---the degrees of e_i,p_i,h_i are equal to i - degsEHP := toList(1..n); ---blocks#0 are indices for e-variables ---blocks#1 are indices for p-variables ---blocks#2 are indices for h-variables + + -- ==== Degree sequence and block indices inside R ==== + -- The degrees of e_i, p_i, h_i are all i, so (1,2,..,n) is repeated + -- three times across the concatenated variable list. + degSeq := toList(1..n); + -- eIdx / pIdx / hIdx are the R-indices of the e / p / h blocks. blocks := {toList(0..(n-1)),toList(n..(2*n-1)),toList(2*n..(3*n-1))}; ---new variables for the E,H,P polynomials - vrs := symbol vrs; - locVarsE := apply(blocks#0,i->vrs_i); - locVarsP := apply(blocks#1,i->vrs_i); - locVarsH := apply(blocks#2,i->vrs_i); ---new rings used for conversion to E- and P- polynomials ---they differ from R in the order of the variables ---R is used by default for conversion to H-polynomials - R.symRingForE = A[locVarsH | locVarsP | locVarsE ,Degrees=>flatten toList(3:degsEHP),MonomialOrder=>GRevLex, MonomialSize => 8]; - R.mapToE = map(R.symRingForE,R,apply(blocks#2|blocks#1|blocks#0,i->(R.symRingForE)_i)); - R.mapFromE = map(R,R.symRingForE,apply(blocks#2|blocks#1|blocks#0,i->R_i)); - R.symRingForP = A[locVarsH | locVarsE | locVarsP,Degrees=>flatten toList(3:degsEHP),MonomialOrder=>GRevLex, MonomialSize => 8]; - R.mapToP = map(R.symRingForP,R,apply(blocks#1|blocks#2|blocks#0,i->(R.symRingForP)_i)); - R.mapFromP = map(R,R.symRingForP,apply(blocks#2|blocks#0|blocks#1,i->R_i)); ---compute conversion tables ---between E-,H- and P- polynomials + eIdx := blocks#0; + pIdx := blocks#1; + hIdx := blocks#2; + + -- ==== Fresh placeholder symbols for the reordered auxiliary rings ==== + -- Note: `vrs := symbol vrs` is the idiomatic way to introduce a + -- fresh local symbol in M2; the LHS declaration also keeps the + -- package's symbol checker happy. + vrs := symbol vrs; + tempSym := vrs; + tempVarsE := apply(eIdx,i->tempSym_i); + tempVarsP := apply(pIdx,i->tempSym_i); + tempVarsH := apply(hIdx,i->tempSym_i); + + -- ==== Auxiliary rings with reordered variables ==== + -- They differ from R only in the order of the variables. + -- R itself is used by default for conversion to H-polynomials. + + -- symRingForE: variable order [h | p | e]. Under GRevLex this makes + -- the e-block smallest, so grbE (defined below) reduces any + -- polynomial to its e-only representative. + R.symRingForE = A[tempVarsH | tempVarsP | tempVarsE ,Degrees=>flatten toList(3:degSeq),MonomialOrder=>GRevLex, MonomialSize => 8]; + R.mapToE = map(R.symRingForE,R,apply(hIdx|pIdx|eIdx,i->(R.symRingForE)_i)); + R.mapFromE = map(R,R.symRingForE,apply(hIdx|pIdx|eIdx,i->R_i)); + + -- symRingForP: variable order [h | e | p]. Under GRevLex this makes + -- the p-block smallest, so grbP reduces any polynomial to its + -- p-only representative. + R.symRingForP = A[tempVarsH | tempVarsE | tempVarsP,Degrees=>flatten toList(3:degSeq),MonomialOrder=>GRevLex, MonomialSize => 8]; + R.mapToP = map(R.symRingForP,R,apply(pIdx|hIdx|eIdx,i->(R.symRingForP)_i)); + R.mapFromP = map(R,R.symRingForP,apply(hIdx|eIdx|pIdx,i->R_i)); + + -- ==== Conversion tables between E-, H- and P- polynomials ==== EtoP(n,R); PtoE(n,R); HtoE(n,R); EtoH(n,R); PtoH(n,R); HtoP(n,R); ---define Groebner bases used for conversion between E-,H- and P- polynomials + + -- ==== Groebner bases for conversion between E-, H- and P- polynomials ==== + -- Each GB lists, for i = 1..n, relations of the form + -- p_i - (p_i expressed in the target basis) + -- h_i - (h_i expressed in the target basis) + -- so that reduction mod the GB kills the non-target blocks. + -- grbE / grbP live in symRingForE / symRingForP; grbH lives in R + -- itself (default GRevLex already makes h smallest). R.grbE = forceGB matrix(R.symRingForE, {flatten apply(splice{1..n},i->{R.mapToE(R_(n-1+i))-R.PtoETable#i,R.mapToE(R_(2*n-1+i))-R.HtoETable#i})}); R.grbH = forceGB matrix(R, {flatten apply(splice{1..n},i->{R_(n-1+i)-R.PtoHTable#i,R_(-1+i)-R.EtoHTable#i})}); R.grbP = forceGB matrix(R.symRingForP, {flatten apply(splice{1..n},i->{R.mapToP(R_(-1+i))-R.EtoPTable#i,R.mapToP(R_(2*n-1+i))-R.HtoPTable#i})}); collectGarbage(); ---construct maps that convert a polynomial in the E-,H-,P- variables ---into one involving only one of the three variables + + -- ==== Basis-projection maps: rewrite f using only E-, P- or H-vars ==== R.mapSymToE = (f) -> R.mapFromE(R.mapToE(f)%R.grbE); R.mapSymToP = (f) -> R.mapFromP(R.mapToP(f)%R.grbP); R.mapSymToH = (f) -> f%R.grbH; ---the Schur level of R is one more than that of its base ring + + -- ==== Schur level: one more than that of the base ring ==== if (A.?schurLevel) then R.schurLevel = A.schurLevel + 1 else R.schurLevel = 1; R) @@ -336,7 +836,7 @@ symmetricRing (Ring) := opts -> R -> ( error"symmetric ring expects finite schurRings"; if coefficientRing R === ZZ then error"base ring has to be QQ"; - R.symmetricRing = symmetricRing(symmetricRing coefficientRing R,numgens R,EHPVariables => R.EHPVariables, SVariable => R.Symbol, GroupActing => R.GroupActing); + R.symmetricRing = symmetricRing(symmetricRing coefficientRing R,numgens R,EHPVariables => R.EHPVariables, SVariable => R.Symbol, GroupActing => R.GroupActing, Basis => R.Basis); R.symmetricRing.Schur = R; R.symmetricRing ) @@ -348,14 +848,72 @@ symmetricRing(ZZ) := opts -> n -> symmetricRing(QQ,n,opts) --------------------------------------------------------------- --------------Jacobi-Trudi------------------------------------- --------------------------------------------------------------- - -----local variables for jacobiTrudi -----they are used in the recursive function jT -auxR = local auxR; -auxn = local auxn; -auxEH = local auxEH; +-- +-- The Jacobi-Trudi identity expresses a Schur function as a +-- determinant in the complete-homogeneous (h) or elementary (e) +-- symmetric functions: +-- +-- s_lambda = det( h_{lambda_i - i + j} ) (H-variant) +-- s_lambda = det( e_{lambda'_i - i + j} ) (E-variant, +-- lambda' = conjugate) +-- +-- `jacobiTrudi` builds the matrix and either calls det() directly +-- (Memoize => false) or expands cofactor-wise via the recursive +-- helper `jT` (Memoize => true), caching results on R.sFunction. +-- +-- File-scope "channel" between jacobiTrudi and jT: +-- `jT` is a plain (non-method) recursive function and needs access to +-- the ambient ring, its dim, and the E/H flag on every recursive call. +-- Rather than thread these through every argument list, we publish them +-- as file-scope locals that `jacobiTrudi` sets just before calling `jT`. +-- These names are a deliberate protocol; do not rename them. +auxR = local auxR; -- the ambient symmetricRing in use +auxn = local auxn; -- R.dim (number of variables) +auxEH = local auxEH; -- 0 for E-variant, 1 for H-variant ---- +-- Shared cached symmetricRing workspace. Multiple internal routines +-- (skewSchurExpansion, plethysm, kostkaNumber, ...) need a transient +-- symmetricRing(QQ, n) just to run jacobiTrudi / toS on a partition +-- before reading off the partition-coefficient list. Constructing a +-- fresh symmetricRing each time costs ~40-90ms, which dominates small +-- calls; caching a single ring and growing it on demand makes repeat +-- calls essentially free. +-- +-- Safety: callers only ever lift partition/coefficient data out of the +-- ring; they never hand a raw element back to the user or compare +-- elements from different calls for identity. Growing the ring between +-- calls therefore can't invalidate earlier results. +workSymRing := null; +workSymRingSize := 0; + +-- Ensure the cached symmetricRing has dim >= need. Grow (not shrink) +-- in powers-of-two-ish increments to amortize allocation cost. +-- Uses *private* (local) symbols for the e/h/p/s variables so that +-- constructing (or growing) the cached ring does NOT rebind the +-- user's global e, h, p, s at the top level. Rebinding those would +-- cause expressions like `e_4` or `h_3` entered after a call into +-- plethysm / skewSchurExpansion to evaluate into the wrong ring. +ensureWorkSymRing := (need) -> ( + if workSymRing === null or workSymRingSize < need then ( + newSize := max(need, 8); + if workSymRingSize > 0 then + newSize = max(newSize, 2 * workSymRingSize); + kv := local kv; + ev := local ev; + hv := local hv; + pv := local pv; + workSymRing = symmetricRing(QQ, newSize, + SVariable => kv, + EHPVariables => (ev, hv, pv)); + workSymRingSize = newSize; + ); + workSymRing + ) + +-- Backward-compat alias for readers; same cache. +ensureSkewWorkRing := ensureWorkSymRing + jacobiTrudi = method(Options => {Memoize => true, EorH => "E"}) jacobiTrudi(BasicList,Ring) := opts -> (lambda,R) -> ( @@ -396,7 +954,21 @@ jacobiTrudi(BasicList,Ring) := opts -> (lambda,R) -> rez ) ---computes the Jacobi-Trudi determinant recursively +-- Computes the Jacobi-Trudi determinant recursively, via cofactor +-- expansion along the last row of the Jacobi-Trudi matrix. +-- +-- The underlying symmetricRing lays its 3n generators out as +-- indices 0 .. n-1 e_1 .. e_n (elementary) +-- indices n .. 2n-1 p_1 .. p_n (power sums) +-- indices 2n .. 3n-1 h_1 .. h_n (complete homogeneous) +-- so we reach e_k and h_k uniformly by offsetting into the generator +-- list. With auxEH in {0,1}, the single expression +-- +-- auxR_(2*auxEH*auxn - 1 + k) +-- +-- selects e_k (auxEH = 0: offset -1, i.e. indices start at 0) or +-- h_k (auxEH = 1: offset 2n-1, i.e. indices start at 2n). We bind +-- this as `basisOffset` for readability. jT = (lambda) -> ( lambda = toList lambda; @@ -404,22 +976,29 @@ jT = (lambda) -> if auxR.sFunction#auxEH#?lambda then rez = auxR.sFunction#auxEH#lambda else ( - ll := #lambda; - if ll == 0 or lambda#0 == 0 then rez = 1_auxR else - if ll == 1 then rez = auxR_(2*auxEH*auxn-1+lambda#0) else + basisOffset := 2*auxEH*auxn - 1; -- see comment above + k := #lambda; + if k == 0 or lambda#0 == 0 then rez = 1_auxR else + if k == 1 then rez = auxR_(basisOffset + lambda#0) else ( - l1 := drop(lambda,-1); - l2 := {}; + -- Cofactor expansion along the last row of the Jacobi-Trudi + -- matrix. At step i we pull out the entry coming from the + -- (ll-1-i)-th part of lambda; `leftPart` is the prefix whose + -- indices have not yet been consumed, `rightPart` collects + -- the shifted tail contributed by the already-consumed + -- parts, and `sign` alternates the cofactor sign. + leftPart := drop(lambda,-1); + rightPart := {}; rez = 0; - sgn := 1; - for i from 0 to ll-1 do + sign := 1; + for i from 0 to k-1 do ( - if lambda#(ll-1-i)+i<=auxn then --just added, won't work for h-polynomials - rez = rez + sgn*auxR_(2*auxEH*auxn-1+lambda#(ll-1-i)+i)*jT(l1|l2); - sgn = - sgn; - l1 = drop(l1,-1); - if lambda#(ll-1-i)>1 then - l2 = {lambda#(ll-1-i)-1} | l2; + if lambda#(k-1-i)+i <= auxn then --just added, won't work for h-polynomials + rez = rez + sign*auxR_(basisOffset + lambda#(k-1-i) + i)*jT(leftPart|rightPart); + sign = - sign; + leftPart = drop(leftPart,-1); + if lambda#(k-1-i) > 1 then + rightPart = {lambda#(k-1-i)-1} | rightPart; ); ); auxR.sFunction#auxEH#lambda = rez; @@ -442,22 +1021,67 @@ powerCycleType(ZZ,List) := (k,cyc) -> rsort(flatten (for i in cyc list (g := gcd(i,k);splice{g:i//g}))) ) +------------------------------------------------------------------------- +-- plethysmMap(d, maxg, R) +------------------------------------------------------------------------- +-- The power-sum plethysm operator p_d acting on a symmetricRing R of +-- rank nS. On the power-sum generators it is the substitution +-- +-- p_i |--> p_{i*d}. +-- +-- When i*d <= nS, p_{i*d} is already a generator of R. When +-- i*d > nS, the element p_{i*d} is not directly a variable; instead +-- its expansion in the elementary-symmetric variables lives in +-- R.PtoETable#(i*d), and R.mapFromE re-embeds that expansion into R. +-- +-- The returned ring map has the layout expected by symmetricRing +-- generators, namely three blocks of length nS each: +-- +-- [ e-slot (nS zeros) | p-slot (images) | h-slot (nS zeros) ]. +-- +-- Only the p-slot is populated (through index maxg); the e- and +-- h-generators are sent to 0 because callers only feed p-polynomials +-- through this map. +------------------------------------------------------------------------- -- d is an integer -- R is symmetricRing n -- returns the plethysm map p_d : R --> R -- which sends p_i to p_(i*d). plethysmMap = (d,maxg,R) -> ( - nS := R.dim; - nSd := nS // d; - fs := splice{nS:0_R}; + nS := R.dim; + nSd := nS // d; -- largest i with i*d <= nS + fs := splice{nS:0_R}; -- e-block: nS zeros topf := min(maxg,nSd); + -- p-block, part 1: i in 1..topf, image p_{i*d} is an actual variable fs = join(fs, apply(1..topf, j -> R.pVariable(d*j))); - if maxg > nSd then - fs = join(fs, apply(topf+1..maxg,j-> R.mapFromE R.PtoETable#(d*j))); + -- p-block, part 2: i in topf+1..maxg, image p_{i*d} is out of range, + -- pull it from the cached E-expansion and re-embed + if maxg > nSd then + fs = join(fs, apply(topf+1..maxg, j -> R.mapFromE R.PtoETable#(d*j))); + -- pad the rest of the p-block (up to nS) and then the full h-block; + -- total remaining length is 2*nS - maxg fs = join(fs, 2*nS-maxg:0_R); map(R,R,fs) ) +------------------------------------------------------------------------- +-- plethysmGL(f, g) -- exterior (GL) plethysm f o g +------------------------------------------------------------------------- +-- Computes the composition of Schur functors applied to +-- GL-representations. Strategy: +-- +-- 1. Rewrite f in power-sum variables: pf = f(p_1,...,p_{nf}). +-- 2. Rewrite g in power-sum variables: pg. +-- 3. For each j, the power-sum plethysm p_j o g is obtained from pg +-- by the substitution p_i |-> p_{i*j}; this is exactly +-- (plethysmMap(j, maxg, SRg)) pg. +-- 4. Then f o g = pf( p_1 o g, p_2 o g, ..., p_{nf} o g ), +-- realized as a ring map phi : SRf -> SRg that sends +-- p_j |--> (plethysmMap(j, maxg, SRg)) pg, +-- and sends the e- and h-generators to 0 (pf is pure in p). +-- 5. Convert the result back to the Schur basis when the ambient +-- ring of g is a SchurRing. +------------------------------------------------------------------------- -- exterior plethysm (corresponding to composition -- of Schur functors of GL-representations) -- f is a polynomial in symmetricRing / SchurRing SA @@ -469,30 +1093,36 @@ plethysmGL(RingElement,RingElement) := (f,g) -> ( Rf := ring f; if schurLevel Rf > 1 then error"Undefined plethysm operation"; - issy := not instance(Rg,SchurRing); + issy := not instance(Rg,SchurRing); -- true => stay in symmetricRing pg := toP g; pf := toP f; - - SRg := ring pg; --symmetric ring of Rg - SRf := ring pf; --symmetric ring of Rf - + + SRg := ring pg; -- symmetric ring of Rg + SRf := ring pf; -- symmetric ring of Rf + nf := SRf.dim; ---maxf is the maximum i for which the variable p_i appears in the expression of pf + -- maxf: largest i such that p_i appears in pf maxf := max(support(pf)/index//max-nf+1,0); - + auxS := SRg; - nS := auxS.dim; - lev := schurLevel auxS; - spg := support(pg)/index; ---maxg is the maximum i for which the variable p_i appears in the expression of pg + nS := auxS.dim; + lev := schurLevel auxS; + spg := support(pg)/index; + -- maxg: largest i such that p_i appears in pg maxg := max(select(spg,i->i<3*nS)//max-nS+1,0); ---if p_(maxf*maxg) hasn't been computed in terms of E-polynomial, then compute it + -- ensure the E-expansion table reaches index maxf*maxg if maxf*maxg >= #auxS.PtoETable then PtoE(maxf*maxg,auxS); ---phi is the map that sends p_i to the plethystic composition p_i\circ pg ---so that phi(f) = f \circ pg (plethysm of f and pg) - phi := map(SRg,SRf,flatten splice {nf:0_SRg, - apply(1..nf, j -> (if j<=maxf then (plethysmMap(j,maxg,SRg))pg else 0_SRg)), - nf:0_SRg}); + + -- phi : SRf -> SRg sends p_i to the plethystic composition p_i o pg, + -- so phi(pf) = pf o pg = f o g. + -- The flattened argument has three blocks matching SRf's generators: + -- e-block (nf zeros), p-block (plethysmMap images), h-block (nf zeros). + phi := map(SRg, SRf, flatten splice { + nf:0_SRg, -- e-block: nf zeros + apply(1..nf, j -> -- p-block: p_j o pg for j<=maxf, else 0 + (if j<=maxf then (plethysmMap(j,maxg,SRg))pg else 0_SRg)), + nf:0_SRg -- h-block: nf zeros + }); pl := phi pf; if issy then pl else toS pl ) @@ -509,6 +1139,78 @@ plethysmSn(RingElement,RingElement) := (f,g) -> symmetricFunction(plethysm(f,classFunction g), ring g) ) +-- Build / fetch a FINITE GL Schur ring large enough to hold the +-- Schur expansion of a plethysm f \circ g, using degrees of f,g. +-- plethysmGL passes through a symmetricRing, which requires finite numgens; +-- the stable (infinity) helper cannot be used here. +plethysmHelperOf = (S, nWanted) -> ( + key := (coefficientRing S, nWanted); + if not S.?plethysmHelpers then S.plethysmHelpers = new MutableHashTable; + if S.plethysmHelpers#?key then S.plethysmHelpers#key + else ( + sSym := getSymbol "splhlp"; + T := schurRing(coefficientRing S, sSym, nWanted); + S.plethysmHelpers#key = T; + T + ) + ) + +-- Estimate a safe numgens for the finite Schur helper used during +-- plethysm: partitions in the result f \circ g have size deg(f)*deg(g) +-- and thus at most deg(f)*deg(g) rows. +plethysmHelperSize = (f,g) -> ( + lf := listForm f; + lg := listForm g; + degF := if #lf == 0 then 0 + else max apply(lf, t -> sum toList first t); + degG := if #lg == 0 then 0 + else max apply(lg, t -> sum toList first t); + max(degF * degG, 4) + ) + +-- Plethysm for the symplectic character ring. +-- f \circ g where g is a character of Sp(2n). +-- Strategy: convert g to the GL-Schur basis in a sufficiently large finite +-- helper ring H, apply GL plethysm in H, then fold the result back to the +-- Sp-basis via the Littlewood inverse (schurToSpRE), which handles +-- modification rules when the target Sp ring has finite rank. +plethysmSp = method() +plethysmSp(RingElement,RingElement) := (f,g) -> ( + S := ring g; + Rf := ring f; + nFin := plethysmHelperSize(f,g); + H := plethysmHelperOf(S, nFin); + gS := spToSchurRE(g, H); + fS := if instance(Rf, SchurRing) and Rf.?GroupActing then ( + if Rf.GroupActing == "Sp" then + spToSchurRE(f, plethysmHelperOf(Rf, nFin)) + else if Rf.GroupActing == "O" then + oToSchurRE(f, plethysmHelperOf(Rf, nFin)) + else f + ) else f; + plS := plethysmGL(fS, gS); + schurToSpRE(plS, S) + ) + +-- Plethysm for the orthogonal character ring (analogous to Sp). +plethysmO = method() +plethysmO(RingElement,RingElement) := (f,g) -> ( + S := ring g; + Rf := ring f; + nFin := plethysmHelperSize(f,g); + H := plethysmHelperOf(S, nFin); + gS := oToSchurRE(g, H); + fS := if instance(Rf, SchurRing) and Rf.?GroupActing then ( + if Rf.GroupActing == "Sp" then + spToSchurRE(f, plethysmHelperOf(Rf, nFin)) + else if Rf.GroupActing == "O" then + oToSchurRE(f, plethysmHelperOf(Rf, nFin)) + else f + ) else f; + plS := plethysmGL(fS, gS); + schurToORE(plS, S) + ) + -- plethysm of symmetric functions plethysm = method() @@ -520,8 +1222,9 @@ auxplet(RingElement,RingElement) := (f,g) -> ( Rg := ring g; pl := local pl; - if Rg.GroupActing == "GL" then pl = plethysmGL else - if Rg.GroupActing == "Sn" then pl = plethysmSn; + if Rg.?plethysmFcn then pl = Rg.plethysmFcn + else if Rg.GroupActing == "GL" then pl = plethysmGL + else if Rg.GroupActing == "Sn" then pl = plethysmSn; sLg := schurLevel Rg; if sLg == 1 then return pl(f,g) else @@ -558,7 +1261,11 @@ plethysm(RingElement,RingElement) := (f,g) -> -- plethysm of s_lambda and g plethysm(BasicList,RingElement) := (lambda,g) -> ( d := sum toList lambda; - Rf := symmetricRing(QQ,d); + -- Reuse the cached workSymRing instead of constructing a fresh + -- symmetricRing(QQ,d) per call. jacobiTrudi only needs dim >= d + -- (a larger dim is harmless; extra e/h/p variables simply never + -- appear in the monomials it produces). See ensureWorkSymRing. + Rf := ensureWorkSymRing max(d, 1); f := jacobiTrudi(lambda,Rf); plethysm(f,g) ) @@ -590,7 +1297,8 @@ plethysm(RingElement,ClassFunction) := (f,cF) -> -- (inner) plethysm of s_lambda with the class function cF (the character of a certain S_n-representation) plethysm(BasicList,ClassFunction) := (lambda,cF) -> ( d := sum toList lambda; - Rf := symmetricRing(QQ,d); + -- Reuse the cached workSymRing; see plethysm(BasicList,RingElement). + Rf := ensureWorkSymRing max(d, 1); f := jacobiTrudi(lambda,Rf); plethysm(f,cF)) @@ -612,7 +1320,30 @@ degSchurPol(RingElement) := ps -> ( ----Transition between various types of symmetric functions---- --------------------------------------------------------------- --- toSymm +----------------------------------------------------------------- +-- Classical basis conversions +----------------------------------------------------------------- +-- At schurLevel 1, symmetricRing(QQ, n) is a free polynomial ring +-- on 3n generators, partitioned as: +-- e_1, ..., e_n (elementary) indices 0 .. n-1 +-- p_1, ..., p_n (power-sum) indices n .. 2n-1 +-- h_1, ..., h_n (complete) indices 2n .. 3n-1 +-- The ring carries Groebner bases grbE, grbH, grbP (installed by +-- symmetricRing(Ring,ZZ)) which kill all but one of the three +-- families of generators. The stored maps R.mapSymToE, +-- R.mapSymToH, R.mapSymToP reduce an arbitrary symmetric polynomial +-- against the relevant GB, rewriting it in one family of generators. +-- +-- Conversion strategy: +-- toSymm : SchurRing -> symmetricRing (via jacobiTrudi) +-- toE / toH / toP (via GB reduction, +-- recursing on schurLevel +-- so higher-level +-- coefficients are +-- rewritten first). +----------------------------------------------------------------- + +-- toSymm toSymm = method() -- if ps is an element of a schurRing R @@ -623,9 +1354,49 @@ toSymm(RingElement) := (ps) -> S := ring ps; if instance(S, SchurRing) then ( + -- Special case: RatGL rings have no associated symmetricRing (they + -- model rational, not polynomial, representations). An element is + -- a polynomial character iff every bipartition has empty beta, in + -- which case we can map it to the symmetricRing via jacobiTrudi on + -- alpha. Otherwise error cleanly. + if S.?GroupActing and S.GroupActing == "RatGL" then ( + -- Walk the RatGL terms: each is indexed by a bipartition + -- (alpha, beta). #beta > 0 <=> non-polynomial (dual) part + -- is present => set hasNeg and bail out afterwards, since + -- there is no canonical map to a symmetricRing in that case. + -- For a stable RatGL ring numgens S === infinity, so we size + -- the target symmetricRing to the largest alpha that actually + -- appears. + polyTerms := new MutableList; + hasNeg := false; + iterateRatGLTerms(ps, (alpha, beta, scalar) -> ( + -- #beta > 0 <=> non-polynomial character -> hasNeg + if #beta > 0 then hasNeg = true + else polyTerms#(#polyTerms) = (alpha, scalar); + )); + if hasNeg then error("toSymm: RatGL element has nonzero negative " + | "(beta) components and is not a polynomial character; no " + | "canonical map to a symmetricRing exists. Use " + | "specialize(f, n) to evaluate at GL(n) instead."); + if #polyTerms == 0 then return 0; + -- maximum #alpha determines the minimum symmetric-ring dim. + maxLen := max apply(toList polyTerms, t -> #(t#0)); + nT := numgens S; + targetDim := if class nT === InfiniteNumber + then max(maxLen, 1) else nT; + Rsym := symmetricRing(coefficientRing (S.ratNegRing), targetDim); + return sum apply(toList polyTerms, (alpha, c) -> + c * jacobiTrudi(alpha, Rsym)); + ); R := symmetricRing S; tms := listForm ps; ---each term s_lambda in ps is transformed into an element of R using the jacobiTrudi routine + -- Each (p, a) in tms represents the Schur monomial a * s_p. + -- jacobiTrudi(p, R) realises s_p as a polynomial in R. + -- The 'try ... else error ...' catches the case where p has + -- more rows than R.dim (jacobiTrudi fails): the user needs a + -- symmetricRing of strictly larger dimension. + -- The coefficient a is lifted to coefficientRing S and fed + -- back through toSymm, handling higher-schurLevel coefficients. sum apply(tms,(p,a)->( (try b:=jacobiTrudi(p,R) then b else error"Need symmetric ring of higher dimension")* toSymm(lift(a,coefficientRing S)))) @@ -660,16 +1431,43 @@ mapSymToP (RingElement) := (f) -> ( if R.?mapSymToP then R.mapSymToP f else f ) +-- Guard used by toE/toH/toP/toSymm: refuse early (with a helpful +-- message) when a SchurRing input is rank-infinite, because the +-- associated symmetricRing cannot be constructed for stable rings. +-- The underlying engine error ("symmetric ring expects finite +-- schurRings") is internal and not actionable; this guard tells the +-- user what to do instead. +stableRingConversionGuard := (R, opname) -> ( + if class R === SchurRing then ( + n := numgens R; + if class n === InfiniteNumber then + error(opname | ": cannot convert an element of a stable " + | "(rank-infinite) Schur ring to an e/h/p-basis " + | "symmetric function. Specialize the element to a " + | "finite rank first (e.g. via `specialize(f, n)`), " + | "or create a finite-rank SchurRing to begin with."); + ); + ) + toE = method() -- writes a symmetric function (possibly in a ring -- with schurLevel larger than one) in terms of -- elementary symmetric polynomials toE (RingElement) := (f) -> ( R := ring f; - if class R === SchurRing then toE toSymm f - else + if class R === SchurRing then ( + stableRingConversionGuard(R, "toE"); + toE toSymm f + ) + else ( if not R.?schurLevel then f else + -- schurLevel > 1: split each term t into leadCoefficient (which + -- lives in the coefficient ring, one schurLevel down) and + -- leadMonomial (a pure level-1 symmetric monomial). Recurse + -- into toE on the coefficient (driving the recursion on + -- schurLevel), and apply mapSymToE to the monomial (a GB + -- reduction at level 1). Sum the pieces back up. if R.schurLevel>1 then terms f/(i->(toE leadCoefficient i*(mapSymToE leadMonomial i)))//sum else mapSymToE f ) @@ -681,10 +1479,16 @@ toP = method() -- power sums toP (RingElement) := (f) -> ( R := ring f; - if class R === SchurRing then toP toSymm f - else + if class R === SchurRing then ( + stableRingConversionGuard(R, "toP"); + toP toSymm f + ) + else ( if not R.?schurLevel then f else + -- Same pattern as toE: leadCoefficient descends one schurLevel + -- (recursive toP), leadMonomial is a level-1 symmetric monomial + -- reduced to the power-sum family via mapSymToP. if R.schurLevel>1 then terms f/(i->(toP leadCoefficient i*(mapSymToP leadMonomial i)))//sum else mapSymToP f ) @@ -696,2444 +1500,7795 @@ toH = method() -- complete symmetric polynomials toH (RingElement) := (f) -> ( R := ring f; - if class R === SchurRing then toH toSymm f - else + if class R === SchurRing then ( + stableRingConversionGuard(R, "toH"); + toH toSymm f + ) + else ( if not R.?schurLevel then f else + -- Same pattern as toE: leadCoefficient recurses one schurLevel + -- down via toH, leadMonomial is reduced at level 1 via + -- mapSymToH (GB reduction to the complete-homogeneous family). if R.schurLevel>1 then terms f/(i->(toH leadCoefficient i*(mapSymToH leadMonomial i)))//sum else mapSymToH f ) ) --- auxiliary functions to be used in --- the recTrans routine -leadTermFcn := local leadTermFcn; -retFcn := local retFcn; -mappingFcn := local mappingFcn; +--------------------------------------------------------------- +--------------Monomial basis (toM)----------------------------- +--------------------------------------------------------------- -toS = method() +-- Kostka number K_{lambda,mu}: number of SSYT of shape lambda, content mu. +-- +-- We compute this via the direct SSYT recursion, which is far faster +-- (and uses less memory) than expanding h_mu into the Schur basis and +-- reading off a coefficient. The recursion, following Stanley (EC2, +-- Prop. 7.10.4 and the horizontal-strip interpretation of h_mu): +-- +-- K_{lambda, mu} = sum_{nu} K_{nu, mu'} +-- +-- where mu' drops the last (smallest) part mu_r of mu, and nu ranges +-- over partitions obtained from lambda by removing a horizontal strip +-- of size mu_r. Equivalently, the last symbol "r" is placed in a +-- horizontal strip of size mu_r in the shape lambda, and the rest of +-- the tableau fills the smaller shape nu = lambda \ strip with content +-- mu'. Base case: K_{{}, {}} = 1. +-- +-- Complexity per call: O(|lambda|^{ell(mu)} * ell(mu)) in the worst +-- case, but memoization on (lambda, mu) makes repeat calls O(1) and +-- makes the full Kostka matrix of degree d an O(p(d)^2) computation. +-- +-- The memo table kostkaMemo is process-local and keyed on normalized +-- (stripped) partitions, so repeated calls with equivalent inputs hit +-- the cache. -toS(RingElement) := (f) -> ( - R := ring f; - if (schurLevel R == 0 or class R === SchurRing) then f else - ( - S := schurRing R; - local hf; - n := R.dim; - d := first degree f; - ngS := numgens S; ---mappingFcn v is used when v = h_i for some i; it returns the Schur polynomial s_i, in the correct Schur ring - mappingFcn = (v) -> (schurRing ring v)_{index v-2*(ring v).dim+1}; ---leadTermFcn takes as input a polynomial pl in the h-variables, ---and returns the variable h_i, with i maximal, such that h_i appears in the expression of pl - leadTermFcn = (pl) -> ( - R := ring pl; - spl := select(support pl,i->index i toS lift(pl,(coefficientRing ring pl)); - promote(recTrans(toH f),S) - ) +kostkaMemo := new MutableHashTable + +-- Enumerate sub-partitions nu of lambda obtained by removing a +-- horizontal strip of size s. A horizontal strip is a skew shape with +-- at most one box in each column, i.e. the consecutive row differences +-- satisfy lambda_i >= nu_i >= lambda_{i+1} (row i of nu fits between +-- row i and row i+1 of lambda). +-- +-- Returns a list of partitions nu (with trailing zeros stripped). +horizontalStripComplements = (lambda, s) -> ( + lam := toList lambda; + l := #lam; + if s == 0 then return {lam}; + if s < 0 or s > sum lam then return {}; + -- aux(i, remaining, prevLamBelow) = choose nu_0, nu_1, ..., nu_{i} + -- given that nu_i <= lam_i and nu_i >= lam_{i+1} (for horizontal + -- strip: row i of nu must lie between lam_i and lam_{i+1}). + aux := (idx, remaining) -> ( + if idx == l then ( + if remaining == 0 then return {{}} else return {}; + ); + lamI := lam#idx; + lowerI := if idx + 1 < l then lam#(idx+1) else 0; + -- nu_idx in [lowerI, lamI]; must also not remove more than + -- `remaining` boxes across this and all later rows. + -- amount removed from this row is lamI - nu_idx + -- so nu_idx ranges: max(lowerI, lamI - remaining) ... lamI + lo := max(lowerI, lamI - remaining); + flatten for v from lo to lamI list + for tail in aux(idx+1, remaining - (lamI - v)) list prepend(v, tail) + ); + -- Strip trailing zeros from each candidate. + apply(aux(0, s), stripTrailingZeros) ) -toS(Thing) := (f) -> f -undocumented(toS,Thing) +kostkaNumber = method() +kostkaNumber(BasicList,BasicList) := (lambda,mu) -> ( + lam := stripTrailingZeros toList lambda; + m := stripTrailingZeros toList mu; + if sum lam != sum m then return 0; + if sum lam == 0 then return 1; + -- Memoize on normalized (lam, m). + key := (lam, m); + if kostkaMemo#?key then return kostkaMemo#key; + -- Peel off the last (smallest) part of m. + mLast := m#(#m - 1); + mRest := drop(m, -1); + -- Sum over all sub-partitions nu of lam obtained by removing a + -- horizontal strip of size mLast. + total := sum for nu in horizontalStripComplements(lam, mLast) list + kostkaNumber(nu, mRest); + kostkaMemo#key = total; + total + ) -toS(Thing,Ring) := (f,T) -> try(lift(f,T)) else f -undocumented(toS,Thing,Ring) +-- toM: expand a symmetric function in the monomial basis. +-- Returns a RingElement in a monomial-basis SchurRing (Basis => "Monomial"). +-- With no target supplied, an associated monomial ring is cached on the +-- input ring (lazy construction) and used as the output ring. +toM = method() -toS(RingElement,SchurRing) := (f, T) -> -( +toM(RingElement) := (f) -> ( R := ring f; - if schurLevel R == 0 then - ( - U := T; - while schurLevel U > 0 do U = coefficientRing U; - toS(f,U) - ) - else - ( - fS := toS f; - dimT := numgens T; - (listForm fS)/(i-> if #i#0<=dimT then T_(i#0)*toS(i#1,coefficientRing T) else 0_T)//sum - ) + if class R =!= SchurRing then return toM(toS f); + n := numgens R; + if n === infinity then error "toM requires a SchurRing with finite number of generators"; + -- If R is itself a monomial-basis ring, f is already in monomial form. + if R.?Basis and R.Basis == "Monomial" then return f; + M := monomialBasisRingOf R; + toM(f, M) ) ---recTrans is a recursive routine that transforms an h-polynomial (in a symmetricRing of positive schurLevel) ---into an s-polynomial, by proceeding one level at a time -recTrans = method() -recTrans (RingElement) := (pl) -> -( ---lead = leading variable = h_i with i maximal - lead := leadTermFcn pl; - isSn := (ring pl).GroupActing == "Sn"; - if lead === null then retFcn pl else - ( ---monomials/coefficients with respect to the leading variable lead - (mon,coe) := coefficients(pl,Variables=>{lead}); - mon = flatten entries mon; - coe = flatten entries coe; - rez := 0; - cdeg := degree(lead,mon#0)+1; - for i from 0 to #mon-1 do - ( - fdeg := degree(lead,mon#i); - while (cdeg>fdeg+1) do - ( - cdeg = cdeg - 1; ---if the group acting at a given level is the symmetric group ---use internal multiplication of symmetric functions ---otherwise use usual multiplication - if isSn then rez = rez**mappingFcn(lead) else - rez = rez*mappingFcn(lead); +toM(RingElement,SchurRing) := (f, M) -> ( + if not (M.?Basis and M.Basis == "Monomial") then + error "expected second argument to be a SchurRing with Basis => \"Monomial\""; + R := ring f; + if class R =!= SchurRing then return toM(toS f, M); + dimM := numgens M; + if dimM === infinity then error "toM requires a target SchurRing with finite number of generators"; + -- Case 1: input is already in monomial basis -- identity map on partition labels + if R.?Basis and R.Basis == "Monomial" then ( + rawRes := raw(0_M); + for term in listForm f do ( + if #(term#0) <= dimM then ( + sc := raw promote(term#1, M); + ba := raw M_(term#0); + rawRes = rawRes + sc * ba; ); - if isSn then rez = rez**mappingFcn(lead)+recTrans(coe#i) - else rez = rez*mappingFcn(lead)+recTrans(coe#i); - cdeg = cdeg - 1; ); - while cdeg>0 do - ( - cdeg = cdeg - 1; ---if the group acting at a given level is the symmetric group ---use internal multiplication of symmetric functions ---otherwise use usual multiplication - if isSn then rez = rez**mappingFcn(lead) else - rez = rez*mappingFcn(lead); - ); - rez - ) + new M from rawRes + ) + -- Case 2: input is in Schur basis -- apply Kostka conversion + else schurToMonomialRE(f, M) ) -recTrans(Thing) := p -> p - --------- --------- ---given a recursive relation for a sequence a_n, given by a convolution of (a_n) with (L_n) ---convolve computes formulas for a_n in terms of L_n ---the main routine is coded in the engine ---the value of conv is used to indicate one of several types of convolution -convolve = method() -convolve(List,ZZ) := (L,conv) -> ( - A := ring L_0; - toList drop(apply(rawConvolve(L/raw//toSequence, conv), f -> new A from f),1) +-- Compute Kostka matrix and its inverse for degree d in n variables +-- Returns (K, Kinv) where K#lambda#{mu} = K_{lambda,mu}, Kinv#mu#{lambda} = (K^{-1})_{mu,lambda} +computeKostkaMatrices = (d, n) -> ( + parts := select(partitions d, p -> #(toList p) <= n); + partsL := apply(parts, p -> toList p); + k := #partsL; + if k == 0 then return (new HashTable, new HashTable); + -- Build Kostka matrix using toS(h_mu); reuse the cached workSymRing. + auxR := ensureWorkSymRing max(n, 1); + KMat := mutableMatrix(QQ, k, k); + for j from 0 to k-1 do ( + mu := partsL#j; + hProd := product for i from 0 to #mu - 1 list auxR.hVariable(mu#i); + sExp := listForm toS hProd; + lookup := new HashTable from apply(sExp, t -> t#0 => t#1); + for i from 0 to k-1 do ( + lam := partsL#i; + if lookup#?lam then KMat_(i,j) = promote(lookup#lam, QQ); + ); + ); + -- Invert + KMatFinal := matrix KMat; + KInvMat := KMatFinal^(-1); + -- Build hash tables + K := new MutableHashTable; + Kinv := new MutableHashTable; + for i from 0 to k-1 do ( + lam := partsL#i; + row := new MutableHashTable; + for j from 0 to k-1 do ( + val := KMat_(i,j); + if val != 0 then row#(partsL#j) = lift(val, ZZ); + ); + K#lam = new HashTable from row; + ); + for j from 0 to k-1 do ( + mu := partsL#j; + row := new MutableHashTable; + for i from 0 to k-1 do ( + val := KInvMat_(j,i); + if val != 0 then row#(partsL#i) = lift(val, ZZ); + ); + Kinv#mu = new HashTable from row; + ); + (new HashTable from K, new HashTable from Kinv) ) ---a_n = p_n ---L_n = e_n -PtoE = (m,R) -> ( - n := R.dim; - A := R.symRingForE; - p2e := prepend(1_A, for i from 1 to n list ((-1)^(i+1) * A_(2*n+i-1))); - if m>n then p2e = join(p2e,toList((m-n):0_A)); - R.PtoETable = if n == 0 then {1_A} else {1_A} | (- convolve(p2e,2)); +-- Convert element from monomial-basis interpretation to Schur-basis interpretation +-- f in SchurRing S where S_(mu) represents m_mu; returns element where S_(lambda) represents s_lambda +-- Uses raw engine operations to avoid triggering overloaded S*S multiplication +monomialToSchurRE = (f, S) -> ( + if f == 0 then return 0_S; + n := numgens S; + lf := listForm f; + rawRes := raw(0_S); + for term in lf do ( + mu := term#0; + c := term#1; + d := sum mu; + if d == 0 then rawRes = rawRes + raw promote(c, S) + else ( + if not S.kostkaCache#?d then S.kostkaCache#d = computeKostkaMatrices(d, n); + Kinv := (S.kostkaCache#d)#1; + muL := toList mu; + if Kinv#?muL then + for pair in pairs Kinv#muL do ( + sc := raw promote(c * (pair#1), S); + ba := raw S_(pair#0); + rawRes = rawRes + sc * ba; + ); + ); + ); + new S from rawRes ) ---a_n = h_n ---L_n = e_n -HtoE = (m,R) -> ( - n := R.dim; - A := R.symRingForE; - h2e := prepend(1_A, for i from 1 to n list (-1)^(i+1)*A_(2*n+i-1)); - R.HtoETable = if n == 0 then {1_A} else {1_A} | convolve(h2e,0); +-- Convert element from Schur-basis interpretation to monomial-basis interpretation +-- f in SchurRing S where S_(lambda) represents s_lambda; returns element where S_(mu) represents m_mu +-- Uses raw engine operations to avoid triggering overloaded S*S multiplication +schurToMonomialRE = (f, S) -> ( + if f == 0 then return 0_S; + n := numgens S; + lf := listForm f; + rawRes := raw(0_S); + for term in lf do ( + lam := term#0; + c := term#1; + d := sum lam; + if d == 0 then rawRes = rawRes + raw promote(c, S) + else ( + if not S.kostkaCache#?d then S.kostkaCache#d = computeKostkaMatrices(d, n); + K := (S.kostkaCache#d)#0; + lamL := toList lam; + if K#?lamL then + for pair in pairs K#lamL do ( + sc := raw promote(c * (pair#1), S); + ba := raw S_(pair#0); + rawRes = rawRes + sc * ba; + ); + ); + ); + new S from rawRes ) ---a_n = h_n ---L_n = p_n -HtoP = (m,R) -> ( - n := R.dim; - A := R.symRingForP; - h2p := prepend(1_A, for i from 1 to n list A_(2*n+i-1)); - R.HtoPTable = if n == 0 then {1_A} else {1_A} | convolve(h2p,1); +-- Get or create the default monomial-basis SchurRing associated to S. +-- Used by toM to produce a RingElement output when no target ring is supplied. +monomialBasisRingOf = (S) -> ( + if S.?monomialBasisRing then S.monomialBasisRing + else ( + n := numgens S; + if n === infinity then error "monomialBasisRingOf requires a SchurRing with finite number of generators"; + -- use a package-private symbol named "m" to avoid clobbering user globals + mSym := getSymbol "m"; + M := schurRing(coefficientRing S, mSym, n, Basis => "Monomial"); + S.monomialBasisRing = M; + M + ) ) ---a_n = e_n ---L_n = p_n -EtoP = (m,R) -> ( - n := R.dim; - A := R.symRingForP; - e2p := prepend(1_A, for i from 1 to n list (-1)^(i+1)*A_(2*n+i-1)); - R.EtoPTable = if n == 0 then {1_A} else {1_A} | convolve(e2p,1); - ) +--------------------------------------------------------------- +--------------End Monomial basis------------------------------- +--------------------------------------------------------------- ---a_n = p_n ---L_n = h_n -PtoH = (m,R) -> ( - n := R.dim; - A := R; - p2h := prepend(1_A, for i from 1 to n list (- A_(2*n+i-1))); - R.PtoHTable = if n == 0 then {1_A} else {1_A} | convolve(p2h,2); +--------------------------------------------------------------- +--------------Sp / O stable character rings-------------------- +--------------------------------------------------------------- + +-- Koike branching formulas for the stable (universal) character ring: +-- sp_lambda = sum_{delta} (-1)^{|delta|/2} s_{lambda/delta} +-- where delta runs over partitions contained in lambda with all +-- columns of even length (equivalently, the conjugate delta' has +-- all even parts -- i.e. delta's parts come in equal pairs). +-- o_lambda = sum_{delta} (-1)^{|delta|/2} s_{lambda/delta} +-- where delta runs over partitions contained in lambda with all +-- parts even. +-- The inverse formulas are obtained by dropping the sign: +-- s_lambda = sum_{delta even cols} sp_{lambda/delta} (skew Sp character) +-- s_lambda = sum_{delta even rows} o_{lambda/delta} (skew O character) +-- In both directions, skew characters expand as sum_nu c^{lambda}_{delta,nu} X_nu +-- where c is the usual Littlewood-Richardson coefficient and X is the +-- relevant basis; this matches the s-expansion of s_{lambda/delta}. + +-- Skew Schur expansion: returns s_{lambda/mu} as a list of (partition, coeff) +-- pairs in the Schur basis. Uses the Jacobi-Trudi determinant. +-- +-- Performance notes. Two caches make this fast on repeated calls: +-- (1) A single symmetricRing is reused across calls (skewWorkRing), +-- grown on demand when a caller needs h_k with k exceeding the +-- current ring's dim. This avoids the ~40-90ms symmetricRing +-- construction overhead that would otherwise dominate each call. +-- (2) Results are memoized on the normalized (lambda, mu) key in +-- skewMemo so repeated skew queries (common in Koike product and +-- Sp/O modification rules) cost O(1) after the first. +-- Both caches are process-global and safe because: +-- - skewWorkRing is used only inside this function; growing it never +-- breaks a previous computation (earlier results have already been +-- lifted out of the ring as partition/coefficient pairs). +-- - skewMemo stores only immutable (partition, integer) pairs. +-- Memo table for skewSchurExpansion results. The cached workSymRing +-- used below is declared earlier in the file (near jacobiTrudi) so that +-- plethysm can share it. +skewMemo := new MutableHashTable; + +skewSchurExpansion = method() +skewSchurExpansion(BasicList, BasicList) := (lambda, mu) -> ( + -- Strip trailing zeros (normalize so cache keys collide) + lam := stripTrailingZeros toList lambda; + m := stripTrailingZeros toList mu; + if sum m > sum lam then return {}; + l := #lam; + if l == 0 then return {({}, 1)}; + mPad := m | toList(l - #m : 0); + for i from 0 to l-1 do if lam#i < mPad#i then return {}; + if sum lam == sum mPad then return {({}, 1)}; + -- Memo lookup on normalized key (stripped lam, stripped m) + key := (lam, m); + if skewMemo#?key then return skewMemo#key; + -- Jacobi-Trudi: det(h_{lam_i - mPad_j - i + j}) for 1 <= i,j <= l + nmax := lam#0 + l; + R := ensureSkewWorkRing nmax; + M := mutableMatrix(R, l, l); + for i from 0 to l-1 do for j from 0 to l-1 do ( + k := lam#i - mPad#j - i + j; + if k < 0 then M_(i,j) = 0_R + else if k == 0 then M_(i,j) = 1_R + else M_(i,j) = R.hVariable(k); + ); + dd := det(matrix M, Strategy => Cofactor); + sExp := toS dd; + -- listForm of a SchurRingElement is list of (partition, coeff) pairs + skewMemo#key = apply(listForm sExp, t -> (t#0, lift(t#1, ZZ))) ) ---a_n = e_n ---L_n = h_n -EtoH = (m,R) -> ( - n := R.dim; - A := R; - e2h := prepend(1_A, for i from 1 to n list (-1)^(i+1)*A_(2*n+i-1)); - R.EtoHTable = if n == 0 then {1_A} else {1_A} | convolve(e2h,0); +-- Enumerate partitions mu, contained in lambda componentwise, with all parts even. +-- Each mu is returned as a list of positive parts (trailing zeros stripped). +evenRowsSubpartitionsOf = method() +evenRowsSubpartitionsOf(BasicList) := (lambda) -> ( + lam := stripTrailingZeros toList lambda; + aux := (idx, prevMax) -> ( + if idx >= #lam then return {{}}; + upper := min(lam#idx, prevMax); + flatten for v from 0 to upper list ( + if odd v then continue; + for t in aux(idx+1, v) list prepend(v, t) + ) + ); + -- Strip trailing zeros off each candidate + apply(aux(0, infinity), stripTrailingZeros) ) +-- Enumerate partitions mu, contained in lambda componentwise, with all columns +-- of even length (equivalently, conjugate mu' has all even parts -- parts of mu +-- come in equal pairs). +evenColsSubpartitionsOf = method() +evenColsSubpartitionsOf(BasicList) := (lambda) -> ( + lam := stripTrailingZeros toList lambda; + if #lam == 0 then return {{}}; + lamC := toList conjugate new Partition from lam; + evens := evenRowsSubpartitionsOf(lamC); + apply(evens, mu -> ( + if #mu == 0 then {} + else toList conjugate new Partition from mu + )) + ) --------------------------------------------------------------- ---------------End transition----------------------------------- ---------------------------------------------------------------- - ---------------------------------------------------------------- --------------Schur Resolutions--------------------------------- +-- Sam-Snowden-Weyman modification rules (border-strip form) --------------------------------------------------------------- - ---recsyz is a recursive method that takes as input an element el of a SchurRing of positive schurLevel ---and returns the sum of the terms having negative coefficients ---it is used in the routine schurRes to determine representations that are forced to be generators ---of syzygy modules in an equivariant resolution -recsyz = method() -recsyz (Thing) := (el) -> min(el,0) -recsyz (RingElement) := (el) -> -( - T := ring el; - listForm el/((u,v)->T_u*recsyz(v))//sum +-- +-- References: +-- [SSW] Sam-Snowden-Weyman, "Homology of Littlewood complexes" +-- Selecta Math. 19 (2013), 655-698; arXiv:1209.3509 +-- Sec. 3.4 (symplectic), Sec. 4.4 (orthogonal). +-- [Kin] King, "Modification rules ... for Bn, Cn, Dn", J. Algebra 107 (1987). +-- +-- Given a (possibly invalid) partition lambda, these rules return a pair +-- (tau, sign) +-- such that, formally, +-- {Sp|O}_lambda == sign * {Sp|O}_tau +-- in the corresponding character ring at rank n, OR null if lambda maps to 0. +-- tau is an admissible partition (length <= n for type C; additional first- +-- two-columns condition for types B, D). +-- +-- The border-strip description (SSW 3.4 / 4.4) finds a border strip R of a +-- prescribed size starting at the first box of the final row of the current +-- partition, and removes it. By Remark 3.4 in [SSW], such a border strip R +-- corresponds exactly to a cell b = (k, 1) in the first column whose hook +-- length equals the prescribed size L, i.e. lambda_k + r - k = L. Removing +-- R then equals removing the hook of (k, 1), and the result is the +-- partition +-- (lambda_1, ..., lambda_{k-1}, lambda_{k+1}-1, ..., lambda_r-1). +-- The number of columns the strip occupies is c(R) = lambda_k. +-- +-- findBorderStripRow(lam, L): +-- Given partition lam (with r = #lam rows) and target strip length L, +-- find the unique k in {1,...,r} with lam_k + r - k = L, or null if none. +findBorderStripRow = (lam, L) -> ( + r := #lam; + k := -1; + for kk from 1 to r do ( + if lam#(kk-1) + r - kk == L then (k = kk; break); + ); + if k == -1 then null else k ) -schurResolution = method(Options => {DegreeLimit => 0, SyzygyLimit => 0}) -schurResolution(RingElement,List) := opts -> (rep,M) -> -( - d := opts.DegreeLimit; - if d == 0 then d = #M-1; - c := opts.SyzygyLimit; +-- removeBorderStripAtFirstColumn(lam, k): +-- Remove the hook of cell (k, 1) from partition lam. +-- Returns (newLam, cR) where cR = # columns occupied by the strip. +removeBorderStripAtFirstColumn = (lam, k) -> ( + r := #lam; + cR := lam#(k-1); -- c(R) = lam_k (# columns occupied by the strip) + newLam := stripTrailingZeros join( + take(lam, k-1), + for i from k+1 to r list lam#(i-1) - 1 + ); + (newLam, cR) + ) - T := ring rep; - n := schurLevel T; ---plets is the list of symmetric powers of the representation rep, from 0 to d - plets := new MutableList; - plets#0 = 1_T; - for i from 1 to d do plets#i = symmetricPower(i,rep); +-- sigmaInvolution(lam, m): +-- The type B/D involution on partitions: replace lam_1^T (first column +-- length) by (m - lam_1^T) and keep lam_i^T for i >= 2, then transpose back. +-- Returns null if the result is not a valid partition (i.e. if +-- m - lam_1^T < lam_2^T). +sigmaInvolution = (lam, m) -> ( + if #lam == 0 then ( + if m == 0 then return {} + else return toList(m:1) -- empty^sigma = column of length m + ); + lamT := toList conjugate new Partition from lam; + a := lamT#0; + newA := m - a; + if #lamT >= 2 and newA < lamT#1 then return null; + if newA < 0 then return null; + if newA == 0 then ( + trunc := drop(lamT, 1); + if #trunc == 0 then return {}; + return toList conjugate new Partition from trunc; + ); + newLamT := prepend(newA, drop(lamT, 1)); + toList conjugate new Partition from newLamT + ) - schurRes(rep,M,new List from plets,DegreeLimit => d,SyzygyLimit => c) +-- sswTypeC(lambda, n): +-- SSW modification rule for Sp(2n). +-- Returns (tau, sign) or null (meaning zero). +sswTypeC = (lambda, n) -> ( + lam := stripTrailingZeros toList lambda; + if #lam <= n then return (lam, 1); + sign := 1; + current := lam; + while #current > n do ( + r := #current; + L := 2*(r - n - 1); + if L == 0 then return null; -- strip must be non-empty + k := findBorderStripRow(current, L); + if k === null then return null; + (newLam, cR) := removeBorderStripAtFirstColumn(current, k); + sign = sign * (if even cR then 1 else -1); + current = newLam; + ); + (current, sign) ) -schurResolution(RingElement,List,List) := opts -> (rep,M,plets) -> -( - d := opts.DegreeLimit; - if d == 0 then d = #M-1; - c := opts.SyzygyLimit; - - schurRes(rep,M,plets,DegreeLimit => d,SyzygyLimit => c) +-- sswTypeBD(lambda, m): +-- SSW modification rule for O(m) (type B if m = 2n+1, type D if m = 2n). +-- Returns (tau, sign) or null. Here tau must satisfy the O-admissibility +-- condition lam^T_1 + lam^T_2 <= m; the iteration stops whenever this +-- holds. Differences from type C (see [SSW 4.4]): +-- (D1) strip length L = 2 * ell(lambda) - m, +-- (D2) sign contribution uses c(R) - 1 instead of c(R), +-- (D3) if an odd total number of strips was removed, apply sigma to tau. +sswTypeBD = (lambda, m) -> ( + lam := stripTrailingZeros toList lambda; + -- Admissibility test: lam^T_1 + lam^T_2 <= m. + admissible := (p) -> ( + if #p == 0 then return true; + pT := toList conjugate new Partition from p; + a := if #pT >= 1 then pT#0 else 0; + b := if #pT >= 2 then pT#1 else 0; + a + b <= m + ); + if admissible lam then return (lam, 1); + sign := 1; + numStripsRemoved := 0; + current := lam; + while not admissible current do ( + r := #current; + L := 2 * r - m; + if L <= 0 then return null; + k := findBorderStripRow(current, L); + if k === null then return null; + (newLam, cR) := removeBorderStripAtFirstColumn(current, k); + -- Sign: (-1)^{c(R) - 1}. + sign = sign * (if odd cR then 1 else -1); + numStripsRemoved = numStripsRemoved + 1; + current = newLam; + ); + -- (D3) If odd number of strips removed, apply sigma. + if odd numStripsRemoved then ( + sigmaResult := sigmaInvolution(current, m); + if sigmaResult === null then return null; + current = sigmaResult; + ); + (current, sign) ) -schurRes = method(Options => options schurResolution) -schurRes(RingElement,List,List) := opts -> (rep,M,plets) -> -( - T := ring rep; - d := opts.DegreeLimit; - c := opts.SyzygyLimit; - - mods := new MutableList from (M | toList((d+1-#M):0)); - notdone := true; - k := 0; ---syzy is the list of the characters of the generators of the syzygy modules - syzy := new MutableList; - syzy#k = {}; - local mo; - local newsyz; - ---syzygy modules are constructed step by step ---the stopping condition is either reaching the limit c of syzygy modules that are computed ---or not finding any new syzygies at a given step - while notdone do - ( - for i from 0 to d do - ( - mo = 0_T; - for sy in syzy#k do - if sy#0 <= i then mo = mo + plets#(i-sy#0) * sy#1 - else break; ---mods is a sequence of representations, mods#i being the degree i of a module that needs to be ``covered'' ---by the differential in the equivariant complex ---mo is the degree i part of the new syzygy module ---it needs to ``cover'' mods#i, i.e. there has to exist a surjective map of representations from ---mo to mods#i - mo = mo - mods#i; ---if there are representations with negative coefficients in mo-mods#i, it means that mo doesn't cover mods#i ---the representations with negative signs must be ``covered'' by new syzygies - newsyz = recsyz(mo); - if newsyz != 0 then syzy#k = syzy#k | {(i,-newsyz)}; - mods#i = mo - newsyz; - ); - if c == 0 then notdone = not (syzy#k == {}) - else notdone = (k i != {}) +-- modificationRule(lambda, n, type): +-- Unified entry point. type is one of: +-- "C" (symplectic, Sp(2n)) +-- "B" (odd orthogonal, O(2n+1)) +-- "D" (even orthogonal, O(2n)) +-- Returns (tau, sign) or null. +modificationRule = (lambda, n, type) -> ( + if type == "C" then sswTypeC(lambda, n) + else if type == "B" then sswTypeBD(lambda, 2*n+1) + else if type == "D" then sswTypeBD(lambda, 2*n) + else error("modificationRule: unknown type " | toString type) ) - + --------------------------------------------------------------- --------------end Schur Resolutions----------------------------- +-- Stable sp_lambda and o_lambda expressed in the Schur basis. --------------------------------------------------------------- +-- +-- There is NO clean closed-form Koike-type formula +-- sp_lambda = sum_{delta in C} (-1)^{|delta|/2} s_{lambda/delta} +-- valid for all lambda: such a formula would force every Schur term s_mu +-- appearing in sp_lambda to have |lambda| - |mu| even, and that fails +-- (e.g. sp_{(2,2,2)} contains -s_{(2,1,1)} with |diff|=1 ... is actually +-- the opposite: in our conventions |lambda/mu| *is* always even; the +-- issue is that the naive even-col class over-counts). +-- +-- Instead we invert the *forward* (correct) Littlewood branching rule +-- s_lambda = sum_{mu, nu : nu has all even cols} c^{lambda}_{mu,nu} sp_mu +-- recursively: +-- sp_lambda = s_lambda - sum_{nu even-col, nu != {}} sum_{mu} +-- c^{lambda}_{mu,nu} sp_mu. +-- Since nu != {} forces |mu| < |lambda|, recursion terminates. +-- We memoize the resulting s-basis coefficient lists. + +-- Cache: partition lambda (list) -> list of (mu, integer) pairs representing +-- sp_lambda = sum_mu (coef) s_mu in the stable universal character ring. +stableSpInSchurCache := new MutableHashTable + +-- Return list of (mu, coef) with coef nonzero integer, such that +-- sp_lambda == sum_mu coef * s_mu. +stableSpInSchur = (lambda) -> ( + lam := stripTrailingZeros toList lambda; + if stableSpInSchurCache#?lam then return stableSpInSchurCache#lam; + acc := new MutableHashTable; + acc#lam = 1; + for nu in evenColsSubpartitionsOf(lam) do ( + if #nu == 0 then continue; + for pair in skewSchurExpansion(lam, nu) do ( + mu := pair#0; + lrCoef := pair#1; + for t in stableSpInSchur(mu) do ( + mu2 := t#0; + c := t#1; + prev := if acc#?mu2 then acc#mu2 else 0; + newVal := prev - lrCoef * c; + if newVal == 0 then ( + if acc#?mu2 then remove(acc, mu2); + ) + else acc#mu2 = newVal; + ); + ); + ); + ans := for k in keys acc list (k, acc#k); + stableSpInSchurCache#lam = ans; + ans + ) +-- Same, but for orthogonal characters (Littlewood uses nu with all even rows). +stableOInSchurCache := new MutableHashTable + +stableOInSchur = (lambda) -> ( + lam := stripTrailingZeros toList lambda; + if stableOInSchurCache#?lam then return stableOInSchurCache#lam; + acc := new MutableHashTable; + acc#lam = 1; + for nu in evenRowsSubpartitionsOf(lam) do ( + if #nu == 0 then continue; + for pair in skewSchurExpansion(lam, nu) do ( + mu := pair#0; + lrCoef := pair#1; + for t in stableOInSchur(mu) do ( + mu2 := t#0; + c := t#1; + prev := if acc#?mu2 then acc#mu2 else 0; + newVal := prev - lrCoef * c; + if newVal == 0 then ( + if acc#?mu2 then remove(acc, mu2); + ) + else acc#mu2 = newVal; + ); + ); + ); + ans := for k in keys acc list (k, acc#k); + stableOInSchurCache#lam = ans; + ans + ) ---------------------------------------------------------------- ---------------Characters of Symmetric Group-------------------- --------------------------------------------------------------- ---given a partition lambda as a nonincreasing sequence of positive integers ---seqToMults returns the representation of this partition as a sequence ---of multiplicities: rez#i is the number of parts of lambda of size (i+1) -seqToMults = method() -seqToMults(List) := (lambda) -> -( - lam := new Partition from lambda; - aux := toList(conjugate lam)|{0}; - rez := {}; - for j from 0 to #aux-2 do - ( - dif := aux#j-aux#(j+1); - rez = rez | {dif}; +----------------------------------------------------------------------------- +-- Sp/O <-> Schur basis conversions: "RE" (Raw Engine) family +----------------------------------------------------------------------------- +-- The four functions below (spToSchurRE, schurToSpRE, oToSchurRE, +-- schurToORE) are the low-level engines used to reinterpret a polynomial +-- between the Sp-/O-basis view of a SchurRing and the Schur-basis view of +-- another (possibly the same) SchurRing T. +-- +-- "RE" suffix = Raw Engine. These routines deliberately use raw(f)*raw(g) +-- on the engine side rather than the overloaded S*S product: for Sp/O +-- rings the high-level product is itself defined in terms of these +-- conversions, so calling it here would recurse back into this file. +-- Working at the raw level short-circuits that. +-- +-- Forward conversions (*ToSchurRE): +-- spToSchurRE / oToSchurRE expand each basis element sp_lambda (resp. +-- o_lambda) in the stable Schur basis by looking up the precomputed +-- tables stableSpInSchur / stableOInSchur. Those tables encode the +-- Koike universal-character branching formulas for Sp/O -> GL. When +-- T has finite rank we simply drop Schur summands s_mu with +-- #mu > rank(T). +-- +-- Reverse conversions (schurTo*RE): +-- These invert the forward map via unitriangular peeling (see the loop +-- comments inside each function). In the finite-rank case we apply +-- the Sam-Snowden-Weyman modification rules (sswTypeC for type C, +-- sswTypeBD for types B/D), and for B/D we further apply +-- sigmaInvolution to keep the output inside the length <= n window +-- the engine can represent. +----------------------------------------------------------------------------- + +-- Convert an Sp-basis element f in S to its Schur-basis interpretation in T. +-- T may equal S (in-place reinterpretation) or be a different SchurRing. +-- Uses raw engine arithmetic to avoid triggering overloaded S*S multiplication. +spToSchurRE = (f, T) -> ( + if f == 0 then return 0_T; + dimT := numgens T; + rawRes := raw(0_T); + -- For each term c * sp_lambda of f, expand sp_lambda in the stable + -- Schur basis via stableSpInSchur (Koike branching) and accumulate + -- into rawRes; truncate to length <= dimT when T has finite rank. + for term in listForm f do ( + lam := term#0; + c := term#1; + for pair in stableSpInSchur(lam) do ( + mu := pair#0; + coef := pair#1; + if dimT === infinity or #mu <= dimT then ( + sc := raw promote(c * coef, T); + ba := raw T_(mu); + rawRes = rawRes + sc * ba; + ); + ); ); - rez + new T from rawRes ) ---given a partition lambda represented in as a sequence of multiplicities mults ---where mults#i is the number of parts of lambda of size (i+1) ---multsToSeq represents lambda as a nonincreasing sequence of positive integers -multsToSeq = method() -multsToSeq(List) := (mults) -> -( - n := #mults; - par := {}; - for i from 0 to n-1 do - par = par | splice{mults#i:(i+1)}; - reverse par +-- Convert a Schur-basis element f to its Sp-basis interpretation in T. +-- +-- Unified stable/finite algorithm (unitriangular peeling): +-- +-- Stable sp_lambda = s_lambda + (lower terms), so we can compute the +-- sp-basis expansion iteratively: peel off an s_lambda term, record it +-- as the sp_lambda coefficient, and subtract (stable) sp_lambda's full +-- s-basis expansion from the running "working" polynomial. Repeat until +-- empty. Termination follows from unitriangularity of sp in s. +-- +-- For stable T (numgens infinity), each peeled sp_lambda is already a +-- valid basis element. +-- +-- For finite T (Sp(2n)), the peeled sp_lambda is a *stable* sp label that +-- maps to the finite ring via the Sam-Snowden-Weyman type-C modification +-- rule: sp_lambda (stable) = sign * sp_tau (finite) or 0. +schurToSpRE = (f, T) -> ( + if f == 0 then return 0_T; + dimT := numgens T; + -- working: mutable { mu -> coefficient } holding the residual Schur + -- expansion still to be peeled. Seeded with the terms of f. + working := new MutableHashTable; + for term in listForm f do working#(term#0) = term#1; + rawRes := raw(0_T); + -- Peeling loop: + -- (1) pick any lambda with nonzero coefficient c in `working`; + -- (2) by unitriangularity, lambda must be a leading sp_lambda -- + -- i.e. sp_lambda = s_lambda + (strictly lower Schur terms); + -- (3) record c * sp_lambda in rawRes (finite rank: apply the + -- type-C modification rule sswTypeC); + -- (4) subtract c * (sp_lambda - s_lambda), i.e. the lower-order + -- s-basis terms, from `working`, and continue. + while #working > 0 do ( + lam := first keys working; + c := working#lam; + remove(working, lam); + if c == 0 then continue; + -- Step (3): contribute c * sp_lambda to the output. + if dimT === infinity then ( + rawRes = rawRes + raw promote(c, T) * raw T_lam; + ) + else ( + -- Finite rank: sswTypeC returns null (term vanishes under + -- the type-C modification rule) or a pair (tau, sign) + -- giving the admissible partition tau and its overall sign. + r := sswTypeC(lam, dimT); + if r =!= null then ( + tau := r#0; + sgnMod := r#1; + rawRes = rawRes + raw promote(c * sgnMod, T) * raw T_tau; + ); + ); + -- Step (4): subtract c * sp_lam in the s-basis (minus the s_lam + -- term itself, which we already consumed by removing lam from + -- working). + for pair in stableSpInSchur(lam) do ( + mu := pair#0; + coef := pair#1; + if mu === lam then continue; + prev := if working#?mu then working#mu else 0; + newVal := prev - c * coef; + if newVal == 0 then ( + if working#?mu then remove(working, mu); + ) + else working#mu = newVal; + ); + ); + new T from rawRes ) ---the size of the centralizer of a permutation of cycle type lambda -centralizerSize = method() -centralizerSize(List) := lambda -> -( - product for i from 0 to #lambda-1 list((i+1)^(lambda#i)*(lambda#i)!) +-- Convert an O-basis element f to Schur-basis interpretation. +oToSchurRE = (f, T) -> ( + if f == 0 then return 0_T; + dimT := numgens T; + rawRes := raw(0_T); + -- Mirrors spToSchurRE, but expands o_lambda using the orthogonal + -- branching table stableOInSchur. + for term in listForm f do ( + lam := term#0; + c := term#1; + for pair in stableOInSchur(lam) do ( + mu := pair#0; + coef := pair#1; + if dimT === infinity or #mu <= dimT then ( + sc := raw promote(c * coef, T); + ba := raw T_(mu); + rawRes = rawRes + sc * ba; + ); + ); + ); + new T from rawRes ) -keysCF := method() -keysCF(ClassFunction) := (cF) -> keys cF - -degree(ClassFunction) := ch -> -( - ke := keysCF ch; - if #ke == 0 then -1 else sum(first ke) +-- Convert a Schur-basis element f to O-basis interpretation in T. +-- +-- Same unified stable/finite algorithm as schurToSpRE (unitriangular +-- peeling), but with even-row Littlewood (GL->O uses nu with all even +-- rows) and with the type B/D SSW rule in the finite case. +-- T.OddOrEven selects: +-- "Odd" (default) -> O(2n+1) = type B_n, m = 2n+1 +-- "Even" -> O(2n) = type D_n, m = 2n +schurToORE = (f, T) -> ( + if f == 0 then return 0_T; + dimT := numgens T; + kind := if T.?OddOrEven then T.OddOrEven else "Odd"; + m := if dimT === infinity then null else + (if kind == "Odd" then 2*dimT+1 else 2*dimT); + -- working: mutable { mu -> coefficient } residual Schur expansion. + working := new MutableHashTable; + for term in listForm f do working#(term#0) = term#1; + rawRes := raw(0_T); + -- Peeling loop (mirrors schurToSpRE): + -- (1) pick lambda with nonzero coefficient c in `working`; + -- (2) unitriangularity: o_lambda = s_lambda + (lower terms); + -- (3) record c * o_lambda (finite case: apply sswTypeBD, then + -- sigmaInvolution if the admissible tau has length > n); + -- (4) subtract c * (o_lambda - s_lambda) from `working` and loop. + while #working > 0 do ( + lam := first keys working; + c := working#lam; + remove(working, lam); + if c == 0 then continue; + -- Step (3): contribute c * o_lambda to the output. + if dimT === infinity then ( + rawRes = rawRes + raw promote(c, T) * raw T_lam; + ) + else ( + -- Finite rank: sswTypeBD returns null (term vanishes) or a + -- pair (tau, sign) giving the admissible partition under + -- the type B/D modification rule. + r := sswTypeBD(lam, m); + if r =!= null then ( + tau := r#0; + sgnMod := r#1; + -- SSW admissibility (col_1(tau)+col_2(tau) <= m) permits + -- partitions with length > n. For the SchurRing engine, + -- which only holds length <= n, apply sigmaInvolution: + -- for SO(m), o_tau == o_{sigma(tau)} (det is trivial). + -- If sigma still doesn't reduce length <= n, the term + -- does not correspond to a ring-representable irrep + -- and is dropped. + if #tau > dimT then ( + sigmaTau := sigmaInvolution(tau, m); + if sigmaTau =!= null and #sigmaTau <= dimT then + rawRes = rawRes + raw promote(c * sgnMod, T) * raw T_sigmaTau; + ) + else rawRes = rawRes + raw promote(c * sgnMod, T) * raw T_tau; + ); + ); + -- Step (4): subtract c * o_lam in the s-basis (minus the s_lam + -- term itself, already consumed above). + for pair in stableOInSchur(lam) do ( + mu := pair#0; + coef := pair#1; + if mu === lam then continue; + prev := if working#?mu then working#mu else 0; + newVal := prev - c * coef; + if newVal == 0 then ( + if working#?mu then remove(working, mu); + ) + else working#mu = newVal; + ); + ); + new T from rawRes ) ---go from symmetric functions to class functions -classFunction = method() -classFunction(RingElement) := (f)-> -( - Rf := ring f; - - R := symmetricRing Rf; - pf := toP f; - n := R.dim; - - if (degree pf)#0 > n then error"Can't interpret ring element as a symmetric function"; - - (mon,coe) := apply(coefficients pf,i->flatten entries i); - ch := new MutableHashTable; - for j from 0 to #mon-1 do - ( - degs := (flatten exponents mon#j)_{(n)..(2*n-1)}; - par := multsToSeq(degs); - ch#par = lift(coe#j,coefficientRing R) * centralizerSize(degs); - ); - new ClassFunction from ch +-- Get or create the associated symplectic-basis SchurRing (stable by default). +symplecticBasisRingOf = (S) -> ( + if S.?symplecticBasisRing then S.symplecticBasisRing + else ( + n := numgens S; + spSym := getSymbol "sp"; + T := if n === infinity + then schurRing(coefficientRing S, spSym, GroupActing => "Sp") + else schurRing(coefficientRing S, spSym, n, GroupActing => "Sp"); + S.symplecticBasisRing = T; + T + ) ) -classFunction(BasicList) := (lambda)-> -( - lam := toList(lambda); - s := symbol s; - R := schurRing(QQ,s,sum lam); - classFunction(R_lam) +-- Get or create the associated orthogonal-basis SchurRing (stable by default). +orthogonalBasisRingOf = (S) -> ( + if S.?orthogonalBasisRing then S.orthogonalBasisRing + else ( + n := numgens S; + oSym := getSymbol "o"; + T := if n === infinity + then schurRing(coefficientRing S, oSym, GroupActing => "O") + else schurRing(coefficientRing S, oSym, n, GroupActing => "O"); + S.orthogonalBasisRing = T; + T + ) ) -ClassFunction + ClassFunction := (ch1,ch2)-> -( - clSum := new MutableHashTable; - l1 := sum((keysCF ch1)#0); - l2 := sum((keysCF ch2)#0); - if l1 != l2 then error("The symmetric functions/characters must have the same degree"); - for i in unique(keysCF(ch1)|keysCF(ch2)) do - ( - a := b := 0; - if ch1#?i then a = ch1#i; - if ch2#?i then b = ch2#i; - if (a+b != 0) then clSum#i = a + b; - ); - new ClassFunction from clSum +-- Get or create the associated plain Schur-basis ring (GroupActing "GL", +-- Basis "Schur") of the same size as S. Used by toS on Sp/O/Monomial inputs. +schurBasisRingOf = (S) -> ( + if S.?schurBasisRing then S.schurBasisRing + else ( + n := numgens S; + sSym := getSymbol "s"; + T := if n === infinity + then schurRing(coefficientRing S, sSym) + else schurRing(coefficientRing S, sSym, n); + S.schurBasisRing = T; + T + ) ) -RingElement * ClassFunction := Number * ClassFunction := (n,ch) -> -( - clProd := new MutableHashTable; - for i in keysCF ch do clProd#i = n*ch#i; - new ClassFunction from clProd +-- Get or create a STABLE (infinity-many-generator) plain Schur-basis helper +-- ring for use by Sp/O multiplication. Finite-n Sp/O multiplication must +-- compute its GL product in an unrestricted Schur ring so partitions with +-- more than numgens S rows (which would be truncated by the engine) are +-- preserved until the modification rule in schurToSpRE/schurToORE can +-- collapse them back into valid Sp/O-basis elements. +stableSchurHelperOf = (S) -> ( + if S.?stableSchurHelper then S.stableSchurHelper + else ( + sSym := getSymbol "s"; + T := schurRing(coefficientRing S, sSym); + S.stableSchurHelper = T; + T + ) ) -ClassFunction * RingElement := ClassFunction * Number := (ch,n) -> n*ch; +--------------------------------------------------------------- +--------------End Sp / O stable character rings---------------- +--------------------------------------------------------------- +-- Module-scope closures read by recTrans. They are set inside +-- toS(RingElement) (symmetricRing branch) immediately before the call to +-- recTrans, and describe ring-specific logic that recTrans needs at every +-- level of the recursion: +-- leadTermFcn(pl) -- pick the leading h-variable h_i of pl, or null +-- when pl has no h-variables left (it is a scalar in +-- the coefficient ring). +-- retFcn(pl) -- base case: lift pl into the coefficient ring and +-- continue with toS there (descends one schurLevel). +-- mappingFcn(v) -- given v = h_i, return the Schur generator s_i of +-- the corresponding SchurRing. +-- These are kept at module scope (rather than passed as arguments) because +-- recTrans is mutually recursive through toS and needs to see the same +-- closures at every level of the h-polynomial. +leadTermFcn := local leadTermFcn; +retFcn := local retFcn; +mappingFcn := local mappingFcn; -ClassFunction - ClassFunction := (ch1,ch2)-> ch1 + (-1)*ch2; +-------------------------------------------------------------------------- +-- toS(f): rewrite f in the Schur basis. +-- +-- Dispatch tree: +-- 1. schurLevel(ring f) == 0 ----> return f unchanged (base ring). +-- 2. ring f is a SchurRing: +-- a. variant bases route through dedicated converters: +-- Basis == "Monomial" -> monomialToSchurRE +-- GroupActing == "Sp" -> spToSchurRE +-- GroupActing == "O" -> oToSchurRE +-- GroupActing == "SL" -> relabel partitions (sl_lambda +-- lifts to s_lambda with +-- lambda_n = 0); +-- b. plain GL/Sn/RatGL rings are already Schur-indexed, so toS is +-- a no-op (use toGL/toSn/convert/specialize to move rings). +-- 3. ring f is a symmetricRing with schurLevel > 0: +-- convert f to the complete-homogeneous (h) basis via toH, then +-- apply recTrans to rewrite the result in the Schur basis. +-- +-- The three closures above are installed in case 3 and consumed by +-- recTrans. The cryptic mappingFcn index formula +-- (schurRing ring v)_{index v - 2*(ring v).dim + 1} +-- decodes as follows: in a symmetricRing of dimension n the variable +-- layout is e_1..e_n, p_1..p_n, h_1..h_n, so the h-block starts at +-- generator-index 2n and h_i has index 2n + i - 1. Subtracting 2n - 1 +-- recovers i, which then indexes the Schur generator s_i on the +-- single-row partition {i}. +-------------------------------------------------------------------------- +toS = method() -ClassFunction == ClassFunction := (ch1,ch2) -> -( - equ := true; - for i in unique(keysCF ch1 | keysCF ch2) do - if not ((not ch1#?i and not ch2#?i) or (ch1#?i and ch2#?i and ch1#i == ch2#i)) then - ( - equ = false; - break; +toS(RingElement) := (f) -> ( + R := ring f; + if schurLevel R == 0 then return f; + if class R === SchurRing then ( + -- Variant-basis SchurRings: convert to an associated plain Schur ring. + local TSch; + if R.?Basis and R.Basis == "Monomial" then ( + TSch = schurBasisRingOf R; + return monomialToSchurRE(f, TSch); ); - equ + if R.?GroupActing and R.GroupActing == "Sp" then ( + TSch = schurBasisRingOf R; + return spToSchurRE(f, TSch); + ); + if R.?GroupActing and R.GroupActing == "O" then ( + TSch = schurBasisRingOf R; + return oToSchurRE(f, TSch); + ); + if R.?GroupActing and R.GroupActing == "SL" then ( + -- sl_lambda lifts to the GL irrep s_lambda (canonical choice + -- with lambda_n = 0). Just relabel partitions. + TSch = schurBasisRingOf R; + dimTSch := numgens TSch; + rawRes := raw(0_TSch); + for term in listForm f do ( + if dimTSch === infinity or #(term#0) <= dimTSch then ( + rawRes = rawRes + + raw promote(term#1, TSch) * raw (TSch_(term#0)); + ); + ); + return new TSch from rawRes; + ); + -- Fall-through: plain GL, Sn, and RatGL rings are all already + -- represented in a Schur-indexed basis, so toS is a no-op -- + -- there is no finer "plain Schur" basis to convert to. Users + -- wishing to move into a *different* ring (e.g. an Sn element + -- into a GL ring, or a RatGL element into a polynomial ring at + -- some specific rank) should use toGL(f, T), toSn(f, T), + -- convert(f, T), or specialize(f, n) explicitly. + return f; + ); + -- symmetricRing with schurLevel > 0: go via the h-basis and recTrans. + ( + S := schurRing R; + local hf; + n := R.dim; + d := first degree f; + ngS := numgens S; + -- mappingFcn: send h-variable v = h_i to the Schur generator s_i + -- in the correct SchurRing (see header for the index arithmetic). + mappingFcn = (v) -> (schurRing ring v)_{index v-2*(ring v).dim+1}; + -- leadTermFcn: return the h-variable of maximal index appearing in + -- pl, or null when pl has no h-variable left (it is a scalar). + leadTermFcn = (pl) -> ( + R := ring pl; + spl := select(support pl,i->index i toS lift(pl,(coefficientRing ring pl)); + promote(recTrans(toH f),S) + ) ) ---go from class functions to symmetric functions -symmetricFunction = method() -symmetricFunction(ClassFunction,Ring) := (ch,S)-> -( - R := symmetricRing S; - rez := 0_R; - n := R.dim; - for lam in keysCF ch do - rez = rez + ch#lam * (product for i from 0 to #lam-1 list R.pVariable(lam#i)) / centralizerSize(seqToMults lam); - if instance(S, SchurRing) then toS rez else rez - ) +toS(Thing) := (f) -> f +undocumented(toS,Thing) -scalarProduct = method() -scalarProduct(ClassFunction,ClassFunction) := (ch1,ch2)-> +toS(Thing,Ring) := (f,T) -> try(lift(f,T)) else f +undocumented(toS,Thing,Ring) + +toS(RingElement,SchurRing) := (f, T) -> ( - scProd := 0; - chProd := internalProduct(ch1,ch2); - for i in keysCF(chProd) do - scProd = scProd + chProd#i / centralizerSize(seqToMults i); - scProd + R := ring f; + if schurLevel R == 0 then + ( + U := T; + while schurLevel U > 0 do U = coefficientRing U; + toS(f,U) + ) + else + ( + fS := toS f; + dimT := numgens T; + (listForm fS)/(i-> if dimT === infinity or #i#0<=dimT then T_(i#0)*toS(i#1,coefficientRing T) else 0_T)//sum + ) ) -scalarProduct(RingElement,RingElement) := (f1,f2)-> -( - ch1 := classFunction f1; - ch2 := classFunction f2; - scalarProduct(ch1,ch2) +----------------------------------------------------------------------- +-- User-facing "change of basis" API for SchurRing variants. +-- +-- Five basis-specific conversion methods plus a universal dispatcher: +-- +-- toSp : expand in symplectic (Sp) characters +-- toO : expand in orthogonal (O) characters +-- toGL : expand in general linear(GL) characters (= plain Schur) +-- toSn : relabel as symmetric-group (Sn) characters via Frobenius +-- convert : universal entry point; routes to one of the above by +-- inspecting the target ring's GroupActing tag +-- +-- Each toX comes in two forms: +-- toX(f) -- no explicit target; build/fetch a default output ring +-- toX(f,T) -- target an explicit SchurRing T (must have the +-- appropriate GroupActing tag) +-- +-- The common dispatch pattern inside each toX(f,T): +-- 1. If f already lives in an X-basis ring, just copy partition +-- labels into T (respecting T's rank) and return. +-- 2. If f lives in a different variant basis, route through plain +-- Schur (toS) first, then re-enter toX. +-- 3. Otherwise f is in a plain Schur ring: invoke the reverse +-- conversion (schurToSpRE / schurToORE / ...). +-- Thing / Number base cases simply promote or return unchanged. +----------------------------------------------------------------------- + +-- ==== toSp ==== +-- toSp: expand a symmetric function in the basis of symplectic characters. +-- Returns a RingElement in a SchurRing with GroupActing => "Sp". With no +-- target supplied, an associated symplectic ring is cached on the input ring +-- (lazy construction) and used as the output ring. +toSp = method() + +toSp(RingElement) := (f) -> ( + R := ring f; + if class R =!= SchurRing then return toSp(toS f); + if R.?GroupActing and R.GroupActing == "Sp" then return f; + T := symplecticBasisRingOf R; + toSp(f, T) ) -internalProduct = method() -ClassFunction * ClassFunction := -internalProduct(ClassFunction,ClassFunction) := (ch1,ch2)-> -( - iProd := new MutableHashTable; - l1 := sum((keysCF ch1)#0); - l2 := sum((keysCF ch2)#0); - if l1 == 0 then return(ch1#{} * ch2); - if l2 == 0 then return(ch2#{} * ch1); - if l1 != l2 then error("The symmetric functions/characters must have the same degree"); - for i in keysCF(ch1) do - if ch2#?i then iProd#i = ch1#i * ch2#i; - new ClassFunction from iProd +toSp(RingElement, SchurRing) := (f, T) -> ( + if not (T.?GroupActing and T.GroupActing == "Sp") then + error "expected second argument to be a SchurRing with GroupActing => \"Sp\""; + R := ring f; + if class R =!= SchurRing then return toSp(toS f, T); + dimT := numgens T; + -- If f is already in the Sp basis, just re-promote partition labels. + if R.?GroupActing and R.GroupActing == "Sp" then ( + rawRes := raw(0_T); + for term in listForm f do ( + if dimT === infinity or #(term#0) <= dimT then ( + sc := raw promote(term#1, T); + ba := raw T_(term#0); + rawRes = rawRes + sc * ba; + ); + ); + return new T from rawRes; + ); + -- If f is in another variant basis (O, Monomial), route through plain S. + if (R.?GroupActing and R.GroupActing == "O") + or (R.?Basis and R.Basis == "Monomial") + then return toSp(toS f, T); + -- Otherwise f is in a plain Schur ring: apply Koike inverse formula. + schurToSpRE(f, T) ) -internalProduct(RingElement,RingElement) := (f1,f2)-> -( - R2 := ring f2; - R := local R; - issy := false; - if (class R2 =!= SchurRing) then issy = true; - R = symmetricRing ring f2; - ch1 := classFunction f1; - ch2 := classFunction f2; - rez := symmetricFunction(internalProduct(ch1,ch2),R); - if issy then rez else - toS rez +toSp(Thing) := (f) -> f +undocumented(toSp,Thing) + +-- ==== toO ==== +-- toO: expand a symmetric function in the basis of orthogonal characters. +-- Returns a RingElement in a SchurRing with GroupActing => "O". The +-- OddOrEven tag attached to T (default "Odd", i.e. O(2n+1) / type B_n) +-- governs which Littlewood branching rule is used when going from a +-- plain Schur expansion into the O basis. +toO = method() + +toO(RingElement) := (f) -> ( + R := ring f; + if class R =!= SchurRing then return toO(toS f); + if R.?GroupActing and R.GroupActing == "O" then return f; + T := orthogonalBasisRingOf R; + toO(f, T) ) --* -chi(BasicList,BasicList) := (lambda, rho) -> -( - la := toList lambda; - rh := toList rho; - ll := sum la; - if ll != sum(rh) then error"Partitions must have the same size."; - R := symmetricRing(QQ,ll); - sl := jacobiTrudi(la,R); - pr := 1_R; - for i from 0 to #rh-1 do pr = pr * R_(ll-1+rh#i); - scalarProduct(sl,pr) +toO(RingElement, SchurRing) := (f, T) -> ( + if not (T.?GroupActing and T.GroupActing == "O") then + error "expected second argument to be a SchurRing with GroupActing => \"O\""; + R := ring f; + if class R =!= SchurRing then return toO(toS f, T); + dimT := numgens T; + -- Source is already in the O basis: just re-promote partition labels. + if R.?GroupActing and R.GroupActing == "O" then ( + rawRes := raw(0_T); + for term in listForm f do ( + if dimT === infinity or #(term#0) <= dimT then ( + sc := raw promote(term#1, T); + ba := raw T_(term#0); + rawRes = rawRes + sc * ba; + ); + ); + return new T from rawRes; + ); + -- Source is in another variant basis (Sp, Monomial): route through plain S. + if (R.?GroupActing and R.GroupActing == "Sp") + or (R.?Basis and R.Basis == "Monomial") + then return toO(toS f, T); + -- Fallback: source is a plain Schur ring; apply Littlewood branching. + schurToORE(f, T) ) -*- ---------------------------------------------------------------- ---------------End characters----------------------------------- ---------------------------------------------------------------- --------------------------------- --- Dimension ------------------- --------------------------------- --- Function to compute the dimension of a virtual representation +toO(Thing) := (f) -> f +undocumented(toO,Thing) + +-- ==== toGL ==== +-- toGL: re-express an element in the plain GL Schur basis. This is a +-- thin synonym for toS that reads naturally when the intent is to +-- obtain a GL character. With a target ring T the element is promoted +-- into T (which must have GroupActing => "GL" and may have finite or +-- infinite rank; finite-rank targets truncate partitions longer than +-- numgens T via specializeGLInto inside toS). +toGL = method() +toGL(RingElement) := (f) -> toS f +toGL(RingElement, SchurRing) := (f, T) -> ( + -- Reject targets whose GroupActing tag is anything other than "GL". + if T.?GroupActing and T.GroupActing != "GL" then + error ("toGL: target ring must have GroupActing => \"GL\", got \"" + | toString T.GroupActing | "\""); + toS(f, T) + ) +toGL(Thing) := (f) -> f +undocumented(toGL, Thing) + +-- ==== toSn ==== +-- toSn: re-express an element as an S_n class function written in the +-- Schur basis (the Frobenius-characteristic convention). The +-- underlying partition data is carried over verbatim; toSn does NOT +-- apply any restriction formula, because the Sn and GL Schur rings use +-- the same partition index set -- only the multiplication differs +-- (internal product vs. Littlewood-Richardson). Finite-rank targets +-- drop partitions with more than numgens T parts. +toSn = method() +toSn(RingElement, SchurRing) := (f, T) -> ( + if not (T.?GroupActing and T.GroupActing == "Sn") then + error "toSn: target ring must have GroupActing => \"Sn\""; + R := ring f; + -- Non-SchurRing inputs: first convert to Schur form. + if class R =!= SchurRing then return toSn(toS f, T); + -- From a variant basis other than plain GL/Sn, go through toS. + if R.?GroupActing and R.GroupActing != "GL" and R.GroupActing != "Sn" then + return toSn(toS f, T); + if R.?Basis and R.Basis == "Monomial" then + return toSn(toS f, T); + -- Copy partition labels into T, respecting the target rank. + dimT := numgens T; + rawRes := raw(0_T); + for term in listForm f do ( + lam := term#0; + c := term#1; + if dimT === infinity or (class dimT =!= InfiniteNumber and #lam <= dimT) then ( + sc := raw promote(c, T); + rawRes = rawRes + sc * raw T_lam; + ); + ); + new T from rawRes + ) +toSn(Thing, SchurRing) := (f, T) -> try promote(f, T) else error "toSn: cannot promote input to target ring" +undocumented(toSn, Thing, SchurRing) + +-- ==== convert (universal dispatch) ==== +-- convert: universal dispatcher. Given any symmetric-function-like +-- element f and a target ring T, pick the right converter. The target +-- ring's classification drives the dispatch: +-- * SchurRing with GroupActing "GL" -> toS(f, T) +-- * SchurRing with GroupActing "Sn" -> toSn(f, T) +-- * SchurRing with GroupActing "Sp" -> toSp(f, T) +-- * SchurRing with GroupActing "O" -> toO(f, T) +-- * SchurRing with GroupActing "SL" -> toS(f, T) (GL collapse) +-- * SchurRing with GroupActing "RatGL" -> toRatGL(f, T) +-- * symmetricRing -> re-apply Jacobi-Trudi in T +-- * any other ring -> try promote(f, T) +-- This is a pure routing layer; no new math is performed here. +convert = method() +convert(RingElement, Ring) := (f, T) -> ( + -- SchurRing target: dispatch on GroupActing + if instance(T, SchurRing) then ( + ga := if T.?GroupActing then T.GroupActing else "GL"; + if ga == "GL" then return toS(f, T); + if ga == "Sn" then return toSn(f, T); + if ga == "Sp" then return toSp(f, T); + if ga == "O" then return toO(f, T); + if ga == "SL" then return toS(f, T); + if ga == "RatGL" then return toRatGL(f, T); + error ("convert: unknown GroupActing on target: " | toString ga); + ); + -- symmetricRing target: re-apply Jacobi-Trudi into T. toSymm alone + -- uses the source ring's pre-attached symmetricRing, which is + -- typically a different ring object from T; computing jacobiTrudi + -- directly in T produces an element that actually lives in T. + if T.?schurLevel and schurLevel T > 0 and class T =!= SchurRing then ( + R := ring f; + if R === T then return f; + -- From a SchurRing: sum c_lambda * jacobiTrudi(lambda, T) + if instance(R, SchurRing) then ( + tms := listForm f; + cR := coefficientRing R; + return sum apply(tms, (p, c) -> + (try b := jacobiTrudi(toList p, T) + else error "convert: target symmetricRing has smaller dim than source partitions; enlarge target") + * promote(lift(c, cR), T)); + ); + -- Otherwise already in some symmetric ring: try plain promotion. + return try promote(f, T) else + error "convert: symmetric-ring target is not compatible with source"; + ); + -- Generic ring: promote if possible + try promote(f, T) else + error ("convert: no route from " | toString ring f | " to " | toString T) + ) +convert(Number, Ring) := (f, T) -> try promote(f, T) else f +convert(Thing, Ring) := (f, T) -> try promote(f, T) else f +undocumented(convert, Thing, Ring) -hooklengths = (lambda) -> ( - mu := conjugate lambda; - product for i from 0 to #lambda-1 list ( - product for j from 0 to lambda#i-1 list ( - lambda#i + mu#j - i - j -1 - )) +----------------------------------------------------------------------- +-- Specialization of universal characters to finite-rank groups. +-- +-- This section implements the restriction of stable (universal) +-- characters to finite-rank classical groups. The main entry point is +-- `specialize(RingElement, ZZ)`, which dispatches on `R.GroupActing`. +-- Below it live the per-variant workers `specialize` (which fetch or +-- build a cached finite target) and the `Into` variants (which +-- accept an explicit target ring T). The `specializedRingOf` +-- helpers memoize one finite ring per (stable source, rank) pair so +-- repeated specializations share a target. +----------------------------------------------------------------------- + +-- ==== Cached target rings ==== + +-- Get or create the cached finite-dimensional Sp(2n) character ring attached +-- to a stable Sp ring S. Keyed by the half-dimension n. +-- +-- The ring is constructed with a rank-tagged symbol (e.g. sp_fin_1, sp_fin_2) +-- so that the globalAssign inside schurRing does NOT clobber the user's +-- original stable symbol (e.g. sp). Users should reach the finite ring via +-- `ring(specialize(f,n))` rather than via a global variable. +specializedSpRingOf = (S, n) -> ( + if not S.?specializedSpRings then S.specializedSpRings = new MutableHashTable; + if (S.specializedSpRings)#?n then (S.specializedSpRings)#n + else ( + spSym := getSymbol("spfin" | toString n); + T := schurRing(coefficientRing S, spSym, n, GroupActing => "Sp"); + (S.specializedSpRings)#n = T; + T + ) ) -dimSchur = method(Options => {GroupActing => "GL"}) -dimSchur(Thing,List) := opts -> (n, lambda) -> ( - -- lambda is a list {a0,a1,...,a(r-1)}, a0 >= a1 >= ... >= a(r-1) > 0 - -- n can be a number or a symbol - powers := new MutableList from toList((if lambda#?0 then lambda#0 else 0) + #lambda - 1 : 0); - base := 1 - #lambda; - for i from 0 to #lambda-1 do - for j from 0 to lambda#i-1 do - powers#(j-i-base) = powers#(j-i-base) + 1; - if not instance(n,ZZ) then n = hold n; - -- now get the hook lengths - answer := local answer; - if opts.GroupActing == "GL" then - ( - num := product for s from 0 to #powers-1 list (n + (base+s))^(powers#s); - answer = num/hooklengths(new Partition from lambda); +-- Get or create the cached finite-dimensional O character ring attached to a +-- stable O ring S. Keyed by the pair (n, kind) where kind is "Odd" for +-- SO(2n+1) / O(2n+1) (type B_n) or "Even" for O(2n) / SO(2n) (type D_n). +specializedORingOf = method(Options => {OddOrEven => "Odd"}) +specializedORingOf(SchurRing, ZZ) := opts -> (S, n) -> ( + kind := opts.OddOrEven; + if kind =!= "Odd" and kind =!= "Even" then + error "specializedORingOf: OddOrEven must be \"Odd\" or \"Even\""; + if not S.?specializedORings then S.specializedORings = new MutableHashTable; + key := (n, kind); + if (S.specializedORings)#?key then (S.specializedORings)#key + else ( + tag := if kind == "Odd" then "finodd" else "fineven"; + oSym := getSymbol("o" | tag | toString n); + T := schurRing(coefficientRing S, oSym, n, GroupActing => "O", OddOrEven => kind); + (S.specializedORings)#key = T; + T ) - else if opts.GroupActing == "Sn" then answer = (sum toList lambda)! / hooklengths(new Partition from lambda); - answer ) -dimSchur(Thing,SchurRingElement) := opts -> (n, F) -> ( - -- assumption: F is an element in a SchurRing of level 1 - if schurLevel(ring F) != 1 then error"Expected a list as input"; - L := listForm F; - sum apply(L, p -> ( - lambda := p#0; - p#1 * dimSchur(n,lambda,opts))) + +-- ==== Main specialize dispatch ==== + +-- Specialize a stable Sp (resp. O) universal character to the finite-dim +-- Sp(2n) (resp. O(2n+1)/O(2n)) character ring. +-- +-- Algorithm: let f live in a stable Sp ring. Writing f in the stable Schur +-- basis via the forward Koike formula gives f = sum_nu c_nu s_nu. The +-- GL(2n) specialization truncates ell(nu) > 2n to zero. The surviving +-- s_nu's restrict to Sp(2n) via Littlewood's finite-dimensional branching +-- rule +-- s_nu |_{Sp(2n)} = sum_{delta even-col} sum_{mu : ell(mu) <= n} +-- c^nu_{delta,mu} sp_mu , +-- i.e., take the even-column skew expansion and drop partitions of length +-- exceeding n. The whole specialization is a genuine ring homomorphism: +-- specialize(f*g, n) == specialize(f,n) * specialize(g,n) at the level of +-- GL(2n) characters. In the sp-basis target ring, the user should be aware +-- that multiplication currently uses the stable Newell-Littlewood product +-- with a length cutoff; a faithful finite-dim product (Phase 5d) is not yet +-- wired in. For an element of a stable Sp ring whose stable Newell- +-- Littlewood product projects inside ell <= n, the two agree. +specialize = method(Options => {OddOrEven => null}) +specialize(RingElement, ZZ) := opts -> (f, n) -> ( + R := ring f; + if class R =!= SchurRing then error "specialize expects a SchurRing element"; + if n < 0 then error "specialize: dimension n must be >= 0"; + if not R.?GroupActing then error "specialize: missing GroupActing attribute on ring"; + if R.GroupActing == "Sp" then ( + if opts.OddOrEven =!= null then + error "specialize: OddOrEven is only meaningful for O rings"; + specializeSp(f, n) + ) + else if R.GroupActing == "O" then ( + kind := if opts.OddOrEven === null then + (if R.?OddOrEven then R.OddOrEven else "Odd") + else opts.OddOrEven; + specializeO(f, n, OddOrEven => kind) + ) + else if R.GroupActing == "GL" then ( + if opts.OddOrEven =!= null then + error "specialize: OddOrEven is only meaningful for O rings"; + specializeGL(f, n) + ) + else if R.GroupActing == "SL" then ( + if opts.OddOrEven =!= null then + error "specialize: OddOrEven is only meaningful for O rings"; + specializeSL(f, n) + ) + else if R.GroupActing == "RatGL" then ( + if opts.OddOrEven =!= null then + error "specialize: OddOrEven is only meaningful for O rings"; + specializeRatGL(f, n) + ) + else if R.GroupActing == "Sn" then ( + if opts.OddOrEven =!= null then + error "specialize: OddOrEven is only meaningful for O rings"; + specializeSn(f, n) + ) + else error("specialize is not implemented for GroupActing => \"" | R.GroupActing | "\"") ) -dimSchur(List,SchurRingElement) := opts -> (lis, F) -> ( - -- assumption: F is an element in a SchurRing - if #lis != schurLevel(ring F) then error"Input list has incorrect size"; - gr := (ring F).GroupActing; - L := listForm F; - sum apply(L, p -> ( - lambda := p#0; - if instance(p#1,SchurRingElement) then dimSchur(drop(lis,1),p#1) * dimSchur(lis#0,lambda,GroupActing => gr) - else p#1 * dimSchur(lis#0,lambda,GroupActing => gr))) +-- Specialize to an entire tower of ranks at once. The list describes the +-- target ranks from the topmost layer inward; `infinity` (or any +-- InfiniteNumber) leaves that layer untouched. Example: for a ring S of +-- schurLevel 2 whose outer layer is a stable Sp ring and inner layer is a +-- stable GL ring, `specialize(f, {4, 3})` produces an element of Sp(8) over +-- GL(3). A shorter list only specializes the outermost layers. +specialize(RingElement, List) := opts -> (f, ranks) -> ( + R := ring f; + if #ranks == 0 then return f; + nTop := ranks#0; + g := if class nTop === InfiniteNumber then f + else specialize(f, nTop, opts); + if #ranks == 1 then return g; + -- recurse into coefficient ring + S := ring g; + A := coefficientRing S; + if class A =!= SchurRing then ( + if #ranks > 1 then error( + "specialize: rank list has " | toString (#ranks) + | " entries but only " + | toString (schurLevel S) + | " SchurRing layers are present"); + return g; + ); + -- Rebuild g by specializing each coefficient + innerRanks := drop(ranks, 1); + -- Specialize one coefficient (which lives in A) + specOneCoef := (c) -> specialize(c, innerRanks, opts); + -- Take inner specialization of an example coefficient to find target A' + sampleCoef := 1_A; + Asp := ring specOneCoef(sampleCoef); + -- Build the specialized outer ring with coefficient ring Asp + S' := local S'; + spSym := S.Symbol; + outerRank := numgens S; + gActing := S.GroupActing; + oddEven := if S.?OddOrEven then S.OddOrEven else null; + basis := if S.?Basis then S.Basis else "Schur"; + S' = if oddEven =!= null then + schurRing(Asp, spSym, outerRank, + GroupActing => gActing, OddOrEven => oddEven, Basis => basis) + else + schurRing(Asp, spSym, outerRank, + GroupActing => gActing, Basis => basis); + rawRes := raw(0_S'); + for term in listForm g do ( + lam := term#0; + c := term#1; + cSpec := specOneCoef(c); + sc := raw promote(cSpec, S'); + ba := raw (S'_(toList lam)); + rawRes = rawRes + sc * ba; + ); + new S' from rawRes ) -dimSchur(SchurRingElement) := opts -> (F) -> ( - schurdims := (S) -> ( - if schurLevel S === 0 then {} - else prepend(numgens S, schurdims coefficientRing S)); - ns := schurdims ring F; - if any(ns, i -> not instance(i,ZZ)) - then error "expected finitely generated Schur rings"; - dS := dimSchur(ns,F); - if liftable(dS,ZZ) then lift(dS,ZZ) else dS +-- Also allow specializing from a stable Schur (GL) ring directly, with an +-- explicit target kind. This is occasionally useful when the user already +-- has a Schur-basis representation and wants to restrict along GL -> Sp/O. +specialize(RingElement, ZZ, SchurRing) := opts -> (f, n, T) -> ( + if not (class T === SchurRing and T.?GroupActing) then + error "specialize: target ring must be an Sp or O SchurRing"; + if opts.OddOrEven =!= null then ( + -- allow user to override/verify T's tag + if T.?OddOrEven and T.OddOrEven =!= opts.OddOrEven then + error("specialize: OddOrEven option " | toString opts.OddOrEven + | " does not match target ring's OddOrEven = " + | toString T.OddOrEven); + ); + if T.GroupActing == "Sp" then specializeSpInto(f, n, T) + else if T.GroupActing == "O" then specializeOInto(f, n, T) + else if T.GroupActing == "GL" then specializeGLInto(f, n, T) + else if T.GroupActing == "SL" then specializeSLInto(f, n, T) + else if T.GroupActing == "Sn" then specializeSnInto(f, n, T) + else error("specialize into GroupActing \"" | T.GroupActing | "\" is not supported") + ) + +-- ==== Per-variant specialize and *Into workers ==== + +-- Inject a GL element (possibly coming from an Sp/O/Monomial ring via toS) +-- into an explicit finite GL ring T, truncating partitions of length > n. +-- The `*Into` form accepts an explicit target; the plain `specializeGL` +-- below fetches a cached target via `specializedGLRingOf`. +specializeGLInto = (f, n, T) -> ( + R := ring f; + local fs; + if class R === SchurRing and R.?GroupActing and R.GroupActing =!= "GL" + and R.GroupActing =!= "SL" then ( + sStable := schurBasisRingOf R; + fs = toS(f, sStable); + ) else fs = f; + rawRes := raw(0_T); + for term in listForm fs do ( + lam := term#0; + c := term#1; + if #lam > n then continue; + sc := raw promote(c, T); + ba := raw (T_(toList lam)); + rawRes = rawRes + sc * ba; + ); + new T from rawRes + ) + +-- Inject a GL/SL element into an explicit finite SL(n) ring T: truncate +-- partitions of length > n, then apply slCanonicalize to normalize +-- representatives modulo the determinant relation. +specializeSLInto = (f, n, T) -> ( + R := ring f; + local fs; + if class R === SchurRing and R.?GroupActing and R.GroupActing =!= "GL" + and R.GroupActing =!= "SL" then ( + sStable := schurBasisRingOf R; + fs = toS(f, sStable); + ) else fs = f; + rawRes := raw(0_T); + for term in listForm fs do ( + lam := term#0; + c := term#1; + if #lam > n then continue; + sc := raw promote(c, T); + ba := raw (T_(toList lam)); + rawRes = rawRes + sc * ba; + ); + slCanonicalize(new T from rawRes, T) + ) + +-- Restrict a stable GL/Sp character into an explicit finite Sp(2n) ring T +-- via Littlewood's branching rule. After pushing f to the Schur basis, +-- partitions with ell(nu) > 2n are discarded (GL(2n) truncation), and +-- each surviving s_nu expands as sum over even-column subpartitions +-- delta of nu, then the skew s_{nu/delta} = sum c^nu_{delta,mu} s_mu is +-- projected to the sp-basis by keeping only #mu <= n. +specializeSpInto = (f, n, T) -> ( + R := ring f; + local fs; + if class R === SchurRing and R.?GroupActing and R.GroupActing == "Sp" then ( + sStable := schurBasisRingOf R; + fs = toS(f, sStable); + ) else fs = toS f; + rawRes := raw(0_T); + for term in listForm fs do ( + nu := term#0; + c := term#1; + if #nu > 2*n then continue; + -- Littlewood Sp branching: s_nu|_Sp(2n) = sum_{delta even-col subpart of nu} + -- sum_mu c^nu_{delta,mu} sp_mu, truncated to #mu <= n. + for delta in evenColsSubpartitionsOf(nu) do ( + for pair in skewSchurExpansion(nu, delta) do ( + mu := pair#0; + lrCoef := pair#1; + if #mu <= n then ( + sc := raw promote(c * lrCoef, T); + ba := raw T_mu; + rawRes = rawRes + sc * ba; + ); + ); + ); + ); + new T from rawRes + ) + +-- Restrict a stable GL/O character into an explicit finite O ring T via +-- Littlewood's branching rule. nuCutoff is 2n+1 for odd O (type B_n) +-- and 2n for even O (type D_n); partitions longer than this are dropped. +-- Each surviving s_nu restricts via sum over even-row subpartitions +-- delta: sum_mu c^nu_{delta,mu} o_mu, keeping only #mu <= n. +specializeOInto = (f, n, T) -> ( + R := ring f; + local fs; + if class R === SchurRing and R.?GroupActing and R.GroupActing == "O" then ( + sStable := schurBasisRingOf R; + fs = toS(f, sStable); + ) else fs = toS f; + kind := if T.?OddOrEven then T.OddOrEven else "Odd"; + nuCutoff := if kind == "Odd" then 2*n+1 else 2*n; + rawRes := raw(0_T); + for term in listForm fs do ( + nu := term#0; + c := term#1; + if #nu > nuCutoff then continue; + -- Littlewood O branching: s_nu|_O = sum_{delta even-row subpart of nu} + -- sum_mu c^nu_{delta,mu} o_mu, truncated to #mu <= n. + for delta in evenRowsSubpartitionsOf(nu) do ( + for pair in skewSchurExpansion(nu, delta) do ( + mu := pair#0; + lrCoef := pair#1; + if #mu <= n then ( + sc := raw promote(c * lrCoef, T); + ba := raw T_mu; + rawRes = rawRes + sc * ba; + ); + ); + ); + ); + new T from rawRes + ) + +-- Restrict a stable Sp character to the cached finite Sp(2n) ring. See +-- `specializeSpInto` for the Littlewood even-column branching rule. +specializeSp = (f, n) -> ( + R := ring f; + T := specializedSpRingOf(R, n); + sStable := schurBasisRingOf R; + fs := toS(f, sStable); + rawRes := raw(0_T); + for term in listForm fs do ( + nu := term#0; + c := term#1; + if #nu > 2*n then continue; + -- Littlewood Sp branching (see specializeSpInto): even-column delta, + -- skew expansion s_{nu/delta}, keep #mu <= n. + for delta in evenColsSubpartitionsOf(nu) do ( + for pair in skewSchurExpansion(nu, delta) do ( + mu := pair#0; + lrCoef := pair#1; + if #mu <= n then ( + sc := raw promote(c * lrCoef, T); + ba := raw T_mu; + rawRes = rawRes + sc * ba; + ); + ); + ); + ); + new T from rawRes ) + +-- Restrict a stable O character to the cached finite O(2n+1) or O(2n) +-- ring, selected by the OddOrEven option. See `specializeOInto` for +-- the Littlewood even-row branching rule. +specializeO = method(Options => {OddOrEven => "Odd"}) +specializeO(RingElement, ZZ) := opts -> (f, n) -> ( + R := ring f; + T := specializedORingOf(R, n, OddOrEven => opts.OddOrEven); + sStable := schurBasisRingOf R; + fs := toS(f, sStable); + kind := opts.OddOrEven; + nuCutoff := if kind == "Odd" then 2*n+1 else 2*n; + rawRes := raw(0_T); + for term in listForm fs do ( + nu := term#0; + c := term#1; + if #nu > nuCutoff then continue; + -- Littlewood O branching (see specializeOInto): even-row delta, + -- skew expansion s_{nu/delta}, keep #mu <= n. + for delta in evenRowsSubpartitionsOf(nu) do ( + for pair in skewSchurExpansion(nu, delta) do ( + mu := pair#0; + lrCoef := pair#1; + if #mu <= n then ( + sc := raw promote(c * lrCoef, T); + ba := raw T_mu; + rawRes = rawRes + sc * ba; + ); + ); + ); + ); + new T from rawRes + ) + +-- Get or create the cached finite-dimensional GL(n) Schur ring attached to a +-- stable GL ring S. Analogous to specializedSpRingOf. +specializedGLRingOf = (S, n) -> ( + if not S.?specializedGLRings then S.specializedGLRings = new MutableHashTable; + if (S.specializedGLRings)#?n then (S.specializedGLRings)#n + else ( + glSym := getSymbol("glfin" | toString n); + T := schurRing(coefficientRing S, glSym, n, GroupActing => "GL"); + (S.specializedGLRings)#n = T; + T + ) + ) + +-- Get or create the cached finite-dimensional SL(n) Schur ring attached to a +-- stable SL (or GL) ring S. +specializedSLRingOf = (S, n) -> ( + if not S.?specializedSLRings then S.specializedSLRings = new MutableHashTable; + if (S.specializedSLRings)#?n then (S.specializedSLRings)#n + else ( + slSym := getSymbol("slfin" | toString n); + T := schurRing(coefficientRing S, slSym, n, GroupActing => "SL"); + (S.specializedSLRings)#n = T; + T + ) + ) + +-- Specialize a GL element to GL(n): truncate partitions of length > n. If +-- the source ring is itself finite with numgens <= n, the engine already +-- handles this and specialize just returns a relabeled element. +specializeGL = (f, n) -> ( + R := ring f; + T := specializedGLRingOf(R, n); + rawRes := raw(0_T); + for term in listForm f do ( + lam := term#0; + c := term#1; + if #lam > n then continue; + sc := raw promote(c, T); + ba := raw (T_(toList lam)); + rawRes = rawRes + sc * ba; + ); + new T from rawRes + ) + +-- Specialize a GL element to SL(n): truncate partitions of length > n, then +-- canonicalize via the determinant relation (strip lambda_n from every part). +-- Also accepts an SL input. +specializeSL = (f, n) -> ( + R := ring f; + T := specializedSLRingOf(R, n); + rawRes := raw(0_T); + for term in listForm f do ( + lam := term#0; + c := term#1; + if #lam > n then continue; + sc := raw promote(c, T); + ba := raw (T_(toList lam)); + rawRes = rawRes + sc * ba; + ); + slCanonicalize(new T from rawRes, T) + ) + --------------------------------------------------------------- ---------End dimension---------------------------------------------- +-- Specialization for Sn rings (truncation by number of parts) --------------------------------------------------------------- +-- Semantic: an Sn-SchurRing of "rank n" (finite n) allows partitions +-- of arbitrary size but bounded by having at most n parts (matching the +-- constructor schurRing(..., n, GroupActing => "Sn")). `specialize(f, n)` +-- on an Sn element (stable or finite rank M >= n) simply drops terms +-- whose partition has more than n parts and rebuilds the result in the +-- cached finite target ring. Unlike GL, there is no modification rule +-- because the finite Sn ring is literally the quotient of the stable +-- ring by the ideal of partitions with > n parts (not a character +-- specialization in the Lie-theory sense, but the user-level analog). + +specializedSnRingOf = (S, n) -> ( + if not S.?specializedSnRings then S.specializedSnRings = new MutableHashTable; + if (S.specializedSnRings)#?n then (S.specializedSnRings)#n + else ( + snSym := getSymbol("snfin" | toString n); + T := schurRing(coefficientRing S, snSym, n, GroupActing => "Sn"); + (S.specializedSnRings)#n = T; + T + ) + ) +specializeSn = (f, n) -> ( + R := ring f; + T := specializedSnRingOf(R, n); + specializeSnInto(f, n, T) + ) + +specializeSnInto = (f, n, T) -> ( + if T.GroupActing =!= "Sn" then + error "specializeSn: target ring must have GroupActing => \"Sn\""; + rawRes := raw(0_T); + for term in listForm f do ( + lam := toList term#0; + c := term#1; + if #lam > n then continue; + sc := raw promote(c, T); + ba := raw (T_lam); + rawRes = rawRes + sc * ba; + ); + new T from rawRes + ) --------------------------------------------------------------- ------------Partitions-related functions------------------------ +-- Specialization for rational GL (Koike-Terada modification rule) --------------------------------------------------------------- ---this part might have to be moved elsewhere ---since it's not directly connected to the package -parts := (d, n) -> ( - -- d is an integer >= 0 - -- n is an integer >= 1 - -- returns a list of all of the partitions of d - -- having <= n parts. - x := partitions(d); - select(x, xi -> #xi <= n)) +-- specializedRatGLRingOf(S, n): get or create a finite-rank GL(n) rational +-- Schur ring attached to a stable (or higher-rank) RatGL ring S. Caching by +-- rank n mirrors specializedGLRingOf / specializedSpRingOf. +specializedRatGLRingOf = (S, n) -> ( + if not S.?specializedRatGLRings then S.specializedRatGLRings = new MutableHashTable; + if (S.specializedRatGLRings)#?n then (S.specializedRatGLRings)#n + else ( + baseR := coefficientRing (S.ratNegRing); + ratSym := getSymbol(toString (S.ratPosSym) | "fin" | toString n); + T := schurRing(baseR, ratSym, n, GroupActing => "RatGL"); + (S.specializedRatGLRings)#n = T; + T + ) + ) --------Generate all the partitions of a set --------with a given shape -locS = local locS; -locL = local locL; -locLengthL = local locLengthL; -locParts = local locParts; -locPartitions = local locPartitions; -locind = local locind; -genPartitions = local genPartitions; +-- specializeRatGL(f, n): push f through the Koike-Terada modification rule at +-- rank n and build the result in a cached finite target ring. The rule is a +-- ring homomorphism: it is a specialization (character evaluation at GL(n)), +-- so it is multiplicative on the universal rational character ring. The rule +-- lives in ratGLModify (above) and iterateRatGLTerms walks the (alpha, beta, +-- scalar) triples. +specializeRatGL = (f, n) -> ( + R := ring f; + T := specializedRatGLRingOf(R, n); + specializeRatGLInto(f, n, T) + ) -genPartitions = method() -genPartitions(ZZ) := (k)-> -( - if k==length locS then (locind = locind + 1;locPartitions#locind = set toList locParts) else - ( - for i from 0 to locLengthL-1 do - if (i==0 and #locParts#0 < locL#0) or (((locL#(i-1)>locL#i) or (#locParts#(i-1)>0)) and (#locParts#i ( + B := T.ratNegRing; + (raw T_alpha) * (raw promote(B_beta, T)) ) -); -partitions(Set,BasicList) := (S,L)-> -( - locS = toList S; - locL = L; - locLengthL = #L; - locParts = new MutableList; - for i from 0 to locLengthL-1 do locParts#i = set{}; - locPartitions = new MutableList; - locind = -1; - genPartitions(0); - toList locPartitions +-- Koike universal character product. For universal rational characters +-- chi_{alpha,beta} the correct product is +-- chi_{alpha,beta} * chi_{gamma,delta} +-- = sum_{epsilon, eta} (s_{alpha/epsilon} s_{gamma/eta})(x) * +-- (s_{beta/eta} s_{delta/epsilon})(y) +-- re-expressed in the bipartition basis. We evaluate the sum by: +-- * iterating over pairs (epsilon, eta), with epsilon contained in both +-- alpha and delta, and eta in both beta and gamma; +-- * lifting the outer skew-Schur expansions to pure-alpha elements of S +-- and the inner ones to pure-beta elements, and multiplying them at the +-- engine level (which performs componentwise LR on the bipartition +-- basis, precisely what is needed for a given (epsilon, eta)). +-- Returns a raw (engine-level) element of S. Used by RatGL multiplication +-- at both stable and finite rank. +ratGLKoikeProductRaw = (S, f1, f2) -> ( + acc := raw(0_S); + iterateRatGLTerms(f1, (alpha, beta, sa) -> ( + iterateRatGLTerms(f2, (gamma, delta, sg) -> ( + -- Common epsilon bound: componentwise min of alpha and delta. + mAD := min(#alpha, #delta); + epsBound := for i from 0 to mAD - 1 list min(alpha#i, delta#i); + mBG := min(#beta, #gamma); + etaBound := for i from 0 to mBG - 1 list min(beta#i, gamma#i); + for eps in allSubpartitionsBoundedBy epsBound do ( + skewAE := skewSchurExpansion(alpha, eps); + if #skewAE == 0 then continue; + skewDE := skewSchurExpansion(delta, eps); + if #skewDE == 0 then continue; + for eta in allSubpartitionsBoundedBy etaBound do ( + skewGH := skewSchurExpansion(gamma, eta); + if #skewGH == 0 then continue; + skewBH := skewSchurExpansion(beta, eta); + if #skewBH == 0 then continue; + -- Build A = sum_{mu1} c^alpha_{mu1,eps} S_{mu1,()}. + rA := raw(0_S); + for pA in skewAE do + rA = rA + (pA#1) * ratGLBasisRaw(S, toList pA#0, {}); + rG := raw(0_S); + for pG in skewGH do + rG = rG + (pG#1) * ratGLBasisRaw(S, toList pG#0, {}); + rB := raw(0_S); + for pB in skewBH do + rB = rB + (pB#1) * ratGLBasisRaw(S, {}, toList pB#0); + rD := raw(0_S); + for pD in skewDE do + rD = rD + (pD#1) * ratGLBasisRaw(S, {}, toList pD#0); + -- Raw engine multiplication is componentwise LR on + -- the bipartition basis, which is what we want. + contribution := rA * rG * rB * rD; + sc := raw promote(sa * sg, S); + acc = acc + sc * contribution; + ); + ); + )); + )); + acc ) ---------end generate partitions +-- specializeRatGLInto(f, n, T): same as specializeRatGL but the target ring T +-- is provided explicitly (must have GroupActing => "RatGL" and numgens T == n). +specializeRatGLInto = (f, n, T) -> ( + if T.GroupActing =!= "RatGL" then + error "specializeRatGLInto: target ring must have GroupActing => \"RatGL\""; + if numgens T =!= n then + error("specializeRatGLInto: target ring has numgens = " | toString numgens T + | " but expected " | toString n); + rawRes := raw(0_T); + iterateRatGLTerms(f, (alpha, beta, scalar) -> ( + for trip in ratGLModify(alpha, beta, n) do ( + alphaP := trip#0; + betaP := trip#1; + coef := trip#2; + sc := raw promote(coef * scalar, T); + rawRes = rawRes + sc * ratGLBasisRaw(T, alphaP, betaP); + ); + )); + new T from rawRes + ) --------------------------------------------------------------- ---------End partitions-related functions----------------------- +-- Lifting normal type-A characters to rational characters --------------------------------------------------------------- +-- toRatGL(f, T): given an element f of a plain GL (or SL) SchurRing, or an +-- element of another RatGL ring, lift it into the RatGL ring T by the obvious +-- embedding alpha |-> (alpha, ()) on basis elements. This realizes the +-- inclusion of polynomial characters into rational characters. +toRatGL = method() +toRatGL(RingElement, SchurRing) := (f, T) -> ( + if T.GroupActing =!= "RatGL" then + error "toRatGL: target ring must have GroupActing => \"RatGL\""; + R := ring f; + rawRes := raw(0_T); + if class R === SchurRing and R.?GroupActing and R.GroupActing == "RatGL" then ( + -- Already rational: re-embed (alpha, beta). If target rank is finite + -- and smaller than the bipartition's "length" ell(alpha)+ell(beta), + -- apply the Koike-Terada modification. + iterateRatGLTerms(f, (alpha, beta, scalar) -> ( + nT := numgens T; + if class nT === InfiniteNumber then ( + sc := raw promote(scalar, T); + rawRes = rawRes + sc * ratGLBasisRaw(T, alpha, beta); + ) + else ( + for trip in ratGLModify(alpha, beta, nT) do ( + alphaP := trip#0; + betaP := trip#1; + coef := trip#2; + sc := raw promote(coef * scalar, T); + rawRes = rawRes + sc * ratGLBasisRaw(T, alphaP, betaP); + ); + ); + )); + ) + else if class R === SchurRing and R.?GroupActing + and (R.GroupActing == "GL" or R.GroupActing == "SL") then ( + -- Lift a plain GL/SL element: alpha |-> (alpha, ()). + nT := numgens T; + for term in listForm f do ( + alpha := toList term#0; + c := term#1; + if (class nT =!= InfiniteNumber) and #alpha > nT then continue; + sc := raw promote(c, T); + rawRes = rawRes + sc * ratGLBasisRaw(T, alpha, {}); + ); + ) + else if class R =!= SchurRing and R.?EHPVariables then ( + -- symmetricRing input: convert to its associated Schur ring (GL) first, + -- then recurse into the GL branch above. + return toRatGL(toS f, T); + ) + else error("toRatGL: source ring must be a SchurRing of type GL/SL/RatGL or a symmetricRing"); + new T from rawRes + ) -beginDocumentation() - -undocumented {Schur} +-- Convenience: if no target ring is supplied, build a stable RatGL ring +-- attached to the source automatically. Parallels toSp/toO overloads. +toRatGL(RingElement) := (f) -> ( + R := ring f; + if class R === SchurRing and R.?GroupActing and R.GroupActing == "RatGL" then + return f; + if not R.?associatedRatGLRing then ( + rtSym := getSymbol "rt"; + R.associatedRatGLRing = schurRing(QQ, rtSym, infinity, GroupActing => "RatGL"); + ); + toRatGL(f, R.associatedRatGLRing) + ) -doc /// -Key - SchurRings -Headline +--------------------------------------------------------------- +------------- King branching formulas -------------------------- +--------------------------------------------------------------- +-- +-- For the three families of classical groups we implement the +-- restriction of an irreducible character from a "total" group to a +-- block-diagonal Levi subgroup, following King 1975 ("Branching rules +-- for classical Lie groups using tensor and spinor methods", +-- J. Phys. A 8, 429-449). In terms of Littlewood-Richardson numbers +-- +-- c^lambda_{alpha,beta,gamma} +-- = coefficient of s_lambda in s_alpha * s_beta * s_gamma, +-- +-- the three rules are +-- +-- GL(m+n) down to GL(m) x GL(n): +-- s_lambda |--> sum_{mu,nu} c^lambda_{(), mu, nu} s_mu x s_nu +-- +-- Sp(2m+2n) down to Sp(2m) x Sp(2n): +-- sp_lambda |--> sum_{delta, mu, nu} +-- c^lambda_{delta, mu, nu} sp_mu x sp_nu, +-- where delta ranges over partitions each of whose columns has +-- even length (equivalently, every part of delta has even +-- multiplicity). +-- +-- O(m+n) down to O(m) x O(n): +-- o_lambda |--> sum_{delta, mu, nu} +-- c^lambda_{delta, mu, nu} o_mu x o_nu, +-- where delta ranges over partitions all of whose parts are even. +-- +-- Partitions mu, nu exceeding the ranks of the respective factor rings +-- are collapsed via the Sam-Snowden-Weyman modification rule +-- (@TO modificationRule@): a single (mu', nu') may appear with a +-- signed integer multiplicity, or (mu, nu) may be suppressed entirely. + +-- Partitions of n all of whose parts appear with even multiplicity +-- (conjugate to partitions-of-n-with-even-parts). +partitionsPartsInPairs = (n) -> ( + if n == 0 then return {{}}; + if odd n then return {}; + apply(partitions(n // 2), p -> flatten apply(toList p, e -> {e, e})) + ) + +-- Partitions of n all of whose parts are even. +partitionsAllPartsEven = (n) -> ( + if n == 0 then return {{}}; + if odd n then return {}; + apply(partitions(n // 2), p -> apply(toList p, e -> 2*e)) + ) + +-- Triple Littlewood-Richardson coefficient c^lambda_{a,b,c} +-- = coefficient of s_lambda in s_a * s_b * s_c (in any GL Schur ring). +-- Returns 0 if any partition does not fit the ring's rank. +tripleLRCoeff = (lambda, a, b, c, T) -> ( + nT := numgens T; + if nT =!= infinity and (#a > nT or #b > nT or #c > nT) then return 0; + prod := T_(toList a) * T_(toList b) * T_(toList c); + cf := 0; + lam := toList lambda; + for t in listForm prod do + if toList first t == lam then cf = last t; + cf + ) + +-- Apply the modification rule to a partition `mu` for a finite-rank +-- target ring S; returns (mu', sign) with sign = 0 meaning "suppressed". +-- For stable targets (numgens infinity) this is the identity. +applyBranchMod = (mu, S) -> ( + n := numgens S; + if n === infinity then return (toList mu, 1); + kind := if S.?GroupActing then S.GroupActing else "GL"; + if kind == "GL" then ( + if #mu > n then return (toList mu, 0) else return (toList mu, 1); + ); + if kind == "Sp" then ( + rSp := modificationRule(toList mu, n, "C"); + if rSp === null then return (toList mu, 0); + return rSp; + ); + if kind == "O" then ( + odd0 := if S.?OddOrEven then S.OddOrEven else "Odd"; + typ := if odd0 == "Odd" then "B" else "D"; + rO := modificationRule(toList mu, n, typ); + if rO === null then return (toList mu, 0); + return rO; + ); + error("applyBranchMod: unknown kind " | toString kind) + ) + +-- Branch a single basis partition lambda of type `kind` using King's rule, +-- into pairs (mu, nu) of partitions indexing the factor rings S1, S2. +-- Returns a MutableHashTable with entries (mu, nu) -> integer. +branchBasisPartition = (lambda, kind, S1, S2, T) -> ( + lam := toList lambda; + d := sum lam; + result := new MutableHashTable; + addEntry := (mu, nu, c) -> ( + key := (toList mu, toList nu); + if result#?key then result#key = result#key + c + else result#key = c; + ); + deltas := if kind == "GL" then {{}} + else if kind == "Sp" then + flatten for de from 0 to d list partitionsPartsInPairs de + else if kind == "O" then + flatten for de from 0 to d list partitionsAllPartsEven de + else error("branch: unsupported kind " | toString kind); + m := numgens S1; + n := numgens S2; + for delta in deltas do ( + rem := d - sum delta; + for k from 0 to rem do ( + parsL := toList \ partitions k; + parsR := toList \ partitions (rem - k); + for mu in parsL do for nu in parsR do ( + c := tripleLRCoeff(lam, delta, mu, nu, T); + if c == 0 then continue; + -- Apply modification rules for finite ranks + muMod := applyBranchMod(mu, S1); + if muMod#1 == 0 then continue; + nuMod := applyBranchMod(nu, S2); + if nuMod#1 == 0 then continue; + addEntry(muMod#0, nuMod#0, c * muMod#1 * nuMod#1); + ); + ); + ); + -- Drop zero entries + for k in keys result do if result#k == 0 then remove(result, k); + result + ) + +branch = method() + +-- Branch an element of a SchurRing with respect to a two-factor restriction. +-- S1 and S2 must have the same GroupActing as the input ring. Returns a +-- HashTable mapping (mu, nu) -> coefficient. The coefficients lie in the +-- coefficient ring of the input ring (in particular over QQ for stable +-- rings). +branch(RingElement, SchurRing, SchurRing) := HashTable => (f, S1, S2) -> ( + R := ring f; + if class R =!= SchurRing then + error "branch: input must live in a SchurRing"; + gk := if R.?GroupActing then R.GroupActing else "GL"; + g1 := if S1.?GroupActing then S1.GroupActing else "GL"; + g2 := if S2.?GroupActing then S2.GroupActing else "GL"; + if g1 =!= gk or g2 =!= gk then error( + "branch: all three rings must share GroupActing (got " + | gk | ", " | g1 | ", " | g2 | ")"); + nR := numgens R; + n1 := numgens S1; + n2 := numgens S2; + -- Sanity check on total rank (skip if any is stable) + if nR =!= infinity and n1 =!= infinity and n2 =!= infinity then ( + if n1 + n2 =!= nR then error( + "branch: numgens(S1) + numgens(S2) must equal numgens of ring f (" + | toString n1 | " + " | toString n2 | " != " | toString nR | ")"); + ); + lfF := listForm f; + d := if #lfF == 0 then 0 + else max apply(lfF, t -> sum toList first t); + helperSize := max(d, 1); + T := plethysmHelperOf(R, helperSize); + result := new MutableHashTable; + addEntry := (key, v) -> ( + if result#?key then result#key = result#key + v + else result#key = v; + ); + for term in lfF do ( + lambda := toList first term; + coef := last term; + partResult := branchBasisPartition(lambda, gk, S1, S2, T); + for k in keys partResult do addEntry(k, coef * partResult#k); + ); + for k in keys result do if result#k == 0 then remove(result, k); + new HashTable from result + ) + +-- Convenience: branch(f, m, n) defaults to anonymous factor rings of ranks +-- m and n with the same GroupActing (and OddOrEven, for O) as ring f. +branch(RingElement, ZZ, ZZ) := HashTable => (f, m, n) -> ( + R := ring f; + gk := if R.?GroupActing then R.GroupActing else "GL"; + sym1 := getSymbol "f1"; + sym2 := getSymbol "f2"; + oddTag := if R.?OddOrEven then R.OddOrEven else "Odd"; + S1 := if gk == "O" + then schurRing(coefficientRing R, sym1, m, GroupActing => "O", OddOrEven => oddTag) + else schurRing(coefficientRing R, sym1, m, GroupActing => gk); + S2 := if gk == "O" + then schurRing(coefficientRing R, sym2, n, GroupActing => "O", OddOrEven => oddTag) + else schurRing(coefficientRing R, sym2, n, GroupActing => gk); + branch(f, S1, S2) + ) + +--------------------------------------------------------------- +----------- End King branching formulas ------------------------ +--------------------------------------------------------------- + +-- recTrans: recursive transform from the h-basis to the Schur basis. +-- Horner-style expansion by the leading h-variable lead = h_i (i maximal +-- appearing in pl). Writing pl = sum_k coe#k * lead^fdeg#k (with fdeg +-- strictly decreasing), we accumulate +-- rez = ((...((0 * s_i + recTrans(coe#0)) * s_i) + recTrans(coe#1)) ...) +-- where the product "*" is dispatched by auxRecTransOp: +-- * for GL/SL/Sp/O rings (ordinary multiplication in the SchurRing), +-- ** for Sn rings (inner tensor product). +-- Each coe#k is itself recursively rewritten by recTrans (it lives in a +-- SchurRing of lower schurLevel, or ultimately at the base case below). +-- At the base case (lead === null, i.e. pl has no remaining h-variables), +-- retFcn lifts pl into the coefficient ring and calls toS there. +recTrans = method() +recTrans (RingElement) := (pl) -> +( +-- lead = leading h-variable = h_i with i maximal, or null if pl is a scalar + lead := leadTermFcn pl; + if lead === null then retFcn pl else + ( + -- read the multiplication op off the ring: * for GL/SL/Sp/O, ** for Sn + auxRecTransOp := (schurRing ring pl).recTransOp; +-- monomials/coefficients of pl viewed as a polynomial in lead + (mon,coe) := coefficients(pl,Variables=>{lead}); + mon = flatten entries mon; + coe = flatten entries coe; + rez := 0; + cdeg := degree(lead,mon#0)+1; + for i from 0 to #mon-1 do + ( + fdeg := degree(lead,mon#i); + -- fill in "gaps" in the Horner accumulator: bring cdeg down to + -- fdeg+1 by multiplying rez by s_i once per missing power + while (cdeg>fdeg+1) do + ( + cdeg = cdeg - 1; + rez = auxRecTransOp(rez, mappingFcn(lead)); + ); + rez = auxRecTransOp(rez, mappingFcn(lead)) + recTrans(coe#i); + cdeg = cdeg - 1; + ); + -- trailing Horner steps for any powers of lead below the smallest fdeg + while cdeg>0 do + ( + cdeg = cdeg - 1; + rez = auxRecTransOp(rez, mappingFcn(lead)); + ); + rez + ) + ) + +recTrans(Thing) := p -> p + +-------- +-------- +--given a recursive relation for a sequence a_n, given by a convolution of (a_n) with (L_n) +--convolve computes formulas for a_n in terms of L_n +--the main routine is coded in the engine +--the value of conv is used to indicate one of several types of convolution +convolve = method() +convolve(List,ZZ) := (L,conv) -> ( + A := ring L_0; + toList drop(apply(rawConvolve(L/raw//toSequence, conv), f -> new A from f),1) + ) + +--a_n = p_n +--L_n = e_n +PtoE = (m,R) -> ( + n := R.dim; + A := R.symRingForE; + p2e := prepend(1_A, for i from 1 to n list ((-1)^(i+1) * A_(2*n+i-1))); + if m>n then p2e = join(p2e,toList((m-n):0_A)); + R.PtoETable = if n == 0 then {1_A} else {1_A} | (- convolve(p2e,2)); + ) + +--a_n = h_n +--L_n = e_n +HtoE = (m,R) -> ( + n := R.dim; + A := R.symRingForE; + h2e := prepend(1_A, for i from 1 to n list (-1)^(i+1)*A_(2*n+i-1)); + R.HtoETable = if n == 0 then {1_A} else {1_A} | convolve(h2e,0); + ) + +--a_n = h_n +--L_n = p_n +HtoP = (m,R) -> ( + n := R.dim; + A := R.symRingForP; + h2p := prepend(1_A, for i from 1 to n list A_(2*n+i-1)); + R.HtoPTable = if n == 0 then {1_A} else {1_A} | convolve(h2p,1); + ) + +--a_n = e_n +--L_n = p_n +EtoP = (m,R) -> ( + n := R.dim; + A := R.symRingForP; + e2p := prepend(1_A, for i from 1 to n list (-1)^(i+1)*A_(2*n+i-1)); + R.EtoPTable = if n == 0 then {1_A} else {1_A} | convolve(e2p,1); + ) + +--a_n = p_n +--L_n = h_n +PtoH = (m,R) -> ( + n := R.dim; + A := R; + p2h := prepend(1_A, for i from 1 to n list (- A_(2*n+i-1))); + R.PtoHTable = if n == 0 then {1_A} else {1_A} | convolve(p2h,2); + ) + +--a_n = e_n +--L_n = h_n +EtoH = (m,R) -> ( + n := R.dim; + A := R; + e2h := prepend(1_A, for i from 1 to n list (-1)^(i+1)*A_(2*n+i-1)); + R.EtoHTable = if n == 0 then {1_A} else {1_A} | convolve(e2h,0); + ) + + +--------------------------------------------------------------- +--------------End transition----------------------------------- +--------------------------------------------------------------- + +--------------------------------------------------------------- +-------------Schur Resolutions--------------------------------- +--------------------------------------------------------------- + +--recsyz is a recursive method that takes as input an element el of a SchurRing of positive schurLevel +--and returns the sum of the terms having negative coefficients +--it is used in the routine schurRes to determine representations that are forced to be generators +--of syzygy modules in an equivariant resolution +recsyz = method() +recsyz (Thing) := (el) -> min(el,0) +recsyz (RingElement) := (el) -> +( + T := ring el; + listForm el/((u,v)->T_u*recsyz(v))//sum + ) + +----------------------------------------------------------------------------- +-- schurResolution +----------------------------------------------------------------------------- +-- Computes the Schur-character data of the minimal free resolution of a +-- GL-equivariant graded module M over a symmetric algebra Sym(V). +-- +-- Inputs: +-- rep : a Schur-ring element giving the GL-character of the representation +-- V (typically s_{(1)}, the defining representation). +-- M : {M_0, M_1, ..., M_{d-1}} -- the Schur characters of the graded +-- pieces of the module M in internal degrees 0, 1, ..., d-1. +-- plets: (in the (rep,M,plets) variant) the pre-computed list +-- [Sym^0 rep, Sym^1 rep, ..., Sym^degreeBound rep] of symmetric +-- powers of V, i.e. the graded characters of Sym(V). In the +-- (rep,M) variant these are computed on the fly. +-- +-- Options: +-- DegreeLimit : the highest internal degree to resolve to (defaults to +-- #M - 1 when 0 is passed). +-- SyzygyLimit : the highest syzygy index to compute (0 means: keep going +-- until a syzygy step produces no new generators). +-- +-- Output: +-- A list of lists {gens_0, gens_1, ...}, where gens_k is a list of pairs +-- (degree, character) giving the generators of the k-th syzygy module of +-- the equivariant minimal free resolution of M. Equivalently, this is the +-- Schur-character refinement of the equivariant Betti table. +----------------------------------------------------------------------------- + +schurResolution = method(Options => {DegreeLimit => 0, SyzygyLimit => 0}) +schurResolution(RingElement,List) := opts -> (rep,M) -> +( + degreeBound := opts.DegreeLimit; + if degreeBound == 0 then degreeBound = #M-1; + syzygyBound := opts.SyzygyLimit; + + T := ring rep; + n := schurLevel T; +--symPowersOfRep = [Sym^0 rep, Sym^1 rep, ..., Sym^degreeBound rep], +--i.e. the graded characters of Sym(V); pre-computed once and passed to schurRes. + symPowersOfRep := new MutableList; + symPowersOfRep#0 = 1_T; + for i from 1 to degreeBound do symPowersOfRep#i = symmetricPower(i,rep); + + schurRes(rep,M,new List from symPowersOfRep,DegreeLimit => degreeBound,SyzygyLimit => syzygyBound) + ) + +schurResolution(RingElement,List,List) := opts -> (rep,M,plets) -> +( + degreeBound := opts.DegreeLimit; + if degreeBound == 0 then degreeBound = #M-1; + syzygyBound := opts.SyzygyLimit; + + schurRes(rep,M,plets,DegreeLimit => degreeBound,SyzygyLimit => syzygyBound) + ) + +schurRes = method(Options => options schurResolution) +schurRes(RingElement,List,List) := opts -> (rep,M,plets) -> +( +----------------------------------------------------------------------------- +-- schurRes -- the actual worker for schurResolution. +-- +-- Iterative covering algorithm. Maintains a "residue" moduleResidue#i: the +-- part of the character in internal degree i that has not yet been realized +-- by the resolution so far. At syzygy step k, for each degree i we: +-- (1) Lift the current k-th syzygy generators into degree i by multiplying +-- each generator's character by the appropriate sym power of V: +-- degreeICover = sum_{sy in syzy#k, sy#0 <= i} plets#(i - sy#0) * sy#1 +-- (2) Subtract the outstanding residue: degreeICover -= moduleResidue#i. +-- (3) Extract the negative-coefficient part via recsyz -- these are the +-- irreducibles the current syzygies fail to cover, which must become +-- fresh syzygy generators at this step. +-- (4) Record (i, -newSyzyPart) as a generator of syzy#k if nonzero. +-- (5) Update moduleResidue#i to reflect what has just been absorbed. +-- Stop either after reaching syzygyBound, or (when syzygyBound == 0) as +-- soon as a syzygy step introduces no new generators. +----------------------------------------------------------------------------- + T := ring rep; + degreeBound := opts.DegreeLimit; + syzygyBound := opts.SyzygyLimit; + +--moduleResidue#i = the degree-i character still needing to be covered by +--the differential in the equivariant complex; starts as M, padded with +--zeros out to degreeBound. + moduleResidue := new MutableList from (M | toList((degreeBound+1-#M):0)); + moreToCompute := true; + syzStep := 0; +--syzygyChars#k = list of (degree, character) pairs for the generators of +--the k-th syzygy module. + syzygyChars := new MutableList; + syzygyChars#syzStep = {}; + local degreeICover; + local newSyzyPart; + +--syzygy modules are constructed step by step; stop either on reaching the +--syzygyBound limit, or on finding no new syzygies at a given step. + while moreToCompute do + ( + for i from 0 to degreeBound do + ( +--(1) Lift the current k-th syzygy generators into degree i. + degreeICover = 0_T; + for sy in syzygyChars#syzStep do + if sy#0 <= i then degreeICover = degreeICover + plets#(i-sy#0) * sy#1 + else break; +--(2) Subtract the residue. degreeICover must cover moduleResidue#i, i.e. +--there must be a surjection of representations from degreeICover onto +--moduleResidue#i; negative coefficients in the difference signal failure. + degreeICover = degreeICover - moduleResidue#i; +--(3) recsyz (defined above) extracts the sum of negative-coefficient +--terms -- exactly the irreducibles that must be covered by fresh syzygies. + newSyzyPart = recsyz(degreeICover); +--(4) Record (i, -newSyzyPart) as a new generator of syzy#k if nonzero. + if newSyzyPart != 0 then syzygyChars#syzStep = syzygyChars#syzStep | {(i,-newSyzyPart)}; +--(5) Update the residue to reflect what has just been absorbed. + moduleResidue#i = degreeICover - newSyzyPart; + ); +--Stopping condition: syzygyBound == 0 means "no user limit" -- keep going +--until a syzygy step produces no new generators; otherwise stop once +--syzStep reaches the requested syzygyBound. + if syzygyBound == 0 then moreToCompute = not (syzygyChars#syzStep == {}) + else moreToCompute = (syzStep i != {}) + ) + +--------------------------------------------------------------- +-------------end Schur Resolutions----------------------------- +--------------------------------------------------------------- + + +--------------------------------------------------------------- +--------------Characters of Symmetric Group-------------------- +--------------------------------------------------------------- + +--given a partition lambda as a nonincreasing sequence of positive integers +--seqToMults returns the representation of this partition as a sequence +--of multiplicities: rez#i is the number of parts of lambda of size (i+1) +seqToMults = method() +seqToMults(List) := (lambda) -> +( + lam := new Partition from lambda; + aux := toList(conjugate lam)|{0}; + rez := {}; + for j from 0 to #aux-2 do + ( + dif := aux#j-aux#(j+1); + rez = rez | {dif}; + ); + rez + ) + +------------------------------------------------------------------------------- +-- Class functions and the Frobenius characteristic map +------------------------------------------------------------------------------- +-- The Frobenius characteristic is the isomorphism +-- +-- ch : R(S_n) ----> Lambda^n +-- +-- between the ring of virtual characters of the symmetric group S_n and the +-- degree-n component of the ring of symmetric functions. Explicitly, +-- +-- ch(chi) = (1/n!) sum_{sigma in S_n} chi(sigma) p_{cycletype(sigma)} +-- = sum_{lambda |- n} (chi(lambda) / z_lambda) p_lambda, +-- +-- where z_lambda = centralizerSize(lambda) is the order of the centralizer +-- in S_n of any permutation of cycle type lambda; equivalently, writing +-- m_i for the number of parts of lambda equal to i, +-- +-- z_lambda = prod_i i^{m_i} * m_i!. +-- +-- A ClassFunction is stored as a HashTable keyed by partitions (encoded as +-- Sequences of parts in weakly-decreasing order); the value chi#lambda is +-- the character value on the conjugacy class of cycle type lambda. If a +-- symmetric function is written in the power-sum basis as +-- +-- f = sum_lambda (c_lambda / z_lambda) p_lambda, +-- +-- then the matching class function is chi#lambda = c_lambda. +------------------------------------------------------------------------------- + +--given a partition lambda represented in as a sequence of multiplicities mults +--where mults#i is the number of parts of lambda of size (i+1) +--multsToSeq represents lambda as a nonincreasing sequence of positive integers +--(inverse of seqToMults, defined elsewhere) +multsToSeq = method() +multsToSeq(List) := (mults) -> +( + n := #mults; + par := {}; + for i from 0 to n-1 do + par = par | splice{mults#i:(i+1)}; + reverse par + ) + +--the size z_lambda of the centralizer in S_n of a permutation of cycle type +--lambda (passed here in multiplicity form: lambda#i = m_{i+1}): +-- z_lambda = prod_i i^{m_i} * m_i! +centralizerSize = method() +centralizerSize(List) := lambda -> +( + product for i from 0 to #lambda-1 list((i+1)^(lambda#i)*(lambda#i)!) + ) + +keysCF := method() +keysCF(ClassFunction) := (cF) -> keys cF + +degree(ClassFunction) := ch -> +( + ke := keysCF ch; + if #ke == 0 then -1 else sum(first ke) + ) + +------------------------------------------------------------------------------- +-- Frobenius maps: classFunction (Lambda^n -> R(S_n)) +-- symmetricFunction (R(S_n) -> Lambda^n) +------------------------------------------------------------------------------- + +--go from symmetric functions to class functions: +--given f = sum_lambda (c_lambda / z_lambda) p_lambda, return chi with +--chi#lambda = c_lambda. We read f in the p-basis and, for each p-monomial, +--recover lambda from the exponent vector and store coeff * z_lambda. +classFunction = method() +classFunction(RingElement) := (f)-> +( + Rf := ring f; + + R := symmetricRing Rf; + pf := toP f; + n := R.dim; + + if (degree pf)#0 > n then error"Can't interpret ring element as a symmetric function"; + + (mon,coe) := apply(coefficients pf,i->flatten entries i); + ch := new MutableHashTable; + for j from 0 to #mon-1 do + ( + -- The symmetricRing has 3n generators laid out as the e-, p-, and + -- h-blocks in that order. Indices n..2n-1 pick out the p-block, so + -- this slice reads the exponents of p_1, p_2, ..., p_n in mon#j, + -- i.e. the multiplicity form of the partition lambda. + degs := (flatten exponents mon#j)_{(n)..(2*n-1)}; + par := multsToSeq(degs); + ch#par = lift(coe#j,coefficientRing R) * centralizerSize(degs); + ); + new ClassFunction from ch + ) + +classFunction(BasicList) := (lambda)-> +( + lam := toList(lambda); + s := symbol s; + R := schurRing(QQ,s,sum lam); + classFunction(R_lam) + ) + +--go from class functions to symmetric functions: +--apply the Frobenius formula ch(chi) = sum_lambda (chi#lambda / z_lambda) p_lambda. +symmetricFunction = method() +symmetricFunction(ClassFunction,Ring) := (ch,S)-> +( + R := symmetricRing S; + rez := 0_R; + n := R.dim; + for lam in keysCF ch do + rez = rez + ch#lam * (product for i from 0 to #lam-1 list R.pVariable(lam#i)) / centralizerSize(seqToMults lam); + if instance(S, SchurRing) then toS rez else rez + ) + +------------------------------------------------------------------------------- +-- ClassFunction arithmetic (pointwise on conjugacy classes) +------------------------------------------------------------------------------- + +ClassFunction + ClassFunction := (ch1,ch2)-> +( + clSum := new MutableHashTable; + l1 := sum((keysCF ch1)#0); + l2 := sum((keysCF ch2)#0); + if l1 != l2 then error("The symmetric functions/characters must have the same degree"); + for i in unique(keysCF(ch1)|keysCF(ch2)) do + ( + a := b := 0; + if ch1#?i then a = ch1#i; + if ch2#?i then b = ch2#i; + if (a+b != 0) then clSum#i = a + b; + ); + new ClassFunction from clSum + ) + +RingElement * ClassFunction := Number * ClassFunction := (n,ch) -> +( + clProd := new MutableHashTable; + for i in keysCF ch do clProd#i = n*ch#i; + new ClassFunction from clProd + ) + +ClassFunction * RingElement := ClassFunction * Number := (ch,n) -> n*ch; + + +ClassFunction - ClassFunction := (ch1,ch2)-> ch1 + (-1)*ch2; + +ClassFunction == ClassFunction := (ch1,ch2) -> +( + equ := true; + for i in unique(keysCF ch1 | keysCF ch2) do + if not ((not ch1#?i and not ch2#?i) or (ch1#?i and ch2#?i and ch1#i == ch2#i)) then + ( + equ = false; + break; + ); + equ + ) + +------------------------------------------------------------------------------- +-- Scalar (Hall) product and internal (Kronecker) product +------------------------------------------------------------------------------- + +--Hall inner product on Lambda: = sum_lambda chi_f(lambda) chi_g(lambda) / z_lambda. +--On irreducibles (Schur functions) this counts common constituents: = delta_{lambda,mu}. +scalarProduct = method() +scalarProduct(ClassFunction,ClassFunction) := (ch1,ch2)-> +( + scProd := 0; + chProd := internalProduct(ch1,ch2); + for i in keysCF(chProd) do + scProd = scProd + chProd#i / centralizerSize(seqToMults i); + scProd + ) + +scalarProduct(RingElement,RingElement) := (f1,f2)-> +( + ch1 := classFunction f1; + ch2 := classFunction f2; + scalarProduct(ch1,ch2) + ) + +--Internal (Kronecker) product: pointwise product of class functions on each +--conjugacy class, corresponding under Frobenius to the tensor product of +--S_n-representations. On symmetric functions it is transported through the +--Frobenius isomorphism classFunction / symmetricFunction. +internalProduct = method() +ClassFunction * ClassFunction := +internalProduct(ClassFunction,ClassFunction) := (ch1,ch2)-> +( + iProd := new MutableHashTable; + l1 := sum((keysCF ch1)#0); + l2 := sum((keysCF ch2)#0); + if l1 == 0 then return(ch1#{} * ch2); + if l2 == 0 then return(ch2#{} * ch1); + if l1 != l2 then error("The symmetric functions/characters must have the same degree"); + for i in keysCF(ch1) do + if ch2#?i then iProd#i = ch1#i * ch2#i; + new ClassFunction from iProd + ) + +internalProduct(RingElement,RingElement) := (f1,f2)-> +( + R2 := ring f2; + R := local R; + issy := false; + if (class R2 =!= SchurRing) then issy = true; + R = symmetricRing ring f2; + ch1 := classFunction f1; + ch2 := classFunction f2; + rez := symmetricFunction(internalProduct(ch1,ch2),R); + if issy then rez else + toS rez + ) + +-* +chi(BasicList,BasicList) := (lambda, rho) -> +( + la := toList lambda; + rh := toList rho; + ll := sum la; + if ll != sum(rh) then error"Partitions must have the same size."; + R := symmetricRing(QQ,ll); + sl := jacobiTrudi(la,R); + pr := 1_R; + for i from 0 to #rh-1 do pr = pr * R_(ll-1+rh#i); + scalarProduct(sl,pr) + ) +*- +--------------------------------------------------------------- +--------------End characters----------------------------------- +--------------------------------------------------------------- + +-------------------------------- +-- Dimension ------------------- +-------------------------------- +-- Function to compute the dimension of a virtual representation + +hooklengths = (lambda) -> ( + mu := conjugate lambda; + product for i from 0 to #lambda-1 list ( + product for j from 0 to lambda#i-1 list ( + lambda#i + mu#j - i - j -1 + )) + ) + +-- Dispatch table for dimension formulas, extensible for Sp/O +dimFormulaTable = new MutableHashTable from { + "GL" => (n, lambda, powers, base) -> ( + if not instance(n,ZZ) then n = hold n; + num := product for s from 0 to #powers-1 list (n + (base+s))^(powers#s); + num/hooklengths(new Partition from lambda) + ), + "Sn" => (n, lambda, powers, base) -> ( + (sum toList lambda)! / hooklengths(new Partition from lambda) + ), + -- Weyl dimension formula for Sp(2n), type C_n, highest weight lambda + -- with at most n parts. Returns 0 if lambda has more than n parts. + -- Formula: dim V_lambda + -- = prod_{1<=i (n, lambda, powers, base) -> ( + if not instance(n,ZZ) then error "Sp dim formula requires an integer n"; + r := #lambda; + if r > n then return 0; + N := n; + L := for i from 1 to N list ((if i <= r then lambda#(i-1) else 0) + N + 1 - i); + num := 1; den := 1; + for i from 1 to N do ( + num = num * L#(i-1); + den = den * (N + 1 - i); + for j from i+1 to N do ( + num = num * (L#(i-1) - L#(j-1)) * (L#(i-1) + L#(j-1)); + den = den * (j - i) * (2*N + 2 - i - j); + ); + ); + num / den + ), + -- Weyl dimension formula for O(2n+1), type B_n. Positive roots + -- e_i - e_j, e_i + e_j (1<=i (n, lambda, powers, base) -> ( + if not instance(n,ZZ) then error "O dim formula requires an integer n"; + r := #lambda; + if r > n then return 0; + N := n; + L := for i from 1 to N list (2*(if i <= r then lambda#(i-1) else 0) + 2*N + 1 - 2*i); + num := 1; den := 1; + for i from 1 to N do ( + num = num * L#(i-1); + den = den * (2*N + 1 - 2*i); + for j from i+1 to N do ( + num = num * (L#(i-1) - L#(j-1)) * (L#(i-1) + L#(j-1)); + den = den * (2*j - 2*i) * (4*N + 2 - 2*i - 2*j); + ); + ); + num / den + ), + -- Weyl dimension formula for SO(2n), type D_n. Positive roots are + -- e_i +/- e_j (1<=i 0 this gives the dimension of a single + -- SO(2n) irrep; the corresponding O(2n) irrep induced from it has the + -- same dimension (we parametrize O(2n) irreps by partitions of length + -- <= n, matching Koike-Terada stable limits). + "O_Even" => (n, lambda, powers, base) -> ( + if not instance(n,ZZ) then error "O dim formula requires an integer n"; + r := #lambda; + if r > n then return 0; + N := n; + if N == 0 then return 1; -- trivial group + L := for i from 1 to N list ((if i <= r then lambda#(i-1) else 0) + N - i); + num := 1; den := 1; + for i from 1 to N do ( + for j from i+1 to N do ( + num = num * (L#(i-1) - L#(j-1)) * (L#(i-1) + L#(j-1)); + den = den * (j - i) * (2*N - i - j); + ); + ); + num / den + ), + -- Legacy alias: plain "O" defaults to B_n (matches pre-split behavior). + "O" => (n, lambda, powers, base) -> ( + if not instance(n,ZZ) then error "O dim formula requires an integer n"; + r := #lambda; + if r > n then return 0; + N := n; + L := for i from 1 to N list (2*(if i <= r then lambda#(i-1) else 0) + 2*N + 1 - 2*i); + num := 1; den := 1; + for i from 1 to N do ( + num = num * L#(i-1); + den = den * (2*N + 1 - 2*i); + for j from i+1 to N do ( + num = num * (L#(i-1) - L#(j-1)) * (L#(i-1) + L#(j-1)); + den = den * (2*j - 2*i) * (4*N + 2 - 2*i - 2*j); + ); + ); + num / den + ) +} + +dimSchur = method(Options => {GroupActing => "GL"}) +dimSchur(Thing,List) := opts -> (n, lambda) -> ( + -- lambda is a list {a0,a1,...,a(r-1)}, a0 >= a1 >= ... >= a(r-1) > 0 + -- n can be a number or a symbol + powers := new MutableList from toList((if lambda#?0 then lambda#0 else 0) + #lambda - 1 : 0); + base := 1 - #lambda; + for i from 0 to #lambda-1 do + for j from 0 to lambda#i-1 do + powers#(j-i-base) = powers#(j-i-base) + 1; + ga := opts.GroupActing; + if not dimFormulaTable#?ga then error("No dimension formula for GroupActing => " | toString ga); + (dimFormulaTable#ga)(n, lambda, powers, base) + ) +dimSchur(Thing,SchurRingElement) := opts -> (n, F) -> ( + -- assumption: F is an element in a SchurRing of level 1 + if schurLevel(ring F) != 1 then error"Expected a list as input"; + L := listForm F; + sum apply(L, p -> ( + lambda := p#0; + p#1 * dimSchur(n,lambda,opts))) + ) + +dimSchur(List,SchurRingElement) := opts -> (lis, F) -> ( + -- assumption: F is an element in a SchurRing + if #lis != schurLevel(ring F) then error"Input list has incorrect size"; + R := ring F; + gr := R.GroupActing; + -- Rational GL: the two layers (alpha outer, beta inner) together define + -- a single GL(n) weight. We bypass the standard recursion and instead + -- apply the Weyl dim formula directly to the composite weight. + if gr == "RatGL" then ( + nOut := lis#0; + nIn := if #lis >= 2 then lis#1 else nOut; + if nOut =!= nIn then + error("dimSchur: RatGL requires equal ranks for both layers; got " + | toString lis); + return dimRatGLList(nOut, F); + ); + -- Resolve O into O_Odd / O_Even via the ring's OddOrEven tag. If the + -- ring is stable (length-infinity), the choice of tag does not matter + -- at the ring level but the specialization point n does: dimSchur is + -- called with a specific n, so we honor whatever tag was set on R + -- (default "Odd" for stable O). + if gr == "O" then ( + if R.?OddOrEven then gr = "O_" | R.OddOrEven + else gr = "O_Odd"; + ); + L := listForm F; + sum apply(L, p -> ( + lambda := p#0; + if instance(p#1,SchurRingElement) then dimSchur(drop(lis,1),p#1) * dimSchur(lis#0,lambda,GroupActing => gr) + else p#1 * dimSchur(lis#0,lambda,GroupActing => gr))) + ) + +dimSchur(SchurRingElement) := opts -> (F) -> ( + schurdims := (S) -> ( + if schurLevel S === 0 then {} + else prepend(numgens S, schurdims coefficientRing S)); + ns := schurdims ring F; + if any(ns, i -> not instance(i,ZZ)) + then error "expected finitely generated Schur rings"; + dS := dimSchur(ns,F); + if liftable(dS,ZZ) then lift(dS,ZZ) else dS + ) + +-- Weyl dimension formula for GL(n) at a general integral highest weight w = +-- (w_1 >= w_2 >= ... >= w_n). Works for weights whose parts can be negative +-- (i.e., rational characters of GL(n)), and reduces to the Schur hook-length +-- formula for dominant polynomial lambda when w_n >= 0. +dimGLweight = (n, w) -> ( + if n === 0 then return 1; + -- Pad w with zeros to length n (dominant, non-increasing assumed). + if #w > n then error("dimGLweight: weight has more parts than rank n = " | toString n); + ww := for i from 0 to n-1 list (if i < #w then w#i else 0); + -- Weyl dim formula: prod_{i n. +dimRatGLBasis = (n, alpha, beta) -> ( + p := #alpha; q := #beta; + if p + q > n then return 0; + w := for i from 1 to n list ( + (if i <= p then alpha#(i-1) else 0) + - (if i > n - q then beta#(n - i) else 0) + ); + dimGLweight(n, w) + ) + +-- Dimension of a general element F of a RatGL ring at rank n, summed over +-- (alpha, beta, scalar) triples. Used by dimSchur(List, SchurRingElement) +-- when gr == "RatGL". +dimRatGLList = (n, F) -> ( + acc := 0; + iterateRatGLTerms(F, (alpha, beta, scalar) -> ( + d := dimRatGLBasis(n, alpha, beta); + acc = acc + scalar * d; + )); + acc + ) +--------------------------------------------------------------- +--------End dimension---------------------------------------------- +--------------------------------------------------------------- + + +--------------------------------------------------------------- +-----------Partitions-related functions------------------------ +--------------------------------------------------------------- +--this part might have to be moved elsewhere +--since it's not directly connected to the package +parts := (d, n) -> ( + -- d is an integer >= 0 + -- n is an integer >= 1 + -- returns a list of all of the partitions of d + -- having <= n parts. + x := partitions(d); + select(x, xi -> #xi <= n)) + +-------Generate all the partitions of a set +-------with a given shape +locS = local locS; +locL = local locL; +locLengthL = local locLengthL; +locParts = local locParts; +locPartitions = local locPartitions; +locind = local locind; +genPartitions = local genPartitions; + +genPartitions = method() +genPartitions(ZZ) := (k)-> +( + if k==length locS then (locind = locind + 1;locPartitions#locind = set toList locParts) else + ( + for i from 0 to locLengthL-1 do + if (i==0 and #locParts#0 < locL#0) or (((locL#(i-1)>locL#i) or (#locParts#(i-1)>0)) and (#locParts#i +( + locS = toList S; + locL = L; + locLengthL = #L; + locParts = new MutableList; + for i from 0 to locLengthL-1 do locParts#i = set{}; + locPartitions = new MutableList; + locind = -1; + genPartitions(0); + toList locPartitions + ) + +--------end generate partitions + +--------------------------------------------------------------- +--------End partitions-related functions----------------------- +--------------------------------------------------------------- + +beginDocumentation() + +undocumented {Schur} + +doc /// +Key + SchurRings +Headline Rings representing irreducible representations of general linear or symmetric groups Description Text - This package makes computations in the representation rings of general linear groups - and symmetric groups possible. - - Given a positive integer {\tt n} we may define a polynomial ring in {\tt n} - variables over an arbitrary base ring , whose monomials correspond to the irreducible - representations of {\tt GL(n)}, and where multiplication is given by the decomposition of - the tensor product of representations. We create such a ring in Macaulay2 using the - @TO schurRing@ function. + This package makes computations in the representation rings of general linear groups + and symmetric groups possible. + + Given a positive integer {\tt n} we may define a polynomial ring in {\tt n} + variables over an arbitrary base ring , whose monomials correspond to the irreducible + representations of {\tt GL(n)}, and where multiplication is given by the decomposition of + the tensor product of representations. We create such a ring in Macaulay2 using the + @TO schurRing@ function. + + Example + S = schurRing(QQ,s,4) + R = schurRing(r,infinity) + + Text + Note that in the above, {\tt n} is allowed to be equal to {\tt \infty}. However, in this + version of the package, many of the features from the case {\tt n} finite are missing + from the infinite case, so the user is advised to use large values for {\tt n} as a + substitute, whenever necessary. + + We determine the relative dimension of a SchurRing over its base using the @TO numgens@ function: + + Example + numgens S + numgens R + + Text + + For {\tt k\leq n}, one may interpret the degree + {\tt k} homogeneous component of a @TO SchurRing@ as the representation ring of the symmetric + group {\tt S_k}. In this ring, the multiplication is different than the one in + the representation ring of {\tt GL(n)}. By default, the elements of a @TO SchurRing@ are + interpreted as (virtual) characters of + a general linear group. This interpretation is controlled by the option @TO GroupActing@, + whose default value is "GL". To indicate that the elements of a Schur ring should + be interpreted as characters of the symmetric group, one has to set the option @TO GroupActing@ + to "Sn". + + Example + Q = schurRing(q,4,GroupActing => "Sn") + + Text + + A monomial in {\tt S} represents the irreducible representation with a given highest weight. + The standard {\tt GL(4)}-representation is + + Example + V = s_1 + + Text + + We may see the dimension of the corresponding irreducible representation using @TO dim@: + + Example + dim V + + Text + Multiplication of elements corresponds to tensor product of representations. The + value is computed using a variant of the Littlewood-Richardson rule. + + Example + V * V + V^3 + + Text + + The third symmetric power of {\tt V} is obtained by + + Example + W = s_{3} + dim W + + Text + + and the third exterior power of {\tt V} can be obtained using + + Example + U = s_{1,1,1} + dim U + + Text + + Alternatively, one can use the functions @TO symmetricPower@ and @TO exteriorPower@: + + Example + W = symmetricPower(3,V) + U = exteriorPower(3,V) + + Text + + We can in fact take symmetric powers and exterior powers of any representation: + + Example + exteriorPower(2,W) + symmetricPower(2,U) + + Text + + and compute even more general forms of @TO plethysm@: + + Example + plethysm(W+U,W+U) + + Text + + Alternatively, we can use the binary operator @TO symbol \@ @ to compute plethysm: + + Example + s_2 @ s_3 + (W+U) @ (W+U) + + Text + + All the above calculations assume that we're dealing with representations of {\tt GL(4)}. + But as symmetric functions of degree three, {\tt W} and {\tt U}, can be thought of as characters of the + symmetric group {\tt S_3}. Let us first ``move'' these symmetric functions into a Schur ring + designed to deal with characters of symmetric groups (like the ring {\tt Q} defined + above): + + Example + W' = toS(W,Q) + U' = toS(U,Q) + + Text + + Now {\tt W'} corresponds to the trivial representation of {\tt S_3}, + and {\tt U'} to the sign representation. As such, we can tensor them together using the + function @TO internalProduct@, or the binary operator @TO symbol *@. + + Example + W' * U' + + Text + + We can generate the class function corresponding to an {\tt S_n}-representation, using + the function @TO classFunction@: + + Example + cfW = classFunction(W') + cfU = classFunction(U') + + Text + + We can multiply class functions together, and transform class functions into symmetric + functions using the function @TO symmetricFunction@: + + Example + cfWU = cfW * cfU + symmetricFunction(cfWU,Q) + + Text + + The result of the previous computation is of course the same as that of taking the product + of {\tt W'} and {\tt U'}. + + We can take exterior and symmetric powers of {\tt S_n}-representations, just as for + {\tt GL}-modules (compare to {\tt o16} and {\tt o17}): + + Example + exteriorPower(2,W') + symmetricPower(2,U') + + Text + + We can write any symmetric function in terms of the standard {\tt e}- (elementary + symmetric), {\tt h}- (complete) and {\tt p}- (power sum) bases, using the functions + @TO toE@, @TO toH@, @TO toP@ respectively: + + Example + toE U + toH U + toP W + + Text + + These expressions live in the Symmetric ring associated to {\tt S}, which can be obtained + using the function @TO symmetricRing@: + + Example + A = symmetricRing S + + Text + + Similarly, any Symmetric ring has a Schur ring attached to it, which can be obtained using + the function @TO schurRing@: + + Example + schurRing A === S + + Text + + We construct tensor products of Schur rings iteratively by allowing Schur rings over + base rings that are also Schur rings: + + Example + T = schurRing(S,t,3) + + Text + + The Schur ring {\tt T} can thus be thought of as the representation ring of + {\tt GL(V)\times GL(V')}, where {\tt V} is as before a vector space of dimension + {\tt 4}, and {\tt V'} is a vector space of dimension {\tt 3}. The representation + corresponding to {\tt V'} is + + Example + V' = t_1 + + Text + + The function @TO schurLevel@ indicates the number of Schur rings that have been + tensored together to obtain any given ring: + + Example + schurLevel T + schurLevel S + schurLevel QQ + + Text + + We can now check Cauchy's formula for decomposing symmetric/exterior powers of a + tensor product: + + Example + symmetricPower(3,V*V') + exteriorPower(3,V*V') + + Text + + We end with the computation of the {\tt GL(n)}- and {\tt S_n}-equivariant resolutions + of the residue field of a polynomial ring in {\tt n} variables. The function that does + this calculation, @TO schurResolution@, is based on an empirical method, which gives + the correct answer in surprisingly many situations. + + In the {\tt GL(n)} situation, we are resolving the residue field which as a representation + has character {\tt 1_S}. The space of linear forms in the polynomial ring + considered as a {\tt GL}-representation has character {\tt V = s_1}. + + Example + n = 4 + M = {1_S} + schurResolution(V,M,DegreeLimit => n) + + Text + + Not surprisingly, the syzygy modules are generated by the exterior powers of {\tt V}. + + The residue field as a representation of the symmetric group {\tt S_n} + has character {\tt s_n}. The space of linear forms in the polynomial ring + considered as an {\tt S_n}-representation coincides with the permutation representation + of {\tt S_n}, thus has character {\tt s_n + s_{n-1,1}}. + + Example + rep = q_n + q_(n-1,1) + M = {q_n} + sR = schurResolution(rep,M,DegreeLimit => n) + + Text + + We can check that the second syzygy module is generated by the second exterior power of the permutation + representation. + + Example + eP2rep = exteriorPower(2,rep) + eP2rep == last sR#2#0 + + Text + + {\bf Variant character rings.} Beyond the general linear and + symmetric groups, the package supports several other families of + representation rings. All of them are realized as @TO SchurRing@s, + with behavior controlled by the option @TO GroupActing@ (and, for + basis conversions, @TO Basis@). The current values are: + + $\bullet$ {\tt "GL"} (default): polynomial representations of + $GL_n$, with basis the Schur functions $s_\lambda$ and the + Littlewood-Richardson product. + + $\bullet$ {\tt "Sn"}: the representation ring of the symmetric + group $S_n$, with basis indexed by partitions and product the + internal product (Kronecker product of $S_n$-characters). See + @TO internalProduct@. + + $\bullet$ {\tt "Sp"}: the (universal or finite-rank) character + ring of the symplectic groups, with basis $sp_\lambda$ and product + the Newell-Littlewood product. See @TO toSp@. + + $\bullet$ {\tt "O"}: the (universal or finite-rank) character ring + of the orthogonal groups, with basis $o_\lambda$. The finite-rank + ring distinguishes type $B$ and type $D$ via the option + @TO OddOrEven@. See @TO toO@. + + $\bullet$ {\tt "RatGL"}: the ring of rational (i.e. finite-dim'l) + representations of $GL_n$, whose irreducibles are indexed by + bipartitions $(\alpha,\beta)$. See @TO toRatGL@. + + $\bullet$ {\tt "SL"}: the ring of polynomial representations of + $SL_n$ (rows of length $n$ are killed). + + Orthogonally, the option @TO Basis@ $=>$ {\tt "Monomial"} replaces + the Schur basis with the monomial symmetric functions $m_\lambda$; + multiplication is implemented by round-tripping through the Schur + basis using Kostka numbers (@TO kostkaNumber@, @TO toM@). + + Example + Sp = schurRing(QQ, sp, 3, GroupActing => "Sp"); + sp_{1} * sp_{1} + O = schurRing(QQ, o, 4, GroupActing => "O"); + o_{1} * o_{2} + Rat = schurRing(QQ, r, 3, GroupActing => "RatGL"); + r_{{1},{1}} * r_{{1},{}} + Mon = schurRing(QQ, m, 4, Basis => "Monomial"); + m_{1} * m_{1} + + Text + + {\bf Stable vs.\ finite-rank rings.} Each of the above flavors + comes in two sizes. Passing a positive integer {\tt n} to + @TO schurRing@ constructs the {\em finite-rank} ring, where + partitions are restricted in length (to $n$ for {\tt GL}/{\tt Sn} + and to the appropriate rank for {\tt Sp}/{\tt O}/{\tt RatGL}) and + the Sam-Snowden-Weyman modification rules are applied + automatically. Passing {\tt infinity} instead constructs the + {\em stable} (universal) ring: a polynomial ring with countably + many generators, one for each partition, on which all operations + are performed without modification. This is the ring of universal + characters of @TO2 {GroupActing,"Koike--Terada"}@ --- a single + computation that specializes to every finite rank. + + Example + StabGL = schurRing(QQ, sg, infinity); + StabSp = schurRing(QQ, tp, infinity, GroupActing => "Sp"); + tp_{1,1} * tp_{1,1} + + Text + + Having computed in the stable ring, one can @TO specialize@ to a + finite rank at the end. The modification rule drops or re-signs + any partition that exceeds the target rank. + + Example + f = tp_{1,1} * tp_{1,1}; + specialize(f, 2) -- inside Sp(4) + specialize(f, 3) -- inside Sp(6) + + Text + + {\bf Conversions between variants.} Every basis conversion is + reversible and functorial. The table below summarizes the + user-level commands for moving between them: + + $\bullet$ @TO toS@, @TO toGL@: to the plain Schur (GL) basis. + + $\bullet$ @TO toE@, @TO toH@, @TO toP@: to the $e/h/p$-basis (lives + in the associated @TO symmetricRing@). + + $\bullet$ @TO toSymm@: inverse of @TO toS@; pushes a Schur-basis + element into the symmetric ring. + + $\bullet$ @TO toM@: to the monomial basis. + + $\bullet$ @TO toSp@, @TO toO@: to the symplectic or orthogonal + character basis, via the inverse Koike branching formulas. + + $\bullet$ @TO toRatGL@: embeds a (polynomial) GL character into + the ring of rational GL characters. + + $\bullet$ @TO toSn@: carries coefficient data from a GL-style ring + into an Sn-style ring (same partition labels, different product). + + $\bullet$ @TO convert@: a universal dispatcher --- given any source + and any target, it picks the right converter. + + $\bullet$ @TO specialize@: stable ring $\to$ finite-rank ring, for + any variant (applies the appropriate modification rule). + + $\bullet$ @TO branch@: restricts a Schur, Sp, or O character along + the diagonal of a product of two classical groups. + + Example + S = schurRing(QQ, s, 5); + Sp = schurRing(QQ, sp, 2, GroupActing => "Sp"); + convert(s_{2,1}, Sp) + convert(oo, S) + + Text + + The compatibility of these conversions can be verified against the + Newell-Littlewood product directly. For instance, in the stable + symplectic ring, $sp_{(1)}^2 = sp_{(2)} + sp_{(1,1)} + 1$; equivalently, + $(s_{(1)})^2 = s_{(2)} + s_{(1,1)}$, and expanding each Schur function + in the Sp basis via @TO toSp@ and adding yields the same element. + + Text + + {\bf Mathematical background.} We collect some pointers for + readers who wish to consult the original sources. + + $\bullet$ {\it Schur-Weyl duality} states that the group algebras + of $GL_n$ and $S_d$ act on $V^{\otimes d}$ with mutually centralizing + images, so the decomposition of $V^{\otimes d}$ as a $GL_n \times + S_d$-module provides a bijection between polynomial representations + of $GL_n$ of degree $d$ and representations of $S_d$. The + @TO2 {"toSn","toSn"}@ / @TO toGL@ pair is the computational + realization of this bijection. + + $\bullet$ The {\it Frobenius characteristic} identifies the + representation ring of $S_n$ with the degree-$n$ component of the + ring of symmetric functions. Under this isomorphism, the + character of a virtual representation corresponds to a symmetric + function, and the internal product of characters to a product + expressible in the power-sum basis. See @TO classFunction@ and + @TO symmetricFunction@. + + $\bullet$ The {\it Koike-Terada universal characters} provide a + single family of symmetric functions whose specializations at + $n$ variables recover the irreducible characters of $Sp(2n)$ (or + $O(n)$) for every $n$. Equivalently, the stable Sp/O character + rings form a purely combinatorial object, and the Sam-Snowden-Weyman + modification rule tells us how to specialize to finite rank. + + $\bullet$ The {\it Newell-Littlewood product} is the multiplication + in the stable Sp/O ring, expressed as a Littlewood-Richardson-like + sum: + $$sp_\mu \cdot sp_\nu = \sum_{\alpha,\beta,\gamma} + c^\mu_{\alpha,\beta}\, c^\nu_{\alpha,\gamma}\, sp_{\beta\gamma},$$ + where $c^\lambda_{\mu\nu}$ are the Littlewood-Richardson + coefficients. The analogous formula holds for $o$. Our + implementation replaces this with the equivalent route + (Schur basis, multiply, back to Sp/O via @TO toSp@/@TO toO@), + which is both faster and easier to certify. + + $\bullet$ {\it King branching formulas} describe the restriction + of a character of a classical group to a two-factor subgroup. + See @TO branch@. + + $\bullet$ {\it Plethysm} $f[g]$ encodes composition of Schur + functors: $f[g]$ is the character of the $GL(V)$-representation + obtained by applying the functor with character $f$ to the + representation with character $g$. Our implementation agrees with + the classical definition on power-sum generators: + $p_n[g(x_1,x_2,\dots)] = g(x_1^n, x_2^n, \dots)$. See + @TO plethysm@. + + Text + + {\bf References.} + + $\bullet$ I.\ G.\ Macdonald, {\it Symmetric Functions and Hall + Polynomials}, 2nd ed., Oxford University Press, 1995. + + $\bullet$ W.\ Fulton, {\it Young Tableaux}, LMSST {\bf 35}, + Cambridge University Press, 1997. + + $\bullet$ W.\ Fulton and J.\ Harris, {\it Representation Theory. + A First Course}, GTM {\bf 129}, Springer, 1991. + + $\bullet$ R.\ P.\ Stanley, {\it Enumerative Combinatorics, Vol.\ 2}, + Cambridge University Press, 1999. + + $\bullet$ B.\ E.\ Sagan, {\it The Symmetric Group. Representations, + Combinatorial Algorithms, and Symmetric Functions}, 2nd ed., GTM + {\bf 203}, Springer, 2001. + + $\bullet$ J.\ Weyman, {\it Cohomology of Vector Bundles and + Syzygies}, Cambridge Tracts in Math.\ {\bf 149}, 2003. + + $\bullet$ K.\ Koike, {\it On the decomposition of tensor products + of the representations of classical groups: by means of universal + characters}, Adv.\ Math.\ {\bf 74} (1989), 57--86. + + $\bullet$ K.\ Koike and I.\ Terada, {\it Young-diagrammatic methods + for the representation theory of the classical groups of type + $B_n$, $C_n$, $D_n$}, J.\ Algebra {\bf 107} (1987), 466--511. + + $\bullet$ R.\ C.\ King, {\it Branching rules for classical Lie + groups using tensor and spinor methods}, J.\ Phys.\ A {\bf 8} + (1975), 429--449. + + $\bullet$ S.\ V.\ Sam and A.\ Snowden, {\it Introduction to twisted + commutative algebras}, arXiv:1209.5122 (2012). + + $\bullet$ S.\ V.\ Sam and A.\ Snowden, {\it Stability patterns in + representation theory}, Forum Math.\ Sigma {\bf 3} (2015), e11. + + $\bullet$ S.\ V.\ Sam, A.\ Snowden, and J.\ Weyman, {\it Homology of + Littlewood complexes}, Selecta Math.\ (N.S.) {\bf 19} (2013), + 655--698. +/// + +doc /// +Key + SchurRing + (symbol _,SchurRing,List) + (symbol _,SchurRing,Sequence) + (symbol _,SchurRing,ZZ) +Headline + The class of all Schur rings +Description + Text + A Schur ring is the representation ring for the general linear group of {\tt n\times n} + matrices, and one can be constructed with @TO schurRing@. + + Example + S = schurRing(QQ,s,4) + + Text + + Alternatively, its elements can be interpreted as virtual characters of symmetric groups, + by setting the value of the option @TO GroupActing@ to {\tt "Sn"}. + + Example + Q = schurRing(QQ,q,4,GroupActing => "Sn") + Text + + The element corresponding to the Young diagram {\tt \{3,2,1\}}, is obtained as follows. + + Example + s_{3,2,1} + + Text + + Alternatively, we can use a @TO Sequence@ instead of a @TO List@ as the index of a Schur + function. + + Example + s_(3,2,1) + + Text + + For Young diagrams with only one row one can use positive integers as subscripts. + + Example + q_4 + + Text + + The name of the Schur ring can be used with a subscript to describe a symmetric + function. + + Example + Q_{2,2} + S_5 + + Text + + The dimension of the underlying virtual {\tt GL}-representation can be obtained + with @TO dim@. + + Example + dim s_{3,2,1} + + Text + + Multiplication in the ring comes from tensor product of representations. + + Example + s_{3,2,1} * s_{1,1} + q_{2,1} * q_{2,1} + + Text + + To extract data in an element in a SchurRing, use @TO "listForm"@: + + Example + listForm (s_{3})^2 + q_{2,1} * q_{2,1} + listForm oo + + Text + + By varying the option @TO GroupActing@ one obtains Schur rings for a wide range + of classical groups. The {\tt "Sp"} flavor is the representation ring of the + symplectic group {\tt Sp(2n)}; multiplication in this ring is governed by the + Newell--Littlewood rule rather than the Littlewood--Richardson rule. + + Example + Sp = schurRing(QQ,sp,4,GroupActing => "Sp"); + sp_{2,1} * sp_{1,1} + dim sp_{2,1} + exteriorPower(2,sp_{1}) + + Text + + For the orthogonal flavor {\tt "O"} one must further specify the parity + of the ambient vector space via @TO OddOrEven@. For instance, + {\tt O(5)} (type {\tt B_2}) is obtained as follows. + + Example + O5 = schurRing(QQ,oo5,5,GroupActing => "O",OddOrEven => "Odd"); + dim oo5_{2,2,1} + + Text + + The rational-{\tt GL} flavor {\tt "RatGL"} implements virtual {\tt GL}-modules + whose weights may be negative, using the {\tt (\alpha,\beta)} pair convention. + + Example + Rg = schurRing(QQ,r,3,GroupActing => "RatGL"); + r_({2,1},{1}) + r_({1},{}) * r_({},{1}) + + Text + + One can also iterate the construction to form tensor products of Schur rings, + which is useful for bivariate characters. + + Example + S = schurRing(QQ,s,4); + T = schurRing(S,t,3); + (s_{2,1} + t_{1,1})^2 + +SeeAlso + schurRing + symmetricRing + GroupActing + Basis + OddOrEven +/// + +doc /// +Key + schurRing + (schurRing,Ring,Symbol,ZZ) + (schurRing,Ring,Thing,ZZ) + (schurRing,Ring,Symbol) + (schurRing,Ring,Thing) + (schurRing,Thing,ZZ) + (schurRing,Thing) +Headline + Make a SchurRing +Description + Text + {\tt S = schurRing(A,s,n)} creates a Schur ring of degree {\tt n} over the base ring + {\tt A}, with variables based on the symbol {\tt s}. This is the representation ring + for the general linear group of {\tt n} by {\tt n} matrices, tensored with the ring + {\tt A}. If {\tt s} is already assigned a value as a variable in a ring, its base + symbol will be used, if it is possible to determine. + + Example + S = schurRing(QQ[x],s,3); + (x*s_{2,1}+s_3)^2 + + Text + Alternatively, the elements of a Schur ring may be interpreted as characters of + symmetric groups. To indicate this interpretation, one has to set the value of the option + @TO GroupActing@ to "Sn". + + Example + S = schurRing(s,4,GroupActing => "Sn"); + exteriorPower(2,s_(3,1)) + + Text + If the dimension {\tt n} is not specified, then one should think of {\tt S} as the + full ring of symmetric functions over the base {\tt A}, i.e. there is no restriction + on the number of parts of the partitions indexing the generators of {\tt S}. + + Example + S = schurRing(ZZ/5,t) + (t_(2,1)-t_3)^2 + + Text + If the base ring {\tt A} is not specified, then @TO QQ@ is used instead. + + Example + S = schurRing(r,2,EHPVariables => (re,rh,rp)) + toH r_(2,1) + + Text + + Beyond the default {\tt GL} flavor, the options @TO GroupActing@ + and @TO Basis@ choose between the symmetric-group ({\tt "Sn"}), + symplectic ({\tt "Sp"}), orthogonal ({\tt "O"}), special-linear + ({\tt "SL"}), rational-GL ({\tt "RatGL"}), and monomial-basis + variants. See the main @TO SchurRings@ page for the full landscape. + + For example, the symplectic representation ring lives in its own + @TO SchurRing@, and the orthogonal flavor takes an additional + @TO OddOrEven@ option to distinguish {\tt O(2n+1)} from {\tt O(2n)}. + + Example + Sp = schurRing(QQ,sp,4,GroupActing => "Sp"); + sp_{2,1}*sp_{1,1} + O4 = schurRing(QQ,o,4,GroupActing => "O",OddOrEven => "Even"); + dim o_{3,2,1} + + Text + + The monomial-symmetric-function basis is activated with + {\tt Basis => "Monomial"}, and multiplication switches to the + corresponding convolution on weak compositions. + + Example + M = schurRing(QQ,m,4,Basis => "Monomial"); + m_{2,1} * m_{1} + toS m_{2,1} + + Text + + One can iterate @TO schurRing@ to produce a tower of Schur rings, + a convenient setting for bi-graded or bivariate character calculations. + + Example + S = schurRing(QQ,s,4); + T = schurRing(S,t,3); + coefficientRing T + s_{1,1} * t_{2,1} + + Text + + Passing {\tt n => infinity} (or omitting the dimension) builds the stable + ring of symmetric functions, with no bound on the number of parts. + + Example + Sinf = schurRing(ZZ/7,u); + numgens Sinf + (u_{2,1})^2 + +SeeAlso + SchurRing + symmetricRing + GroupActing + Basis + OddOrEven +/// + +doc /// +Key + (coefficientRing, SchurRing) +Headline + Coefficient ring of a Schur ring +Usage + coefficientRing S +Inputs + S:SchurRing +Description + Text + Given a Schur ring {\tt S}, the function returns its coefficient ring. + The coefficient ring may be any commutative ring that Macaulay2 supports, + including other Schur rings obtained by iterating @TO schurRing@. + + Example + S = schurRing(ZZ[x],s,4); + coefficientRing S + A = schurRing(QQ,a,3); + B = schurRing(A,b,2); + coefficientRing B + + Text + + For a tower of Schur rings, {\tt coefficientRing} peels off one layer at a + time, allowing the user to navigate the entire construction. + + Example + T = schurRing(B,t,2); + coefficientRing T + coefficientRing coefficientRing T + + Text + + One can build Schur rings over finite-field coefficients as well. + + Example + P = schurRing(ZZ/5,p,4); + coefficientRing P + (p_{2,1} + p_{1})^2 +SeeAlso + schurRing + SchurRing +/// + +document { + Key => {SchurRingIndexedVariableTable,(symbol _,SchurRingIndexedVariableTable,Thing)}, + "This class is used as part of the implementation of a type of indexed variable used just for Schur rings.", + PARA{"It is what makes the partition-indexed notation ", TT "s_{3,2,1}", " or ", + TT "s_(3,2,1)", " work: when you write ", TT "s", " in a Schur ring, ", TT "s", + " is bound to a ", TO "SchurRingIndexedVariableTable", " whose ", TT "_", + " method accepts a list, a sequence, or an integer and returns the corresponding", + " Schur-basis element. The same mechanism drives the partition-indexed notation + in every flavor of ", TO "SchurRing", " -- monomial, symplectic, orthogonal, + symmetric-group, and rational-GL variants all share this indexing interface."}, + EXAMPLE { + "S = schurRing(QQ, s, 4);", + "class s", + "s_{2,1}", + "s_(2,1)", + "s_2" + }, + PARA{"Each Schur ring comes with its own indexed variable table, and partitions with + many parts print in the same compact way."}, + EXAMPLE { + "T = schurRing(QQ, t, 5);", + "t_{4,3,2,1}", + "t_{1,1,1,1}", + "dim t_{4,3,2,1}" + }, + PARA{"For rings with ", TT "Basis => \"Monomial\"", ", the same table selects the + monomial-symmetric-function basis elements instead."}, + EXAMPLE { + "M = schurRing(QQ, m, 4, Basis => \"Monomial\");", + "m_{2,1}", + "m_{2,1} * m_{1}" + }, + PARA{"In the rational-GL flavor, subscripts are pairs of lists encoding positive + and negative weights."}, + EXAMPLE { + "R = schurRing(QQ, r, 3, GroupActing => \"RatGL\");", + "r_({2,1},{1})" + }, + SeeAlso => { IndexedVariableTable, SchurRing, schurRing } + } + +doc /// +Key + symmetricRing + (symmetricRing,Ring,ZZ) + (symmetricRing,ZZ) +Headline + Make a Symmetric ring +Usage + symmetricRing(A,n) + symmetricRing n +Inputs + A:Ring + n:ZZ +Description + Text + + The method {\tt symmetricRing} creates a Symmetric ring of dimension {\tt n} over a base ring + {\tt A}. This is the subring of the ring of symmetric functions over the base {\tt A} + consisting of polynomials in the first {\tt n} elementary (or complete, or power sum) + symmetric functions. If {\tt A} is not specified, then it is assumed to be @TO QQ@. + + Example + R = symmetricRing(QQ[x,y,z],4) + e_2*x+y*p_3+h_2 + toS oo + + Text + + The elements of a Symmetric ring can be interpreted as characters of either symmetric or + general linear groups. This is controlled by the value of the option @TO GroupActing@, whose + default value is "GL" (general linear group). The other possibility for its value is + "Sn" (symmetric group). + + Example + R = symmetricRing(QQ,3,GroupActing => "Sn") + toE symmetricPower(2,e_2) + + Text + + The three symmetric-function generators -- elementary, complete, and power + sum -- are all accessible in the same ring, and conversions between them + are handled by @TO toE@, @TO toH@, @TO toP@, and @TO toS@. + + Example + R = symmetricRing(QQ,5); + toS ((R.pVariable 2)^3) + toH (R.pVariable 2) + toS (R.eVariable 2 * R.hVariable 2) + + Text + + A Symmetric ring can be built over any commutative base; coefficients can + be polynomial rings, modular rings, or even other Schur rings. + + Example + R7 = symmetricRing(ZZ/7,4); + (R7.eVariable 2)^3 + Rt = symmetricRing(QQ[t],3,GroupActing => "Sn"); + toS(Rt.eVariable 2 * t) + +SeeAlso + SchurRing + schurRing + eVariable + hVariable + pVariable +/// + +doc /// +Key + eVariable +Headline + Elementary symmetric functions in a Symmetric ring +Description + Text + For a Symmetric ring {\tt R} of dimension {\tt n}, {\tt R.eVariable} is a function + which assigns to each index {\tt 1\leq i\leq n} the {\tt i}-th elementary symmetric + function. If {\tt i} is outside the given bounds, an error is returned. + + Example + R = symmetricRing(QQ,5,EHPVariables => (a,b,c)); + R.eVariable 3 + + Text + + The elementary generators are related to the Schur basis via {\tt e_k = + s_{1^k}}; @TO toS@ realizes this on any product of {\tt e}-variables. + + Example + R = symmetricRing(QQ,4); + toS (R.eVariable 2) + toS ((R.eVariable 2)^2) + + Text + + One may also use {\tt eVariable} from a Symmetric ring built with a + non-standard coefficient ring or group acting on it. + + Example + Rsn = symmetricRing(QQ,4,GroupActing => "Sn"); + toS symmetricPower(2, Rsn.eVariable 2) + Rmod = symmetricRing(ZZ/5,3); + (Rmod.eVariable 1)^5 + +SeeAlso + hVariable + pVariable + symmetricRing + toS +/// + +doc /// +Key + hVariable +Headline + Complete symmetric functions in a Symmetric ring +Description + Text + For a Symmetric ring {\tt R} of dimension {\tt n}, {\tt R.hVariable} is a function + which assigns to each index {\tt 1\leq i\leq n} the {\tt i}-th complete symmetric + function. If {\tt i} is outside the given bounds, an error is returned. + + Example + R = symmetricRing(QQ,2,EHPVariables => (x,y,z)); + R.hVariable 2 + + Text + + Complete symmetric functions translate to one-row Schur functions via + {\tt h_k = s_k}; this is the other half of the {\tt e/h}-duality. + + Example + R = symmetricRing(QQ,4); + toS (R.hVariable 3) + toS (R.eVariable 2 * R.hVariable 2) + + Text + + They interact cleanly with power sums: Newton's identities are realized by + @TO toE@ and @TO toP@, and here we convert a cube of {\tt h_2} into the + power-sum basis. + + Example + R = symmetricRing(QQ,3); + toP ((R.hVariable 2)^2) + + Text + + Complete symmetric functions are available over any coefficient ring. + + Example + Rt = symmetricRing(QQ[t],3); + toS (Rt.hVariable 2 + t * Rt.eVariable 2) + +SeeAlso + eVariable + pVariable + symmetricRing + toS +/// + +doc /// +Key + pVariable +Headline + Power-sum symmetric functions in a Symmetric ring +Description + Text + For a Symmetric ring {\tt R} of dimension {\tt n}, {\tt R.pVariable} is a function + which assigns to each index {\tt 1\leq i\leq n} the {\tt i}-th power-sum symmetric + function. If {\tt i} is outside the given bounds, an error is returned. + + Example + R = symmetricRing(QQ,4); + R.pVariable 2 + + Text + + Power sums are an algebraically independent generating set for the ring of + symmetric functions over @TO QQ@, and products of {\tt p_i}'s expand into + the Schur basis using the character table of the symmetric group. + + Example + R = symmetricRing(QQ,5); + toS ((R.pVariable 2)^3) + + Text + + The same variable in a Symmetric ring with {\tt GroupActing => "Sn"} + represents a virtual character of a symmetric group, and conversion to + elementary and complete generators is handled by @TO toE@ and @TO toH@. + + Example + Rsn = symmetricRing(QQ,4,GroupActing => "Sn"); + toE (Rsn.pVariable 2) + toH (Rsn.pVariable 3) + + Text + + Power sums are also useful as the natural input to @TO plethysm@. + + Example + R = symmetricRing(QQ,4); + toS plethysm(R.pVariable 2, R.hVariable 2) + +SeeAlso + eVariable + hVariable + symmetricRing + toS + plethysm +/// + +doc /// + Key + (numgens,SchurRing) + Headline + Number of generators of Schur ring. + Description + Text + + Given a Schur ring {\tt S}, the function {\tt numgens} outputs the number + of generators of {\tt S}. This is equal to the relative dimension of {\tt S} + over its base ring, and also to the maximal number of parts of a partition + allowed as an index for the elements of {\tt S}. + + Example + R = schurRing(QQ,r,6); + numgens R + S = schurRing(s); + numgens S + + Text + + When a Schur ring is built on top of another Schur ring as its coefficient + ring, {\tt numgens} measures only the outermost (relative) layer. Nested + Schur rings thus model tensor products of representation rings, each layer + tracked by its own @TO numgens@. + + Example + A = schurRing(QQ,a,3); + B = schurRing(A,b,2); + numgens B + numgens coefficientRing B + + Text + + The {\tt numgens} value for a stable Schur ring (built with {\tt n => infinity} + or without an explicit rank) is @TO infinity@, reflecting the fact that no + partition length is excluded. + + Example + Sinf = schurRing(QQ,u); + numgens Sinf + u_{4,3,2,1} + + Text + + The same rule applies to the symplectic and orthogonal flavors. + + Example + Sp = schurRing(QQ,sp,4,GroupActing => "Sp"); + numgens Sp + O = schurRing(QQ,o,5,GroupActing => "O",OddOrEven => "Odd"); + numgens O + SeeAlso + schurRing + SchurRing +/// + +doc /// + Key + (schurRing,Ring) + Headline + The Schur ring corresponding to a given Symmetric ring. + Usage + S = schurRing R + Inputs + R:Ring + Outputs + S:SchurRing + Description + Text + + Given a ring {\tt R}, the function {\tt schurRing} attempts to return a + Schur ring {\tt S} that is associated to {\tt R} in a natural way. Namely, if + the attribute {\tt R.Schur} points to a Schur ring, then the function returns + that ring. If {\tt R} is already a Schur ring, then the ring {\tt R} is returned. + Otherwise, if the Schur level of {\tt R} is at least one, then the function + constructs a Schur ring over the base ring {\tt A} of {\tt R}, having the same + relative dimension over {\tt A} as {\tt R}. If the Schur level of {\tt R} is zero, then + an error is returned. + + Example + R = schurRing(QQ,r,6); + schurRing R + Q = symmetricRing(QQ,3); + A = schurRing Q; + schurRing Q + + Text + + Passing an existing Schur ring to {\tt schurRing} simply returns it, which is + convenient as a guard when a function wants to accept either a Symmetric ring + or a Schur ring as input. + + Example + S = schurRing(QQ,s,4); + schurRing S === S + + Text + + For a Symmetric ring with the {\tt "Sn"} interpretation, the associated Schur + ring inherits this flavor and is cached on the ring. + + Example + Rsn = symmetricRing(QQ,4,GroupActing => "Sn"); + Ssn = schurRing Rsn; + numgens Ssn + + Text + + The construction also works over polynomial coefficient rings, producing a + Schur ring with the same parameters as coefficients. + + Example + Rx = symmetricRing(QQ[x],3); + Sx = schurRing Rx; + coefficientRing Sx + SeeAlso + symmetricRing + SchurRing + schurRing +/// + +doc /// + Key + (symmetricRing,Ring) + Headline + The Symmetric ring corresponding to a given (Schur) ring. + Usage + R = symmetricRing S + Inputs + S:Ring + Outputs + R:Ring + Description + Text + + Given a (Schur) ring {\tt S}, the function {\tt symmetricRing} returns a + (Symmetric) ring {\tt R} that is associated to {\tt S} in a natural way. Namely, if + the attribute {\tt S.symmetricRing} points to a ring, then the function returns + that ring. If {\tt S} is not a Schur ring, then the function returns {\tt S}. + Otherwise, if {\tt S} is a Schur ring, then the function + constructs a polynomial ring over the Symmetric ring {\tt R_A} of the base ring {\tt A} of + {\tt R}, having the same relative dimension over {\tt R_A} as {\tt S} over {\tt A}. + + Example + A = schurRing(QQ,a,6); + B = schurRing(A,b,3); + symmetricRing B + symmetricRing ZZ + + Text + + For a plain Schur ring, the associated Symmetric ring is a polynomial ring + in the elementary, complete, and power-sum generators. + + Example + S = schurRing(QQ,s,4); + R = symmetricRing S; + (R.eVariable 2)^2 + toS ((R.eVariable 2)^2) + + Text + + The construction plays well with coefficient rings of different flavors, and + with the symmetric-group interpretation via {\tt GroupActing => "Sn"}. + + Example + Ssn = schurRing(QQ,c,4,GroupActing => "Sn"); + Rsn = symmetricRing Ssn; + numgens Rsn + + Text + + On a tower of Schur rings, {\tt symmetricRing} produces a tower of Symmetric + rings mirroring the coefficient-ring structure. + + Example + A = schurRing(QQ,a,3); + B = schurRing(A,b,2); + RB = symmetricRing B; + coefficientRing RB + SeeAlso + schurRing + SchurRing + symmetricRing +/// + +doc /// + Key + toS + (toS,RingElement) + (toS,RingElement,SchurRing) + Headline + Schur (s-) basis representation + Usage + fs = toS f + fs = toS(f,S) + Description + Text + + Given a symmetric function {\tt f}, the function + {\tt toS} yields a representation of {\tt f} as a linear + combination of Schur functions. + + If {\tt f} is an element of a Symmetric ring {\tt R} and the output Schur ring {\tt S} + is not specified, then the output {\tt fs} is an element of the Schur ring + associated to {\tt R} (see @TO schurRing@). + + Example + R = symmetricRing(QQ,4); + fs = toS(e_1*h_2+p_3) + S = schurRing(s,2); + toS(fs,S) + + Text + + This also works over tensor products of Symmetric/Schur rings. + + Example + R = symmetricRing(4, EHPVariables => (a,b,c), SVariable => r); + S = symmetricRing(R, 2, EHPVariables => (x,y,z), SVariable => s); + T = symmetricRing(S, 3, SVariable => t); + A = schurRing T; + f = a_3*x_2*e_1 - b_1*z_2*p_3 + toS f + + Text + + The Jacobi-Trudi determinant $s_\lambda = \det(h_{\lambda_i - i + j})$ + is inverted by {\tt toS}: feeding a Jacobi-Trudi expression back + through {\tt toS} recovers a single Schur label. + + Example + R = symmetricRing(QQ,5); + toS jacobiTrudi({3,2,1},R) + + Text + + {\bf GL to Sn.} The GL Schur basis and the Frobenius-characteristic + (Sn) basis share the same partition index set. Combining {\tt toS} + with @TO toSn@ carries coefficient data between the two flavors. + + Example + G = schurRing(QQ, g, 4); + Sn = schurRing(QQ, n, 4, GroupActing => "Sn"); + a = toSn(g_{2,1}, Sn) + toS(a, G) + + Text + + {\bf Sp {\rm to} S {\rm to} Sp.} Elements of a variant-basis + ring (e.g.\ {\tt "Sp"}, {\tt "O"}) can be expanded into the plain + GL Schur basis with {\tt toS}, and the resulting combination + re-expressed in a variant-basis ring via @TO convert@. + + Example + Sp = schurRing(QQ, sp, 4, GroupActing => "Sp"); + f = toS sp_{2,1} + Sp' = schurRing(QQ, sq, 4, GroupActing => "Sp"); + convert(f, Sp') + + SeeAlso + toH + toE + toP + toSn + toSymm + jacobiTrudi +/// + +doc /// + Key + toE + (toE,RingElement) + Headline + Elementary symmetric (e-) basis representation + Usage + fe = toE f + Inputs + f:RingElement + element of a Symmetric or Schur ring + Outputs + fe:RingElement + element of a Symmetric ring + Description + Text + + Given a symmetric function {\tt f}, the function + {\tt toE} yields a representation of {\tt f} as a polynomial + in the elementary symmetric functions. + + If {\tt f} is an element of a Schur ring {\tt S} then the output {\tt fe} is an + element of the Symmetric ring associated to {\tt S} (see @TO symmetricRing@). + + Example + R = symmetricRing 7; + toE(h_3*e_3) + S = schurRing(s,4) + toE S_{3,2,1} + + Text + + This also works over tensor products of Symmetric/Schur rings. + + Example + R = schurRing(r, 4, EHPVariables => (a,b,c)); + S = schurRing(R, s, 2, EHPVariables => (x,y,z)); + T = schurRing(S, t, 3); + A = symmetricRing T; + f = (r_1+s_1+t_1)^2 + toE f + + Text + + {\bf Variant bases.} When {\tt f} lives in a @TO SchurRing@ + with {\tt GroupActing} in $\{${\tt "Sp"}, {\tt "O"}, + {\tt "RatGL"}, {\tt "SL"}$\}$ or {\tt Basis => "Monomial"}, + the output is obtained by treating the partition labels of + {\tt f} as plain Schur labels and applying the Jacobi-Trudi + determinant. This is {\it not} the same as the symmetric + function representing the character of the underlying irrep. + To obtain the character expansion, first call @TO toS@ to get + the plain GL Schur expansion, and then compose with {\tt toE}: + {\tt toE(toS f)}. + + Example + Sp = schurRing(QQ, sp, 3, GroupActing => "Sp"); + toE sp_{2,1} + toE toS sp_{2,1} + + Text + + Composing {\tt toE} with @TO toS@ lets you convert an arbitrary + $e$/$h$/$p$-expression into the $e$-basis via the Schur basis, + which is sometimes numerically cleaner than Newton's identities. + + Example + R = symmetricRing(QQ,4); + toE toS (e_1 * h_2 + p_3) + toE toS (h_2^2) + + Text + + The names of the output variables are controlled by + @TO EHPVariables@; {\tt toE} writes its result in the first + slot of that triple. + + Example + Rxyz = symmetricRing(QQ, 4, EHPVariables => (x,y,z)); + toE(y_3) + toE(z_2) + + Text + + {\bf Stable rings.} If {\tt f} lives in a rank-infinite + SchurRing (created with {\tt numgens => infinity}), {\tt toE} + raises an error: the associated symmetric ring can only be + constructed at finite rank. Use @TO specialize@ to fix a rank + first. + + Example + Sinf = schurRing(QQ, u, infinity); + try toE u_{2,1} else "error: stable ring has no symmetricRing" + toE specialize(u_{2,1}, 3) + + SeeAlso + toH + toS + toP + toSymm + specialize + EHPVariables +/// + +doc /// + Key + toH + (toH,RingElement) + Headline + Complete symmetric (h-) basis representation + Usage + fh = toH f + Inputs + f:RingElement + element of a Symmetric or Schur ring + Outputs + fh:RingElement + element of a Symmetric ring + Description + Text + + Given a symmetric function {\tt f}, the function + {\tt toH} yields a representation of {\tt f} as a polynomial + in the complete symmetric functions. + + If {\tt f} is an element of a Schur ring {\tt S} then the output {\tt fh} is an + element of the Symmetric ring associated to {\tt S} (see @TO symmetricRing@). + + Example + R = symmetricRing 7; + toH(h_3*e_3) + S = schurRing(s,4) + toH S_{3,2,1} + + Text + + This also works over tensor products of Symmetric/Schur rings. + + Example + R = schurRing(r, 4, EHPVariables => (a,b,c)); + S = schurRing(R, s, 2, EHPVariables => (x,y,z)); + T = schurRing(S, t, 3); + A = symmetricRing T; + f = (r_1+s_1+t_1)^2 + toH f + + Text + + {\bf Variant bases.} When {\tt f} lives in a @TO SchurRing@ + with {\tt GroupActing} in $\{${\tt "Sp"}, {\tt "O"}, + {\tt "RatGL"}, {\tt "SL"}$\}$ or {\tt Basis => "Monomial"}, + partition labels are treated as plain Schur labels (Jacobi-Trudi + is applied directly); the result is not the same as the + character expansion. Use {\tt toH(toS f)} for the latter. + + Example + O = schurRing(QQ, o, 4, GroupActing => "O"); + toH o_{2,1} + toH toS o_{2,1} + + Text + + Roundtripping through the Schur basis recovers the original + $h$-expression (up to the usual commutative-polynomial rewriting). + + Example + R = symmetricRing(QQ,5); + toH toS (h_2 * h_3) + toH toE toS (h_2^2) + + Text + + The names of the output variables follow @TO EHPVariables@; + {\tt toH} writes its result using the second name in that triple. + + Example + Rxyz = symmetricRing(QQ, 4, EHPVariables => (x,y,z)); + toH(x_1 * x_2) + toH(z_3) + + Text + + {\bf Stable rings.} Rank-infinite SchurRings raise an error; + use @TO specialize@ to fix a rank first. + + Example + Sinf = schurRing(QQ, v, infinity); + try toH v_{3,1} else "error: stable ring has no symmetricRing" + toH specialize(v_{3,1}, 4) + + SeeAlso + toE + toS + toP + toSymm + specialize + EHPVariables +/// + +doc /// + Key + toP + (toP,RingElement) + Headline + Power-sum (p-) basis representation + Usage + fp = toP f + Inputs + f:RingElement + element of a Symmetric or Schur ring + Outputs + fp:RingElement + element of a Symmetric ring + Description + Text + + Given a symmetric function {\tt f}, the function + {\tt toP} yields a representation of {\tt f} as a polynomial + in the power-sum symmetric functions. + + If {\tt f} is an element of a Schur ring {\tt S} then the output {\tt fp} is an + element of the Symmetric ring associated to {\tt S} (see @TO symmetricRing@). + + Example + R = symmetricRing 7; + toP(h_3*e_3) + S = schurRing(s,4) + toP S_{3,2,1} + + Text + + This also works over tensor products of Symmetric/Schur rings. + + Example + R = schurRing(r, 4, EHPVariables => (a,b,c)); + S = schurRing(R, s, 2, EHPVariables => (x,y,z)); + T = schurRing(S, t, 3); + A = symmetricRing T; + f = (r_1+s_1+t_1)^2 + toP f + + Text + + {\bf Variant bases.} Same caveat as @TO toE@ and @TO toH@: on + elements of a variant-basis Schur ring, labels are treated as + plain Schur labels; use {\tt toP(toS f)} to get the character + expansion in the power-sum basis. + + Example + Sp = schurRing(QQ, sp, 3, GroupActing => "Sp"); + toP sp_{2,1} + toP toS sp_{2,1} + + Text + + Composing {\tt toP} with @TO toE@ or @TO toH@ exercises + Newton's identities: {\tt toP(toE f)} returns {\tt f} in the + $p$-basis, and {\tt toH(toP f)} returns it back in the $h$-basis. + + Example + R = symmetricRing(QQ,5); + toP toE (p_2 * p_3) + toH toP (h_2 * h_3) + + Text + + The output variables for {\tt toP} use the third slot of + @TO EHPVariables@. + + Example + Rxyz = symmetricRing(QQ, 4, EHPVariables => (x,y,z)); + toP(y_3) + toP(x_1 * x_2) + + Text + + {\bf Stable rings.} Rank-infinite SchurRings raise an error; + use @TO specialize@ to fix a rank first. + + Example + Sinf = schurRing(QQ, w, infinity); + try toP w_{2,2} else "error: stable ring has no symmetricRing" + toP specialize(w_{2,2}, 4) + + SeeAlso + toH + toS + toE + toSymm + specialize + EHPVariables +/// + +doc /// +Key + jacobiTrudi + (jacobiTrudi,BasicList,Ring) +Headline + Jacobi-Trudi determinant +Usage + f = jacobiTrudi(lambda,R) +Inputs + lambda:BasicList + a nonincreasing list of integers, or a partition + R:Ring + a Symmetric ring +Outputs + f:RingElement + an element of a Symmetric ring +Description + Text + + Given a partition {\tt lambda} and Symmetric ring {\tt R}, + the method evaluates the Jacobi-Trudi determinant corresponding + to the partition {\tt lambda}, yielding a representation of + the Schur function {\tt s_{lambda}} as a symmetric function + in {\tt R}. The default option is to represent this symmetric + function in terms of {\tt e-}polynomials. + + Example + R = symmetricRing(QQ,10); + jacobiTrudi({3,2,2,1},R) + jacobiTrudi(new Partition from {4,4,1},R,EorH => "H") + toS oo + + Text + + Selecting {\tt EorH => "H"} uses the conjugate determinant + formula $s_\lambda = \det(h_{\lambda_i - i + j})$. The two + branches produce different {\tt e}- vs {\tt h}-polynomials + but always represent the same Schur function: + + Example + R = symmetricRing(QQ,8); + lam = {4,3,2,1}; + fe = jacobiTrudi(lam,R,EorH => "E"); + fh = jacobiTrudi(lam,R,EorH => "H"); + toS fe + toS fh + toS fe == toS fh + + Text + + The routine caches intermediate subdeterminants on the ring + via @TO [jacobiTrudi,Memoize]@, so a second call on a large + partition returns almost instantly: + + Example + R = symmetricRing(QQ,6); + elapsedTime jacobiTrudi({4,3,2,1},R); + elapsedTime jacobiTrudi({4,3,2,1},R); + + Text + + Passing a partition through @TO toSymm@ applied to the + corresponding Schur label reproduces the Jacobi-Trudi output: + + Example + R = symmetricRing(QQ,5); + S = schurRing R; + jacobiTrudi({3,2,1},R) == toSymm(S_{3,2,1}) + + Text + + {\tt jacobiTrudi} works over tensor products of Symmetric + rings, producing a determinant in the outermost set of + generators: + + Example + R = symmetricRing(QQ, 4, EHPVariables => (a,b,c)); + T = symmetricRing(R, 3, EHPVariables => (x,y,z)); + jacobiTrudi({2,1},T) + jacobiTrudi({3,2},T, EorH => "H") +/// + +doc/// + Key + EorH + [jacobiTrudi,EorH] + Headline + e- or h- representation of Jacobi-Trudi determinant + Usage + EorH => s + Inputs + s:String + either "E" or "H" + Description + Text + This option allows one to choose between evaluating the + Jacobi-Trudi determinant in the {\tt e}- or {\tt h}- basis. + If the length of the conjugate partition {\tt lambda'} is + larger than the length of {\tt lambda}, then it is + computationally less expensive to set the option {\tt EorH} + to {\tt "H"}. Otherwise, the default value {\tt "E"} is more + efficient. + + Example + R = symmetricRing(QQ,8); + fe = jacobiTrudi({2,2,2,2,2},R,EorH => "E"); + fh = jacobiTrudi({2,2,2,2,2},R,EorH => "H"); + fe + fh + + Text + + Although the two polynomials are superficially different, + they are equal as symmetric functions, as seen after + applying @TO toS@: + + Example + toS fe == toS fh + + Text + + When the conjugate partition is much longer than + {\tt lambda} itself, the {\tt "H"}-branch requires a + smaller determinant and runs measurably faster. For + example on {\tt lambda = (10)} the conjugate is + $(1^{10})$, so {\tt "H"} only sets up a 1x1 determinant: + + Example + R = symmetricRing(QQ,12); + elapsedTime jacobiTrudi({10},R,EorH => "E",Memoize => false); + elapsedTime jacobiTrudi({10},R,EorH => "H",Memoize => false); +/// + +doc/// + Key + [jacobiTrudi,Memoize] + Headline + Store values of the jacobiTrudi function. + Usage + Memoize => b + Inputs + b:Boolean + Description + Text + + If the option is set to {\tt true} then all the values of the jacobiTrudi + function that are computed are recorded into a special hash table attached + to the symmetric ring inside which the computations are done. This makes + repeated evaluations on related partitions substantially faster, at the + cost of some extra memory in the ring. + + Example + R = symmetricRing(QQ,6); + jacobiTrudi({4,3,2,1},R,Memoize => true) == jacobiTrudi({4,3,2,1},R,Memoize => false) + elapsedTime jacobiTrudi({5,4,3,2,1},R,Memoize => true); + elapsedTime jacobiTrudi({5,4,3,2,1},R,Memoize => true); + elapsedTime jacobiTrudi({5,4,3,2,1},R,Memoize => false); +/// + +doc /// +Key + plethysm + (plethysm,RingElement,RingElement) +Headline + Plethystic operations on representations +Usage + pl = plethysm(f,g) + pl = f @ g +Inputs + f:RingElement + element of Symmetric ring or Schur ring + g:RingElement + element of Symmetric ring or Schur ring +Outputs + pl:RingElement + element of the ring of {\tt g} +Description + Text + Given a symmetric function {\tt f} and the character {\tt g} of a virtual representation of a product + of general linear and symmetric groups, the method computes the character of the + plethystic composition of {\tt f} and {\tt g}. The result of this operation will be an element of + the ring of {\tt g}. We use the binary operator @TO symbol \@ @ as a synonym for the plethysm function. + + Example + R = symmetricRing(QQ,5); + pl = plethysm(h_2,h_3) + toS pl + S = schurRing(QQ,q,3); + h_2 @ q_{2,1} + plethysm(q_{2,1},q_{2,1}) + T = schurRing(S,t,2,GroupActing => "Sn"); + plethysm(q_{1,1,1}-q_{2,1}+q_{3},q_{2,1}*t_2-t_{1,1}) + p_3 @ (q_{2,1}*t_2-t_{1,1}) + + Text + + Since the power-sum basis behaves multiplicatively under + plethysm, one has the identity + {\tt plethysm(p_m, p_n) = p_{mn}}, and more generally any + complete/power-sum pair commutes under plethysm, as shown + below: + + Example + R = symmetricRing(QQ,8); + toS plethysm(p_2,p_3) == toS p_6 + toS plethysm(h_3,p_2) == toS plethysm(p_2,h_3) + + Text + + The symmetric and antisymmetric squares of an irreducible + representation decompose via {\tt Sym^2 = plethysm(\{2\},-)} + and {\tt \Lambda^2 = plethysm(\{1,1\},-)}, and their sum + recovers the ordinary tensor square: + + Example + S = schurRing(QQ,s,4); + sym2 = plethysm({2},s_{2,1}) + wedge2 = plethysm({1,1},s_{2,1}) + s_{2,1}*s_{2,1} - sym2 - wedge2 + + Text + + Plethysm makes sense for representations of symmetric groups + as well. In an {\tt Sn}-flavored ring, {\tt plethysm(lambda, chi)} + applies the Schur functor {\tt S_lambda} to the + {\tt Sn}-representation {\tt chi}: + + Example + Sn = schurRing(QQ,c,4,GroupActing => "Sn"); + plethysm({2,1},c_{2,1,1}) + + Text + + Plethysm also works over tensor products of Schur rings, + mixing a GL factor and an {\tt Sn} factor: + + Example + G = schurRing(QQ,g,3); + N = schurRing(G,n,3,GroupActing => "Sn"); + plethysm({2},g_1*n_{2,1}) +/// + +doc /// +Key + (plethysm,BasicList,RingElement) +Headline + Plethystic operations on representations +Usage + pl = plethysm(lambda,g) +Inputs + lambda:BasicList + nonincreasing sequence of positive integers, or partition + g:RingElement + element of Symmetric ring or Schur ring +Outputs + pl:RingElement + element of the ring of {\tt g} +Description + Text + + The method computes the character of the representation obtained by applying the Schur functor + {\tt S_{\lambda}} to the representation with character {\tt g}, where + {\tt \lambda} is a partition. + + Example + R = symmetricRing(QQ,3); + S = schurRing(QQ,q,3); + toE plethysm({2,1},e_1*e_2-e_3) + plethysm({2,1,1},q_{1,1}) + T = schurRing(S,t,4,GroupActing => "Sn"); + plethysm({1,1},q_1*t_{3,1}) + + Text + + Even simple plethysms of Schur functions are not obvious a priori. + For example, {\tt Sym^2} of the antisymmetric square + $\Lambda^2 V = S_{1,1}V$ breaks up as: + + Example + S = schurRing(QQ,s,4); + plethysm({2},s_{1,1}) + + Text + + Applying a partition directly lets one extract isotypic + summands, e.g.\ the two pieces of {\tt V^{\otimes 2}} for + an {\tt Sn}-representation: + + Example + Sn = schurRing(QQ,c,4,GroupActing => "Sn"); + sym2chi = plethysm({2}, c_{3,1}) + wed2chi = plethysm({1,1}, c_{3,1}) + c_{3,1}*c_{3,1} - sym2chi - wed2chi + + Text + + For representations of products of groups, the plethysm is + applied diagonally; here on a GL x GL tensor product: + + Example + A = schurRing(QQ,a,3); + B = schurRing(A,b,2); + plethysm({2},a_1*b_1) +/// + +doc /// +Key + (plethysm,RingElement,ClassFunction) + (plethysm,BasicList,ClassFunction) +Headline + Plethystic operations on class functions +Usage + pl = plethysm(f,cF) + pl = plethysm(lambda,cF) +Description + Text + + These methods describe the result of applying plethystic operations to a virtual + character of a symmetric group. These operations are described either via a symmetric + function {\tt f}, or a partition {\tt lambda}. Since {\tt cF} corresponds to an {\tt S_n}- + representation, the option @TO GroupActing@ is irrelevant in this case. + + Example + cF = new ClassFunction from {{2} => 1, {1,1} => -1}; + pl1 = plethysm({1,1},cF) + R = symmetricRing 5; + pl2 = plethysm(e_1+e_2,cF) + S = schurRing R; + symmetricFunction(cF,S) + symmetricFunction(pl1,S) + symmetricFunction(pl2,S) + + Text + + Applying the partition {\tt \{2\}} to the sign character of + {\tt S_2} gives {\tt Sym^2} of the sign, which is the trivial + representation of {\tt S_4}: + + Example + sgn = new ClassFunction from {{2} => -1, {1,1} => 1}; + pl = plethysm({2},sgn) + symmetricFunction(pl, schurRing(QQ, s, 4, GroupActing => "Sn")) + + Text + + Plethysm by a power-sum class function is Adams-type: + {\tt p_k} sends a representation to its {\tt k}-th Adams + operation (on class functions). + + Example + cF2 = new ClassFunction from {{3} => 2, {2,1} => 0, {1,1,1} => -1}; + R2 = symmetricRing 4; + plethysm(p_2, cF2) +/// +-* +doc /// +Key + (exteriorPower,ZZ,RingElement) +Headline + Exterior power of a representation +Usage + ep = exteriorPower(n,rep) +Inputs + n:ZZ + rep:RingElement + an element of a SchurRing +Outputs + ep:RingElement +Description + Text + Given a representation {\tt rep} of a product of general linear + groups, and a positive integer {\tt n}, the function returns the + {\tt n}-th exterior power of this representation. + Example - S = schurRing(QQ,s,4) - R = schurRing(r,infinity) - - Text - Note that in the above, {\tt n} is allowed to be equal to {\tt \infty}. However, in this - version of the package, many of the features from the case {\tt n} finite are missing - from the infinite case, so the user is advised to use large values for {\tt n} as a - substitute, whenever necessary. + S = schurRing(QQ,s,2) + T = schurRing(S,t,3) + exteriorPower(4,s_{1}+t_{1}) +/// - We determine the relative dimension of a SchurRing over its base using the @TO numgens@ function: +doc /// +Key + (symmetricPower,ZZ,RingElement) +Headline + Symmetric power of a representation +Usage + ep = symmetricPower(n,rep) +Inputs + n:ZZ + rep:RingElement + an element of a SchurRing +Outputs + ep:RingElement +Description + Text + + Given a representation {\tt rep} of a product of general linear + groups, and a positive integer {\tt n}, the function returns the + {\tt n}-th symmetric power of this representation. + + Example + S = schurRing(QQ,s,2) + T = schurRing(S,t,3) + symmetricPower(4,s_{1}+t_{1}) +/// +*- + +doc /// +Key + schurResolution + (schurResolution,RingElement,List,List) + (schurResolution,RingElement,List) +Headline + Compute an ``approximate'' equivariant resolution of a module. +Usage + resol = schurResolution(rep,M,lS) + resol = schurResolution(rep,M) +Inputs + rep:RingElement + element of a SchurRing + M:List + list of representations, corresponding to the homogeneous components of a module {\tt M}. + lS:List + list of representations, corresponding to the homogeneous components of a polynomial ring {\tt S}. +Outputs + resol:List +Description + Text + + Given a representation {\tt rep} of a (product of) general linear + or symmetric group(s) {\tt G}, we consider the symmetric algebra {\tt S = Sym(rep)} + and an {\tt S}-module {\tt M} which is also a {\tt G}-module in such + a way that the {\tt S}-module structure on {\tt M} respects the + {\tt G}-action. More generally, {\tt S} can be any graded ring, of which one inputs only + finitely many homogeneous components as a list {\tt lS} of characters of {\tt G}. The main reason + why we allow this generality is because most of the time it is computationally expensive to calculate + the symmetric powers of the representation {\tt rep}, so we give the user the option to compute these + symmetric powers by different methods and use the results as input for the schurResolution routine. + + We are interested in computing an equivariant + resolution of {\tt M}. This depends on both the {\tt G}- and {\tt S}-module structure + of {\tt M} in general, but in many examples that occur in practice, it turns out that + the differentials in the resolution have maximal rank among all + {\tt G}-module homomorphisms between the free modules in the resolution. + We will therefore assume that this is the case for the module {\tt M} that we are + trying to resolve, and thus disregard its {\tt S}-module structure. + + More precisely, the assumptions that we make about {\tt M} are as follows: {\tt M} is + a graded {\tt S}-module, with {\tt M_i = 0} for {\tt i<0}, where the grading on {\tt S} is standard, + given by setting the degrees of the elements of {\tt rep} equal to 1. Since we assumed + that the {\tt G}-structure of {\tt M} determines the syzygies, all the relevant + information is concentrated in finitely many homogeneous components of {\tt M} (namely + up to {\tt reg(M)+pd(M)}, the sum of the regularity and the projective dimension of + {\tt M}). We will thus assume that {\tt M} + is given as a list of {\tt G}-representations, corresponding to (a subset of) the + relevant homogeneous components. The function {\tt schurResolution} takes as + inputs the representation {\tt rep}, the module {\tt M}, and as optional arguments a {\tt DegreeLimit} + {\tt d}, and a {\tt SyzygyLimit} {\tt c}. The ring {\tt S} itself can occur as input data, being + described as a list of {\tt G}-representations, just like {\tt M}. + The routine outputs the generators of degree at most {\tt d} of the + first {\tt c+1} syzygy modules (from {\tt 0} to {\tt c}). They are listed as a + sequence of pairs, consisting of the degree of the generators of the syzygy modules + together with the characters of the {\tt G}-representations they correspond to. + If the syzygy bound {\tt c} is not given, + then all syzygy modules are computed. If the degree bound {\tt d} is not given, then + it is assumed to be equal to the largest degree among the homogeneous components of + {\tt M} in the input, i.e. one less than the length of the @TO List@ {\tt M}. + + The example below computes the resolution of the quadratic Veronese + surface in {\tt P^5}. + + Example + S = schurRing(QQ,s,3) + rep = s_{2} + M = {1_S,s_{2},s_{4},s_{6},s_{8},s_{10},s_{12}} + schurResolution(rep,M) - Example - numgens S - numgens R - Text - - For {\tt k\leq n}, one may interpret the degree - {\tt k} homogeneous component of a @TO SchurRing@ as the representation ring of the symmetric - group {\tt S_k}. In this ring, the multiplication is different than the one in - the representation ring of {\tt GL(n)}. By default, the elements of a @TO SchurRing@ are - interpreted as (virtual) characters of - a general linear group. This interpretation is controlled by the option @TO GroupActing@, - whose default value is "GL". To indicate that the elements of a Schur ring should - be interpreted as characters of the symmetric group, one has to set the option @TO GroupActing@ - to "Sn". - Example - Q = schurRing(q,4,GroupActing => "Sn") + Next, we compute the syzygies of degree at most {\tt 7} in the resolution + of the cubic Veronese embedding of {\tt P^2}. - Text - - A monomial in {\tt S} represents the irreducible representation with a given highest weight. - The standard {\tt GL(4)}-representation is - Example - V = s_1 + rep = s_{3} + M = {1_S,s_{3},s_{6},s_{9},s_{12},s_{15},s_{18},s_{21},s_{24},s_{27}} + d = 7 + schurResolution(rep,M,DegreeLimit => d) Text - We may see the dimension of the corresponding irreducible representation using @TO dim@: - + We can compute the resolution of the ideal of {\tt 2\times 2} minors of a {\tt 3\times 4} + matrix, which corresponds to the Segre embedding of {\tt P^2\times P^3}: + Example - dim V + T = schurRing(S,t,4) + rep = s_1 * t_1 + M = {1_T} | apply(splice{1..8},i -> s_i * t_i) + schurResolution(rep,M) Text - Multiplication of elements corresponds to tensor product of representations. The - value is computed using a variant of the Littlewood-Richardson rule. + + The following example computes the equivariant resolution of the residue field of a + polynomial ring in {\tt n=5} variables, with respect to the action of the symmetric + group {\tt S_n}. Example - V * V - V^3 + n = 5; + S = schurRing(QQ,s,n,GroupActing => "Sn"); + rep = s_n + s_{n-1,1}; + M = {s_n} + schurResolution(rep,M,DegreeLimit => n) - Text - - The third symmetric power of {\tt V} is obtained by - - Example - W = s_{3} - dim W - Text - - and the third exterior power of {\tt V} can be obtained using + + Generalizing this, we can compute the equivariant resolution of the quotient of the + polynomial ring in {\tt n=5} variables by the ideal of square-free monomials of + degree two, with respect to the action of the symmetric group {\tt S_n}. Example - U = s_{1,1,1} - dim U - + M = {s_n} | splice{n:rep}; + schurResolution(rep,M) + Text - - Alternatively, one can use the functions @TO symmetricPower@ and @TO exteriorPower@: - + + Ordinary Segre embeddings P^a x P^b can be treated as above by + taking a tensor product of GL-factors. For P^1 x P^3, keeping + only the syzygies in degree at most {\tt 4}: + Example - W = symmetricPower(3,V) - U = exteriorPower(3,V) - + U = schurRing(QQ,u,2); + V = schurRing(U,v,4); + rep = u_1 * v_1; + M = {1_V} | apply(splice{1..5}, i -> u_i * v_i); + schurResolution(rep,M,DegreeLimit => 4) + Text - - We can in fact take symmetric powers and exterior powers of any representation: - + + The cubic Veronese of P^2 has a rich equivariant resolution; + asking for the first few syzygies of total degree at most {\tt 4} + gives the classical Koszul-type pattern: + Example - exteriorPower(2,W) - symmetricPower(2,U) + S = schurRing(QQ,s,3); + rep = s_{3}; + M = apply(splice{0..8}, i -> s_{3*i}); + schurResolution(rep,M,DegreeLimit => 4, SyzygyLimit => 3) Text - - and compute even more general forms of @TO plethysm@: - + + A larger example: the equivariant resolution of the residue + field of {\tt Sym(V)} at {\tt rank(V) = 6} under the + symmetric-group action: + Example - plethysm(W+U,W+U) - + n = 6; + Sb = schurRing(QQ,s,n,GroupActing => "Sn"); + rep = s_n + s_{n-1,1}; + schurResolution(rep, {s_n}, DegreeLimit => n) + +/// + +doc /// +Key + [schurResolution,DegreeLimit] +Headline + Specifies the maximal degree of syzygies to be computed +Description Text - - Alternatively, we can use the binary operator @TO symbol \@ @ to compute plethysm: - + This is an optional argument for the @TO schurResolution@ routine. It specifies an upper bound for the + degrees of the generators of the syzygy modules in the equivariant resolution of an equivariant module {\tt M} + to be computed by the routine. If a {\tt DegreeLimit} is not specified, then it is assumed to be equal to the + maximal degree in which the module {\tt M} is specified as a representation. + Example - s_2 @ s_3 - (W+U) @ (W+U) + A = schurRing(a,3,GroupActing => "Sn"); + B = schurRing(A,b,2); + rep = (a_3 + a_{2,1}) * b_1 + d = dim rep + M = {a_3 * 1_B}; + sR = schurResolution(rep,M,DegreeLimit => d) Text - - All the above calculations assume that we're dealing with representations of {\tt GL(4)}. - But as symmetric functions of degree three, {\tt W} and {\tt U}, can be thought of as characters of the - symmetric group {\tt S_3}. Let us first ``move'' these symmetric functions into a Schur ring - designed to deal with characters of symmetric groups (like the ring {\tt Q} defined - above): - + + Decreasing {\tt DegreeLimit} truncates the output to syzygies + of total degree at most the specified value, while the + representations themselves are unchanged: + Example - W' = toS(W,Q) - U' = toS(U,Q) + S = schurRing(QQ,s,3); + rep = s_{2}; + M = {1_S,s_{2},s_{4},s_{6},s_{8},s_{10},s_{12}}; + schurResolution(rep,M) + schurResolution(rep,M,DegreeLimit => 3) + +SeeAlso + [schurResolution,SyzygyLimit] +/// +doc /// +Key + [schurResolution,SyzygyLimit] +Headline + Specifies the number of syzygy modules to be computed +Description Text - - Now {\tt W'} corresponds to the trivial representation of {\tt S_3}, - and {\tt U'} to the sign representation. As such, we can tensor them together using the - function @TO internalProduct@, or the binary operator @TO symbol *@. - + This is an optional argument for the @TO schurResolution@ routine. It specifies an upper bound for the + number of syzygy modules in the equivariant resolution of an equivariant module {\tt M} to be computed + by the routine. If a {\tt SyzygyLimit} is not specified, then all syzygy modules are computed. + + The example below computes the {\tt 0}-th to {\tt 3}-rd syzygy modules of the {\tt 5}-th Veronese embedding + of {\tt P^2}. + Example - W' * U' + S = schurRing(s,3); + rep = s_{5}; + M = {1_S,s_{5},s_{10},s_{15},s_{20},s_{25},s_{30}}; + schurResolution(rep,M,SyzygyLimit => 3) + + Text + + Lowering {\tt SyzygyLimit} simply chops the output at the + requested homological position. For the quadratic Veronese + of P^2, asking for only the first syzygy module yields: + + Example + T = schurRing(QQ,t,3); + rep2 = t_{2}; + M2 = {1_T,t_{2},t_{4},t_{6},t_{8},t_{10},t_{12}}; + schurResolution(rep2,M2,SyzygyLimit => 1) + schurResolution(rep2,M2,SyzygyLimit => 2) + +SeeAlso + [schurResolution,DegreeLimit] +/// + +doc /// + Key + schurLevel + (schurLevel,Ring) + Headline + Number of SchurRings the ring is a tensor product of. + Usage + lev = schurLevel(R) + Inputs + R:Ring + Outputs + lev:ZZ + Description + Text + + For the representation ring {\tt R} of a product of {\tt lev} + general linear and/or symmetric groups, the function returns + {\tt lev}. If {\tt R} is not a representation ring, the + function returns 0. + + Example + R = schurRing(QQ,r,3); + S = schurRing(R,s,5); + T = schurRing(S,t,2); + schurLevel R + schurLevel S + schurLevel T + schurLevel QQ + Text + + A three-level tower mixing {\tt GL}-factors with an {\tt S_n}-factor + counts each layer, regardless of which group is acting: + + Example + A = schurRing(QQ,a,3); + B = schurRing(A,b,4); + C = schurRing(B,c,2,GroupActing => "Sn"); + schurLevel C + + Text + + A Symmetric ring (produced by @TO symmetricRing@) sits over a + single tower slot and reports {\tt schurLevel} equal to 1, while + an ordinary polynomial ring or the base field report 0: + + Example + schurLevel(symmetricRing(QQ,5)) + schurLevel(QQ[x,y,z]) + schurLevel ZZ +/// + +doc /// + Key + (partitions,Set,BasicList) + Headline + Partitions of a set + Usage + par = partitions(S,L) + Inputs + S:Set + L:BasicList + a nonincreasing list of integers, or a partition + Outputs + par:List + Description + Text + + Given a set {\tt S} and a partition {\tt L=\{l_1\geq l_2\geq\cdots\}}, + the method returns the list of set-partitions of {\tt S} of type + {\tt L}, i.e. ways of writing {\tt S=S_1\cup S_2\cup\cdots} with the + {\tt S_i} pairwise disjoint and {\tt |S_i|=l_i}. The blocks come out + as an unordered collection of sets. + + Example + partitions(set{1,2,3,4},{2,1,1}) + partitions(set{a,b,c,d,e},new Partition from {3,2}) + + Text + + Changing the shape {\tt L} changes the cycle type. Two blocks of + size 2 and one fixed point on five points is the number of + permutations of cycle type {\tt (2,2,1)} divided by the size of + the corresponding centralizer: + + Example + partitions(set{1,2,3,4,5},{2,2,1}) + #partitions(set{1,2,3,4,5},{2,2,1}) + + Text + + Passing a @TO Partition@ is equivalent to passing the underlying + list; counting block-partitions of shape {\tt (3,1)} recovers + {\tt 4 \choose 1}: + + Example + partitions(set{1,2,3,4},new Partition from {3,1}) + #partitions(set{1,2,3,4},new Partition from {3,1}) + + Text + + Supplying a single-block shape returns a singleton list: the + only set-partition of type {\tt (n)} is {\tt S} itself. + + Example + partitions(set{1,2,3},{3}) +/// + +-* +doc /// + Key + (chi,BasicList,BasicList) + Headline + Irreducible character of symmetric group +Usage + v = chi(lambda,rho) +Inputs + lambda:BasicList + a nondecreasing list of positive integers, or a partition + rho:BasicList + a nondecreasing list of positive integers, or a partition +Outputs + v:QQ +Description Text + + Given two partitions {\tt lambda} and {\tt rho} of the integer {\tt N}, this method + computes the value of the irreducible character of the symmetric group + corresponding to the partition {\tt lambda} evaluated on + any permutation of cycle-type {\tt rho}. + + The character of the trivial representation takes the value + 1 on any permutation: - We can generate the class function corresponding to an {\tt S_n}-representation, using - the function @TO classFunction@: - - Example - cfW = classFunction(W') - cfU = classFunction(U') - - Text - - We can multiply class functions together, and transform class functions into symmetric - functions using the function @TO symmetricFunction@: - Example - cfWU = cfW * cfU - symmetricFunction(cfWU,Q) + chi({4},{2,1,1}) Text - The result of the previous computation is of course the same as that of taking the product - of {\tt W'} and {\tt U'}. - - We can take exterior and symmetric powers of {\tt S_n}-representations, just as for - {\tt GL}-modules (compare to {\tt o16} and {\tt o17}): - + The character of the sign representation takes the value -1 on + a cycle of length 4: + Example - exteriorPower(2,W') - symmetricPower(2,U') + chi({1,1,1,1},{4}) +SeeAlso + symmetricFunction + classFunction +/// +*- +doc /// +Key + SchurRingElement +Headline + A type describing elements of a SchurRing +Description Text - - We can write any symmetric function in terms of the standard {\tt e}- (elementary - symmetric), {\tt h}- (complete) and {\tt p}- (power sum) bases, using the functions - @TO toE@, @TO toH@, @TO toP@ respectively: - + Elements of any @TO SchurRing@ -- whether a {\tt GL}-ring, a + symmetric-group ring, or an orthogonal/symplectic ring -- have + type {\tt SchurRingElement}. Products, sums and (in characteristic + zero) rational scalings of Schur ring elements are again of this + type. + Example - toE U - toH U - toP W - + S = schurRing(s,5) + a = s_{3,2,1} + instance(a,SchurRingElement) + + T = schurRing(S,t,3,GroupActing => "Sn") + b = t_{2,1}+t_3 + instance(a*b,SchurRingElement) + Text - - These expressions live in the Symmetric ring associated to {\tt S}, which can be obtained - using the function @TO symmetricRing@: - + The same type is used for symplectic characters. Multiplying two + {\tt Sp}-characters stays inside the ring: + Example - A = symmetricRing S - + Sp = schurRing(QQ,sp,3,GroupActing => "Sp"); + u = sp_{2,1}; + instance(u,SchurRingElement) + instance(u*u, SchurRingElement) + Text - - Similarly, any Symmetric ring has a Schur ring attached to it, which can be obtained using - the function @TO schurRing@: - - Example - schurRing A === S - - Text - - We construct tensor products of Schur rings iteratively by allowing Schur rings over - base rings that are also Schur rings: - + For an {\tt Sn}-flavored Schur ring the ordinary ring + multiplication is the tensor product of characters, while + @TO internalProduct@ gives the pointwise (Kronecker) product: + Example - T = schurRing(S,t,3) - + Sn = schurRing(QQ,c,4,GroupActing => "Sn"); + internalProduct(c_{3,1}, c_{2,1,1}) + instance(c_{3,1} * c_{2,1,1}, SchurRingElement) + Text - - The Schur ring {\tt T} can thus be thought of as the representation ring of - {\tt GL(V)\times GL(V')}, where {\tt V} is as before a vector space of dimension - {\tt 4}, and {\tt V'} is a vector space of dimension {\tt 3}. The representation - corresponding to {\tt V'} is - + In a two-level ring the SchurRingElement type is closed under + multiplication across levels: + Example - V' = t_1 - + G = schurRing(QQ,g,3); + H = schurRing(G,h,2); + z = g_{1,1} * h_{2} + instance(z, SchurRingElement) +/// + +doc /// + Key + (dim,List,SchurRingElement) + (dim,Thing,SchurRingElement) + (dim,SchurRingElement) + Headline + dimension of representation + Usage + d = dim(lis,s) + d = dim(n,s) + d = dim s + Inputs + lis: List + or @TO Thing@ + s: SchurRingElement + Outputs + d: ZZ + or @TO Expression@ + Description + Text + + The method returns the dimension of the virtual representation whose + character is represented by {\tt s}. + + Example + S = schurRing(s,3) + dim s_2 + T = schurRing(t,4,GroupActing => "Sn") + dim t_{2,2} + U = schurRing(T,u,3) + dim (t_{2,2}*u_2) + + Text + + Schur characters see the rank of the ambient vector space. + The representation {\tt s_{2,1}} of {\tt GL(3)} is 8-dimensional, + while the same shape in {\tt GL(4)} gives a 20-dimensional + representation: + + Example + dim ((schurRing(s3,3))_{2,1}) + dim ((schurRing(s4,4))_{2,1}) + + Text + + Dimensions are also computed for symplectic and orthogonal + characters. For orthogonal groups the option @TO OddOrEven@ + selects {\tt O(2m+1)} versus {\tt O(2m)}: + + Example + Sp = schurRing(QQ, sp, 3, GroupActing => "Sp"); + dim sp_{2,1} + Oodd = schurRing(QQ, od, 3, GroupActing => "O", OddOrEven => "Odd"); + dim od_{2,1} + Oeven = schurRing(QQ, oe, 3, GroupActing => "O", OddOrEven => "Even"); + dim oe_{2,1} + + Text + + If {\tt S} is a @TO SchurRing@ of level 1, the ring of polynomial representations of some {\tt GL(V)}, it + may sometimes be convenient to compute dimensions of {\tt GL(V)}-representations symbolically, without + specifying the dimension of {\tt V}. Letting {\tt n} denote the parameter corresponding to {\tt dim(V)} + we have for example: + + Example + S = schurRing(s,3) + dim(n,s_2) + dim(n,s_{1,1}) + dim(n,s_{2,1}) + + Text + + Similar calculations make sense over products of general linear groups. The dimensions of the representations + can be computed symbolically as functions of a number of parameters + equal to the @TO schurLevel@ of the ring. The parameters corresponding to levels where the group acting + is a symmetric group don't have a good interpretation, so they are disregarded in the dimension calculation. + The order of the input parameters is the descending order of the @TO schurLevel@s: in the example below + {\tt a} corresponds to {\tt Q}, {\tt b} corresponds to {\tt T} and {\tt c} corresponds to {\tt S}. + + Example + S = schurRing(s,3) + T = schurRing(S,t,4) + Q = schurRing(T,q,5,GroupActing => "Sn") + dExpr = dim({a,b,c},s_{2}*t_{1,1}*q_{4,1}) + P = QQ[a,b,c] + value dExpr + dim({1,2,3},s_{2}*t_{1,1}*q_{4,1}) + + Text + + Over a two-level {\tt GL\times GL} tower the formula factors + as a product of dimensions, one per level, in descending + {\tt schurLevel} order: + + Example + A = schurRing(aR,3); + B = schurRing(A,bR,2); + dim(aR_{2,1} * bR_{1,1}) + dim({4,5}, aR_{2,1} * bR_{1,1}) +/// + + +doc /// +Key + ClassFunction + (symbol +,ClassFunction,ClassFunction) + (symbol -,ClassFunction,ClassFunction) + (symbol *,ClassFunction,ClassFunction) + (symbol *,ClassFunction,RingElement) + (symbol *,RingElement,ClassFunction) + (symbol *,ClassFunction,Number) + (symbol *,Number,ClassFunction) + (symbol ==,ClassFunction,ClassFunction) +Headline + The class of all Class functions +Description Text - - The function @TO schurLevel@ indicates the number of Schur rings that have been - tensored together to obtain any given ring: - + A class function (or virtual character of a symmetric group {\tt S_n}) + is a function that is constant on the conjugacy classes of {\tt S_n}. + Class functions for {\tt S_n} are in one-to-one correspondence with + symmetric functions of degree {\tt n}. The class functions corresponding + to actual representations of {\tt S_n} are called {\tt characters}. + + The character of the standard representation of {\tt S_3} is + Example - schurLevel T - schurLevel S - schurLevel QQ - + S = schurRing(QQ,s,3); + classFunction(s_{2,1}) + Text - - We can now check Cauchy's formula for decomposing symmetric/exterior powers of a - tensor product: - + The character of the sign representation of {\tt S_5} is + Example - symmetricPower(3,V*V') - exteriorPower(3,V*V') - + S = schurRing(QQ,s,5); + classFunction(s_{1,1,1,1,1}) + Text - - We end with the computation of the {\tt GL(n)}- and {\tt S_n}-equivariant resolutions - of the residue field of a polynomial ring in {\tt n} variables. The function that does - this calculation, @TO schurResolution@, is based on an empirical method, which gives - the correct answer in surprisingly many situations. - - In the {\tt GL(n)} situation, we are resolving the residue field which as a representation - has character {\tt 1_S}. The space of linear forms in the polynomial ring - considered as a {\tt GL}-representation has character {\tt V = s_1}. - + We can go back and forth between class functions and symmetric functions. + Example - n = 4 - M = {1_S} - schurResolution(V,M,DegreeLimit => n) - + R = symmetricRing(QQ,3); + cF = new ClassFunction from {{1,1,1} => 2, {3} => -1}; + sF = symmetricFunction(cF,R) + toS sF + classFunction sF + Text - - Not surprisingly, the syzygy modules are generated by the exterior powers of {\tt V}. + We can add, subtract, multiply, scale class functions: - The residue field as a representation of the symmetric group {\tt S_n} - has character {\tt s_n}. The space of linear forms in the polynomial ring - considered as an {\tt S_n}-representation coincides with the permutation representation - of {\tt S_n}, thus has character {\tt s_n + s_{n-1,1}}. + Example + S = schurRing(QQ,s,4); + c1 = classFunction(S_{2,1,1}-S_{4}); + c2 = classFunction(S_{3,1}); + c1 + c2 + c1 * c2 + 3*c1 - c2*2 + + Text + The trivial and sign representations of {\tt S_4} are the characters + of the shapes {\tt (4)} and {\tt (1,1,1,1)}. Their pointwise product + (which is {\tt c_1 * c_2} on {\tt ClassFunction}) gives the sign + representation back: Example - rep = q_n + q_(n-1,1) - M = {q_n} - sR = schurResolution(rep,M,DegreeLimit => n) - + T = schurRing(QQ,t,4); + triv = classFunction(t_{4}) + sgn = classFunction(t_{1,1,1,1}) + triv * sgn == sgn + Text - - We can check that the second syzygy module is generated by the second exterior power of the permutation - representation. - + The regular representation of {\tt S_n} has character {\tt n!} on the + identity class {\tt (1^n)} and 0 elsewhere. By Frobenius + reciprocity, it pairs trivially with the trivial character: + Example - eP2rep = exteriorPower(2,rep) - eP2rep == last sR#2#0 + reg = new ClassFunction from {{1,1,1,1} => 24} + scalarProduct(reg, triv) + + Text + Products of class functions of induced/restricted representations + recover well-known decompositions: the tensor square of the + standard representation of {\tt S_4} pairs nontrivially with both + the trivial and sign characters: + + Example + std = classFunction(t_{3,1}); + sq = std * std; + scalarProduct(sq, triv) + scalarProduct(sq, sgn) /// doc /// Key - SchurRing - (symbol _,SchurRing,List) - (symbol _,SchurRing,Sequence) - (symbol _,SchurRing,ZZ) + (degree,ClassFunction) Headline - The class of all Schur rings + Degree of virtual character Description Text - A Schur ring is the representation ring for the general linear group of {\tt n\times n} - matrices, and one can be constructed with @TO schurRing@. - - Example - S = schurRing(QQ,s,4) - - Text - - Alternatively, its elements can be interpreted as virtual characters of symmetric groups, - by setting the value of the option @TO GroupActing@ to {\tt "Sn"}. - + For a virtual character {\tt ch} of a symmetric group on {\tt n} + letters, the degree of {\tt ch} is {\tt n}. + Example - Q = schurRing(QQ,q,4,GroupActing => "Sn") + S = schurRing(s,5); + ch = classFunction s_(3,1,1) + degree ch + Text - - The element corresponding to the Young diagram {\tt \{3,2,1\}}, is obtained as follows. - + The degree of {\tt classFunction(s_\lambda)} always agrees with + {\tt |\lambda|}: + Example - s_{3,2,1} + lam = {4,2,1}; + degree classFunction(new Partition from lam) == sum lam Text - - Alternatively, we can use a @TO Sequence@ instead of a @TO List@ as the index of a Schur - function. + A sum of characters of the same {\tt S_n} keeps the same degree -- + mixing distinct partitions of {\tt 5} still yields a class function + of degree {\tt 5}: Example - s_(3,2,1) + R = symmetricRing(QQ,5); + mix = classFunction(jacobiTrudi({4,1},R)) + 2*classFunction(jacobiTrudi({3,2},R)); + degree mix +/// +doc /// +Key + symmetricFunction + (symmetricFunction,ClassFunction,Ring) +Headline + Converts class function to symmetric function +Usage + f = symmetricFunction(ch,S) +Inputs + ch:ClassFunction + S:Ring + a Symmetric or Schur ring +Outputs + f:RingElement + element of a Symmetric or Schur ring +Description Text - - For Young diagrams with only one row one can use positive integers as subscripts. - - Example - q_4 - - Text - - The name of the Schur ring can be used with a subscript to describe a symmetric - function. - + Given a virtual character {\tt cF} of a symmetric group, and given a + Symmetric ring {\tt S}, the method computes the corresponding + symmetric function as an element of {\tt S}. The conversion uses + the Frobenius characteristic map: the regular representation + ({\tt n!} on the identity, zero elsewhere) maps to {\tt n! e_1^n} + in the {\tt e}-basis, equivalently to {\tt sum_\lambda f^\lambda s_\lambda} + in the {\tt s}-basis. + Example - Q_{2,2} - S_5 - + S = symmetricRing(QQ,4); + cF = new ClassFunction from {{1,1,1,1}=>24}; + symmetricFunction(cF,S) + symmetricFunction(cF,schurRing S) + Text - - The dimension of the underlying virtual {\tt GL}-representation can be obtained - with @TO dim@. - + The standard representation of {\tt S_4} has character given by + {\tt classFunction(s_{3,1})}; passing it through {\tt symmetricFunction} + recovers {\tt s_{3,1}} on the @TO SchurRing@ side: + Example - dim s_{3,2,1} - + R = symmetricRing(QQ,4); + Sch = schurRing R; + stdCh = classFunction(jacobiTrudi({3,1},R)); + symmetricFunction(stdCh, Sch) + Text - - Multiplication in the ring comes from tensor product of representations. - + Composing {\tt classFunction} with {\tt symmetricFunction} is the + identity on the {\tt S_n}-side of the Frobenius correspondence -- + roundtripping a class function through the symmetric function + ring returns it unchanged: + Example - s_{3,2,1} * s_{1,1} - q_{2,1} * q_{2,1} + sF = symmetricFunction(stdCh, R); + classFunction sF == stdCh Text - - To extract data in an element in a SchurRing, use @TO "listForm"@: - + The trivial character of {\tt S_5} has Frobenius image {\tt h_5}; + the sign character maps to {\tt e_5}: + Example - listForm (s_{3})^2 - q_{2,1} * q_{2,1} - listForm oo - + R5 = symmetricRing(QQ,5); + symmetricFunction(classFunction{5}, R5) + symmetricFunction(classFunction{1,1,1,1,1}, R5) SeeAlso - schurRing + classFunction +-- chi /// doc /// Key - schurRing - (schurRing,Ring,Symbol,ZZ) - (schurRing,Ring,Thing,ZZ) - (schurRing,Ring,Symbol) - (schurRing,Ring,Thing) - (schurRing,Thing,ZZ) - (schurRing,Thing) + classFunction + (classFunction,RingElement) Headline - Make a SchurRing + Converts symmetric function to class function +Usage + ch = classFunction(f) +Inputs + f:RingElement + element of a Symmetric ring +Outputs + ch:ClassFunction Description Text - {\tt S = schurRing(A,s,n)} creates a Schur ring of degree {\tt n} over the base ring - {\tt A}, with variables based on the symbol {\tt s}. This is the representation ring - for the general linear group of {\tt n} by {\tt n} matrices, tensored with the ring - {\tt A}. If {\tt s} is already assigned a value as a variable in a ring, its base - symbol will be used, if it is possible to determine. - + Given a symmetric function {\tt f}, homogeneous of degree {\tt N}, + the method computes the corresponding virtual character of the + symmetric group {\tt S_N}. + + The character of the standard representation of {\tt S_5} is + Example - S = schurRing(QQ[x],s,3); - (x*s_{2,1}+s_3)^2 - + R = symmetricRing(QQ,5); + classFunction(jacobiTrudi({4,1},R)) + Text - Alternatively, the elements of a Schur ring may be interpreted as characters of - symmetric groups. To indicate this interpretation, one has to set the value of the option - @TO GroupActing@ to "Sn". - + + The character of the second exterior power of the standard representation of {\tt S_5} is + Example - S = schurRing(s,4,GroupActing => "Sn"); - exteriorPower(2,s_(3,1)) - + R = symmetricRing(QQ,5); + classFunction(jacobiTrudi({3,1,1},R)) + Text - If the dimension {\tt n} is not specified, then one should think of {\tt S} as the - full ring of symmetric functions over the base {\tt A}, i.e. there is no restriction - on the number of parts of the partitions indexing the generators of {\tt S}. - + The sign representation of {\tt S_n} corresponds to the Schur + polynomial of shape {\tt (1^n)}. Its class function takes the + value {\tt sgn(\sigma)} on a permutation of cycle type {\tt \rho} + -- that is, {\tt -1} raised to the number of even-length cycles: + Example - S = schurRing(ZZ/5,t) - (t_(2,1)-t_3)^2 + Ssign = schurRing(QQ,s,5); + classFunction(s_{1,1,1,1,1}) Text - If the base ring {\tt A} is not specified, then @TO QQ@ is used instead. + Small-{\tt n} character tables are assembled row-by-row from + {\tt classFunction}. For {\tt S_3} there are three conjugacy + classes {\tt (1^3), (2,1), (3)} and three irreducibles, and the + values are collected below: Example - S = schurRing(r,2,EHPVariables => (re,rh,rp)) - toH r_(2,1) - + R3 = symmetricRing(QQ,3); + for lam in {{3},{2,1},{1,1,1}} list classFunction(jacobiTrudi(lam,R3)) + + Text + Tensor products of {\tt S_n}-representations correspond to + {\tt internalProduct} of class functions. For {\tt S_4} the + tensor square of the standard representation decomposes into + irreducibles by pairing with each irreducible character via + @TO scalarProduct@: + + Example + R4 = symmetricRing(QQ,4); + std = classFunction(jacobiTrudi({3,1},R4)); + sq = internalProduct(std, std); + for lam in {{4},{3,1},{2,2},{2,1,1},{1,1,1,1}} list + scalarProduct(sq, classFunction(jacobiTrudi(lam,R4))) + SeeAlso - SchurRing - symmetricRing + symmetricFunction +-- chi /// doc /// Key - (coefficientRing, SchurRing) + (classFunction,BasicList) Headline - Coefficient ring of a Schur ring + Character of irreducible representation of symmetric group Usage - coefficientRing S + ch = classFunction(l) Inputs - S:SchurRing + l:BasicList + partition +Outputs + ch:ClassFunction Description Text - Given a Schur ring {\tt S}, the function returns its coefficient ring. - + Given a partition {\tt l} of {\tt N}, the method computes the + character of the irreducible {\tt S_N}-representation corresponding + to the partition {\tt l}. + Example - S = schurRing(ZZ[x],s,4); - coefficientRing S - A = schurRing(QQ,a,3); - B = schurRing(A,b,2); - coefficientRing B -/// + R = symmetricRing(QQ,7); + cF = classFunction({3,2,1}) + toS(symmetricFunction(cF,R)) -document { - Key => {SchurRingIndexedVariableTable,(symbol _,SchurRingIndexedVariableTable,Thing)}, - "This class is used as part of the implementation of a type of indexed variable used just for Schur rings.", - SeeAlso => { IndexedVariableTable } - } + Text + Enumerating the irreducible characters of {\tt S_4} and pairing + them with @TO scalarProduct@ recovers the orthonormality relations + from representation theory -- the diagonal entries are 1 and the + off-diagonal entries are 0: + + Example + chars = for lam in partitions 4 list classFunction toList lam; + matrix for a in chars list for b in chars list scalarProduct(a,b) + + Text + Irreducible characters of different shapes are orthogonal. Here + we verify orthogonality for three partitions of 6: + + Example + c1 = classFunction({3,2,1}); + c2 = classFunction({4,1,1}); + c3 = classFunction({2,2,2}); + scalarProduct(c1,c2) + scalarProduct(c2,c3) + + Text + Self-pairings return 1 for each irreducible, independent of the + shape, and the trivial (row shape) and sign (column shape) + characters are the 1-dimensional irreducibles: + + Example + scalarProduct(c1,c1) + classFunction({5}) + classFunction({1,1,1,1,1}) +SeeAlso + symmetricFunction + +/// doc /// Key - symmetricRing - (symmetricRing,Ring,ZZ) - (symmetricRing,ZZ) + scalarProduct Headline - Make a Symmetric ring -Usage - symmetricRing(A,n) - symmetricRing n -Inputs - A:Ring - n:ZZ + Standard pairing on symmetric functions/class functions Description Text - The method {\tt symmetricRing} creates a Symmetric ring of dimension {\tt n} over a base ring - {\tt A}. This is the subring of the ring of symmetric functions over the base {\tt A} - consisting of polynomials in the first {\tt n} elementary (or complete, or power sum) - symmetric functions. If {\tt A} is not specified, then it is assumed to be @TO QQ@. - + This method computes the standard Hall scalar product on the ring + {\tt \Lambda} of symmetric functions. One way to define this product + is by imposing that the collection of Schur functions {\tt s_{\lambda}} + form an orthonormal basis. + + Alternatively, by the correspondence between symmetric functions + and virtual characters of symmetric groups, this scalar product + coincides with the standard scalar product on class functions. + + The number of standard tableaux of shape {\tt \{4,3,2,1\}} is: + Example - R = symmetricRing(QQ[x,y,z],4) - e_2*x+y*p_3+h_2 - toS oo + R = symmetricRing(QQ,10); + S = schurRing(QQ,s,10); + scalarProduct(h_1^10,s_{4,3,2,1}) Text - - The elements of a Symmetric ring can be interpreted as characters of either symmetric or - general linear groups. This is controlled by the value of the option @TO GroupActing@, whose - default value is "GL" (general linear group). The other possibility for its value is - "Sn" (symmetric group). - + + The Schur basis is orthonormal: {\tt } equals + {\tt 1} if {\tt \lambda = \mu} and {\tt 0} otherwise. + + Example + T = schurRing(QQ,t,5); + scalarProduct(t_{3,1,1}, t_{3,1,1}) + scalarProduct(t_{3,1,1}, t_{2,2,1}) + scalarProduct(t_{4,1}, t_{3,2}) + + Text + + The power-sum basis is orthogonal with norms given by the + centralizer sizes: {\tt = z_\rho}. Here we + verify this for the three partitions of {\tt 3}. + Example - R = symmetricRing(QQ,3,GroupActing => "Sn") - toE symmetricPower(2,e_2) - + U = symmetricRing(QQ,4); + scalarProduct(p_3, p_3) == centralizerSize{0,0,1} + scalarProduct(p_2*p_1, p_2*p_1) == centralizerSize{1,1} + scalarProduct(p_1^3, p_1^3) == centralizerSize{3} + + Text + + By the Cauchy identity, {\tt } counts the + elements of {\tt S_n}, i.e.\ it equals {\tt n!}: + + Example + scalarProduct(p_1^4, p_1^4) SeeAlso - SchurRing + internalProduct + centralizerSize /// doc /// Key - eVariable + (scalarProduct,RingElement,RingElement) Headline - Elementary symmetric functions in a Symmetric ring + Standard scalar product of symmetric functions +Usage + sp = scalarProduct(f1,f2) +Inputs + f1:RingElement + element of a Symmetric Ring + f2:RingElement + element of a Symmetric Ring +Outputs + sp:QQ Description Text - For a Symmetric ring {\tt R} of dimension {\tt n}, {\tt R.eVariable} is a function - which assigns to each index {\tt 1\leq i\leq n} the {\tt i}-th elementary symmetric - function. If {\tt i} is outside the given bounds, an error is returned. - + + Given symmetric functions {\tt f1} and {\tt f2}, the method + computes the standard Hall pairing between {\tt f1} and {\tt f2}. + Example - R = symmetricRing(QQ,5,EHPVariables => (a,b,c)); - R.eVariable 3 + R = symmetricRing(QQ,5); + S = schurRing R + scalarProduct(h_5,p_5) + scalarProduct(S_{4,1},p_5) + + Text + + Indeed, the coefficients of {\tt s_5} and {\tt s_{4,1}} in the + s-basis expansion of {\tt p_5} are as computed above: + + Example + toS p_5 + + Text + + The pairing {\tt } equals {\tt (-1)^{n-1}}, reflecting + the sign character of the symmetric group: + + Example + scalarProduct(e_2, p_2) + scalarProduct(e_3, p_3) + scalarProduct(e_4, p_4) + Text + + The pairing {\tt } recovers the Kostka number + {\tt K_{\lambda,\mu}}, the number of semistandard Young tableaux of + shape {\tt \lambda} and content {\tt \mu}. We cross-check this + against @TO kostkaNumber@: + + Example + scalarProduct(S_{3,2}, h_2*h_2*h_1) == kostkaNumber({3,2}, {2,2,1}) + scalarProduct(S_{3,2}, h_1^5) == kostkaNumber({3,2}, {1,1,1,1,1}) + scalarProduct(S_{4,1}, h_3*h_2) == kostkaNumber({4,1}, {3,2}) SeeAlso - hVariable - pVariable + kostkaNumber /// doc /// Key - hVariable + (scalarProduct,ClassFunction,ClassFunction) Headline - Complete symmetric functions in a Symmetric ring + Standard scalar product of class functions +Usage + sp = scalarProduct(ch1,ch2) +Inputs + ch1:ClassFunction + ch2:ClassFunction +Outputs + sp:QQ Description Text - For a Symmetric ring {\tt R} of dimension {\tt n}, {\tt R.hVariable} is a function - which assigns to each index {\tt 1\leq i\leq n} the {\tt i}-th complete symmetric - function. If {\tt i} is outside the given bounds, an error is returned. - + + Given virtual characters {\tt ch1} and {\tt ch2}, the method + computes the standard pairing between {\tt ch1} and {\tt ch2}. + Example - R = symmetricRing(QQ,2,EHPVariables => (x,y,z)); - R.hVariable 2 + ch1 = new ClassFunction from {{3,2} => 2, {2,2,1} => -2, {3,1,1} => 2, {5} => 1}; + ch2 = new ClassFunction from {{2,2,1} => -2, {5} => 1, {1,1,1,1,1} => 5, {3,2} => 3, {4,1} => -2}; + scalarProduct(ch1,ch2) + + Text + + The irreducible characters of {\tt S_n} indexed by distinct + partitions are orthonormal. For partitions of {\tt 5}, we can + verify the full orthogonality relations: + Example + S = schurRing(QQ,s,5); + cF32 = classFunction s_{3,2}; + cF41 = classFunction s_{4,1}; + cF221 = classFunction s_{2,2,1}; + scalarProduct(cF32, cF32) + scalarProduct(cF32, cF41) + scalarProduct(cF41, cF221) + + Text + + Decomposing an arbitrary virtual character into irreducibles by + pairing with each Schur class function recovers the multiplicities: + + Example + psi = cF32 + 2*cF41 - cF221; + {scalarProduct(psi, cF32), scalarProduct(psi, cF41), scalarProduct(psi, cF221)} SeeAlso - eVariable - pVariable + classFunction /// doc /// Key - pVariable + internalProduct Headline - Power-sum symmetric functions in a Symmetric ring + Internal product of symmetric functions/class functions Description Text - For a Symmetric ring {\tt R} of dimension {\tt n}, {\tt R.pVariable} is a function - which assigns to each index {\tt 1\leq i\leq n} the {\tt i}-th power-sum symmetric - function. If {\tt i} is outside the given bounds, an error is returned. - + + This method computes the internal (Kronecker) product of two homogeneous + symmetric functions of the same degree. If we think of these functions + as virtual characters of some symmetric group, then their internal + product is just the character of the tensor product of the corresponding + virtual representations. We use the binary operator @TO symbol **@ as a + shorthand for @TO internalProduct@. + + The complete symmetric function of degree {\tt n} corresponds + to the trivial {\tt S_n}-representation and is therefore + the unit of the representation ring of {\tt S_n}: + Example - R = symmetricRing(QQ,4); - R.pVariable 2 + R = symmetricRing(QQ,5); + S = schurRing(QQ,s,3); + internalProduct(h_3,s_{2,1}) + toE(h_3 ** e_3) -SeeAlso - eVariable - hVariable -/// + Text -doc /// - Key - (numgens,SchurRing) - Headline - Number of generators of Schur ring. - Description - Text - - Given a Schur ring {\tt S}, the function {\tt numgens} outputs the number - of generators of {\tt S}. This is equal to the relative dimension of {\tt S} - over its base ring, and also to the maximal number of parts of a partition - allowed as an index for the elements of {\tt S}. - - Example - R = schurRing(QQ,r,6); - numgens R - S = schurRing(s); - numgens S -/// + The square of the sign representation is the trivial representation: -doc /// - Key - (schurRing,Ring) - Headline - The Schur ring corresponding to a given Symmetric ring. - Usage - S = schurRing R - Inputs - R:Ring - Outputs - S:SchurRing - Description - Text - - Given a ring {\tt R}, the function {\tt schurRing} attempts to return a - Schur ring {\tt S} that is associated to {\tt R} in a natural way. Namely, if - the attribute {\tt R.Schur} points to a Schur ring, then the function returns - that ring. If {\tt R} is already a Schur ring, then the ring {\tt R} is returned. - Otherwise, if the Schur level of {\tt R} is at least one, then the function - constructs a Schur ring over the base ring {\tt A} of {\tt R}, having the same - relative dimension over {\tt A} as {\tt R}. If the Schur level of {\tt R} is zero, then - an error is returned. - - Example - R = schurRing(QQ,r,6); - schurRing R - Q = symmetricRing(QQ,3); - A = schurRing Q; - schurRing Q - SeeAlso - symmetricRing -/// + Example + toH internalProduct(e_3,e_3) -doc /// - Key - (symmetricRing,Ring) - Headline - The Symmetric ring corresponding to a given (Schur) ring. - Usage - R = symmetricRing S - Inputs - S:Ring - Outputs - R:Ring - Description - Text - - Given a (Schur) ring {\tt S}, the function {\tt symmetricRing} returns a - (Symmetric) ring {\tt R} that is associated to {\tt S} in a natural way. Namely, if - the attribute {\tt S.symmetricRing} points to a ring, then the function returns - that ring. If {\tt S} is not a Schur ring, then the function returns {\tt S}. - Otherwise, if {\tt S} is a Schur ring, then the function - constructs a polynomial ring over the Symmetric ring {\tt R_A} of the base ring {\tt A} of - {\tt R}, having the same relative dimension over {\tt R_A} as {\tt S} over {\tt A}. - - Example - A = schurRing(QQ,a,6); - B = schurRing(A,b,3); - symmetricRing B - symmetricRing ZZ - SeeAlso - schurRing -/// + Text -doc /// - Key - toS - (toS,RingElement) - (toS,RingElement,SchurRing) - Headline - Schur (s-) basis representation - Usage - fs = toS f - fs = toS(f,S) - Description - Text + Working in a Schur ring directly, Kronecker products of Schur + functions give the decomposition of tensor products of irreducible + {\tt S_n}-representations. The Kronecker square of {\tt s_{2,1}} + (the standard representation of {\tt S_3}) decomposes as: - Given a symmetric function {\tt f}, the function - {\tt toS} yields a representation of {\tt f} as a linear - combination of Schur functions. + Example + T = schurRing(QQ,t,3); + internalProduct(t_{2,1}, t_{2,1}) - If {\tt f} is an element of a Symmetric ring {\tt R} and the output Schur ring {\tt S} - is not specified, then the output {\tt fs} is an element of the Schur ring - associated to {\tt R} (see @TO schurRing@). - - Example - R = symmetricRing(QQ,4); - fs = toS(e_1*h_2+p_3) - S = schurRing(s,2); - toS(fs,S) - - Text - - This also works over tensor products of Symmetric/Schur rings. - - Example - R = symmetricRing(4, EHPVariables => (a,b,c), SVariable => r); - S = symmetricRing(R, 2, EHPVariables => (x,y,z), SVariable => s); - T = symmetricRing(S, 3, SVariable => t); - A = schurRing T; - f = a_3*x_2*e_1 - b_1*z_2*p_3 - toS f - - SeeAlso - toH - toE - toP + Text + + The exterior square of the sign representation of {\tt S_3} is + the trivial representation, which on the symmetric-function side + is the identity {\tt s_{1,1,1} \otimes s_{1,1,1} = s_3}: + + Example + internalProduct(t_{1,1,1}, t_{1,1,1}) +SeeAlso + scalarProduct /// doc /// - Key - toE - (toE,RingElement) - Headline - Elementary symmetric (e-) basis representation - Usage - fe = toE f - Inputs - f:RingElement - element of a Symmetric or Schur ring - Outputs - fe:RingElement - element of a Symmetric ring - Description - Text +Key + (internalProduct,RingElement,RingElement) +Headline + Kronecker product of symmetric functions +Usage + ip = internalProduct(f1,f2) +Inputs + f1:RingElement + element of a Symmetric ring or a Schur ring + f2:RingElement + element of a Symmetric ring or a Schur ring +Outputs + ip:Ring + a Symmetric ring or a Schur Ring +Description + Text - Given a symmetric function {\tt f}, the function - {\tt toE} yields a representation of {\tt f} as a polynomial - in the elementary symmetric functions. + Given symmetric functions {\tt f1} and {\tt f2}, the method + computes the Kronecker product {\tt ip} between {\tt f1} and {\tt f2}. + The output {\tt ip} is an element in the ring of {\tt f2}. - If {\tt f} is an element of a Schur ring {\tt S} then the output {\tt fe} is an - element of the Symmetric ring associated to {\tt S} (see @TO symmetricRing@). + Example + R = symmetricRing(QQ,6); + S = schurRing(QQ,s,6); + toE(h_3**e_3) + Q = schurRing(QQ,q,6); + internalProduct(s_{3,3},q_{4,2}) - Example - R = symmetricRing 7; - toE(h_3*e_3) - S = schurRing(s,4) - toE S_{3,2,1} + Text - Text + An error is returned if {\tt f1} and {\tt f2} don't have the + same degree. - This also works over tensor products of Symmetric/Schur rings. - - Example - R = schurRing(r, 4, EHPVariables => (a,b,c)); - S = schurRing(R, s, 2, EHPVariables => (x,y,z)); - T = schurRing(S, t, 3); - A = symmetricRing T; - f = (r_1+s_1+t_1)^2 - toE f - - SeeAlso - toH - toS - toP -/// + Products of complete homogeneous functions give characters of + permutation representations of {\tt S_n}; their Kronecker product + decomposes accordingly. For instance, in degree {\tt 4}: -doc /// - Key - toH - (toH,RingElement) - Headline - Complete symmetric (h-) basis representation - Usage - fh = toH f - Inputs - f:RingElement - element of a Symmetric or Schur ring - Outputs - fh:RingElement - element of a Symmetric ring - Description - Text + Example + internalProduct(h_3*h_1, h_2*h_2) - Given a symmetric function {\tt f}, the function - {\tt toH} yields a representation of {\tt f} as a polynomial - in the complete symmetric functions. + Text - If {\tt f} is an element of a Schur ring {\tt S} then the output {\tt fh} is an - element of the Symmetric ring associated to {\tt S} (see @TO symmetricRing@). + The same computation can be carried out directly in a Schur ring + with option {\tt GroupActing => "Sn"}, where multiplication + {\tt *} is the internal product. The Kronecker square of the + standard representation {\tt s_{3,1}} of {\tt S_4} decomposes as + trivial + sign + standard + {\tt s_{2,2}}: - Example - R = symmetricRing 7; - toH(h_3*e_3) - S = schurRing(s,4) - toH S_{3,2,1} + Example + Sn = schurRing(QQ,c,4,GroupActing => "Sn"); + c_{3,1} * c_{3,1} + internalProduct(c_{3,1}, c_{3,1}) - Text + Text - This also works over tensor products of Symmetric/Schur rings. - - Example - R = schurRing(r, 4, EHPVariables => (a,b,c)); - S = schurRing(R, s, 2, EHPVariables => (x,y,z)); - T = schurRing(S, t, 3); - A = symmetricRing T; - f = (r_1+s_1+t_1)^2 - toH f - - SeeAlso - toE - toS - toP + The method is compatible with @TO toSn@: we can first convert a + product of {\tt h}'s into the Schur basis of an {\tt S_n} ring, + then take Kronecker products there. + + Example + toSn(h_2*h_1*h_1, Sn) * c_{3,1} /// doc /// - Key - toP - (toP,RingElement) - Headline - Power-sum (p-) basis representation - Usage - fp = toP f - Inputs - f:RingElement - element of a Symmetric or Schur ring - Outputs - fp:RingElement - element of a Symmetric ring - Description - Text +Key + (internalProduct,ClassFunction,ClassFunction) +Headline + Tensor product of virtual representations +Usage + ip = internalProduct(ch1,ch2) +Inputs + ch1:ClassFunction + ch2:ClassFunction +Outputs + ip:ClassFunction +Description + Text - Given a symmetric function {\tt f}, the function - {\tt toP} yields a representation of {\tt f} as a polynomial - in the power-sum symmetric functions. + Given virtual characters {\tt ch1} and {\tt ch2}, the method + computes the character of the tensor product of corresponding + virtual representations of the symmetric group. - If {\tt f} is an element of a Schur ring {\tt S} then the output {\tt fp} is an - element of the Symmetric ring associated to {\tt S} (see @TO symmetricRing@). + Example + ch1 = new ClassFunction from {{4,4} => 2, {8} => -1, {5,2,1} => 2, {3,2,2,1} => 1}; + ch2 = new ClassFunction from {{2,2,2,2} => -4, {5,2,1} => 1, {3,2,2,1} => 3}; + internalProduct(ch1,ch2) + ch1 * ch2 - Example - R = symmetricRing 7; - toP(h_3*e_3) - S = schurRing(s,4) - toP S_{3,2,1} + Text - Text + A classical example: the tensor square of the standard + representation of {\tt S_4} has character values obtained by + squaring the standard character. Pairing with itself recovers + the multiplicities of the irreducibles in the tensor square: - This also works over tensor products of Symmetric/Schur rings. - - Example - R = schurRing(r, 4, EHPVariables => (a,b,c)); - S = schurRing(R, s, 2, EHPVariables => (x,y,z)); - T = schurRing(S, t, 3); - A = symmetricRing T; - f = (r_1+s_1+t_1)^2 - toP f - - SeeAlso - toH - toS - toE + Example + S = schurRing(QQ,s,4); + std = classFunction s_{3,1}; + sq = internalProduct(std, std) + scalarProduct(sq, classFunction s_{4}) + scalarProduct(sq, classFunction s_{3,1}) + scalarProduct(sq, classFunction s_{2,2}) + scalarProduct(sq, classFunction s_{2,1,1}) +SeeAlso + classFunction /// doc /// Key - jacobiTrudi - (jacobiTrudi,BasicList,Ring) + centralizerSize + (centralizerSize,List) Headline - Jacobi-Trudi determinant + Size of the centralizer of a permutation Usage - f = jacobiTrudi(lambda,R) + n = centralizerSize(rho) Inputs - lambda:BasicList - a nonincreasing list of integers, or a partition - R:Ring - a Symmetric ring + rho:List Outputs - f:RingElement - an element of a Symmetric ring + n:ZZ Description Text - - Given a partition {\tt lambda} and Symmetric ring {\tt R}, - the method evaluates the Jacobi-Trudi determinant corresponding - to the partition {\tt lambda}, yielding a representation of - the Schur function {\tt s_{lambda}} as a symmetric function - in {\tt R}. The default option is to represent this symmetric - function in terms of {\tt e-}polynomials. - + + {\tt rho} is a list representing the cycle type of some permutation: + the {\tt i}-th entry in {\tt rho} is the number of cycles of length + {\tt i} of the permutation. + The output of the function {\tt centralizerSize} is the size of the + centralizer in the symmetric group of any permutation of cycle type + {\tt rho}. If the cycle type {\tt rho} corresponds to a partition + {\tt \lambda}, then {\tt centralizerSize(rho)} is also the value of + the square norm {\tt }. + Example - R = symmetricRing(QQ,10); - jacobiTrudi({3,2,2,1},R) - jacobiTrudi(new Partition from {4,4,1},R,EorH => "H") - toS oo + centralizerSize{1,1,1} + R = symmetricRing(QQ,6); + u = p_1 * p_2 * p_3; + scalarProduct(u,u) + + Text + + A few values of {\tt z_\rho} for small cycle types: the identity + of {\tt S_n} has centralizer size {\tt n!}, while an {\tt n}-cycle + has centralizer of size {\tt n} (generated by itself). + + Example + centralizerSize{4} -- cycle type (1,1,1,1), all of S_4 + centralizerSize{0,0,0,1} -- a single 4-cycle + centralizerSize{2,1} -- cycle type (2,1,1) + + Text + + Burnside's classical identity {\tt \sum_{\lambda \vdash n} 1/z_\lambda = 1} + expresses that the uniform measure on {\tt S_n} sums to {\tt 1}. + We verify this for {\tt n = 5}: + + Example + parts5 = {{5}, {4,1}, {3,2}, {3,1,1}, {2,2,1}, {2,1,1,1}, {1,1,1,1,1}}; + cycMult = la -> apply(toList(1..max la), i -> #positions(la, j -> j == i)); + sum(parts5, la -> 1/centralizerSize cycMult la) +SeeAlso + scalarProduct /// -doc/// - Key - EorH - [jacobiTrudi,EorH] - Headline - e- or h- representation of Jacobi-Trudi determinant - Usage - EorH => s - Inputs - s:String - either "E" or "H" - Description - Text - This option allows one to choose between evaluating the - Jacobi-Trudi determinant in the {\tt e}- or {\tt h}- basis. - If the length of the conjugate partition {\tt lambda'} is - larger than the length of {\tt lambda}, then it is - computationally less expensive to set the option {\tt EorH} - to {\tt "H"}. Otherwise, the default value {\tt "E"} is more - efficient. -/// +doc /// + Key + Memoize + Headline + Option to record values of the jacobiTrudi function + Description + Text -doc/// - Key - [jacobiTrudi,Memoize] - Headline - Store values of the jacobiTrudi function. - Usage - Memoize => b - Inputs - b:Boolean - Description - Text - - If the option is set to {\tt true} then all the values of the jacobiTrudi - function that are computed are recorded into a special hash table attached - to the symmetric ring inside which the computations are done. -/// + This is an optional argument for the @TO jacobiTrudi@ + function, allowing one to store its values + in order to speed up computations. When {\tt Memoize => true}, + every computed value is cached in a hash table on the symmetric + ring, so repeated calls on the same partition return the cached + value in constant time. + + Example + R = symmetricRing(QQ, 10); + elapsedTime jacobiTrudi({4,3,2,1}, R, Memoize => true); + elapsedTime jacobiTrudi({4,3,2,1}, R, Memoize => true); + + Text + + The cache is attached to the ring {\tt R}. After one partition + is memoized, subsequent calls with a different partition perform + the full Jacobi-Trudi determinant expansion, then cache it as + well: + + Example + elapsedTime jacobiTrudi({5,3,2}, R, Memoize => true); + elapsedTime jacobiTrudi({5,3,2}, R, Memoize => true); + + Text + + Without {\tt Memoize => true}, each call recomputes the + determinant from scratch; for large partitions this can be + substantially more expensive than a single cached lookup. + + Example + elapsedTime jacobiTrudi({4,3,2,1}, R); + elapsedTime jacobiTrudi({4,3,2,1}, R); + SeeAlso + jacobiTrudi +/// doc /// Key - plethysm - (plethysm,RingElement,RingElement) + SVariable + [schurRing,SVariable] + [symmetricRing,SVariable] Headline - Plethystic operations on representations -Usage - pl = plethysm(f,g) - pl = f @ g -Inputs - f:RingElement - element of Symmetric ring or Schur ring - g:RingElement - element of Symmetric ring or Schur ring -Outputs - pl:RingElement - element of the ring of {\tt g} + Specifies symbol representing s-functions Description Text - Given a symmetric functions {\tt f} and the character {\tt g} of a virtual representation of a product - of general linear and symmetric groups, the method computes the character of the - plethystic composition of {\tt f} and {\tt g}. The result of this operation will be an element of - the ring of {\tt g}. We use the binary operator @TO symbol \@ @ as a synonym for the plethysm function. + This is an optional argument for the constructor of a Symmetric ring. It indicates the + symbol to be used to denote s-functions in the associated Schur ring. The default value + is {\tt s}. Example - R = symmetricRing(QQ,5); - pl = plethysm(h_2,h_3) - toS pl - S = schurRing(QQ,q,3); - h_2 @ q_{2,1} - plethysm(q_{2,1},q_{2,1}) - T = schurRing(S,t,2,GroupActing => "Sn"); - plethysm(q_{1,1,1}-q_{2,1}+q_{3},q_{2,1}*t_2-t_{1,1}) - p_3 @ (q_{2,1}*t_2-t_{1,1}) + R = symmetricRing(QQ,5,SVariable => getSymbol"s"); + S = schurRing R + s_2^2 + + Text + + The chosen symbol becomes the actual indexing name for the + basis elements of the Schur ring. Any symbol may be used; for + instance, {\tt sigma} yields Schur elements {\tt sigma_\lambda} + which multiply by Littlewood-Richardson: + + Example + T = schurRing(QQ,sigma,4) + sigma_{1,1}^2 + + Text + + An {\tt Sn}-flavored ring using a custom {\tt SVariable} stores + characters of the symmetric group on that symbol: + + Example + Sn = schurRing(QQ,sig,4,GroupActing => "Sn"); + sig_{3,1} * sig_{2,2} + + Text + + Distinct {\tt SVariable} choices allow a tensor product of two + Schur rings to carry unambiguous variable names for each + factor, so a bi-representation has a clean display: + + Example + S = schurRing(QQ,s,3); + T = schurRing(S,tau,2); + s_{2,1} * tau_{1,1} + +SeeAlso + EHPVariables /// doc /// Key - (plethysm,BasicList,RingElement) + EHPVariables + [schurRing,EHPVariables] + [symmetricRing,EHPVariables] Headline - Plethystic operations on representations -Usage - pl = plethysm(lambda,g) -Inputs - lambda:BasicList - nonincreasing sequence of positive integers, or partition - g:RingElement - element of Symmetric ring or Schur ring -Outputs - pl:RingElement - element of the ring of {\tt g} + Specifies sequence of symbols representing e-, h-, and p-functions Description Text - - The method computes the character of the representation obtained by applying the Schur functor - {\tt S_{\lambda}} to the representation with character {\tt g}, where - {\tt \lambda} is a partition. + This is an optional argument for the constructor of a Symmetric or Schur ring. It indicates the + symbols to be used to denote e-, h-, and p-functions in the associated Symmetric ring. The + default values are {\tt (e,h,p)}. + + Example + S = schurRing(QQ,s,4,EHPVariables => (getSymbol"a",getSymbol"b",getSymbol"c")); + R = symmetricRing S + epol = toE s_{2,2,2} + toS epol + + Text + + The three symbols become the polynomial-ring generators of + the associated @TO symmetricRing@. With the default + {\tt (e,h,p)}, inspecting {\tt gens} shows all three families + indexed by {\tt 1..n}: Example R = symmetricRing(QQ,3); - S = schurRing(QQ,q,3); - toE plethysm({2,1},e_1*e_2-e_3) - plethysm({2,1,1},q_{1,1}) - T = schurRing(S,t,4,GroupActing => "Sn"); - plethysm({1,1},q_1*t_{3,1}) -/// + gens R -doc /// -Key - (plethysm,RingElement,ClassFunction) - (plethysm,BasicList,ClassFunction) -Headline - Plethystic operations on class functions -Usage - pl = plethysm(f,cF) - pl = plethysm(lambda,cF) -Description Text - - These methods describe the result of applying plethystic operations to a virtual - character of a symmetric group. These operations are described either via a symmetric - function {\tt f}, or a partition {\tt lambda}. Since {\tt cF} corresponds to an {\tt S_n}- - representation, the option @TO GroupActing@ is irrelevant in this case. - + + A custom triple works the same way. Below, {\tt a_i, c_i, b_i} + correspond respectively to the elementary, power-sum and + complete homogeneous symmetric functions in the associated + Symmetric ring: + Example - cF = new ClassFunction from {{2} => 1, {1,1} => -1}; - pl1 = plethysm({1,1},cF) - R = symmetricRing 5; - pl2 = plethysm(e_1+e_2,cF) - S = schurRing R; - symmetricFunction(cF,S) - symmetricFunction(pl1,S) - symmetricFunction(pl2,S) -/// --* -doc /// -Key - (exteriorPower,ZZ,RingElement) -Headline - Exterior power of a representation -Usage - ep = exteriorPower(n,rep) -Inputs - n:ZZ - rep:RingElement - an element of a SchurRing -Outputs - ep:RingElement -Description + S2 = schurRing(QQ,s,4,EHPVariables => (getSymbol"a",getSymbol"b",getSymbol"c")); + R2 = symmetricRing S2; + gens R2 + Text - - Given a representation {\tt rep} of a product of general linear - groups, and a positive integer {\tt n}, the function returns the - {\tt n}-th exterior power of this representation. - + + The option propagates across tensor products of Schur rings, + so each factor may carry its own e/h/p alphabet without name + clashes: + Example - S = schurRing(QQ,s,2) - T = schurRing(S,t,3) - exteriorPower(4,s_{1}+t_{1}) + A = schurRing(QQ,sa,3,EHPVariables => (getSymbol"ea",getSymbol"ha",getSymbol"pa")); + B = schurRing(A,sb,2,EHPVariables => (getSymbol"eb",getSymbol"hb",getSymbol"pb")); + schurLevel B + +SeeAlso + SVariable /// doc /// Key - (symmetricPower,ZZ,RingElement) + GroupActing + [schurRing,GroupActing] + [symmetricRing,GroupActing] Headline - Symmetric power of a representation -Usage - ep = symmetricPower(n,rep) -Inputs - n:ZZ - rep:RingElement - an element of a SchurRing -Outputs - ep:RingElement + Specifies the group that is acting Description Text - - Given a representation {\tt rep} of a product of general linear - groups, and a positive integer {\tt n}, the function returns the - {\tt n}-th symmetric power of this representation. - + This is an optional argument for the @TO schurRing@ and @TO symmetricRing@ functions. + When the exterior or symmetric powers of a symmetric function {\tt g} are computed, the result + depends on whether {\tt g} is interpreted as a virtual representation of a general + linear, symmetric, symplectic, or orthogonal group. The option {\tt GroupActing} specifies the + interpretation to be considered. Its possible values are {\tt "GL"} (the default), + {\tt "Sn"}, {\tt "SL"}, {\tt "Sp"}, {\tt "O"}, and {\tt "RatGL"}. + Example - S = schurRing(QQ,s,2) - T = schurRing(S,t,3) - symmetricPower(4,s_{1}+t_{1}) -/// -*- + S = schurRing(s,2); + exteriorPower(3,s_2) + T = schurRing(t,2,GroupActing => "Sn"); + symmetricPower(2,t_{1,1}) -doc /// -Key - schurResolution - (schurResolution,RingElement,List,List) - (schurResolution,RingElement,List) -Headline - Compute an ``approximate'' equivariant resolution of a module. -Usage - resol = schurResolution(rep,M,lS) - resol = schurResolution(rep,M) -Inputs - rep:RingElement - element of a SchurRing - M:List - list of representations, corresponding to the homogeneous components of a module {\tt M}. - lS:List - list of representations, corresponding to the homogeneous components of a polynomial ring {\tt S}. -Outputs - resol:List -Description Text - - Given a representation {\tt rep} of a (product of) general linear - or symmetric group(s) {\tt G}, we consider the symmetric algebra {\tt S = Sym(rep)} - and an {\tt S}-module {\tt M} which is also a {\tt G}-module in such - a way that the {\tt S}-module structure on {\tt M} respects the - {\tt G}-action. More generally, {\tt S} can be any graded ring, of which one inputs only - finitely many homogeneous components as a list {\tt lS} of characters of {\tt G}. The main reason - why we allow this generality is because most of the time it is computationally expensive to calculate - the symmetric powers of the representation {\tt rep}, so we give the user the option to compute these - symmetric powers by different methods and use the results as input for the schurResolution routine. - - We are interested in computing an equivariant - resolution of {\tt M}. This depends on both the {\tt G}- and {\tt S}-module structure - of {\tt M} in general, but in many examples that occur in practice, it turns out that - the differentials in the resolution have maximal rank among all - {\tt G}-module homomorphisms between the free modules in the resolution. - We will therefore assume that this is the case for the module {\tt M} that we are - trying to resolve, and thus disregard its {\tt S}-module structure. - - More precisely, the assumptions that we make about {\tt M} are as follows: {\tt M} is - a graded {\tt S}-module, with {\tt M_i = 0} for {\tt i<0}, where the grading on {\tt S} is standard, - given by setting the degrees of the elements of {\tt rep} equal to 1. Since we assumed - that the {\tt G}-structure of {\tt M} determines the syzygies, all the relevant - information is concentrated in finitely many homogeneous components of {\tt M} (namely - up to {\tt reg(M)+pd(M)}, the sum of the regularity and the projective dimension of - {\tt M}). We will thus assume that {\tt M} - is given as a list of {\tt G}-representations, corresponding to (a subset of) the - relevant homogeneous components. The function {\tt schurResolution} takes as - inputs the representation {\tt rep}, the module {\tt M}, and as optional arguments a {\tt DegreeLimit} - {\tt d}, and a {\tt SyzygyLimit} {\tt c}. The ring {\tt S} itself can occur as input data, being - described as a list of {\tt G}-representations, just like {\tt M}. - The routine outputs the generators of degree at most {\tt d} of the - first {\tt c+1} syzygy modules (from {\tt 0} to {\tt c}). They are listed as a - sequence of pairs, consisting of the degree of the generators of the syzygy modules - together with the characters of the {\tt G}-representations they correspond to. - If the syzygy bound {\tt c} is not given, - then all syzygy modules are computed. If the degree bound {\tt d} is not given, then - it is assumed to be equal to the largest degree among the homogeneous components of - {\tt M} in the input, i.e. one less than the length of the @TO List@ {\tt M}. - - The example below computes the resolution of the quadratic Veronese - surface in {\tt P^5}. - + + The first example computes the decomposition of {\tt \Lambda^3(Sym^2(V))} into irreducible + {\tt GL(V)}-representations, while the second one computes the + second symmetric power of the sign representation of the symmetric group {\tt S_2}. + + Multiplication differs sharply between the {\tt "GL"} and + {\tt "Sn"} interpretations. Under {\tt "GL"}, the product is + the Littlewood-Richardson tensor product of polynomial + representations, which is degree-additive in the partitions. + Under {\tt "Sn"}, multiplication is the ordinary tensor + product of characters of a single symmetric group, so only + partitions of the {\it same} size may be multiplied, and the + product is expanded in the Kronecker coefficients: + Example - S = schurRing(QQ,s,3) - rep = s_{2} - M = {1_S,s_{2},s_{4},s_{6},s_{8},s_{10},s_{12}} - schurResolution(rep,M) + Sgl = schurRing(QQ,s,4); + s_{2,1} * s_{2,1} + Ssn = schurRing(QQ,t,4,GroupActing => "Sn"); + t_{2,1,1} * t_{2,1,1} Text - - Next, we compute the syzygies of degree at most {\tt 7} in the resolution - of the cubic Veronese embedding of {\tt P^2}. - + + The values {\tt "Sp"} and {\tt "O"} select the stable (universal) character ring of + the symplectic and orthogonal groups, respectively. The basis elements are indexed by + partitions, with {\tt sp_\lambda} (respectively {\tt o_\lambda}) standing for the + irreducible symplectic (respectively orthogonal) character associated to {\tt \lambda}. + Multiplication in these rings is the Newell-Littlewood product, implemented by + conversion to the Schur basis via Koike's branching formulas and back: + Example - rep = s_{3} - M = {1_S,s_{3},s_{6},s_{9},s_{12},s_{15},s_{18},s_{21},s_{24},s_{27}} - d = 7 - schurResolution(rep,M,DegreeLimit => d) + Sp = schurRing(QQ,sp,GroupActing => "Sp"); + sp_{1} * sp_{1} + sp_{1,1} + toS sp_{1,1} + O = schurRing(QQ,o,GroupActing => "O"); + o_{1} * o_{1} + toS o_{2} Text - - We can compute the resolution of the ideal of {\tt 2\times 2} minors of a {\tt 3\times 4} - matrix, which corresponds to the Segre embedding of {\tt P^2\times P^3}: - + + Exterior and symmetric powers are likewise reinterpreted. + In the orthogonal ring, exterior powers of the second + fundamental character mix several Newell-Littlewood terms: + Example - T = schurRing(S,t,4) - rep = s_1 * t_1 - M = {1_T} | apply(splice{1..8},i -> s_i * t_i) - schurResolution(rep,M) + Ofin = schurRing(QQ,obar,5,GroupActing => "O"); + exteriorPower(3, obar_2) Text - - The following example computes the equivariant resolution of the residue field of a - polynomial ring in {\tt n=5} variables, with respect to the action of the symmetric - group {\tt S_n}. - + + Setting {\tt GroupActing => "SL"} forces the top-row + reduction {\tt s_\lambda = s_{(\lambda_1 - \lambda_n, \ldots, \lambda_{n-1} - \lambda_n)}}, + reflecting the fact that the determinant representation is + trivial in {\tt SL}. Long partitions collapse accordingly: + Example - n = 5; - S = schurRing(QQ,s,n,GroupActing => "Sn"); - rep = s_n + s_{n-1,1}; - M = {s_n} - schurResolution(rep,M,DegreeLimit => n) + SL3 = schurRing(QQ,sl,3,GroupActing => "SL"); + sl_{3,2,1} + sl_{4,2,2} + sl_{2,1,1} Text - - Generalizing this, we can compute the equivariant resolution of the quotient of the - polynomial ring in {\tt n=5} variables by the ideal of square-free monomials of - degree two, with respect to the action of the symmetric group {\tt S_n}. - + + Finally, {\tt GroupActing => "RatGL"} produces the ring of + rational (mixed) polynomial representations of {\tt GL(V)}, + whose characters are indexed by pairs of partitions + (bipartitions), and whose tensor product is again a + Littlewood-Richardson-style rule: + Example - M = {s_n} | splice{n:rep}; - schurResolution(rep,M) + Rg = schurRing(QQ,r,3,GroupActing => "RatGL"); + r_({1},{}) * r_({},{1}) + Text + + Stable rings (no dimension specified) allow arbitrarily many parts in the + partition labels; a concrete dimension can be supplied to restrict the partitions + and to model a finite-dimensional group. + +SeeAlso + toSp + toO + toS /// doc /// Key - [schurResolution,DegreeLimit] + Basis + [schurRing,Basis] + [symmetricRing,Basis] Headline - Specifies the maximal degree of syzygies to be computed + Specifies the basis to use for a Schur ring Description Text - This is an optional argument for the @TO schurResolution@ routine. It specifies an upper bound for the - degrees of the generators of the syzygy modules in the equivariant resolution of an equivariant module {\tt M} - to be computed by the routine. If a {\tt DegreeLimit} is not specified, then it is assumed to be equal to the - maximal degree in which the module {\tt M} is specified as a representation. - + This is an optional argument for the @TO schurRing@ and @TO symmetricRing@ + functions. It selects how the partition-indexed generators of the ring are + interpreted as symmetric functions. The possible values are {\tt "Schur"} + (the default) and {\tt "Monomial"}. + + When {\tt Basis => "Schur"}, the ring generator {\tt s_\lambda} represents the + Schur function indexed by {\tt \lambda}. Multiplication uses the + Littlewood-Richardson rule supplied by the Macaulay2 engine. + Example - A = schurRing(a,3,GroupActing => "Sn"); - B = schurRing(A,b,2); - rep = (a_3 + a_{2,1}) * b_1 - d = dim rep - M = {a_3 * 1_B}; - sR = schurResolution(rep,M,DegreeLimit => d) + S = schurRing(QQ,s,4); + s_{2,1} * s_{1} + + Text + When {\tt Basis => "Monomial"}, the ring generator {\tt m_\lambda} instead + represents the monomial symmetric function indexed by {\tt \lambda}. + Multiplication is implemented by converting to the Schur basis, multiplying + there via Littlewood-Richardson, and converting back to the monomial basis + using Kostka numbers. The resulting ring is abstractly isomorphic to the + Schur-basis ring but its elements are displayed and stored as linear + combinations of monomial symmetric functions. + + Example + M = schurRing(QQ,m,4,Basis => "Monomial"); + m_{1} * m_{1} + m_{2,1} * m_{1} + + Text + The consistency of the two bases can be verified against @TO toM@, which + converts a symmetric function to the monomial basis: + + Example + S = schurRing(QQ,s,4); + M = schurRing(QQ,m,4,Basis => "Monomial"); + toM(s_{1} * s_{1},M) == m_{1} * m_{1} + + Text + A monomial-basis product agrees with the Schur-basis product + after round-tripping through {\tt toS}, verifying that the two + rings are isomorphic with isomorphism {\tt toM}/{\tt toS}: + + Example + x = s_{2,1} * s_{1}; + y = toM(x,M); + y + toS(y,S) == x + + Text + Converting back from the monomial basis to the Schur basis + recovers the original Schur element: + + Example + toS(m_{2,1} + m_{1,1,1}, S) + + Text + The monomial-to-Schur transition is essentially indexed by + @TO kostkaNumber@: the coefficient of {\tt m_\mu} in + {\tt s_\lambda} equals {\tt K_{\lambda,\mu}}. For example, + the Kostka numbers with {\tt \lambda = (2,1)} reproduce the + monomial expansion of {\tt s_{(2,1)}}: + + Example + toM s_{2,1} + kostkaNumber({2,1},{2,1}) + kostkaNumber({2,1},{1,1,1}) SeeAlso - [schurResolution,SyzygyLimit] + schurRing + symmetricRing + toM + kostkaNumber + GroupActing /// - + doc /// Key - [schurResolution,SyzygyLimit] + kostkaNumber + (kostkaNumber,BasicList,BasicList) Headline - Specifies the number of syzygy modules to be computed + Compute a Kostka number +Usage + k = kostkaNumber(lambda,mu) +Inputs + lambda:BasicList + a partition + mu:BasicList + a composition (or partition) of the same size as {\tt lambda} +Outputs + k:ZZ + the Kostka number {\tt K_{\lambda,\mu}} Description Text - This is an optional argument for the @TO schurResolution@ routine. It specifies an upper bound for the - number of syzygy modules in the equivariant resolution of an equivariant module {\tt M} to be computed - by the routine. If a {\tt SyzygyLimit} is not specified, then all syzygy modules are computed. - - The example below computes the {\tt 0}-th to {\tt 3}-rd syzygy modules of the {\tt 5}-th Veronese embedding - of {\tt P^2}. - + + The Kostka number {\tt K_{\lambda,\mu}} is the number of + semistandard Young tableaux of shape {\tt \lambda} and content {\tt \mu}. + Equivalently, it is the coefficient of the Schur function {\tt s_\lambda} + in the product of complete symmetric functions {\tt h_{\mu_1} h_{\mu_2} \cdots}. + Example - S = schurRing(s,3); - rep = s_{5}; - M = {1_S,s_{5},s_{10},s_{15},s_{20},s_{25},s_{30}}; - schurResolution(rep,M,SyzygyLimit => 3) + kostkaNumber({2,1},{1,1,1}) + kostkaNumber({3},{2,1}) + kostkaNumber({2,1},{2,1}) + kostkaNumber({2,1},{3}) + + Text + + Returns {\tt 0} whenever {\tt \lambda} and {\tt \mu} do not have the same + size, or whenever {\tt \mu} does not dominate {\tt \lambda} (so that no + tableaux of the requested shape and content exist). + + Text + + The full Kostka matrix {\tt K_{\lambda,\mu}} for partitions of + {\tt 4}, listed in reverse dominance order + {\tt (4), (3,1), (2,2), (2,1,1), (1,1,1,1)}, is upper + triangular with ones on the diagonal (a reflection of the fact + that Schur functions form an {\tt h}-triangular basis with + respect to the monomial basis): + + Example + P = {{4},{3,1},{2,2},{2,1,1},{1,1,1,1}}; + matrix apply(P, la -> apply(P, mu -> kostkaNumber(la,mu))) + + Text + + A useful boundary identity: for any partition {\tt \mu} of + {\tt n}, the Kostka number with {\tt \lambda = (n)} is {\tt 1}, + counting the unique row tableau: + + Example + for mu in P list kostkaNumber({4}, mu) + + Text + + Similarly, the Kostka number {\tt K_{\lambda,(1^n)}} counts + standard Young tableaux of shape {\tt \lambda}, which for + {\tt \lambda = (2,2)} is {\tt 2} (the hook-length value), + matching the dimension of the corresponding irreducible + {\tt S_4}-representation: + + Example + kostkaNumber({2,2}, {1,1,1,1}) + kostkaNumber({2,1,1}, {1,1,1,1}) SeeAlso - [schurResolution,DegreeLimit] + toM + toS + Basis /// - + doc /// - Key - schurLevel - (schurLevel,Ring) - Headline - Number of SchurRings the ring is a tensor product of. - Usage - lev = schurLevel(R) - Inputs - R:Ring - Outputs - lev:ZZ - Description - Text - - For the representation ring {\tt R} of a product of {\tt lev} - general linear groups, the function returns {\tt lev}. If {\tt R} - is not a representation ring, then the function returns 0. - - Example - R = schurRing(QQ,r,3); - S = schurRing(R,s,5); - T = schurRing(S,t,2); - schurLevel R - schurLevel S - schurLevel T - schurLevel QQ + Key + toM + (toM,RingElement) + (toM,RingElement,SchurRing) + Headline + Monomial (m-) basis representation + Usage + fm = toM f + fm = toM(f,M) + Description + Text + + Given a symmetric function {\tt f}, the function {\tt toM} returns a + representation of {\tt f} as a linear combination of monomial symmetric + functions. The output is a {\tt RingElement} in a + @TO SchurRing@ with @TO Basis@ {\tt => "Monomial"}, so that the + basis element {\tt m_\mu} represents the monomial symmetric function + indexed by the partition {\tt \mu}. + + Internally, the conversion uses Kostka numbers: the Schur-to-monomial + transition is {\tt s_\lambda = \sum_\mu K_{\lambda,\mu} m_\mu}. + + Example + S = schurRing(QQ,s,4); + toM s_{2,1} + toM s_{3} + toM (s_{2,1} + 2*s_{1,1,1}) + + Text + + If the target monomial ring {\tt M} is not specified, the first call to + {\tt toM} on an element of {\tt S} creates and caches an associated + monomial-basis ring with the same number of generators and the same + coefficient ring as {\tt S}; subsequent calls reuse this cached ring. + + Example + ring(toM s_{2,1}) === ring(toM s_{3}) + + Text + + Alternatively one can supply the target monomial ring explicitly. + + Example + M = schurRing(QQ,m,4,Basis => "Monomial"); + toM(s_{2,1},M) + + Text + + The function also accepts elements of a Symmetric ring, in which case + the input is first converted to the Schur basis via @TO toS@. + + Example + R = symmetricRing(QQ,4); + toM(h_2 * h_1) + + Text + + The identity {\tt h_n = \sum_\mu m_\mu}, summed over all + partitions of {\tt n}, reflects the fact that the Kostka + numbers {\tt K_{(n),\mu}} are all {\tt 1}: + + Example + R5 = symmetricRing(QQ,5); + toM h_3 + toM h_4 + + Text + + Power-sum functions admit a particularly simple monomial + expansion: {\tt p_n = m_{(n)}} identically, since + {\tt p_n = \sum_i x_i^n}. This can be read off for any + {\tt n}: + + Example + toM p_3 + toM p_5 + + Text + + Longer partitions can still be handled; the coefficients + are the corresponding Kostka numbers: + + Example + S6 = schurRing(QQ,s,6); + toM s_{4,1,1,1} + + Text + + The map {\tt toM} is invertible via @TO toS@, so a Schur + element roundtrips through the monomial basis and back to + the same Schur ring: + + Example + f = s_{3,2,1}; + toS(toM f, S6) == f + + SeeAlso + toS + toH + toE + toP + kostkaNumber + Basis /// doc /// - Key - (partitions,Set,BasicList) - Headline - Partitions of a set - Usage - par = partitions(S,L) - Inputs - S:Set - L:BasicList - a nonincreasing list of integers, or a partition - Outputs - par:List - Description - Text + Key + toSp + (toSp,RingElement) + (toSp,RingElement,SchurRing) + Headline + Expansion in the basis of symplectic characters + Usage + fsp = toSp f + fsp = toSp(f,Sp) + Description + Text - Given a set {\tt S} and a partition {\tt L=\{l_1\geq l_2\cdots\}}, the method - returns the list of partitions of the set {\tt S} of type - {\tt L}, i.e. representations of {\tt S} as {\tt S=S_1\cup S_2\cup\cdots}, where - the {\tt S_i}'s are disjoint subsets of {\tt S} having {\tt t_i} elements. + Given a symmetric function {\tt f}, the function {\tt toSp} returns the + expression of {\tt f} in the basis of irreducible symplectic characters. + The output is a {\tt RingElement} in a @TO SchurRing@ with @TO GroupActing@ + {\tt => "Sp"}, so that the basis element {\tt sp_\lambda} stands for the + irreducible {\tt Sp}-representation associated to the partition {\tt \lambda}. - Example - partitions(set{1,2,3,4},{2,1,1}) - partitions(set{a,b,c,d,e},new Partition from {3,2}) -/// + The conversion implements the inverse Koike branching formula + {\tt s_\lambda = \sum_\delta sp_{\lambda/\delta}}, where {\tt \delta} ranges + over partitions {\tt \delta \subseteq \lambda} all of whose columns have even + length (equivalently, the parts of {\tt \delta} occur in equal pairs), and + skew characters are expanded using Littlewood-Richardson coefficients. --* -doc /// - Key - (chi,BasicList,BasicList) - Headline - Irreducible character of symmetric group -Usage - v = chi(lambda,rho) -Inputs - lambda:BasicList - a nondecreasing list of positive integers, or a partition - rho:BasicList - a nondecreasing list of positive integers, or a partition -Outputs - v:QQ -Description - Text + Example + S = schurRing(QQ,s); + toSp s_{2} + toSp s_{1,1} + toSp s_{3,1} + toSp (s_{2,1} + 2*s_{1,1}) + + Text + + If the target symplectic ring is not specified, the first call to {\tt toSp} + on an element of {\tt S} creates and caches an associated stable symplectic + ring with the same coefficient ring as {\tt S}; subsequent calls reuse the + cached ring. + + Example + ring(toSp s_{2,1}) === ring(toSp s_{3,1}) + + Text + + Alternatively one can supply the target symplectic ring explicitly. + + Example + Sp = schurRing(QQ,sp,4,GroupActing => "Sp"); + toSp(s_{2,1},Sp) + + Text + + The conversion is inverse to @TO toS@ on the symplectic ring, and + multiplication of symplectic characters factors through this round-trip. + At stable rank, {\tt toS(sp_{(1,1)}) = s_{(1,1)} - 1} and + {\tt toS(sp_{(2,2)}) = s_{(2,2)} - s_{(1,1)}}; the product + {\tt sp_{(1)}^2} equals the Newell-Littlewood product + {\tt sp_{(2)} + sp_{(1,1)} + 1}. - Given two partitions {\tt lambda} and {\tt rho} of the integer {\tt N}, this method - computes the value of the irreducible character of the symmetric group - corresponding to the partition {\tt lambda} evaluated on - any permutation of cycle-type {\tt rho}. + Example + Sp = schurRing(QQ,sp,GroupActing => "Sp"); + toS sp_{1,1} + toS sp_{2,2} + toSp(toS(sp_{2,1}, S), Sp) == sp_{2,1} + sp_{1}*sp_{1} + sp_{1,1}*sp_{1,1} - The character of the trivial representation takes the value - 1 on any permutation: - - Example - chi({4},{2,1,1}) - - Text - - The character of the sign representation takes the value -1 on - a cycle of length 4: - - Example - chi({1,1,1,1},{4}) -SeeAlso - symmetricFunction - classFunction -/// -*- + Text -doc /// -Key - SchurRingElement -Headline - A type describing elements of a SchurRing -Description - Example - S = schurRing(s,5) - a = s_{3,2,1} - instance(a,SchurRingElement) + At finite rank {\tt n} (the ring parameter {\tt numVariables}), the + modification rule of Sam-Snowden-Weyman is applied automatically, so + characters whose partitions exceed {\tt n} rows are folded back to + admissible Sp basis elements (or zero). Weyl dimensions are + available via @TO dim@. Plethysm is supported and routes through the + GL Schur ring. - T = schurRing(S,t,3,GroupActing => "Sn") - b = t_{2,1}+t_3 - instance(a*b,SchurRingElement) + Example + Sp4 = schurRing(QQ,sp4,2,GroupActing => "Sp"); + sp4_{1,1}^2 + dim sp4_{2,2} + plethysm({2}, sp4_{1}) + + Text + + An explicit symplectic character decomposition from a + Schur polynomial can be obtained directly; linearity lets + the user combine several Schur functions at once: + + Example + S = schurRing(QQ,s); + toSp(s_{2,2} + s_{1,1}) + + Text + + Plethysm of a symplectic character is well-defined through + a round-trip to the Schur basis. Here, the plethysm of + the trivial {\tt (2)}-power on the defining representation + {\tt sp_{(1)}} recovers the usual {\tt Sym^2} character, + and {\tt toS} confirms it is {\tt s_{(2)}}: + + Example + Sp = schurRing(QQ,sp,GroupActing => "Sp"); + q = plethysm({2}, sp_{1}) + toS q + + Text + + At finite rank, the modification rule folds partitions + that exceed the rank. Passing {\tt s_{(2,1,1,1)}} to the + rank-{\tt 2} symplectic ring {\tt Sp_4} returns the + modified (and much shorter) character: + + Example + toSp(s_{2,1,1,1}, Sp4) + + SeeAlso + toO + toS + GroupActing + dim + plethysm /// doc /// Key - (dim,List,SchurRingElement) - (dim,Thing,SchurRingElement) - (dim,SchurRingElement) + toO + (toO,RingElement) + (toO,RingElement,SchurRing) Headline - dimension of representation + Expansion in the basis of orthogonal characters Usage - d = dim(lis,s) - d = dim(n,s) - d = dim s - Inputs - lis: List - or @TO Thing@ - s: SchurRingElement - Outputs - d: ZZ - or @TO Expression@ + fo = toO f + fo = toO(f,O) Description - Text - - The method returns the dimension of the virtual representation whose character is represented by {\tt s}. - - Example - S = schurRing(s,3) - dim s_2 - T = schurRing(t,4,GroupActing => "Sn") - dim t_{2,2} - U = schurRing(T,u,3) - dim (t_{2,2}*u_2) + Text - Text - - If {\tt S} is a @TO SchurRing@ of level 1, the ring of polynomial representations of some {\tt GL(V)}, it - may sometimes be convenient to compute dimensions of {\tt GL(V)-}representations symbolically, without - specifying the dimension of {\tt V}. Letting {\tt n} denote the parameter corresponding to {\tt dim(V)} we - have for example - - Example - S = schurRing(s,3) - dim(n,s_2) - dim(n,s_{1,1}) - dim(n,s_{2,1}) - - Text - - Similar calculations make sense over products of general linear groups. The dimensions of the representations - can be computed symbolically as functions of a number of parameters - equal to the @TO schurLevel@ of the ring. The parameters corresponding to levels where the group acting - is a symmetric group don't have a good interpretation, so they are disregarded in the dimension calculation. - The order of the input parameters is the descending order of the @TO schurLevel@s: in the example below - {\tt a} corresponds to {\tt Q}, {\tt b} corresponds to {\tt T} and {\tt c} corresponds to {\tt S}. - - Example - S = schurRing(s,3) - T = schurRing(S,t,4) - Q = schurRing(T,q,5,GroupActing => "Sn") - dExpr = dim({a,b,c},s_{2}*t_{1,1}*q_{4,1}) - P = QQ[a,b,c] - value dExpr - dim({1,2,3},s_{2}*t_{1,1}*q_{4,1}) -/// + Given a symmetric function {\tt f}, the function {\tt toO} returns the + expression of {\tt f} in the basis of irreducible orthogonal characters. + The output is a {\tt RingElement} in a @TO SchurRing@ with @TO GroupActing@ + {\tt => "O"}, so that the basis element {\tt o_\lambda} stands for the + irreducible {\tt O}-representation associated to the partition {\tt \lambda}. + + The conversion implements the inverse Koike branching formula + {\tt s_\lambda = \sum_\delta o_{\lambda/\delta}}, where {\tt \delta} ranges + over partitions {\tt \delta \subseteq \lambda} with all parts even, and skew + characters are expanded using Littlewood-Richardson coefficients. + + Example + S = schurRing(QQ,s); + toO s_{2} + toO s_{1,1} + toO s_{3} + toO (2*s_{2,1} - s_{1,1,1}) + + Text + + If the target orthogonal ring is not specified, the first call to {\tt toO} + on an element of {\tt S} creates and caches an associated stable orthogonal + ring; alternatively one can supply the target ring explicitly. + + Example + O = schurRing(QQ,o,4,GroupActing => "O"); + toO(s_{2,1},O) + + Text + + The conversion is inverse to @TO toS@ on the orthogonal ring. At + stable rank, {\tt toS(o_{(2)}) = s_{(2)} - 1} and + {\tt toS(o_{(1,1)}) = s_{(1,1)}}; the product {\tt o_{(1)}^2} + equals the Newell-Littlewood product + {\tt o_{(2)} + o_{(1,1)} + 1}. + + Example + O = schurRing(QQ,o,GroupActing => "O"); + toS o_{2} + toS o_{1,1} + toO(toS(o_{2,1}, S), O) == o_{2,1} + o_{1}*o_{1} + + Text + + At finite rank, the modification rule for types B_n / D_n is applied + automatically. The tag @TO OddOrEven@ (default {\tt "Odd"}) + distinguishes {\tt O(2n+1)} (type B_n) from {\tt O(2n)} (type D_n) + and is used by @TO dim@ to pick the right Weyl dimension formula. + Plethysm routes through the GL Schur ring. + + Example + OB2 = schurRing(QQ,oB,2,GroupActing => "O", OddOrEven => "Odd"); + oB_{1}^2 + dim oB_{2,2} + plethysm({2}, oB_{1}) + + Text + + The simplest nontrivial example of the inverse Koike + branching is the second symmetric power: {\tt s_{(2)}} + decomposes as {\tt o_{(2)} + 1} (the traceless part plus + the invariant form): + + Example + S = schurRing(QQ,s); + toO s_{2} + + Text + The conversion composes with the other symmetric-function + transitions. One can start from an element of a + Symmetric ring expressed in {\tt e}- or {\tt h}-variables, + and let {\tt toO} route it through the Schur basis: + + Example + R = symmetricRing(QQ,5); + toO (e_2) + toO (h_2 * h_1) + + Text + + A rank comparison between odd and even orthogonal groups + shows that the Weyl dimension truly depends on the value + of {\tt OddOrEven}: the partition {\tt (2,1)} indexes a + {\tt 105}-dimensional irreducible of {\tt O(2\cdot 3+1) = O(7)} + and a {\tt 64}-dimensional irreducible of {\tt O(2\cdot 3) = O(6)}: + + Example + Oodd = schurRing(QQ, od, 3, GroupActing => "O", OddOrEven => "Odd"); + Oeven = schurRing(QQ, oe, 3, GroupActing => "O", OddOrEven => "Even"); + dim od_{2,1} + dim oe_{2,1} + + SeeAlso + toSp + toS + GroupActing + OddOrEven + dim + plethysm +/// doc /// Key - ClassFunction - (symbol +,ClassFunction,ClassFunction) - (symbol -,ClassFunction,ClassFunction) - (symbol *,ClassFunction,ClassFunction) - (symbol *,ClassFunction,RingElement) - (symbol *,RingElement,ClassFunction) - (symbol *,ClassFunction,Number) - (symbol *,Number,ClassFunction) - (symbol ==,ClassFunction,ClassFunction) + branch + (branch,RingElement,SchurRing,SchurRing) + (branch,RingElement,ZZ,ZZ) Headline - The class of all Class functions + Restrict a Schur, Sp, or O character along a two-factor subgroup +Usage + h = branch(f, S1, S2) + h = branch(f, m, n) +Inputs + f:RingElement + an element of a SchurRing of type GL, Sp, or O (stable or finite rank) + S1:SchurRing + the first factor ring (same GroupActing as the ring of {\tt f}) + S2:SchurRing + the second factor ring (same GroupActing as the ring of {\tt f}) + m:ZZ + the rank of the first factor (alternate interface) + n:ZZ + the rank of the second factor +Outputs + h:HashTable + mapping pairs of partitions {\tt (mu, nu)} to coefficients. The entry {\tt h#(mu,nu)} is the multiplicity of the irreducible indexed by the pair in the branching of {\tt f}. Description Text - A class function (or virtual character of a symmetric group {\tt S_n}) is a function that is constant - on the conjugacy classes of {\tt S_n}. Class functions for {\tt S_n} are in one to one - correspondence with symmetric functions of degree {\tt n}. The class functions corresponding - to actual representations of {\tt S_n} are called {\tt characters}. - - The character of the standard representation of {\tt S_3} is + + Implements the classical branching rules of R.\ C.\ King, + {\it Branching rules for classical Lie groups using tensor and + spinor methods}, J.\ Phys.\ A {\bf 8} (1975), 429--449. For any + partition $\lambda$ and a two-factor restriction of the classical + group, the character decomposes by a universal formula expressed + via the triple Littlewood--Richardson coefficient + $c^\lambda_{\alpha,\beta,\gamma}$ (the coefficient of + $s_\lambda$ in $s_\alpha s_\beta s_\gamma$). + + $\bullet$ {\tt GL}: $s_\lambda \mid_{GL(m)\times GL(n)} = \sum c^\lambda_{\mu,\nu}\, s_\mu \otimes s_\nu$ + (the coproduct of Schur functions). + + $\bullet$ {\tt Sp}: $sp_\lambda \mid_{Sp(2m)\times Sp(2n)} = \sum_{\delta\text{ cols-even}} c^\lambda_{\delta,\mu,\nu}\, sp_\mu \otimes sp_\nu$, + the sum running over partitions $\delta$ whose conjugate has all parts even (equivalently, the parts of $\delta$ appear with even multiplicity). + + $\bullet$ {\tt O}: $o_\lambda \mid_{O(a)\times O(b)} = \sum_{\delta\text{ rows-even}} c^\lambda_{\delta,\mu,\nu}\, o_\mu \otimes o_\nu$, + where $\delta$ ranges over partitions with all parts even. + + For finite-rank factors the output partitions are collapsed via the Sam--Snowden--Weyman modification rules, so any $(\mu,\nu)$ that is killed or re-signed by the rule is handled automatically. Example - S = schurRing(QQ,s,3); - classFunction(s_{2,1}) - + S = schurRing(QQ, s, infinity); + A = schurRing(QQ, a, infinity); + B = schurRing(QQ, b, infinity); + pairs branch(S_{2,1}, A, B) Text - The character of the sign representation of {\tt S_5} is + + A slightly larger GL example: the restriction of $s_{3,2}$ along + $GL(\cdot) \times GL(\cdot)$ produces the full coproduct of the + Schur function, one term per ordered pair $(\mu,\nu)$ with + $c^{(3,2)}_{\mu,\nu}$ nonzero: Example - S = schurRing(QQ,s,5); - classFunction(s_{1,1,1,1,1}) - + pairs branch(S_{3,2}, A, B) Text - We can go back and forth between class functions and symmetric functions. - + + The Sp branching picks up an extra {\tt (mu, nu) = ({}, {})} term for $sp_{1,1}$ via $\delta = (1,1)$: + Example - R = symmetricRing(QQ,3); - cF = new ClassFunction from {{1,1,1} => 2, {3} => -1}; - sF = symmetricFunction(cF,R) - toS sF - classFunction sF - + Sp = schurRing(QQ, sp, infinity, GroupActing => "Sp"); + Asp = schurRing(QQ, asp, infinity, GroupActing => "Sp"); + Bsp = schurRing(QQ, bsp, infinity, GroupActing => "Sp"); + pairs branch(Sp_{1,1}, Asp, Bsp) Text - We can add, subtract, multiply, scale class functions: - + + For $sp_{2,1}$ the Sp branching sum runs over the two columns-even + deltas $\delta = ()$ and $\delta = (1,1)$; the latter gives the + correction terms supported on partitions of total weight one: + Example - S = schurRing(QQ,s,4); - c1 = classFunction(S_{2,1,1}-S_{4}); - c2 = classFunction(S_{3,1}); - c1 + c2 - c1 * c2 - 3*c1 - c2*2 - -/// + pairs branch(Sp_{2,1}, Asp, Bsp) + Text -doc /// -Key - (degree,ClassFunction) -Headline - Degree of virtual character -Description + On the orthogonal side, the branching of $o_{2,1}$ runs + over rows-even deltas $\delta = ()$ and $\delta = (2)$, + contributing correction terms of total weight one: + + Example + O = schurRing(QQ, oGp, infinity, GroupActing => "O"); + AO = schurRing(QQ, aO, infinity, GroupActing => "O"); + BO = schurRing(QQ, bO, infinity, GroupActing => "O"); + pairs branch(oGp_{2,1}, AO, BO) Text - For a virtual character {\tt ch} of a symmetric group on {\tt n} letters, the degree - of {\tt ch} is {\tt n}. - + + The {\tt ZZ, ZZ} form is a convenience that builds anonymous factor rings of the requested ranks (with the same {\tt GroupActing} and, for O, the same {\tt OddOrEven}). + Example - S = schurRing(s,5); - ch = classFunction s_(3,1,1) - degree ch + Sp4 = schurRing(QQ, sp4, 2, GroupActing => "Sp"); + pairs branch(Sp4_{2,1}, 1, 1) + Text + + The {\tt (m,n)} shortcut agrees with the result of spelling out + anonymous factor rings. For the GL branching of $s_{3,2}$ along + $GL(2)\times GL(2)$: + + Example + pairs branch(S_{3,2}, 2, 2) +SeeAlso + schurRing + specialize + GroupActing + modificationRule /// + doc /// Key - symmetricFunction - (symmetricFunction,ClassFunction,Ring) + toRatGL + (toRatGL,RingElement,SchurRing) Headline - Converts class function to symmetric function + Lift a Schur (GL, SL) character into a rational-GL Schur ring Usage - f = symmetricFunction(ch,S) + g = toRatGL(f, R) Inputs - ch:ClassFunction - S:Ring - a Symmetric or Schur ring -Outputs f:RingElement - element of a Symmetric or Schur ring + an element of a SchurRing with GroupActing {\tt "GL"} or {\tt "SL"}, or already {\tt "RatGL"} + R:SchurRing + a target SchurRing with GroupActing {\tt "RatGL"} +Outputs + g:RingElement + the image of {\tt f} in {\tt R}, with each Schur basis element $s_\lambda$ sent to the rational Schur character $s_{\lambda, ()}$ Description Text - Given a virtual character {\tt cF} of a - symmetric group, and given a Symmetric ring {\tt S}, - the method computes the corresponding symmetric function - as an element of {\tt S}. - + + A GroupActing {\tt "RatGL"} ring represents rational GL characters + with bipartition weights $(\alpha, \beta)$, where $\alpha$ is the + positive part and $\beta$ the negative part of the highest weight. + See Koike, {\it On the decomposition of tensor products of the + representations of classical groups}, Adv.\ Math.\ {\bf 74} + (1989). Ordinary GL/SL Schur characters $s_\lambda$ embed as + rational characters with trivial second weight, $s_{\lambda, ()}$; + {\tt toRatGL} performs this embedding coefficientwise. + + The IndexedVariableTable for a RatGL ring accepts either a bipartition + ($s_{\{\alpha,\beta\}}$) or a plain partition ($s_{2,1}$, which is + implicitly $s_{\{2,1\},\{\}}$). + Example - S = symmetricRing(QQ,4); - cF = new ClassFunction from {{1,1,1,1}=>24}; - symmetricFunction(cF,S) - symmetricFunction(cF,schurRing S) + G = schurRing(QQ, getSymbol "g", infinity, GroupActing => "GL"); + R = schurRing(QQ, getSymbol "sRat", infinity, GroupActing => "RatGL"); + toRatGL(g_{2,1} + 3 * g_1, R) + Text + + The simplest embedding sends the standard GL character $s_{1}$ + to the rational character $s_{(1),()}$. A slightly richer + input (here an LR product re-expressed by {\tt toRatGL}) goes + through coefficientwise, so the GL-basis expansion on the right + matches the one on the left: + + Example + toRatGL(g_{2,1}, R) + toRatGL(g_{1} * g_{1}, R) + Text + + Rational GL characters admit negative powers of the determinant. + The bipartition $s_{(), (1)}$ is the dual of the standard + representation, and the tensor product of the standard with its + dual decomposes as $s_{(1),(1)} \oplus s_{(),()}$ (the adjoint + representation plus the trivial): + + Example + Rdual = schurRing(QQ, ratD, infinity, GroupActing => "RatGL"); + ratD_{{1},{}} * ratD_{{},{1}} + Text + + If {\tt f} already lives in a RatGL ring, it is re-embedded into + the target {\tt R}. When {\tt R} has finite rank $n$, a pair + $(\alpha,\beta)$ with $\ell(\alpha)+\ell(\beta) \leq n$ is + {\it admissible} and $s_{\alpha,\beta}$ is left unchanged; + otherwise the Koike--Terada modification rule is applied. The + rule iteratively removes a border strip of length + $L = \ell(\alpha)+\ell(\beta)-n-1$ starting at the first box of + the last row, from both $\alpha$ and $\beta$, contributing a sign + $(-1)^{c(R_\alpha)+c(R_\beta)-1}$ per step (where $c(R)$ counts + the columns the strip occupies), until the result is admissible. + The character vanishes if at some step $L = 0$ or either + partition admits no border strip of the required length. + + The admissible boundary case $\ell(\alpha)+\ell(\beta) = n+1$ + therefore always vanishes ($L = 0$): + + Example + S = schurRing(QQ, getSymbol "sStab", infinity, GroupActing => "RatGL"); + T = schurRing(QQ, getSymbol "sFin", 3, GroupActing => "RatGL"); + toRatGL(sStab_{{1,1},{1,1}} + sStab_{{1},{1}}, T) + Text + + Embedding the stable bipartitions $((2,1),(\,))$, + $((1,1,1),(\,))$, and $((1),(1,1))$ into a finite-rank $GL(2)$ + rational ring keeps the first and kills the last two. The first + is admissible; the other two hit the boundary + $\ell(\alpha)+\ell(\beta) = n+1 = 3$ so $L = 0$: + + Example + Tfin2 = schurRing(QQ, getSymbol "sFin2", 2, GroupActing => "RatGL"); + toRatGL(sStab_{{2,1},{}}, Tfin2) + toRatGL(sStab_{{1,1,1},{}}, Tfin2) + toRatGL(sStab_{{1},{1,1}}, Tfin2) + Text + + When $\ell(\alpha)+\ell(\beta) > n+1$ the rule is genuinely + non-trivial and can produce a non-zero modified bipartition + with a sign. For instance, at $GL(3)$ with + $\alpha = (4,3,2,2)$ and $\beta = (5,2,2,1,1)$ the rule removes + a border strip of length $5$ from each partition, leaving + $(4,1,1)$ and $(5,1)$; a further pass (now with $L=1$) strips + one box from each, giving $(4,1)$ and $(5)$ with an overall + sign of $-1$: + + Example + Tfin3 = schurRing(QQ, getSymbol "sFin3", 3, GroupActing => "RatGL"); + toRatGL(sStab_{{4,3,2,2},{5,2,2,1,1}}, Tfin3) + + Text + + Another non-trivial example at $GL(4)$: the columns + $(1^3,1^3)$ satisfy $\ell(\alpha)+\ell(\beta) = 6 > n+1 = 5$, + so one border strip of length $L = 1$ is removed from each, + producing $(1^2,1^2)$ with an overall sign of $-1$: + + Example + Tfin4 = schurRing(QQ, getSymbol "sFin4", 4, GroupActing => "RatGL"); + toRatGL(sStab_{{1,1,1},{1,1,1}}, Tfin4) SeeAlso - classFunction --- chi + schurRing + specialize + GroupActing /// + doc /// Key - classFunction - (classFunction,RingElement) + toSymm + (toSymm,RingElement) + (toSymm,Number) Headline - Converts symmetric function to class function + Convert a Schur ring element to an element of the associated symmetric ring Usage - ch = classFunction(f) + g = toSymm f Inputs f:RingElement - element of a Symmetric ring + typically an element of a @TO SchurRing@ (scalars are returned unchanged) Outputs - ch:ClassFunction + g:RingElement + the image of {\tt f} in the @TO symmetricRing@ attached to its parent ring Description Text - Given a symmetric function {\tt f}, homogeneous of degree {\tt N}, - the method computes the corresponding virtual character - of the symmetric group {\tt S_N}. - - The character of the standard representation of {\tt S_5} is - + + Every @TO SchurRing@ has an associated @TO symmetricRing@ with + variables $e_i$, $h_i$, $p_i$. {\tt toSymm} rewrites a Schur-basis + element in that symmetric ring via the Jacobi-Trudi determinant + $s_\lambda = \det(h_{\lambda_i - i + j})$. Scalars are returned + unchanged. + + This is the dual of @TO toS@, and the two together let you move + freely between Schur-basis and $e$/$h$/$p$-basis representations. + Example - R = symmetricRing(QQ,5); - classFunction(jacobiTrudi({4,1},R)) + S = schurRing(QQ, s, 4); + toSymm s_{2,1} + Example + toS oo Text - The character of the second exterior power of the standard representation of {\tt S_5} is - + A larger partition produces a bigger Jacobi-Trudi expansion in + the $e$-basis; here is $s_{3,2,1}$ written in the + symmetric ring: + Example - R = symmetricRing(QQ,5); - classFunction(jacobiTrudi({3,1,1},R)) + toSymm s_{3,2,1} + Text + + {\tt toSymm} is additive and is a left-inverse of @TO toS@, so + {\tt toS toSymm} is the identity on Schur-basis elements. It + distributes over sums and scalars just like any ring map: + Example + toSymm(s_{2,1} + 3 * s_{1,1}) + toS toSymm s_{2,1} == s_{2,1} + Text + + In a tensor-product (two-layer) Schur ring, {\tt toSymm} is + applied to the outermost layer only, so a product like + $s_{2,1}\otimes t_{1,1}$ returns an element of the symmetric + ring of {\tt S} times the original {\tt t} factor: + + Example + T = schurRing(S, t, 3); + toSymm(s_{2,1} * t_{1,1}) SeeAlso - symmetricFunction --- chi + toS + toE + toH + toP + jacobiTrudi /// + doc /// Key - (classFunction,BasicList) + toGL + (toGL,RingElement) + (toGL,RingElement,SchurRing) Headline - Character of irreducible representation of symmetric group + Express an element in the plain GL Schur basis Usage - ch = classFunction(l) + g = toGL f + g = toGL(f, T) Inputs - l:BasicList - partition + f:RingElement + any symmetric-function-like element (in a @TO SchurRing@ of any + flavor or in a @TO symmetricRing@) + T:SchurRing + optional target ring; must have {\tt GroupActing => "GL"} Outputs - ch:ClassFunction + g:RingElement + {\tt f} re-expressed in the Schur basis of an associated (or + user-supplied) GL character ring Description Text - Given a partition {\tt l} of {\tt N}, the method computes the character of the irreducible - {\tt S_N}-representation corresponding to the partition {\tt l}. - + + {\tt toGL} is a readability synonym for @TO toS@. It is provided + so that calling code documenting an intent to obtain a {\em GL + character} reads naturally, in contrast to the more neutral + {\tt toS}. Conversions from @TO "Basis"@ {\tt "Monomial"} + rings and from {\tt GroupActing}-variant rings ({\tt "Sp"}, + {\tt "O"}, {\tt "SL"}, {\tt "RatGL"}) are handled transparently. + Example - R = symmetricRing(QQ,7); - cF = classFunction({3,2,1}) - toS(symmetricFunction(cF,R)) -SeeAlso - symmetricFunction + R = symmetricRing(QQ, 4); + toGL(R_{0} * R_{5}) -- e_1 * h_2 + Example + T = schurRing(QQ, t, 3); + toGL(R_{0} * R_{5}, T) + Text -/// + {\tt toGL} and @TO toS@ produce the same output on symmetric-ring + elements; only the name of the function differs: -doc /// -Key - scalarProduct -Headline - Standard pairing on symmetric functions/class functions -Description + Example + toGL(e_1 * h_2) == toS(e_1 * h_2) + toGL(h_3 - p_3) == toS(h_3 - p_3) Text - - This method computes the standard scalar product on the ring {\tt \Lambda} - of symmetric functions. One way to define this product is by imposing - that the collection of Schur functions {\tt s_{\lambda}} form - an orthonormal basis. - - Alternatively, by the correspondence between symmetric functions - and virtual characters of symmetric groups, this scalar product - coincides with the standard scalar product on class functions. - The number of standard tableaux of shape {\tt \{4,3,2,1\}} is: - + A monomial-basis Schur ring expands to the plain Schur basis + in the same way as a Kostka-inverse computation: the monomial + symmetric function $m_\lambda$ is a signed sum of Schur + functions. + Example - R = symmetricRing(QQ,10); - S = schurRing(QQ,s,10); - scalarProduct(h_1^10,s_{4,3,2,1}) + M = schurRing(QQ, m, 3, Basis => "Monomial"); + toGL m_{2,1} + Text + + Conversion from an {\tt Sp}-ring applies the Koike-Terada + branching rule that expands a symplectic character in the GL + Schur basis; the {\tt GL(3)} Schur expansion of the $Sp(6)$ + character $sp_{2,1}$ is: + + Example + Sp = schurRing(QQ, sp, 3, GroupActing => "Sp"); + toGL sp_{2,1} + Text + + Finally, {\tt toGL} of a polynomial expression built from + $e$-, $h$-, or $p$-generators in a {\tt symmetricRing} is an + efficient way to ask for its Schur decomposition: + + Example + U = symmetricRing(QQ, 4); + toGL(e_1 * p_2 + h_3) SeeAlso - internalProduct + toS + toSn + toSp + toO + convert /// + doc /// Key - (scalarProduct,RingElement,RingElement) + toSn + (toSn,RingElement,SchurRing) Headline - Standard scalar product of symmetric functions + Promote a Schur-basis element into an Sn character ring Usage - sp = scalarProduct(f1,f2) + g = toSn(f, T) Inputs - f1:RingElement - element of a Symmetric Ring - f2:RingElement - element of a Symmetric Ring + f:RingElement + element of a @TO SchurRing@ or @TO symmetricRing@ + T:SchurRing + target ring; must have {\tt GroupActing => "Sn"} Outputs - sp:QQ + g:RingElement + the element of {\tt T} with the same partition-indexed + coefficients as {\tt f} (after converting {\tt f} to the Schur + basis) Description Text - - Given symmetric functions {\tt f1} and {\tt f2}, the method - computes the standard pairing between {\tt f1} and {\tt f2}. - + + The GL and Sn flavors of a @TO SchurRing@ share the same partition + index set -- the distinction is in the multiplication (LR product + vs.\ internal product) and in the semantics (polynomial GL + character vs.\ Frobenius characteristic of an $S_n$-class + function). {\tt toSn} simply carries the coefficient data across. + + Finite-rank targets drop partitions with more than {\tt numgens T} + parts. Inputs in a variant basis ({\tt "Sp"}, {\tt "O"}, + {\tt "RatGL"}, {\tt "Monomial"}) are first expanded in the plain + Schur basis via @TO toS@. + Example - R = symmetricRing(QQ,5); - S = schurRing R - scalarProduct(h_5,p_5) - scalarProduct(S_{4,1},p_5) - + S = schurRing(QQ, s, 4); + Sn = schurRing(QQ, n, 4, GroupActing => "Sn"); + toSn(s_{2,1} + 3 * s_{1,1,1}, Sn) + Example + -- internal product: trivial rep at n=3 has Frobenius characteristic s_3 + a = toSn(s_3, Sn); + a * a Text - - Indeed, the coefficients of {\tt s_5} and {\tt s_{4,1}} in the - s-basis expansion of {\tt h_5} are as computed above: - + + Partition labels on the two sides match, but the multiplication + does not: the GL (LR) product of $s_{2,1}$ with itself lives + in degree 6, while the $S_3$-Kronecker product of the standard + representation with itself stays in degree 3 and decomposes as + $n_{3} + n_{2,1} + n_{1,1,1}$: + Example - R = symmetricRing(QQ,5); - toS p_5 + S3 = schurRing(QQ, s3, 3); + Sn3 = schurRing(QQ, n3, 3, GroupActing => "Sn"); + s3_{2,1} * s3_{2,1} + b = toSn(s3_{2,1}, Sn3); + b * b + Text + + Inputs in variant bases ({\tt "Sp"}, {\tt "O"}, {\tt "RatGL"}, + {\tt "Monomial"}) are first expanded through @TO toS@, so + {\tt toSn} also accepts symplectic or orthogonal characters and + returns the corresponding $S_n$-class function: + + Example + Sp = schurRing(QQ, sp, 3, GroupActing => "Sp"); + toSn(sp_{2,1}, Sn3) + Oo = schurRing(QQ, ooX, 3, GroupActing => "O"); + toSn(ooX_{2,1}, Sn3) +SeeAlso + toS + toGL + internalProduct + convert /// + doc /// Key - (scalarProduct,ClassFunction,ClassFunction) + convert + (convert,RingElement,Ring) Headline - Standard scalar product of class functions + Universal dispatcher for converting between Schur ring flavors Usage - sp = scalarProduct(ch1,ch2) + g = convert(f, T) Inputs - ch1:ClassFunction - ch2:ClassFunction + f:RingElement + any symmetric-function-like element + T:Ring + target @TO SchurRing@ or @TO symmetricRing@ Outputs - sp:QQ + g:RingElement + {\tt f} expressed in the natural basis of {\tt T} Description Text - - Given virtual characters {\tt ch1} and {\tt ch2}, the method - computes the standard pairing between {\tt ch1} and {\tt ch2}. - + + {\tt convert} is a thin routing layer that inspects the target + ring's classification and dispatches to the appropriate + specialized converter: + + \begin{itemize} + \item @TO SchurRing@ with {\tt GroupActing => "GL"} $\to$ @TO toS@, + \item @TO SchurRing@ with {\tt GroupActing => "Sn"} $\to$ @TO toSn@, + \item @TO SchurRing@ with {\tt GroupActing => "Sp"} $\to$ @TO toSp@, + \item @TO SchurRing@ with {\tt GroupActing => "O"} $\to$ @TO toO@, + \item @TO SchurRing@ with {\tt GroupActing => "SL"} $\to$ @TO toS@, + \item @TO SchurRing@ with {\tt GroupActing => "RatGL"} $\to$ @TO toRatGL@, + \item @TO symmetricRing@ $\to$ @TO toSymm@ followed by promotion. + \end{itemize} + + No new conversion mathematics is performed here; {\tt convert} + exists purely to simplify user-level code that does not want to + know which specialized converter to call. + Example - ch1 = new ClassFunction from {{3,2} => 2, {2,2,1} => -2, {3,1,1} => 2, {5} => 1}; - ch2 = new ClassFunction from {{2,2,1} => -2, {5} => 1, {1,1,1,1,1} => 5, {3,2} => 3, {4,1} => -2}; - scalarProduct(ch1,ch2) -/// + S = schurRing(QQ, s, 4); + Sp = schurRing(QQ, p, 2, GroupActing => "Sp"); + O = schurRing(QQ, o, 4, GroupActing => "O"); + Sn = schurRing(QQ, n, 4, GroupActing => "Sn"); + convert(s_{2,1}, Sp) + convert(s_{2,1}, O) + convert(s_{2,1}, Sn) + Text -doc /// -Key - internalProduct -Headline - Internal product of symmetric functions/class functions -Description + The dispatch works from any source basis to any target basis. + Below we start from a monomial-basis Schur ring and send the + same element into each of the flavors -- the dispatcher + selects {\tt toS}, {\tt toSp}, {\tt toO}, and {\tt toSn} + respectively: + + Example + M = schurRing(QQ, m, 4, Basis => "Monomial"); + convert(m_{2,1}, S) + convert(m_{2,1}, Sp) + convert(m_{2,1}, O) + convert(m_{2,1}, Sn) Text - - This method computes the internal (Kronecker) product of two homogeneous symmetric - functions of the same degree. If we think of these functions as being - virtual characters of some symmetric group, then their internal product - is just the character of the tensor product of the corresponding virtual - representations. We use the binary operator @TO symbol **@ as a shorthand for - @TO internalProduct@. - The complete symmetric function of degree {\tt n} corresponds - to the trivial {\tt S_n}-representation and is therefore - the unit of the representation ring of {\tt S_n}: - + To a rational-GL target, the dispatcher uses @TO toRatGL@, + which embeds a plain GL Schur character $s_\lambda$ as the + bipartition $s_{(\lambda, ())}$: + Example - R = symmetricRing(QQ,5); - S = schurRing(QQ,s,3); - internalProduct(h_3,s_{2,1}) - toE(h_3 ** e_3) + RRat = schurRing(QQ, rRat, 4, GroupActing => "RatGL"); + convert(s_{2,1}, RRat) + convert(s_{3} + 2*s_{1,1}, RRat) Text - - The square of the sign representation is the trivial representation: - + + A {\tt symmetricRing} source is also handled: here the + dispatcher routes through {\tt toSymm} and lands in the + Schur basis of the target ring. + Example - R = symmetricRing(QQ,5); - toH internalProduct(e_3,e_3) + U = symmetricRing(QQ, 4); + convert(e_1 * h_2, S) + convert(p_3, S) SeeAlso - scalarProduct + toS + toSn + toGL + toSp + toO + toRatGL + toSymm /// + doc /// Key - (internalProduct,RingElement,RingElement) + specialize + (specialize,RingElement,ZZ) + (specialize,RingElement,List) Headline - Kronecker product of symmetric functions + Specialize a stable character to a finite rank Usage - ip = internalProduct(f1,f2) + g = specialize(f, n) + g = specialize(f, ranks) Inputs - f1:RingElement - element of a Symmetric ring or a Schur ring - f2:RingElement - element of a Symmetric ring or a Schur ring + f:RingElement + an element of a @TO SchurRing@ (of any {\tt GroupActing} flavor) + n:ZZ + the target rank (a nonnegative integer) + ranks:List + a list of target ranks, one per @TO schurLevel@; an entry equal to + {\tt infinity} leaves that layer unchanged Outputs - ip:Ring - a Symmetric ring or a Schur Ring + g:RingElement + the image of {\tt f} in the corresponding finite-rank ring Description Text - - Given symmetric functions {\tt f1} and {\tt f2}, the method - computes the Kronecker product {\tt ip} between {\tt f1} and {\tt f2}. - The output {\tt ip} is an element in the ring of {\tt f2}. - + + Every ring produced by @TO schurRing@ comes in two sizes: the + {\em stable} ring (rank {\tt infinity}), which is a universal + object admitting arbitrarily many row labels, and the finite-rank + ring (rank {\tt n}), on which the relevant representation-theoretic + modification rule is enforced (see @TO modificationRule@). + The function {\tt specialize} bridges the two: it maps every + partition-indexed basis element of the stable ring to its image in + the finite-rank ring, collapsing or re-signing partitions that are + ``too long'' via the modification rule of + Sam-Snowden-Weyman (for {\tt Sp}/{\tt O}/{\tt RatGL}) or simply + truncating (for {\tt GL}/{\tt SL}/{\tt Sn}). + + {\bf GL specialization} drops every Schur label with more than + {\tt n} parts: + Example - R = symmetricRing(QQ,6); - S = schurRing(QQ,s,6); - toE(h_3**e_3) - Q = schurRing(QQ,q,6); - internalProduct(s_{3,3},q_{4,2}) + S = schurRing(QQ, s, infinity); + f = s_{3,2,1} + s_{2,1} + s_{1} + specialize(f, 3) + specialize(f, 2) + Text - - An error is returned if {\tt f1} and {\tt f2} don't have the - same degree. -/// -doc /// -Key - (internalProduct,ClassFunction,ClassFunction) -Headline - Tensor product of virtual representations -Usage - ip = internalProduct(ch1,ch2) -Inputs - ch1:ClassFunction - ch2:ClassFunction -Outputs - ip:ClassFunction -Description + {\bf Sp specialization} applies the type-C modification rule: + characters $sp_\lambda$ with $\ell(\lambda) > n$ are re-expressed + in the finite-rank ring (possibly with a sign, or as zero). + + Example + Sp = schurRing(QQ, sp, infinity, GroupActing => "Sp"); + specialize(sp_{2,1} + sp_{1,1,1}, 2) + specialize(sp_{1,1,1}, 1) + Text - - Given virtual characters {\tt ch1} and {\tt ch2}, the method - computes the character of the tensor product of corresponding - virtual representations of the symmetric group. - + + Concrete low-rank $Sp$ specializations show the modification in + action. At rank 1 ($Sp(2)$), $sp_{1,1,1}$ is modified via + {\tt "C"} to $-sp_{1}$, and at rank 2 ($Sp(4)$), $sp_{2,1,1}$ + has $\ell(\lambda) = 3 > 2$ and is also modified to a signed + lower-rank character: + Example - ch1 = new ClassFunction from {{4,4} => 2, {8} => -1, {5,2,1} => 2, {3,2,2,1} => 1}; - ch2 = new ClassFunction from {{2,2,2,2} => -4, {5,2,1} => 1, {3,2,2,1} => 3}; - internalProduct(ch1,ch2) - ch1 * ch2 -/// + specialize(sp_{1,1,1}, 1) + specialize(sp_{2,1,1}, 1) + + Text + + {\bf O specialization} distinguishes type $B_n$ ({\tt O(2n+1)}) + from type $D_n$ ({\tt O(2n)}) via the @TO OddOrEven@ option. If + the stable ring has a stored {\tt OddOrEven} attribute, that value + is used; otherwise the option must be supplied at the call site. + + Example + O = schurRing(QQ, o, infinity, GroupActing => "O"); + specialize(o_{2,1}, 3) + specialize(o_{2,1}, 2, OddOrEven => "Even") + + Text + + The two $O$ flavors give genuinely different images of the same + partition on the same target rank. For $\lambda = (2,1)$ at + rank 3 we compare $O(7)$ with $O(6)$: + + Example + specialize(o_{2,1}, 3, OddOrEven => "Odd") + specialize(o_{2,1}, 3, OddOrEven => "Even") + + Text + + {\bf SL specialization} drops rows of length equal to the full + rank, i.e. "columns" of height $n$, because the determinant + representation is trivial in $SL(n)$. Here the stable + $s_{3,2}$ collapses to $s_1$ in $SL(2)$, and $s_{3,3,1}$ + collapses to $s_{2,2}$ in $SL(3)$: + + Example + SL = schurRing(QQ, sl, infinity, GroupActing => "SL"); + specialize(sl_{3,2}, 2) + specialize(sl_{3,3,1}, 3) + + Text + + {\bf Tower specialization}. For a @TO SchurRing@ obtained by + iterating the @TO schurRing@ constructor over a coefficient ring + that is itself a @TO SchurRing@, one can specialize several layers + at once. The layers are listed from outermost to innermost, and + an entry equal to {\tt infinity} leaves that layer stable. + + Example + A = schurRing(QQ, a, infinity); + B = schurRing(A, b, infinity, GroupActing => "Sp"); + specialize(b_{1,1} * a_{2}, {2, 3}) -doc /// -Key - centralizerSize - (centralizerSize,List) -Headline - Size of the centralizer of a permutation -Usage - n = centralizerSize(rho) -Inputs - rho:List -Outputs - n:ZZ -Description Text - {\tt rho} is a list representing the cycle type of some permutation: the i-th entry in {\tt rho} is the number of cycles of length i of the permutation. - The output of the function {\tt centralizerSize} is the size of the centralizer in the symmetric group of any permutation of cycle type {\tt rho}. The cycle type {\tt rho} - corresponds to a partition {\tt lambda}, in which case {\tt centralizerSize(rho)} is also the value of the square norm of the symmetric function {\tt p_{lambda}}. + In a two-layer tower of GL and Sp flavors, the outer and inner + ranks can be adjusted independently; here is the same element + specialized to a second choice of ranks: Example - centralizerSize{1,1,1} - R = symmetricRing(QQ,6); - u = p_1 * p_2 * p_3; - scalarProduct(u,u) -/// + specialize(b_{2,1} + a_{3,1,1}, {3, 2}) -doc /// - Key - Memoize - Headline - Option to record values of the jacobiTrudi function - Description - Text - - This is an optional argument for the @TO jacobiTrudi@ - function, allowing one to store its values - in order to speed up computations. +SeeAlso + schurRing + modificationRule + OddOrEven + toSp + toO + toRatGL /// doc /// Key - SVariable - [schurRing,SVariable] - [symmetricRing,SVariable] + OddOrEven + [schurRing,OddOrEven] + [symmetricRing,OddOrEven] + [specialize,OddOrEven] Headline - Specifies symbol representing s-functions + Select type $B_n$ or type $D_n$ for an orthogonal Schur ring Description Text - This is an optional argument for the constructor of a Symmetric ring. It indicates the - symbol to be used to denote s-functions in the associated Schur ring. The default value - is {\tt s}. - + This is an optional argument for the @TO schurRing@ constructor + (and, by extension, the @TO symmetricRing@ and @TO specialize@ + methods), relevant only when {\tt GroupActing => "O"}. It + distinguishes the two flavors of orthogonal group: odd + ({\tt O(2n+1)}, type $B_n$) and even ({\tt O(2n)}, type $D_n$). + Its possible values are {\tt "Odd"} (the default) and {\tt "Even"}; + any other value raises an error. + + For stable orthogonal rings (rank {\tt infinity}) the distinction + is invisible at the level of multiplication, but it affects the + dimension formula and the modification rule applied when + @TO specialize@ is called. + Example - R = symmetricRing(QQ,5,SVariable => getSymbol"s"); - S = schurRing R - s_2^2 + OB = schurRing(QQ, oB, 2, GroupActing => "O", OddOrEven => "Odd"); + OD = schurRing(QQ, oD, 2, GroupActing => "O", OddOrEven => "Even"); + dim oB_{1} + dim oD_{1} -SeeAlso - EHPVariables -/// + Text + + At rank 3 and $\lambda = (2,1)$, the difference is much more + pronounced: {\tt O(7)} (type $B_3$) gives a 105-dimensional + irreducible, whereas {\tt O(6)} (type $D_3$) gives a + 64-dimensional one: + + Example + Oodd = schurRing(QQ, od, 3, GroupActing => "O", OddOrEven => "Odd"); + Oeven = schurRing(QQ, oe, 3, GroupActing => "O", OddOrEven => "Even"); + dim od_{2,1} + dim oe_{2,1} -doc /// -Key - EHPVariables - [schurRing,EHPVariables] - [symmetricRing,EHPVariables] -Headline - Specifies sequence of symbols representing e-, h-, and p-functions -Description Text - This is an optional argument for the constructor of a Symmetric or Schur ring. It indicates the - symbols to be used to denote e-, h-, and p-functions in the associated Symmetric ring. The - default values are {\tt (e,h,p)}. - + + The same partition of larger weight separates the two flavors + by an even bigger factor. For $\lambda = (3,2)$ the $O(7)$ + Weyl dimension is 693 while the $O(6)$ Weyl dimension is 300: + Example - S = schurRing(QQ,s,4,EHPVariables => (getSymbol"a",getSymbol"b",getSymbol"c")); - R = symmetricRing S - epol = toE s_{2,2,2} - toS epol + dim od_{3,2} + dim oe_{3,2} + + Text + + Supplying {\tt OddOrEven} for a non-orthogonal ring (for example + {\tt GroupActing => "Sp"} or {\tt "GL"}) is an error. + + Example + try (schurRing(QQ, bad, 2, GroupActing => "GL", OddOrEven => "Odd")) else print "schurRing rejected OddOrEven on a non-O ring" SeeAlso - SVariable + GroupActing + schurRing + specialize + toO + dim /// doc /// Key - GroupActing - [schurRing,GroupActing] - [symmetricRing,GroupActing] + modificationRule Headline - Specifies the group that is acting + Apply the Sam-Snowden-Weyman modification rule +Usage + result = modificationRule(lambda, n, type) +Inputs + lambda:BasicList + a partition + n:ZZ + the target rank + type:String + one of {\tt "C"}, {\tt "B"}, or {\tt "D"} -- see below +Outputs + result:Sequence + either a pair {\tt (tau, sign)} giving the modified partition + $\tau$ and a sign $\pm 1$, or @TO null@ if $\lambda$ reduces to + zero under the rule Description Text - This is an optional argument for the @TO schurRing@ and @TO symmetricRing@ functions. - When the exterior or symmetric powers of a symmetric function {\tt g} are computed, the result - depends on whether {\tt g} is interpreted as a virtual representation of a general - linear or symmetric group. The option {\tt GroupActing} specifies the interpretation - to be considered. Its possible values are {\tt "GL"} and {\tt "Sn"}, with the former - being the default. - + + This is the underlying combinatorial primitive used by + @TO specialize@ and by the finite-rank multiplication in + {\tt Sp} and {\tt O} character rings. It implements the + modification rules of Sam, Snowden, and Weyman, which describe + how a universal classical group character becomes a character of + the finite-rank group (or vanishes). + + The {\tt type} argument selects the classical family: + + $\bullet$ {\tt "C"}: symplectic groups {\tt Sp(2n)}. + + $\bullet$ {\tt "B"}: odd orthogonal groups {\tt O(2n+1)}. + + $\bullet$ {\tt "D"}: even orthogonal groups {\tt O(2n)}. + + Given a partition {\tt lambda}, the rule either returns a pair + {\tt (tau, sign)}, meaning that the universal character indexed by + {\tt lambda} equals {\tt sign} times the finite-rank character + indexed by {\tt tau}, or returns {\tt null}, meaning the + finite-rank character vanishes. + Example - S = schurRing(s,2); - exteriorPower(3,s_2) - T = schurRing(t,2,GroupActing => "Sn"); - symmetricPower(2,t_{1,1}) - + modificationRule({2,1,1}, 1, "C") + modificationRule({2,1,1}, 2, "C") + modificationRule({3,1,1}, 1, "B") + modificationRule({2,2}, 1, "D") + Text - - The first example computes the decomposition of {\tt \Lambda^3(Sym^2(V))} into irreducible - {\tt GL(V)}-representations, while the second one computes the - second symmetric power of the sign representation of the symmetric group {\tt S_2}. -/// + A partition can become ``stuck in the bulk'' after modification: + the rule reduces a long partition to a shorter (but still + nonempty) partition, possibly with a sign. For example + $\lambda = (4,4,2,1)$ at rank 2 in type $B$ reduces to + $\tau = (4,4,1)$ with sign $-1$: + + Example + modificationRule({4,4,2,1}, 2, "B") + + Text + + Other partitions cancel to {\tt null}: the rule applies and the + finite-rank character vanishes outright. For instance, in type + $C$ the partition $(3,2,2,2,1)$ at rank 2 is killed, and so is + $(4,3,2,1)$ at rank 2: + + Example + modificationRule({3,2,2,2,1}, 2, "C") + modificationRule({4,3,2,1}, 2, "C") + + Text + + Type $B$ and type $D$ give different answers on the same + partition even at the same rank. For $\lambda = (3,2,1)$ at + rank 2, type $B$ keeps the partition unchanged (with sign + $+1$) while type $D$ kills it: + + Example + modificationRule({3,2,1}, 2, "B") + modificationRule({3,2,1}, 2, "D") + + Text + + Conversely, $\lambda = (4,3,2)$ at rank 2 is killed by type + $B$ but survives (with a sign) in type $D$: + + Example + modificationRule({4,3,2}, 2, "B") + modificationRule({4,3,2}, 2, "D") + + Text + + Most users will not call {\tt modificationRule} directly; it is + invoked automatically by the finite-rank multiplication and by + @TO specialize@. It is exported so that library code that wishes + to implement custom variants (e.g. twisted character rings, or + non-standard specialization schemes) can share the same + combinatorics. + +SeeAlso + specialize + toSp + toO + GroupActing + OddOrEven +/// -------------------- -- test Jacobi-Trudi @@ -3514,78 +9669,935 @@ assert( (l) == {{1},{10},{20},{15},{4}} ) -- end test schurResolution --------------------------- +------------------------------------ +-- tests for kostkaNumber +------------------------------------ +TEST /// +-- Standard known Kostka numbers +assert(kostkaNumber({2,1},{2,1}) == 1) +assert(kostkaNumber({2,1},{1,1,1}) == 2) +assert(kostkaNumber({3},{2,1}) == 1) +assert(kostkaNumber({3},{3}) == 1) +assert(kostkaNumber({1,1,1},{1,1,1}) == 1) +assert(kostkaNumber({2,2},{2,1,1}) == 1) +-- Dominance order: K = 0 if mu does not dominate lambda +assert(kostkaNumber({2,1},{3}) == 0) +-- Size mismatch +assert(kostkaNumber({2,1},{2,2}) == 0) +/// + +------------------------------------ +-- tests for toM +------------------------------------ +TEST /// +S = schurRing(QQ,s,4); + +-- Output is a RingElement in a monomial-basis SchurRing +r1 = toM s_{2,1}; +assert(class ring r1 === SchurRing); +assert((ring r1).Basis == "Monomial"); + +-- s_{2,1} = m_{2,1} + 2 m_{1,1,1} +lf1 = new HashTable from listForm r1; +assert(lf1#{2,1} == 1); +assert(lf1#{1,1,1} == 2); +assert(#(keys lf1) == 2); + +-- s_{3} = m_3 + m_{2,1} + m_{1,1,1} +r2 = toM s_{3}; +lf2 = new HashTable from listForm r2; +assert(lf2#{3} == 1); +assert(lf2#{2,1} == 1); +assert(lf2#{1,1,1} == 1); + +-- s_{1,1,1} = m_{1,1,1} +r3 = toM s_{1,1,1}; +lf3 = new HashTable from listForm r3; +assert(lf3#{1,1,1} == 1); +assert(#(keys lf3) == 1); + +-- Linearity +r4 = toM(s_{3} + 2*s_{2,1}); +lf4 = new HashTable from listForm r4; +assert(lf4#{3} == 1); +assert(lf4#{2,1} == 3); +assert(lf4#{1,1,1} == 5); + +-- Zero +r5 = toM(0_S); +assert(r5 == 0); + +-- The associated monomial ring is cached across calls +assert(ring r1 === ring r2); + +-- Two-argument form with user-supplied target ring +MM = schurRing(QQ,mm,4,Basis => "Monomial"); +r6 = toM(s_{2,1},MM); +assert(ring r6 === MM); +lf6 = new HashTable from listForm r6; +assert(lf6#{2,1} == 1); +assert(lf6#{1,1,1} == 2); + +-- Monomial-basis input: identity +r7 = toM MM_{2,1}; +lf7 = new HashTable from listForm r7; +assert(lf7#{2,1} == 1); +assert(#(keys lf7) == 1); + +-- Error if second argument is not a monomial-basis ring +caught := false; +try toM(s_{2,1},S) else caught = true; +assert(caught); + +-- Works through a Symmetric ring by first going to Schur +R = symmetricRing(QQ,4); +r8 = toM(h_2); +lf8 = new HashTable from listForm r8; +assert(lf8#{2} == 1); +assert(lf8#{1,1} == 1); +/// -end +------------------------------------ +-- tests for Monomial-basis ring +------------------------------------ +TEST /// +M = schurRing(QQ,m,4,Basis => "Monomial"); +assert(M.Basis == "Monomial"); + +-- m_1 * m_1 = m_2 + 2 m_{1,1} +p1 = m_{1} * m_{1}; +lf1 = new HashTable from listForm p1; +assert(lf1#{2} == 1); +assert(lf1#{1,1} == 2); + +-- m_2 * m_1 = m_3 + m_{2,1} +p2 = m_{2} * m_{1}; +lf2 = new HashTable from listForm p2; +assert(lf2#{3} == 1); +assert(lf2#{2,1} == 1); + +-- m_{1,1} * m_1 = m_{2,1} + 3 m_{1,1,1} +p3 = m_{1,1} * m_{1}; +lf3 = new HashTable from listForm p3; +assert(lf3#{2,1} == 1); +assert(lf3#{1,1,1} == 3); + +-- Consistency with Schur-basis product: m_1 = s_1, so m_1*m_1 in M +-- should match the m-expansion of s_1*s_1 computed via toM into M +S = schurRing(QQ,s,4); +assert(toM(s_{1} * s_{1}, M) == m_{1} * m_{1}); +/// + +-------------------- +-- tests for Sp stable ring (GroupActing "Sp") +-------------------- +TEST /// +Sp = schurRing(QQ,sp,GroupActing => "Sp"); +S = schurRing(QQ,s); +assert(Sp.GroupActing == "Sp"); +-- Koike branching: sp_1,1 = s_1,1 - 1; use two-arg toS to target S explicitly. +assert(toS(sp_{1,1}, S) == S_{1,1} - 1); +-- sp_2 = s_2 (Sym^2 is irreducible for Sp) +assert(toS(sp_{2}, S) == S_{2}); +-- sp_{1,1,1} = s_{1,1,1} - s_1 +assert(toS(sp_{1,1,1}, S) == S_{1,1,1} - S_{1}); +-- sp_{2,1,1} = s_{2,1,1} - s_2 - s_{1,1} + 1. +-- Verify: at Sp(6), dim sp_{2,1,1} = 105 - 21 - 15 + 1 = 70, matches +-- Littlewood branching s_{2,1,1}|_Sp(6) = sp_{2,1,1} + sp_{2} + sp_{1,1}. +assert(toS(sp_{2,1,1}, S) == S_{2,1,1} - S_{2} - S_{1,1} + 1); +/// -restart -loadPackage"SchurRings" +TEST /// +S = schurRing(QQ,s); +Sp = schurRing(QQ,sp,GroupActing => "Sp"); +-- Inverse Koike: s_{1,1} = sp_{1,1} + 1 +assert(toSp(s_{1,1}, Sp) == Sp_{1,1} + 1_Sp); +-- s_2 = sp_2 +assert(toSp(s_2, Sp) == Sp_{2}); +-- Round trip: toSp o toS is identity on sp-basis +for lam in {{3,2,1}, {4,2}, {2,1,1}, {5}, {1,1,1,1}} do + assert(toSp(toS(Sp_lam, S), Sp) == Sp_lam); +-- Round trip: toS o toSp is identity on s-basis +for lam in {{3,2,1}, {4,2}, {2,1,1}, {5}, {1,1,1,1}} do + assert(toS(toSp(S_lam, Sp), S) == S_lam); +/// -d = 11 +TEST /// +-- Sp multiplication is the Newell-Littlewood product +Sp = schurRing(QQ,sp,GroupActing => "Sp"); +-- sp_1 * sp_1 = sp_2 + sp_{1,1} + 1 (since V otimes V for Sp = Sym^2 + Lambda^2, +-- and Lambda^2 contains a 1-dim invariant form) +assert(sp_{1} * sp_{1} == sp_{2} + sp_{1,1} + 1_Sp); +-- sp_2 * sp_1 = sp_3 + sp_{2,1} + sp_1 +assert(sp_{2} * sp_{1} == sp_{3} + sp_{2,1} + sp_{1}); +-- sp_{1,1} * sp_1 = sp_{2,1} + sp_{1,1,1} + sp_1 +assert(sp_{1,1} * sp_{1} == sp_{2,1} + sp_{1,1,1} + sp_{1}); +-- Associativity: (sp_1 * sp_1) * sp_1 == sp_1 * (sp_1 * sp_1) +assert((sp_{1} * sp_{1}) * sp_{1} == sp_{1} * (sp_{1} * sp_{1})); +/// -mul = method() -mul(List,List,List,List) := (lam,mu,nu,del) -> -( - m := sum lam; - (llam,lmu,lnu,ldel) := (lam,mu,nu,del); - if not llam#?1 then llam = llam | {0}; - if not lmu#?1 then lmu = lmu | {0}; - if not lnu#?1 then lnu = lnu | {0}; - if not ldel#?1 then ldel = ldel | {0}; - e := llam#1 + lmu#1 + lnu#1 + ldel#1; - f := max(llam#1, lmu#1, lnu#1, ldel#1); - local rez; - if e>=m-1 then - ( - rez = m//2 - f + 1; - if e%2 == 1 and m%2 == 0 then rez = rez - 1; - ) - else - ( - rez = (e+1)//2 - f + 1; - if e%2 == 1 then rez = rez - 1; - ); - max(0,rez) - ) - -T_1 = schurRing(QQ,t1,2); - -l = {(T_1)_{1}} -for i from 2 to 4 do -( - T_i = schurRing(T_(i-1),value concatenate("t",toString i),2); - l = l | {(T_i)_{1}}; -) -rep = product l +-------------------- +-- tests for O stable ring (GroupActing "O") +-------------------- +TEST /// +O = schurRing(QQ,o,GroupActing => "O"); +S = schurRing(QQ,s); +assert(O.GroupActing == "O"); +-- Koike branching: o_2 = s_2 - 1 (Sym^2 loses the invariant bilinear form) +assert(toS(o_{2}, S) == S_{2} - 1); +-- o_{1,1} = s_{1,1} (Lambda^2 V is irreducible for O(n), n >= 3) +assert(toS(o_{1,1}, S) == S_{1,1}); +-- o_3 = s_3 - s_1 +assert(toS(o_{3}, S) == S_{3} - S_{1}); +-- o_{2,1} = s_{2,1} - s_1 +assert(toS(o_{2,1}, S) == S_{2,1} - S_{1}); +/// -mods = new MutableList; -mods#0 = 1_(T_4) +TEST /// +S = schurRing(QQ,s); +O = schurRing(QQ,o,GroupActing => "O"); +-- Inverse Koike: s_2 = o_2 + 1 +assert(toO(s_{2}, O) == O_{2} + 1_O); +-- s_{1,1} = o_{1,1} +assert(toO(s_{1,1}, O) == O_{1,1}); +-- Round trip: toO o toS is identity on o-basis +for lam in {{3,2,1}, {4,2}, {2,1,1}, {5}, {1,1,1,1}} do + assert(toO(toS(O_lam, S), O) == O_lam); +-- Round trip: toS o toO is identity on s-basis +for lam in {{3,2,1}, {4,2}, {2,1,1}, {5}, {1,1,1,1}} do + assert(toS(toO(S_lam, O), S) == S_lam); +/// -for i from 1 to d do -( - pars = {{i}} | apply(splice{1..(i//2)},j->{i-j,j}); - mods#i = sum (for p in (toList (set pars)^**4) list - mul(p#0,p#1,p#2,p#3)*(T_1)_(p#0)*(T_2)_(p#1)*(T_3)_(p#2)*(T_4)_(p#3)); -) -M = toList mods +TEST /// +-- O multiplication is the Newell-Littlewood product for O +O = schurRing(QQ,o,GroupActing => "O"); +-- o_1 * o_1 = o_2 + o_{1,1} + 1 +assert(o_{1} * o_{1} == o_{2} + o_{1,1} + 1_O); +-- o_2 * o_1 = o_3 + o_{2,1} + o_1 +assert(o_{2} * o_{1} == o_{3} + o_{2,1} + o_{1}); +-- Associativity +assert((o_{1} * o_{1}) * o_{1} == o_{1} * (o_{1} * o_{1})); +/// + +TEST /// +-- Cross-conversion between Sp and O: route via a shared Schur ring. +Sp = schurRing(QQ,sp,GroupActing => "Sp"); +O = schurRing(QQ,o,GroupActing => "O"); +S = schurRing(QQ,s); +assert(toSp(o_{2}, Sp) == toSp(toS(o_{2}, S), Sp)); +assert(toO(sp_{2}, O) == toO(toS(sp_{2}, S), O)); +-- Cached associated rings reused +assert(ring(toSp o_{2}) === ring(toSp o_{1,1})); +/// + +-------------------- +-- tests for O odd/even (B_n vs D_n) distinction +-------------------- +TEST /// +-- Direct construction of finite Odd (B_n) and Even (D_n) O rings. +Oodd = schurRing(QQ, ob, 2, GroupActing => "O", OddOrEven => "Odd"); +Oeven = schurRing(QQ, od, 2, GroupActing => "O", OddOrEven => "Even"); +assert(Oodd.OddOrEven == "Odd"); +assert(Oeven.OddOrEven == "Even"); + +-- O(5) = B_2 Weyl dimensions +assert(dim ob_{1} == 5); -- defining rep +assert(dim ob_{1,1} == 10); -- Lambda^2 V, adjoint of SO(5) +assert(dim ob_{2} == 14); -- Sym^2 V minus trace +assert(dim ob_{2,1} == 35); + +-- O(4) = D_2 Weyl dimensions (SO(4) = SU(2) x SU(2) / Z_2) +assert(dim od_{1} == 4); -- defining rep +assert(dim od_{2} == 9); -- Sym^2 V minus trace (dim 10-1) +-- Lambda^2 V splits under SO(4) into 3+3; partition (1,1) picks one component. +assert(dim od_{1,1} == 3); +-- SO(4) = SU(2) x SU(2) / Z_2. Partition (a,b) with a>=b>=0 corresponds to +-- (j_1,j_2) = ((a+b)/2,(a-b)/2), dim (2j_1+1)(2j_2+1); (2,1) -> (3/2,1/2). +assert(dim od_{2,1} == 8); + +-- Default "O" ring (no OddOrEven) falls back to Odd (B_n): backward compat. +Odefault = schurRing(QQ, odef, 2, GroupActing => "O"); +assert(Odefault.OddOrEven == "Odd"); +assert(dim odef_{1} == 5); +/// +TEST /// +-- specialize with OddOrEven option. +Ost = schurRing(QQ, o2, GroupActing => "O"); +-- Default: Odd (B_n). +g = specialize(o2_{1}, 2); +assert((ring g).OddOrEven == "Odd"); +assert(dim g == 5); +-- Explicit OddOrEven => "Even" selects D_n. +g2 = specialize(o2_{1}, 2, OddOrEven => "Even"); +assert((ring g2).OddOrEven == "Even"); +assert(dim g2 == 4); +-- Odd and Even at the same n get distinct cached rings. +assert(ring g =!= ring g2); +/// -resol = schurResolution(rep,M,DegreeLimit => d) -resol/(i->(sum apply(i,j->dim(last j)))) +TEST /// +-- OddOrEven is rejected for non-O rings. +ok := true; +try (schurRing(QQ, xx, GroupActing => "Sp", OddOrEven => "Odd"); ok = false) else null; +assert ok; +-- Invalid value rejected. +ok = true; +try (schurRing(QQ, yy, 3, GroupActing => "O", OddOrEven => "Bogus"); ok = false) else null; +assert ok; +/// -restart -uninstallPackage"SchurRings" -installPackage"SchurRings" -check SchurRings -viewHelp SchurRings +-------------------- +-- tests for SL(n) Schur ring +-------------------- +TEST /// +-- Basic SL(n) canonicalization: partitions with n nonzero rows collapse +-- by subtracting the last row from every part. +SL3 = schurRing(QQ, sl, 3, GroupActing => "SL"); +-- sl_{1,1,1} = det = trivial rep in SL(3) +assert(sl_{1,1,1} == 1_SL3); +-- sl_{2,1,1} = sl_{1} after subtracting 1 from each row +assert(sl_{2,1,1} == sl_{1}); +-- sl_{3,3,3} = trivial +assert(sl_{3,3,3} == 1_SL3); +-- sl_{4,2,1} = sl_{3,1} +assert(sl_{4,2,1} == sl_{3,1}); +-- Partitions with fewer than n parts are unchanged +assert(sl_{2,1} == sl_{2,1}); +assert(sl_{3} == sl_{3}); +/// -restart -debug loadPackage"SchurRings" +TEST /// +-- SL multiplication agrees with GL mult followed by det-collapse. +-- In SL(3), sl_{1} * sl_{1,1} = s_{2,1} + s_{1,1,1} = s_{2,1} + 1. +SL3 = schurRing(QQ, sl, 3, GroupActing => "SL"); +assert(sl_{1} * sl_{1,1} == sl_{2,1} + 1_SL3); +-- sl_{1} * sl_{1} = sl_{2} + sl_{1,1} +assert(sl_{1} * sl_{1} == sl_{2} + sl_{1,1}); +-- (sl_{1})^3 = sl_{3} + 2 sl_{2,1} + sl_{1,1,1} +-- = sl_{3} + 2 sl_{2,1} + 1 (in SL(3)) +assert(sl_{1}^3 == sl_{3} + 2*sl_{2,1} + 1_SL3); +-- Associativity +assert((sl_{2} * sl_{1}) * sl_{1} == sl_{2} * (sl_{1} * sl_{1})); +/// -S = schurRing(QQ,s,3) -T = schurRing(S,t,4) -rep = s_1 * t_1 -symmetricPower(8,rep) +TEST /// +-- SL(2): every SL(2) rep is indexed by a single integer (highest weight). +SL2 = schurRing(QQ, sl2, 2, GroupActing => "SL"); +-- sl2_{1,1} = det = trivial +assert(sl2_{1,1} == 1_SL2); +-- sl2_{2,1} = sl2_{1} +assert(sl2_{2,1} == sl2_{1}); +-- Clebsch-Gordan for SU(2): V_a * V_b = sum_{k} V_{a+b-2k}, k=0..min(a,b). +-- In partition language: sl2_{a} * sl2_{b} = sum sl2_{a+b-2k}. +-- sl2_{2} * sl2_{2} = sl2_{4} + sl2_{2} + 1 (spins 2,1,0) +assert(sl2_{2} * sl2_{2} == sl2_{4} + sl2_{2} + 1_SL2); +-- sl2_{3} * sl2_{1} = sl2_{4} + sl2_{2} +assert(sl2_{3} * sl2_{1} == sl2_{4} + sl2_{2}); +/// + +TEST /// +-- Stable SL (numgens = infinity) coincides with stable GL. +SLstable = schurRing(QQ, slinf, GroupActing => "SL"); +-- No det-collapse happens; partitions are preserved. +assert(slinf_{1,1,1,1} != 1_SLstable); +-- Multiplication matches plain GL. +assert(slinf_{1} * slinf_{1} == slinf_{2} + slinf_{1,1}); +/// + +TEST /// +-- toS on an SL element lifts to the associated plain GL ring. +SL3 = schurRing(QQ, sl, 3, GroupActing => "SL"); +f = sl_{2,1}; +g = toS f; +T = ring g; +assert(class T === SchurRing); +assert(T.GroupActing == "GL"); +assert(numgens T == 3); +assert(g == T_{2,1}); +-- sl_{1,1,1} = 1 lifts to 1 in GL, not to s_{1,1,1}. +assert(toS (sl_{1,1,1}) == 1_T); +/// + +-------------------- +-- tests for SSW modification rules +-------------------- +TEST /// +-- Paper's worked example ([SSW] Example 3.20): +-- lambda = (6,5,4,4,3,3,2), n=2, gives tau=(6,5), i=8, sign=+1. +assert(modificationRule({6,5,4,4,3,3,2}, 2, "C") == ({6,5}, 1)); + +-- Admissible partitions pass through unchanged with sign +1. +assert(modificationRule({3,2,1}, 3, "C") == ({3,2,1}, 1)); +assert(modificationRule({2,1}, 3, "C") == ({2,1}, 1)); +assert(modificationRule({}, 2, "C") == ({}, 1)); + +-- sp_{1,1} in Sp(2): SSW-C returns null (strip size = 0), so the +-- formal character is zero in Sp(2). Corresponds to Koike: s_{1,1} = +-- sp_{1,1} + 1, and in Sp(2), s_{1,1} = Lambda^2 V = det = trivial, so +-- sp_{1,1} must be 0. +assert(modificationRule({1,1}, 1, "C") === null); + +-- sp_{1,1,1} in Sp(2): SSW-C returns ((1), -1). Corresponds to +-- s_{1,1,1} = Lambda^3 V = 0 for dim V = 2 via Koike +-- s_{1,1,1} = sp_{1,1,1} + sp_{1} = -sp_{1} + sp_{1} = 0. +assert(modificationRule({1,1,1}, 1, "C") == ({1}, -1)); + +-- Longer test: lambda = (2,2,1), n=1. r=3, L=2. k with lam_k+3-k=2: +-- k=1: 2, no. k=2: 3, no. k=3: 3, no. No k works -> null. +assert(modificationRule({2,2,1}, 1, "C") === null); + +-- Testing type D (m = 2n, even-dim orthogonal O(2n)). +-- lambda = (1,1) in O(2): admissible (lam^T_1 + lam^T_2 = 2+0 = 2 = m). +-- so we should get ((1,1), 1). +assert(modificationRule({1,1}, 1, "D") == ({1,1}, 1)); + +-- Testing type B (m = 2n+1, odd-dim orthogonal O(2n+1)). +-- lambda = (1) in O(3): admissible (lam^T = (1); 1+0 = 1 <= 3). +assert(modificationRule({1}, 1, "B") == ({1}, 1)); +/// + +-- Regression: Weyl dimensions of Sp(2n) and O(2n+1), O(2n) via dim +TEST /// +-- Sp(4) = C_2 Weyl dims +Sp4 = schurRing(QQ, getSymbol "sp4", 2, GroupActing => "Sp"); +assert(dim Sp4_{1} == 4); +assert(dim Sp4_{2} == 10); +assert(dim Sp4_{1,1} == 5); +assert(dim Sp4_{2,1} == 16); +assert(dim Sp4_{2,2} == 14); +-- Sp(4) multiplication +assert(Sp4_{1,1}*Sp4_{1,1} == Sp4_{2,2} + Sp4_{2} + 1_Sp4); +-- dim of a product equals product of dims +assert(dim(Sp4_{1}*Sp4_{1}) == 16); + +-- Sp(6) = C_3 Weyl dims +Sp6 = schurRing(QQ, getSymbol "sp6", 3, GroupActing => "Sp"); +assert(dim Sp6_{1} == 6); +assert(dim Sp6_{1,1} == 14); +assert(dim Sp6_{2} == 21); +assert(dim Sp6_{1,1,1} == 14); + +-- O(5) = B_2 Weyl dims (numgens 2, Odd) +O5 = schurRing(QQ, getSymbol "o5", 2, GroupActing => "O", OddOrEven => "Odd"); +assert(dim O5_{1} == 5); +assert(dim O5_{2} == 14); +assert(dim O5_{1,1} == 10); +assert(dim O5_{2,1} == 35); +assert(O5_{1}*O5_{1} == O5_{2} + O5_{1,1} + 1_O5); +assert(dim(O5_{1}*O5_{1}) == 25); + +-- O(6) = D_3 Weyl dims (numgens 3, Even) +O6 = schurRing(QQ, getSymbol "o6", 3, GroupActing => "O", OddOrEven => "Even"); +assert(dim O6_{1} == 6); +assert(dim O6_{1,1} == 15); +assert(dim O6_{2} == 20); +/// + +-- Regression: plethysm in Sp and O rings routes via GL Schur +TEST /// +-- Stable GL helper +Sp = schurRing(QQ, getSymbol "sp", GroupActing => "Sp"); +O = schurRing(QQ, getSymbol "oo", GroupActing => "O"); + +-- plethysm of identity (s_1) is identity +assert(plethysm({1}, Sp_{2}) == Sp_{2}); +assert(plethysm({1}, O_{1,1}) == O_{1,1}); + +-- Sym^2 and Lambda^2 of the defining rep for Sp and O (stable) +assert(plethysm({2}, Sp_{1}) == Sp_{2}); -- adjoint of Sp +assert(plethysm({1,1}, Sp_{1}) == Sp_{1,1} + 1_Sp); +assert(plethysm({2}, O_{1}) == O_{2} + 1_O); +assert(plethysm({1,1}, O_{1}) == O_{1,1}); -- adjoint of O + +-- Finite-rank plethysm works and matches detour via Schur +Sp4 = schurRing(QQ, getSymbol "sp4", 2, GroupActing => "Sp"); +O5 = schurRing(QQ, getSymbol "o5", 2, GroupActing => "O", OddOrEven => "Odd"); +assert(plethysm({2}, Sp4_{1}) == Sp4_{2}); +assert(plethysm({2}, O5_{1}) == O5_{2} + 1_O5); +/// + +-------------------------------------------------------------------- +-- Branching formulas (King, J. Phys. A 8 (1975), 429-449) +-------------------------------------------------------------------- +TEST /// +-- GL branching (stable): Delta(s_lambda) = sum c^lambda_{mu,nu} s_mu (x) s_nu +SGL = schurRing(QQ, getSymbol "sGL", infinity, GroupActing => "GL"); +A = schurRing(QQ, getSymbol "aGL", infinity, GroupActing => "GL"); +B = schurRing(QQ, getSymbol "bGL", infinity, GroupActing => "GL"); +h = branch(SGL_{2}, A, B); +assert(h#({2}, {}) == 1); +assert(h#({1}, {1}) == 1); +assert(h#({}, {2}) == 1); +h = branch(SGL_{1,1}, A, B); +assert(h#({1,1}, {}) == 1); +assert(h#({1}, {1}) == 1); +assert(h#({}, {1,1}) == 1); + +-- Finite-rank GL(6) -> GL(3) x GL(3): dimensions must match +SGL6 = schurRing(QQ, getSymbol "sGL6", 6, GroupActing => "GL"); +A3 = schurRing(QQ, getSymbol "aGL3", 3, GroupActing => "GL"); +B3 = schurRing(QQ, getSymbol "bGL3", 3, GroupActing => "GL"); +h = branch(SGL6_{2}, A3, B3); +d := sum for k in keys h list lift(h#k, ZZ) * dim(A3_(k#0)) * dim(B3_(k#1)); +assert(d == 21); -- dim Sym^2(C^6) = 21 +h = branch(SGL6_{1,1}, A3, B3); +d = sum for k in keys h list lift(h#k, ZZ) * dim(A3_(k#0)) * dim(B3_(k#1)); +assert(d == 15); -- dim Lambda^2(C^6) = 15 +/// + +TEST /// +-- Sp branching (stable): extra terms from delta with parts in pairs. +Sp = schurRing(QQ, getSymbol "spS", infinity, GroupActing => "Sp"); +Asp = schurRing(QQ, getSymbol "asp", infinity, GroupActing => "Sp"); +Bsp = schurRing(QQ, getSymbol "bsp", infinity, GroupActing => "Sp"); +h = branch(Sp_{1,1}, Asp, Bsp); +-- (mu, nu) from delta = {} (standard LR) plus (,) from delta = {1,1} (cols-even) +assert(h#({}, {}) == 1); +assert(h#({1,1}, {}) == 1); +assert(h#({}, {1,1}) == 1); +assert(h#({1}, {1}) == 1); + +-- Finite Sp(4) -> Sp(2) x Sp(2): dimensions match after modification. +RSp4 = schurRing(QQ, getSymbol "sprSp4", 2, GroupActing => "Sp"); +R1 = schurRing(QQ, getSymbol "sprA", 1, GroupActing => "Sp"); +R2 = schurRing(QQ, getSymbol "sprB", 1, GroupActing => "Sp"); +h = branch(RSp4_{1,1}, R1, R2); +d := sum for k in keys h list lift(h#k, ZZ) * dim(R1_(k#0)) * dim(R2_(k#1)); +assert(d == 5); +h = branch(RSp4_{2}, R1, R2); +d = sum for k in keys h list lift(h#k, ZZ) * dim(R1_(k#0)) * dim(R2_(k#1)); +assert(d == 10); +h = branch(RSp4_{2,1}, R1, R2); +d = sum for k in keys h list lift(h#k, ZZ) * dim(R1_(k#0)) * dim(R2_(k#1)); +assert(d == 16); +/// + +TEST /// +-- O branching (stable): extra terms from delta with all parts even. +OS = schurRing(QQ, getSymbol "oS", infinity, GroupActing => "O"); +OA = schurRing(QQ, getSymbol "oA", infinity, GroupActing => "O"); +OB = schurRing(QQ, getSymbol "oB", infinity, GroupActing => "O"); +h = branch(OS_{2}, OA, OB); +assert(h#({}, {}) == 1); -- from delta = {2} +assert(h#({2}, {}) == 1); +assert(h#({}, {2}) == 1); +assert(h#({1}, {1}) == 1); + +-- Finite O(7) -> O(3) x O(4): +RO7 = schurRing(QQ, getSymbol "or7", 3, GroupActing => "O", OddOrEven => "Odd"); +RO3 = schurRing(QQ, getSymbol "or3", 1, GroupActing => "O", OddOrEven => "Odd"); +RO4 = schurRing(QQ, getSymbol "or4", 2, GroupActing => "O", OddOrEven => "Even"); +h = branch(RO7_{1}, RO3, RO4); +d := sum for k in keys h list lift(h#k, ZZ) * dim(RO3_(k#0)) * dim(RO4_(k#1)); +assert(d == 7); +h = branch(RO7_{2}, RO3, RO4); +d = sum for k in keys h list lift(h#k, ZZ) * dim(RO3_(k#0)) * dim(RO4_(k#1)); +assert(d == 27); -- dim(Sym^2 C^7 - trace) + +-- ZZ shortcut form: branch(f, m, n) builds factor rings automatically. +h2 = branch(RO7_{1}, 1, 2); +assert(#keys h2 >= 1); +/// + +-------------------------------------------------------------------- +-- specialize on GL/SL rings and on multi-level towers +-------------------------------------------------------------------- +TEST /// +-- Stable GL -> finite GL(n) truncates partitions of length > n. +GLst = schurRing(QQ, getSymbol "glstT", infinity, GroupActing => "GL"); +f1 = specialize(GLst_{2,1}, 2); +assert(f1 != 0); +assert((ring f1).GroupActing == "GL"); +assert(numgens ring f1 == 2); +-- partition with too many rows vanishes +assert(specialize(GLst_{1,1,1}, 2) == 0); + +-- SL specialize collapses determinant row. +SLst = schurRing(QQ, getSymbol "slstT", infinity, GroupActing => "SL"); +f2 = specialize(SLst_{2,1}, 2); +assert((ring f2).GroupActing == "SL"); +-- In SL(2), s_{2,1} == det * s_{1} -> sl_{1}. +assert(f2 == (ring f2)_{1}); +/// + +TEST /// +-- Multi-level towers: GL over GL. +A = schurRing(QQ, getSymbol "aTowr", 3, GroupActing => "GL"); +B = schurRing(A, getSymbol "bTowr", 3, GroupActing => "GL"); +-- Multiplication works (previously tested) and specialize works on topmost. +g = specialize(B_{2,1}, 2); +assert(g != 0); +assert((ring g).GroupActing == "GL"); +assert(numgens ring g == 2); + +-- Multi-level list specialize: outer rank then inner rank. +r = specialize(B_{2,1}, {2, 2}); +assert(schurLevel ring r == 2); +assert(numgens ring r == 2); +assert(numgens coefficientRing ring r == 2); +-- infinity placeholder leaves a layer alone. +r2 = specialize(B_{2,1}, {2, infinity}); +assert(numgens ring r2 == 2); +assert(numgens coefficientRing ring r2 == 3); +/// + +TEST /// +-- Stable Sp and O towers over a GL coefficient ring: multiplication. +AG = schurRing(QQ, getSymbol "agTT", 3, GroupActing => "GL"); +SpG = schurRing(AG, getSymbol "spgTT", 2, GroupActing => "Sp"); +-- Sp(4) over AG: V tensor V = sp_2 + sp_{1,1} + 1. +assert(SpG_{1} * SpG_{1} == SpG_{2} + SpG_{1,1} + 1_SpG); + +OG = schurRing(AG, getSymbol "ogTT", 2, GroupActing => "O", OddOrEven => "Odd"); +-- V tensor V in O(5) = B_2: o_2 + o_{1,1} + 1. +assert(OG_{1} * OG_{1} == OG_{2} + OG_{1,1} + 1_OG); + +-- Specialize Sp-over-GL stays a level-2 ring with same GL coefficient ring. +SpSt = schurRing(AG, getSymbol "spstTT", infinity, GroupActing => "Sp"); +f = (AG_{1}) * SpSt_{2,1}; +sf = specialize(f, 2); +assert(schurLevel ring sf == 2); +assert(coefficientRing ring sf === AG); +-- Coefficient preserved through specialize. +assert(not zero sf); +/// + +TEST /// +-- RatGL stable ring: construction, bipartition indexing, Koike product. +S = schurRing(QQ, getSymbol "sRat", infinity, GroupActing => "RatGL"); +-- Bipartition indexing uses the IVT symbol: sRat_{{alpha},{beta}}. +assert(sRat_{{1},{}} != 0); +assert(sRat_{1} == sRat_{{1},{}}); -- flat partition syntax lifts with empty second weight +assert(sRat_{2,1} == sRat_{{2,1},{}}); +assert(sRat_{{},{}} == 1_S); -- trivial character + +-- Koike product: chi_{(1),()} * chi_{(),(1)} = chi_{(1),(1)} + chi_{(),()} +v = sRat_{{1},{}}; +vdul = sRat_{{},{1}}; +assert(v * vdul == sRat_{{1},{1}} + sRat_{{},{}}); + +-- chi_{(1),(1)}^2 = sum over 5 bipartitions + trivial + 2*adjoint. +prod = (sRat_{{1},{1}})^2; +expectedProd = (sRat_{{2},{2}} + sRat_{{2},{1,1}} + sRat_{{1,1},{2}} + + sRat_{{1,1},{1,1}} + 2*sRat_{{1},{1}} + sRat_{{},{}}); +assert(prod == expectedProd); + +-- Koike-Terada boundary case: when ell(alpha) + ell(beta) = n+1 the +-- border-strip length L = ell(alpha)+ell(beta)-n-1 is zero, so the +-- character specializes to 0. +assert(specialize(sRat_{{1,1},{1,1}}, 3) == 0); +assert(specialize(sRat_{{2},{1}}, 1) == 0); +assert(specialize(sRat_{{1},{1}}, 1) == 0); + +-- Non-trivial modification: at GL(3), alpha=(4,3,2,2), beta=(5,2,2,1,1) +-- has ell+ell=9 > n+1=4, and the Koike-Terada rule reduces it by two +-- border-strip passes to the admissible pair ((4,1),(5)) with sign -1. +Tfin3 = schurRing(QQ, getSymbol "tFin3", 3, GroupActing => "RatGL"); +assert(toRatGL(sRat_{{4,3,2,2},{5,2,2,1,1}}, Tfin3) == -tFin3_{{4,1},{5}}); +-- Another non-trivial pass: at GL(4) the column bipartition (1^3,1^3) +-- drops to (1^2,1^2) with sign -1 (one border-strip step of length 1). +Tfin4 = schurRing(QQ, getSymbol "tFin4", 4, GroupActing => "RatGL"); +assert(toRatGL(sRat_{{1,1,1},{1,1,1}}, Tfin4) == -tFin4_{{1,1},{1,1}}); + +-- Ring-homomorphism property of specialize. +a = sRat_{{2,1},{1}}; +b = sRat_{{1},{2}}; +assert(specialize(a*b, 3) == specialize(a,3) * specialize(b,3)); + +-- toRatGL: lift a plain GL character by adjoining trivial second weight. +G = schurRing(QQ, getSymbol "gRG", infinity, GroupActing => "GL"); +R = schurRing(QQ, getSymbol "tRat", infinity, GroupActing => "RatGL"); +assert(toRatGL(gRG_{2,1} + 3*gRG_{1}, R) == tRat_{{2,1},{}} + 3*tRat_{{1},{}}); +/// + +TEST /// +-- RatGL finite ranks: dimension formula and modification on products. +T = schurRing(QQ, getSymbol "uRat", 3, GroupActing => "RatGL"); +-- Fundamental dims at GL(3). +assert(dim(1_T) == 1); +assert(dim(uRat_{{1},{}}) == 3); -- standard V +assert(dim(uRat_{{},{1}}) == 3); -- dual V* +assert(dim(uRat_{{1},{1}}) == 8); -- adjoint (trace-free part of V tensor V*) +assert(dim(uRat_{{2},{}}) == 6); -- Sym^2 V +assert(dim(uRat_{{1,1},{}}) == 3); -- Alt^2 V + +-- Adjoint squared at GL(3): dimensions total 64 = 8^2. chi_{(1,1),(1,1)} +-- hits the Koike-Terada boundary (ell sum 4 = n+1 = 4, so L = 0) and +-- vanishes in the expansion. +adj = uRat_{{1},{1}}; +adj2 = adj^2; +assert(dim adj2 == 64); +expectedAdj2 = (uRat_{{2},{2}} + uRat_{{2},{1,1}} + uRat_{{1,1},{2}} + + 2*uRat_{{1},{1}} + uRat_{{},{}}); +assert(adj2 == expectedAdj2); + +-- Alt^2 V tensor Alt^2 V* at GL(3) isomorphic to V tensor V* (dim 9). +-- Stable decomposition has chi_{(1,1),(1,1)} + chi_{(1),(1)} + chi_{(),()}; +-- the first term is dropped at GL(3). +prodAltAlt = uRat_{{1,1},{}} * uRat_{{},{1,1}}; +assert(dim prodAltAlt == 9); +assert(prodAltAlt == uRat_{{1},{1}} + uRat_{{},{}}); +/// + + +------------------------------------------------------------------------- +-- Tier B API (toSymm exported, toGL, toSn, convert) + Tier A guards -- +------------------------------------------------------------------------- + +TEST /// +-- toSymm round-trip: s_{2,1} -> Jacobi-Trudi -> back to s_{2,1} +S = schurRing(QQ, getSymbol "s", 4); +f = s_{2,1}; +g = toSymm f; +assert(ring g =!= S); -- lands in the associated symmetric ring +assert(toS g == f); -- round-trips +-- scalar passes through +assert(toSymm(3_S) == toSymm(3)); +-- toSymm on a plain number is the identity +assert(toSymm(5) == 5); +/// + +TEST /// +-- toGL synonym and target-ring promotion. +R = symmetricRing(QQ, 4); +-- h_3 -> Schur is s_3. (R_{0} ... R_{11} lays out e/p/h; avoid depending +-- on the exact index order by using the named generators.) +debug SchurRings +use R; +assert(toGL(h_3) == toS(h_3)); + +T = schurRing(QQ, getSymbol "t", 3); +assert(toGL(h_3, T) == t_{3}); +assert(ring toGL(h_3, T) === T); + +-- toGL rejects a non-GL target. +Sn = schurRing(QQ, getSymbol "nT", 4, GroupActing => "Sn"); +assert(try (toGL(h_3, Sn); false) else true); +/// + +TEST /// +-- toSn: re-label into an Sn ring; respect finite-rank drop. +S = schurRing(QQ, getSymbol "s", 4); +Sn = schurRing(QQ, getSymbol "n", 4, GroupActing => "Sn"); + +x = s_{2,1} + 3 * s_{1,1,1}; +y = toSn(x, Sn); +assert(ring y === Sn); +assert(y == n_{2,1} + 3 * n_{1,1,1}); + +-- internalProduct via the re-labeled element: s_{3} corresponds to the +-- trivial S_3-irrep (dim 1) whose internal product with itself is +-- itself. At schurRing level this exercises the Sn multiplication path. +t = toSn(s_{3}, Sn); +assert(t * t == n_{3}); + +-- From a symmetric ring: toSn(h_2) should equal toS(h_2) relabeled. +R = symmetricRing(QQ, 4); +use R; +assert(toSn(h_2, Sn) == n_{2}); + +-- Rejects wrong target. +assert(try (toSn(s_{2,1}, S); false) else true); + +-- Finite-rank drop: a partition with more parts than numgens Sn is dropped. +Sn2 = schurRing(QQ, getSymbol "nT2", 2, GroupActing => "Sn"); +assert(toSn(s_{1,1,1}, Sn2) == 0_Sn2); +assert(toSn(s_{2} + s_{1,1,1}, Sn2) == nT2_{2}); +/// + +TEST /// +-- convert dispatcher: route by target's GroupActing / ring kind. +SGL = schurRing(QQ, getSymbol "sC", 4); +SSn = schurRing(QQ, getSymbol "nC", 4, GroupActing => "Sn"); +SSp = schurRing(QQ, getSymbol "pC", 2, GroupActing => "Sp"); +SOrt = schurRing(QQ, getSymbol "oC", 4, GroupActing => "O"); +SRat = schurRing(QQ, getSymbol "rC", infinity, GroupActing => "RatGL"); + +x = sC_{2,1}; +assert(convert(x, SSp) == toSp(x, SSp)); +assert(convert(x, SOrt) == toO (x, SOrt)); +assert(convert(x, SSn) == toSn(x, SSn)); +assert(convert(x, SRat) == toRatGL(x, SRat)); +assert(convert(x, SGL) == x); -- identity + +-- symmetric-ring target +R = symmetricRing(QQ, 4); +cR = convert(x, R); +assert(ring cR === R); +-- Value check: jacobiTrudi({2,1}) in R is the symmetric function of s_{2,1}. +assert(cR == jacobiTrudi({2,1}, R)); +/// + +TEST /// +-- Tier A#2: stable (rank-infinite) SchurRings reject toE/toH/toP with +-- a clear, actionable error. +SinfGL = schurRing(QQ, getSymbol "sInf", infinity); +assert(try (toE(sInf_{2,1}); false) else true); +assert(try (toH(sInf_{2,1}); false) else true); +assert(try (toP(sInf_{2,1}); false) else true); + +-- Same for a stable Sn ring. +SinfSn = schurRing(QQ, getSymbol "nInf", infinity, GroupActing => "Sn"); +assert(try (toE(nInf_{2,1}); false) else true); + +-- Finite-rank Schur ring still works. +Sfin = schurRing(QQ, getSymbol "sFin", 4); +use Sfin; +assert(toE(sFin_{2,1}) != 0); +assert(toH(sFin_{2,1}) != 0); +assert(toP(sFin_{2,1}) != 0); +/// + +TEST /// +-- Tier A#3 documentation: verify Sp/O -> toE/toH/toP behavior (treated as +-- plain Schur labels). sp_{2,1} labeled as a Schur partition goes to +-- Jacobi-Trudi of {2,1} = h_1 h_2 - h_3. (CHARACTER semantics instead +-- would require going through toS first, which lands in a different +-- GL Schur ring whose symmetricRing is distinct -- we document that +-- elsewhere; here we only verify the "label" behavior.) +Sp3 = schurRing(QQ, getSymbol "spA", 3, GroupActing => "Sp"); +use Sp3; +hLabel = toH(spA_{2,1}); +RH = ring hLabel; +assert(hLabel == RH.hVariable(1) * RH.hVariable(2) - RH.hVariable(3)); + +-- And toS of sp_{2,1} at Sp(6) is the known Koike inverse s_{2,1} - s_1. +tsElt = toS(spA_{2,1}); +glRing = ring tsElt; +assert(tsElt == glRing_{2,1} - glRing_{1}); +/// + +TEST /// +-- Tier C#9: skewSchurExpansion returns the same result on the second +-- call (memoization), and the values are correct. +debug SchurRings; +a = skewSchurExpansion({3,2,1}, {2,1}); +b = skewSchurExpansion({3,2,1}, {2,1}); +assert(a == b); +-- Known LR content: s_{3,2,1 / 2,1} = s_3 + 2 s_{2,1} + s_{1,1,1}. +-- (The two LR tableaux for s_{2,1} come from the two valid column-strict +-- fillings of the disconnected skew shape with content (2,1).) +-- Sort the partition list so the assertion is order-independent. +expected = sort {({1,1,1}, 1), ({2,1}, 2), ({3}, 1)}; +assert(sort a == expected); + +-- Trivial corners. +assert(skewSchurExpansion({}, {}) == {({}, 1)}); +assert(skewSchurExpansion({3}, {3}) == {({}, 1)}); +assert(skewSchurExpansion({3}, {4}) == {}); +assert(skewSchurExpansion({2,1}, {2,2}) == {}); -- mu not contained in lam + +-- Another known case: s_{2,1 / 1} = s_2 + s_{1,1}. +assert(sort skewSchurExpansion({2,1}, {1}) == sort {({2}, 1), ({1,1}, 1)}); +/// --- Local Variables: --- compile-command: "make -C $M2BUILDDIR/Macaulay2/packages PACKAGES=SchurRings pre-install" --- End: +TEST /// +-- Tier C#10: the shared cached workSymRing used by plethysm, +-- skewSchurExpansion, and the Kostka routines must NOT rebind the +-- user's global e/h/p/s symbols when it is created or grown. Before +-- this guard was added, calling plethysm would leak the cache ring into +-- the top-level environment and subsequent expressions like `e_4` or +-- `h_3` would evaluate into the wrong ring, breaking == comparisons. +R = symmetricRing(QQ, 12); +use R; +beforeRing = ring (e_4); +assert(beforeRing === R); +-- Force creation of the cached workSymRing (skewSchurExpansion uses it, +-- as do plethysm(BasicList,RingElement) and kostkaNumber). +lambda = new Partition from {3}; +p1 = plethysm(lambda, e_4); +-- After plethysm, user's e/h/p must still resolve inside R. +assert(ring (e_4) === R); +assert(ring (h_3) === R); +assert(ring (p_2) === R); +-- And the plethysm result must live in the user's ring, not the cache. +assert(ring p1 === R); +-- Sanity: equality with the `@` form (h_3 @ e_4) still holds -- both +-- sides must live in R. +p2 = h_3 @ e_4; +assert(ring p2 === R); +assert(p1 == p2); +/// + +TEST /// +-- kostkaNumber: values against known references (hook-length formula, +-- trivial content, dominance-order zeros), and cross-check against +-- the independent h_mu -> Schur polynomial expansion. +assert(kostkaNumber({3,1}, {1,1,1,1}) == 3); +assert(kostkaNumber({2,2}, {1,1,1,1}) == 2); +assert(kostkaNumber({5}, {2,1,1,1}) == 1); +assert(kostkaNumber({3,2,1}, {3,2,1}) == 1); +assert(kostkaNumber({2,2,1}, {3,1,1}) == 0); -- mu not dominated +assert(kostkaNumber({}, {}) == 1); +assert(kostkaNumber({3,2}, {2,2,1}) == 2); + +-- Cross-check against h_mu -> Schur expansion on every pair in degree 6. +R = symmetricRing(QQ, 6); +use R; +for mu in partitions 6 do ( + muL := toList mu; + hProd := product for i from 0 to #muL - 1 list (R.hVariable(muL#i)); + refMap := new MutableHashTable; + for t in listForm toS hProd do refMap#(t#0) = lift(t#1, ZZ); + for la in partitions 6 do ( + lamL := toList la; + vRef := if refMap#?lamL then refMap#lamL else 0; + assert(kostkaNumber(lamL, muL) == vRef); + ); + ); +/// + +TEST /// +-- Sn specialize: stable Sn -> finite Sn via truncation by #parts. +-- Also verify the finite -> finite form. +Sn = schurRing(QQ, getSymbol "snA", infinity, GroupActing => "Sn"); +use Sn; +f = snA_{3,2} + snA_{4,1,1}; -- two partitions: 2 parts and 3 parts +g = specialize(f, 2); +-- Target ring snfin2 has at most 2 parts, so the 3-part term drops. +assert(ring g =!= Sn); +assert((ring g).GroupActing == "Sn"); +assert(numgens ring g == 2); + +-- Finite Sn -> smaller finite Sn (the (3,2) term survives; drops nothing yet). +Sn5 = schurRing(QQ, getSymbol "snB", 5, GroupActing => "Sn"); +use Sn5; +h = snB_{3,2} + snB_{1,1,1,1,1}; -- both fit in 5 parts +g2 = specialize(h, 2); +assert(numgens ring g2 == 2); +-- Only the 2-part partition survives (the 5-parts one drops). +assert(size g2 == 1); +/// + +TEST /// +-- toRatGL from a symmetric ring: should route through toS. +-- And toSymm on a RatGL "polynomial" element (no negative components) +-- should recover the symmetric-ring representation. +R = symmetricRing(QQ, 4); +use R; +Rt = schurRing(QQ, getSymbol "rtR", infinity, GroupActing => "RatGL"); +x = toRatGL(h_2, Rt); +assert(ring x === Rt); +-- h_2 = s_2, so x should equal rt_{{2},{}}. +assert(x == rtR_{{2},{}}); + +-- toSymm round-trip on a polynomial RatGL element: rt_{{2,1},{}} +-- should map back to the symmetric function for s_{2,1}. +use Rt; +y = rtR_{{2,1},{}}; +ys = toSymm y; +-- Must live in some symmetricRing, and toS(ys) should recover s_{2,1}. +assert((ring ys).?EHPVariables); -- it's a symmetric ring +-- Going through the associated SchurRing, confirm the partition. +zs = toS ys; +assert((listForm zs)#0#0 == {2,1}); +assert((listForm zs)#0#1 == 1); + +-- toSymm on a non-polynomial (beta nonempty) element must error cleanly. +z = rtR_{{2,1},{1}}; +assert(try (toSymm z; false) else true); +/// + + +end diff --git a/M2/Macaulay2/packages/SemidefiniteProgramming.m2 b/M2/Macaulay2/packages/SemidefiniteProgramming.m2 index e86ab2f0ee0..e37c73cba08 100644 --- a/M2/Macaulay2/packages/SemidefiniteProgramming.m2 +++ b/M2/Macaulay2/packages/SemidefiniteProgramming.m2 @@ -523,7 +523,8 @@ simpleSDP2 = {Verbosity => 0} >> o -> (C,A,mb,y,checktrivial,UntilObjNegative) - mu = mu/theta; while true do ( S := C - sum toList apply(0..m-1, i-> y_(i,0) * A_i); - try Sinv := solve(S, id_(target S)) else ( + Sinv := solve(S, id_(target S)); + if Sinv === null then ( verbose1("Slack matrix is singular", o); return (Xnull,,,StatusFailed) ); -- compute Hessian: @@ -1083,3 +1084,6 @@ TEST /// --refine assert(norm(y1+sqrt 2)"M2") +/// diff --git a/M2/Macaulay2/packages/SimplicialComplexes/Code.m2 b/M2/Macaulay2/packages/SimplicialComplexes/Code.m2 index c8f2e0b657c..4bee2c09987 100644 --- a/M2/Macaulay2/packages/SimplicialComplexes/Code.m2 +++ b/M2/Macaulay2/packages/SimplicialComplexes/Code.m2 @@ -392,7 +392,7 @@ star (SimplicialComplex, RingElement) := (S, f) -> (simplicialComplex(monomialId SimplicialComplex * SimplicialComplex := (D, D') -> ( S := ring D ** ring D'; fromD := map(S, ring D); - fromD' := map(S, ring D'); + fromD' := if ring D === ring D' then map(S, ring D, (gens S)_{#gens ring D..#gens S - 1}) else map(S, ring D'); simplicialComplex monomialIdeal(fromD ideal D + fromD' ideal D') ) diff --git a/M2/Macaulay2/packages/SimplicialDecomposability.m2 b/M2/Macaulay2/packages/SimplicialDecomposability.m2 index 47e050ec094..f242cf904bf 100644 --- a/M2/Macaulay2/packages/SimplicialDecomposability.m2 +++ b/M2/Macaulay2/packages/SimplicialDecomposability.m2 @@ -266,7 +266,7 @@ shellingOrder SimplicialComplex := opts -> S -> ( if tmp != toList(0..#F-1) then error("shellingOrder: Option Permutation must be the same length as the number of facets and must be increasing consecutive integers."); F = F_(opts.Permutation); ) - else if opts.Random then F = random F; + else if opts.Random then F = shuffle F; O := {}; -- The pure case is easier, so separate it diff --git a/M2/Macaulay2/packages/SimplicialModules.m2 b/M2/Macaulay2/packages/SimplicialModules.m2 new file mode 100644 index 00000000000..16cc9ce188a --- /dev/null +++ b/M2/Macaulay2/packages/SimplicialModules.m2 @@ -0,0 +1,210 @@ +newPackage( + "SimplicialModules", + AuxiliaryFiles => true, + Version => "0.1", + Date => "April 27, 2026", + Authors => { + {Name => "Keller VandeBogert", Email => "kvandebo@nd.edu", HomePage => "https://sites.google.com/view/kellervandebogert/home"}, + {Name => "Michael DeBellevue", Email => "michael.debellevue@gmail.com"}}, + Headline => "methods for working in the category of simplicial modules", + Keywords => {"Homological Algebra", "Commutative Algebra"}, + PackageExports => {"Complexes", "SchurFunctors"} + ) + +export {"SimplicialModule", + "SimplicialModuleMap", + "simplicialModule", + --"combineSFactors", + "forgetComplex", + "forgetDegeneracy", + "isSimplicialModule", + "isSimplicialMorphism", + "randomSimplicialMap", + "tensorwithComponents", + "topDegree", + "exteriorInclusion", + "extPower", + "naiveNorm", + "schurMap", + "simplicialTensor", + "symmetricQuotient", + "tensorLES", + --"CheckMap", + "CheckComplex", + "RememberSummands", + "Degeneracy", + "TopDegree", + "ss", + "complexLength", + "CheckSum", + "complexMap", + "summandSurjection" + } + +-- normalize is owned and exported by SchurFunctors; SimplicialModules picks +-- it up automatically through PackageExports above and installs its own +-- normalize methods on simplicial modules in Normalization.m2. + + + + +------------------------------------------------------------------------------ +------------------------------------------------------------------------------ +-- **CODE** -- +------------------------------------------------------------------------------ +------------------------------------------------------------------------------ + +load "./SimplicialModules/SimplicialMapUtilities.m2" +load "./SimplicialModules/SimplicialModule.m2" +load "./SimplicialModules/Normalization.m2" + + + +----------------------------------------------------------------------------- +-- Documentation +----------------------------------------------------------------------------- + +beginDocumentation() + +load "./SimplicialModules/SimplicialModuleDOC.m2" + + + +----------------------------------------------------------------------------- +-- Tests +----------------------------------------------------------------------------- + +load "./SimplicialModules/SimplicialModuleTESTS1.m2" + + + +----------------------------------------------------------------------------- +-- Development +----------------------------------------------------------------------------- + +end-- + +uninstallPackage "SimplicialModules" +restart +debug installPackage "SimplicialModules" +debug loadPackage("SimplicialModules", LoadDocumentation => true, Reload => true) +debug needsPackage "SimplicialModules" +check SimplicialModules +viewHelp SimplicialModules + + Q = QQ[x_1..x_3]; + K = koszulComplex vars Q; + isWellDefined simplicialModule(K, 4) --no degeneracy here + S = simplicialModule(K,4, Degeneracy => true) + S.dd**Q^1 + isWellDefined S + Sdegen = simplicialModule(K,4,Degeneracy => true) + S.dd + --S.ss --should return error, no such key + Sdegen.ss --this is the version that actually has degeneracy maps cached + simplicialModule(K, 5) + C = complex(Q^1, Base => 1) + simplicialModule(C, 3) + isWellDefined oo + simplicialModule(complex(Q^2, Base => 2), 6, Degeneracy => true) + isWellDefined oo --nice, now we can be confident the objects we are messing with actually make any sense + oo.ss + id_S + phi = exteriorInclusion(S); + prune phi + prune coker phi + K = koszulComplex {x_1,x_2} + Sn = simplicialModule(K, 4, Degeneracy => true) + phi = exteriorInclusion(K) + isCommutative phi + isWellDefined phi + isCommutative prune phi + isWellDefined prune phi + sphi = exteriorInclusion(Sn); + psphi = prune sphi; + isWellDefined (source psphi).cache.pruningMap + isCommutative (source psphi).cache.pruningMap + isSimplicialMorphism (source psphi).cache.pruningMap --pruningMaps are well-defined morphisms + + S = simplicialModule(K,6) + + + p = randomComplexMap(K, K, Degree => 1, InternalDegree => 1) + sp = simplicialModule p + isWellDefined sp + isCommutative sp + normalize(sp, CheckComplex => false) + prune oo ---reobtain p + p' = randomComplexMap(K, K, Degree => -1) + sp' = simplicialModule p' + isWellDefined sp' + isCommutative sp' + normalize(sp', CheckComplex => false) + prune oo --reobtain p' + p = randomComplexMap(K, K, Degree => 1, Cycle => true, InternalDegree => 2) + sp = simplicialModule p + isWellDefined sp + isCommutative sp + normalize(sp, CheckComplex => false) + prune oo ---reobtain p + diffs = simplicialModule K.dd + isWellDefined diffs + isCommutative diffs + + image(id_S) == S + simplicialModule(id_K, 6) == id_S + isSimplicialMorphism id_S + simplicialModule(complex(Q^0), 6, Degeneracy => true) -- should be able to input 0 and get a well-defined output + isWellDefined oo + phi = randomSimplicialMap(S, S, Cycle => true, InternalDegree => 1) + isWellDefined phi + isCommutative phi + + bS = basis(0, forgetComplex S) + prune bS.dd + isWellDefined bS + isWellDefined prune bS + bid = basis(0, id_(forgetComplex S)) + isWellDefined bid + isCommutative bid + isWellDefined prune bid + isCommutative prune bid + + fS = forgetComplex S + fS.?ss + + bS = basis(1, forgetComplex S) + isWellDefined bS + prune bS.dd + isWellDefined oo + prune bS.ss + isWellDefined oo + normalize bS + prune oo + prune basis(1, K) + + bS = basis(3, forgetComplex S) + isWellDefined bS + prune bS.dd; + isWellDefined oo + prune bS.ss; + isWellDefined oo + normalize bS + prune oo + prune basis(3, koszulComplex vars Q) + + tS = truncate(1, S) + isWellDefined tS + prune tS + isWellDefined prune tS + tS = truncate(1, fS) + isWellDefined tS + prune tS + isWellDefined prune tS + isCommutative truncate(2, id_S) + isCommutative truncate(2, id_fS) + + K = koszulComplex {x_1,x_2} + isWellDefined (Sc = schurMap({1,1}, K)) + prune ext(2, K) + prune schurMap({2,1}, K, TopDegree => 4) diff --git a/M2/Macaulay2/packages/SimplicialModules/Normalization.m2 b/M2/Macaulay2/packages/SimplicialModules/Normalization.m2 new file mode 100644 index 00000000000..4f685b7e782 --- /dev/null +++ b/M2/Macaulay2/packages/SimplicialModules/Normalization.m2 @@ -0,0 +1,328 @@ + altSumFace = method(); +altSumFace(SimplicialModule,ZZ) := (S,n) -> (sum(0..n,i->(-1)^i*(S.dd)_(n,i))) + +altSumFace(Complex,ZZ) := (C,n) -> (altSumFace(simplicialModule(C,n),n)) + +--this function computes the naive normalization +--of a simplicial object, with is just the complex built from the +--modules of the simplicial object equipped with differential which is just the alternating sum of the face maps. +naiveNorm = method(); +naiveNorm(SimplicialModule,ZZ) := (S,n) -> (complex for i from 1 to n list altSumFace(S,i)) + +naiveNorm(SimplicialModule) := S -> naiveNorm(S, S.topDegree) + +naiveNorm(Complex,ZZ) := (C,n) -> (naiveNorm(simplicialModule(C,n),n)) + + + +--we assume that S is Gamma(C) for some complex C here +--sym = method(); +--sym(ZZ,Matrix) := (d,M) -> ( +-*symMult = method(); +symMult(List,ZZ,Ring) := (L,d,Q) -> (Qn := Q[z_1..z_d]; + M1 := tensor(for i in L list basis(i,Qn)); + M2 := basis(sum L,Qn); + sub(M1//M2,Q) + )*- + +--this function constructs the Dold-Puppe extension of the Schur functor to the category +--of chain complexes. The list L should be a partition +schurMap = method(Options => {Degeneracy => false,TopDegree => null}); +schurMap(List,SimplicialModule) := SimplicialModule => opts -> (lambda,S) -> (tdeg := topDegree S; + --C := S.complex; + -- fastSchurSparse handles face/degeneracy maps that are column-sparse + -- (at most one nonzero per column); falls back to generic schur otherwise. + fastOrFallback := f -> ( + r := fastSchurSparse(lambda, f); + if r =!= null then r else schur(lambda, f) + ); + L := hashTable for i to tdeg list i => schurModule(lambda,combineSFactors(S,i)); + H1 := applyValues(S.dd.map, fastOrFallback); + if opts.Degeneracy === true then H2 := applyValues(S.ss.map, fastOrFallback); + --print("we made it"); + if opts.Degeneracy === true then return simplicialModule(L,H1,H2,tdeg); + simplicialModule(L,H1,tdeg) + ) + +schurMap(List,SimplicialModuleMap) := SimplicialModuleMap => opts -> (lambda,phi) -> ( + S1 := source phi; + S2 := target phi; + if instance((keys phi.map)#0,Sequence) then error "expected SimplicialModuleMap to have singly graded indices"; + fastOrFallback := v -> ( + r := fastSchurSparse(lambda, v); + if r =!= null then r else schur(lambda, v) + ); + map(schurMap(lambda,S2),schurMap(lambda,S1),applyValues(phi.map, fastOrFallback),Degree => degree phi) + ) + +schurMap(List,Complex) := Complex => opts -> (lambda,C) -> (S := if opts.TopDegree =!= null then simplicialModule(C,opts.TopDegree) + else simplicialModule(C,(sum lambda)*length(C)); + Sn := schurMap(lambda,S); + normalize(Sn) + ) + +schurMap(List,ComplexMap) := ComplexMap => opts -> (lambda,phi) -> (phin := if opts.TopDegree =!= null then simplicialModule(phi,opts.TopDegree) + else simplicialModule(phi,(sum lambda)*(max(length(source phi),length target phi))); + phik := schurMap(lambda,phin); + normalize(phik) + ) + +-*sym = method(Options => {Degeneracy => false,TopDegree => null}); +sym(ZZ,Matrix) := Matrix => opts -> (d,phi) -> (Q := ring phi; + n := rank source phi; + m := rank target phi; + phitens := tensor((d:phi)); + M1 := symMult(toList(d:1),n,Q); + M2 := symMult(toList(d:1),m,Q); + matrix entries transpose ((matrix entries transpose(M2*phitens))//transpose(M1)) + ) + + +sym(ZZ,SimplicialModule) := SimplicialModule => opts -> (d,S) -> (tdeg := topDegree S; + --C := S.complex; + L = hashTable for i to tdeg list i => symmetricPower(d,combineSFactors(S,i)); + H1 = hashTable for i in keys (S.dd.map) list i => map(symmetricPower(d,target (S.dd)_i),symmetricPower(d,source (S.dd)_i),sym(d,((S.dd)_i))); + if opts.Degeneracy === true then H2 = hashTable for i in keys (S.ss.map) list i => sym(d,((S.ss)_i)); + --print("we made it"); + if opts.Degeneracy === true then return simplicialModule(L,H1,H2,tdeg); + simplicialModule(L,H1,tdeg) + ) + +sym(ZZ,SimplicialModuleMap) := SimplicialModuleMap => opts -> (d,phi) -> ( + S1 := source phi; + S2 := target phi; + if instance((keys phi.map)#0,Sequence) then error "expected SimplicialModuleMap to have singly graded indices"; + map(sym(d,S2),sym(d,S1),new HashTable from for i to max(topDegree S1,topDegree S2) list i => sym(d,phi_i),Degree => degree phi) + ) + +sym(ZZ,Complex) := Complex => opts -> (d,C) -> (if opts.TopDegree =!= null then S = simplicialModule(C,opts.TopDegree) + else S = simplicialModule(C,d*length(C)); + Sn := sym(d,S); + normalize(Sn) + ) + +sym(ZZ,ComplexMap) := ComplexMap => opts -> (d,phi) -> (if opts.TopDegree =!= null then phin = simplicialModuleMap(phi,opts.TopDegree) + else phin = simplicialModuleMap(phi,d*(max(length(source phi),length target phi))); + phik := sym(d,phin); + normalize(phik) + )*- + + +--this function computes the Dold-Puppe extension of the exterior power functor +--to the category of chain complexes. The integer d is the degree of the exterior power +extPower = method(Options => {Degeneracy=>false,TopDegree => null}) +extPower(ZZ,SimplicialModule) := SimplicialModule => opts -> (d,S) -> (tdeg := topDegree S; + --C := S.complex; + -- fastExteriorPowerSparse handles the common case where every column + -- has at most one nonzero entry (all d_k for k>0 and all degeneracies + -- coming from the Dold-Kan construction); it falls back to M2's + -- generic exteriorPower when the matrix is dense. + fastOrFallback := f -> ( + r := fastExteriorPowerSparse(d, f); + if r =!= null then r else exteriorPower(d, f) + ); + L := hashTable for i to tdeg list i => exteriorPower(d,combineSFactors(S,i)); + H1 := applyValues(S.dd.map, fastOrFallback); + H2 := if opts.Degeneracy === true then applyValues(S.ss.map, fastOrFallback); + --print("we made it"); + if opts.Degeneracy === true then return simplicialModule(L,H1,H2,tdeg); + simplicialModule(L,H1,tdeg) + ) + +extPower(ZZ,SimplicialModuleMap) := SimplicialModuleMap => opts -> (d,phi) -> ( + S1 := source phi; + S2 := target phi; + if instance((keys phi.map)#0,Sequence) then error "expected SimplicialModuleMap to have singly graded indices"; + map(extPower(d,S2),extPower(d,S1),applyValues(phi.map, v -> exteriorPower(d, v)),Degree => degree phi) + ) + +extPower(ZZ,Complex) := Complex => opts -> (d,C) -> (S := if opts.TopDegree =!= null then simplicialModule(C,opts.TopDegree) + else simplicialModule(C,d*length(C)); + Sn := extPower(d,S); + normalize(Sn) + ) + +extPower(ZZ,ComplexMap) := ComplexMap => opts -> (d,phi) -> (phin := if opts.TopDegree =!= null then simplicialModule(phi,opts.TopDegree) + else simplicialModule(phi,d*(max(length(source phi),length target phi))); + phik := extPower(d,phin); + normalize(phik) + ) + +--need to make this smarter: it should cache components so you can recover them easily + +--This function computes the simplicial tensor product and caches the direct sum +--indices so that the user can easily access components of the resulting face maps +--on particular direct summands of the tensor product +simplicialTensor = method(Options => {Degeneracy=>false,TopDegree => null}) +simplicialTensor(List) := SimplicialModule => opts -> T -> (if instance(T_0,SimplicialModule) then ( + degens := all(T, i->i.?ss); + tdeg := max apply(T,i->topDegree i); + L := hashTable for i to tdeg list i => tensorwithComponents(apply(T,s->s_i)); + H1 := applyPairs((T_0).dd.map, (i, v) -> (i, map(L#(i_0-1),L#(i_0),tensor(apply(T,s->s.dd_i))))); + H2 := if opts.Degeneracy === true or degens then applyPairs((T_0).ss.map, (i, v) -> (i, tensorwithComponents(apply(T,s->s.ss_i)))); + if opts.Degeneracy === true or degens then return simplicialModule(L,H1,H2,tdeg); + return simplicialModule(L,H1,tdeg); + ); + tLength := max apply(T,j->try length j else length source j); + if instance(T_0,Complex) then return normalize simplicialTensor(apply(T,j->simplicialModule(j,length(T)*tLength))); + if instance(T_0, ComplexMap) then return normalize tensor(apply(T, j -> simplicialModule(j, length(T)*tLength))); + ) + +simplicialTensor(SimplicialModule,SimplicialModule) := SimplicialModule => opts -> (S,T) -> (simplicialTensor({S,T})) + +simplicialTensor(ComplexMap, ComplexMap) := ComplexMap => opts -> (f,g) -> simplicialTensor({f,g}) + +simplicialTensor(ZZ,SimplicialModule) := SimplicialModule => opts -> (d,S) -> (simplicialTensor(toList(d:S),opts)) + +simplicialTensor(ZZ,Complex) := Complex => opts -> (d,C) -> (simplicialTensor(toList(d:C))) + +simplicialTensor(Complex,Complex) := Complex => opts -> (C,D) -> (simplicialTensor({C,D})) + + +SimplicialModule ** SimplicialModule := SimplicialModule => (S,T) -> (simplicialTensor(S,T)) + +hhh = method(); +hhh(Complex) := C -> (sum toList apply(0..length C,i->length HH_i(C))) + +degenMorphisms = method() +degenMorphisms(SimplicialModule,ZZ,ZZ) := (S,n,k) -> (Cn := naiveNorm(S,n); + map(Cn,Cn,i->(S.ss)_(i,k),Degree => 1) + ) + +degenMorphisms(Complex,ZZ,ZZ) := (C,n,k) -> (degenMorphisms(simplicialModule(C,n),n,k)) + +faceMorphisms = method() +faceMorphisms(SimplicialModule,ZZ,ZZ) := (S,n,k) -> (Cn := naiveNorm(S,n); + map(Cn,Cn,i->(S.dd)_(i,k),Degree => -1) + ) + +faceMorphisms(Complex,ZZ,ZZ) := (C,n,k) -> (faceMorphisms(simplicialModule(C,n),n,k)) + +makeNormMap = method(); +makeNormMap(SimplicialModule,ZZ) := (S,d) -> ( + K2 := intersect(for i from 1 to d list ker S.dd_(d,i)); + K1 := if d>1 then intersect(for i from 1 to d-1 list ker S.dd_(d-1,i)) else + if d ==1 then S_0; + I1 := if K1==0 then map(S_(d-1),(ring S)^0,0) + else inducedMap(S_(d-1),K1); + I2 := if K2==0 then map(S_d,(ring S)^0,0) + else inducedMap(S_(d),K2); + if I1 == 0 or I2 == 0 then return map(source I1,source I2,0); + ((S.dd_(d,0)*I2)//I1) + ) + +makeNormMap(SimplicialModuleMap,ZZ) := (phi,d) -> ( + S1 := source phi; + S2 := target phi; + if d==0 then return phi_0; + K1 := intersect( for i from 1 to d list ker S1.dd_(d,i)); + K2 := intersect( for i from 1 to d list ker S2.dd_(d,i)); + inducedMap(K2,K1,phi_d) + ) + +--this is the normalization functor from the category of simplicial modules +--back to the category of nonnegatively-graded chain complexes. +--The dispatcher `normalize = method(Options => true)` lives in SchurFunctors, +--where `normalize Filling` is also installed; we share that method here so the +--two packages cooperate on a single symbol. +normalize(SimplicialModule,ZZ) := Complex => {CheckSum => true, CheckComplex => true} >> opts -> (S,d) -> ( + if opts.CheckComplex and S.?complex then return naiveTruncation(S.complex,0,d); + n := length components S; + if opts.CheckSum and n>1 then return directSum for i to n-1 list normalize((components S)_i,d); + complex for i from 1 to d list makeNormMap(S,i)) + +normalize(SimplicialModule) := Complex => {CheckSum => true, CheckComplex => true} >> opts -> S -> (if S.?complex then return normalize(S,S.complexLength,opts); + normalize(S,S.topDegree,opts) + ) + +normalize(SimplicialModuleMap,ZZ) := ComplexMap => {CheckSum => true, CheckComplex => true} >> opts -> (phi,d) -> ( + if opts.CheckComplex and phi.cache.?complexMap then return naiveTruncation(phi.cache.complexMap,0,d); + n := length components phi; + if opts.CheckSum and n>1 then return directSum for i to n-1 list normalize((components phi)_i,d); + src := source phi; + trg := target phi; + C1 := normalize(src,d,opts); + C2 := normalize(trg,d,opts); + map(C2,C1,new HashTable from for i to max(max C1,max C2) list i => (if (f:=makeNormMap(phi,i)) == 0 then continue else map(C2_i, C1_i, f))) + ) + +normalize(SimplicialModuleMap) := ComplexMap => {CheckSum => true, CheckComplex => true} >> opts -> phi -> (d := max(topDegree source phi,topDegree target phi); + normalize(phi,d,opts) + ) + + +--this function computes the image of the 2nd exterior power into the tensor product +exteriorInclusion = method(); +exteriorInclusion(Module) := M -> ( + inducedMap(M**M,image(id_M**id_M-tensorCommutativity(M,M))) + ) + + +exteriorInclusion(SimplicialModule) := S -> (cS := forgetComplex S; + w2S := extPower(2,S); + T := S**S; + map(T,w2S,applyValues(cS.module, m -> dual wedgeProduct(1,1,dual m))) + ) + +exteriorInclusion(Complex,ZZ) := (C,d) -> (normalize exteriorInclusion(simplicialModule(C,d))) + +exteriorInclusion(Complex) := C -> (exteriorInclusion(C,length C)) + +--computes the image of the surjection from the simplicial tensor product +--onto the second symmetric power of a complex +symmetricQuotient = method(); +symmetricQuotient(Module) := M -> ( + phi := exteriorInclusion(M); + inducedMap(coker phi,ambient coker phi) + ) + +symmetricQuotient(SimplicialModule) := S -> ( + phi := exteriorInclusion(S); + inducedMap(coker phi,target phi) + ) + +symmetricQuotient(Complex,ZZ) := (C,d) -> (normalize symmetricQuotient(simplicialModule(C,d))) + +symmetricQuotient(Complex) := C -> (symmetricQuotient(C,length C)) + + +--this computes the long exact sequence of homology induced by the canonical short +--exact sequence of complexes $0 --> \wedge^2 --> T^2 --> Sym^2 --> 0$ +tensorLES = method(); +tensorLES(Complex,ZZ) := (C,d) -> (phi1 := exteriorInclusion(C,d); + phi2 := inducedMap(coker phi1,target phi1); + longExactSequence(prune phi2,prune phi1, Concentration => LengthLimit => d-1) + ) + + + +posComps = (n,d) -> (compositions(d, n-d))/(i -> i + toList(d : 1)) + +surjectionBijection = L -> ( + n := length L - 1; + d := sum L - 1; + lo := 0; + H := new MutableHashTable from {}; + for i from 0 to n do ( + for j from lo to lo + L_i-1 do ( + H#(j) = i; + ); + lo = lo + L_i; + ); + new HashTable from H + ) + +summandSurjection = method() +summandSurjection(ZZ,ZZ) := (n,d) -> ( + L := sort posComps(n+1,d+1); + L/surjectionBijection + ) + + + + + + + + + diff --git a/M2/Macaulay2/packages/SimplicialModules/SimplicialMapUtilities.m2 b/M2/Macaulay2/packages/SimplicialModules/SimplicialMapUtilities.m2 new file mode 100644 index 00000000000..f5a94d9b6e8 --- /dev/null +++ b/M2/Macaulay2/packages/SimplicialModules/SimplicialMapUtilities.m2 @@ -0,0 +1,338 @@ +entryCalculatorModified = (mu,j) -> ( + -- Calculates the (mu,j)'th entry of the A matrix + -- mu is a composition and j ranges from 0 to n + -- Note the we get compositions with strictly positive parts + -- by taking compositions with non-negative parts and adding one + u := 0; + for mui in (reverse mu) do ( + u=u+mui+1; + if u == j+1 then ( + if mui == 0 then + return 2 + else + return 1 + ); + ); + 0 +) + +promoteMaptoComplex = (d,k,C) -> (matrix d)**id_(C_k) + +promoteFaceMapZerotoComplex = (d,C) -> matrix ( + -- This runs over the whole matrix again and so is probably not an efficient way of doing things + -- Having the output of FaceMapZero be a hash table would be faster + d / (i -> apply(i,j -> ( + if j<0 then ( + id_(C_(abs(j+1))) + -- abs because identities were stored as negatives + -- +1 first because we stored identity of C_k as -k-1 + ) + else if j>0 then ( + dd^C_(j-1) + -- -1 because we stored the differential of C_k as k+1 + -- I don't actually think we need to do that though? probably could just store it as k + ) + else 0 + ) + ) + ) + ) + +zeroMatrix = (numRow,numCol) -> toList (numRow:(toList(numCol:0))); + +-- AMatrix(n,k) builds all n+1 columns of the A-matrix in a single pass over +-- compositions(k+1, n-k). Each composition mu contributes non-zero entries to +-- at most (k+1) distinct columns (one per partial sum from the right), so the +-- total work is |compositions| * (k+1), not (n+1) * |compositions| * (k+1) as +-- it would be if we called ACol independently for each column. +AMatrix = memoize((n,k) -> ( + comps := compositions(k+1,n-k); + numMu := #comps; + numCols := n+1; + cols := for j from 0 to numCols-1 list new MutableList from toList(numMu:0); + for r from 0 to numMu-1 do ( + mu := comps#r; + u := 0; + muLen := #mu; + for idx from 0 to muLen-1 do ( + mui := mu_(muLen-1-idx); + u = u + mui + 1; + if u <= numCols then + (cols#(u-1))#r = (if mui == 0 then 2 else 1); + ); + ); + cols/toList + )) + +ACol = (n,k,j) -> (AMatrix(n,k))#j + +rowOfId = (n,l) -> splice {l:0,1,(n-1-l):0} +-- n-1 so that the result has n-many columns + + +faceMapZero = (n,len) -> ( + -- Output is topDeg-many rows and sum(len,i->binomial(n,i))-many columns + -* This probably can be optimized + The output is extremely sparse, so should probably be reimplemented + as a hash table or function of some kind that specifies rules for + the i,j'th entry of the table + *- + -- If bounded => true then do this + -- TODO implement option toggle for bounded complexes + -- (otherwise why sum these binomial coefficients) + maxRows := sum(len+1,i->binomial(n-1,i)); + -- The number of rows, taking into account complex length bound + -- When len is larger than n, we'll have 2^n many rows + offset := 0; -- keeps track of what the top zero pad should be + verticalStrips := for k to len list ( + -- First loop over the vertical strips of columns + bink := binomial(n,k); -- Each strip is width binomial(n,k) + topZeros := zeroMatrix(offset,bink); + -- Create appropriately sized top pad of zeros + modifiedMat := for row to bink-1 list ( + -- This modified identity matrix uses Pascal's identity + -- First binomial(n-1,k-1) entries will be differential + if row<=binomial(n-1,k-1)-1 then splice{row:0,k+1,(bink-1-row):0} + --k+1 to keep track of which differential we'll need (k'th diff of complex) + else splice{row:0, -k-1,(bink-1-row):0} + -- -k-1 to keep track of which identity map we'll need (identity map of C_k) + ); + botZeros := zeroMatrix(maxRows-offset-binomial(n,k),binomial(n,k)); + -- Create appropriately sized bottom pad of zeros + offset = offset + binomial(n-1,k-1); + -- Update the offset + verticalStrip := flatten{topZeros,modifiedMat,botZeros} + -- Concat the modified matrix with the pads + ); + for row to maxRows-1 list ( + -- This concats the rows of the vertical strips together + flatten apply(verticalStrips, strip->strip#row) + ) + ) + + +faceMapnk = (n,k) -> ( + -- output is binomial(n-1,k)-many rows and binomial(n,k)-many columns + if k==n then {{0}} + else ( + Abigvector := ACol(n,k,n); + onePos := positions(Abigvector, i -> i==1); + onePos / (l -> rowOfId(binomial(n,k),l)) + ) + ) + + +faceMapik = (n,k,i) -> ( + -- I think there are supposed to be binomial(n-1,k)-many rows + -- There are binomial(n,k)-many columns + -- I think my timing code was wack and this is actually slower than the earlier implementation + -- Need to go back and check that + -- From what I recall, they were both pretty close though so it's not a high priority + Abigvector := ACol(n,k,i); + Asmallvector := ACol(n-1,k,i-1); + zeroPos := positions(Abigvector, i -> i==0); + onePos := positions(Abigvector, i -> i==1); + outputMat := new MutableList from zeroPos / (l -> rowOfId(binomial(n,k),l)); + sumPositions := positions(Asmallvector, i->i>=1); + for l to #sumPositions-1 do ( + myIndex := sumPositions_l; + outputMat#myIndex = outputMat#myIndex + (rowOfId(binomial(n,k),onePos#l)); + ); + toList outputMat +) + +-- Direct block-matrix construction of the i=0 face map d_0 : S_n -> S_{n-1}. +-- Bypasses the sparse encoding + promote pipeline used for generic faceMapi. +-- Pascal's identity: source block of width binomial(n,k) over C_k splits into +-- binomial(n-1,k-1) "dd" columns (target block k-1) and binomial(n-1,k) "id" +-- columns (target block k). M2's Kronecker product A ** B replaces each entry +-- of A with that entry times B, so id_(R^a) ** M is the direct sum of a copies +-- of M -- the order matters here. +faceMap0Direct = (n, C) -> ( + (lo, hi) := concentration C; + maxK := min(hi, n); + R := ring C; + blocks := for kp from 0 to maxK list ( + tarRank := binomial(n-1, kp); + for k from 0 to maxK list ( + if k == kp + 1 and tarRank > 0 then ( + aDD := tarRank; -- = binomial(n-1, k-1) + bDD := binomial(n-1, k); + ddBlock := id_(R^aDD) ** dd^C_k; + if bDD == 0 then ddBlock + else ddBlock | map(target ddBlock, C_k ** R^bDD, 0) + ) + else if k == kp then ( + aID := binomial(n-1, k-1); + bID := binomial(n-1, k); + if bID == 0 then map(C_k ** R^0, C_k ** R^(aID+bID), 0) + else if aID == 0 then id_(R^bID) ** id_(C_k) + else map(C_k ** R^bID, C_k ** R^aID, 0) | (id_(R^bID) ** id_(C_k)) + ) + else ( + map(C_kp ** R^(binomial(n-1, kp)), C_k ** R^(binomial(n, k)), 0) + ) + ) + ); + matrix blocks + ) + +faceMapi = (n,i,C) -> ( + -- Need to check if this can be sped up by direct summing as lists + -- Instead of first passing to matrices + -- and then saving the creation of the matrix option till the very end + (lo, hi) := concentration C; + maxK := min(hi,n-1); + -- Are we running till maxK? or until maxK+1? + if i==0 then ( + rawMat := faceMap0Direct(n, C); + -- Rewrap with source/target modules consistent with the i>0 path. + -- faceMap0Direct uses "matrix blocks" which may create different module + -- objects; here we rebuild source/target using the same directSum structure + -- as the module hash table in simplicialModule(Complex,...). + maxKn := min(hi, n); + srcParts := for k from 0 to maxKn list directSum toList(binomial(n,k):(C_k)); + tarParts := for k from 0 to maxK list directSum toList(binomial(n-1,k):(C_k)); + srcMod := if #srcParts == 1 then srcParts#0 else directSum srcParts; + tarMod := if #tarParts == 1 then tarParts#0 else directSum tarParts; + map(tarMod, srcMod, matrix rawMat) + ) + else if i==n then ( + preMat := fold(directSum,for k from 0 to maxK list ( + promoteMaptoComplex(faceMapnk(n,k),k,C) + -- Notice that we only pass in k at most n-1, so the check for k==n in faceMapi isn't needed + )); + preMat | map(target preMat,C_n,0) + ) + else ( + preMat = fold(directSum,for k from 0 to maxK list ( + promoteMaptoComplex(faceMapik(n,k,i),k,C) + )); + preMat | map(target preMat,C_n,0) + ) + ) + +degenMapik = (n, k, i) -> ( + -- We'll have binomial(n,k) many columns #A(n+1,k)=binomial(n+1,k)-many rows + + numCols := binomial(n,k); + zeroes := new List from (numCols) : 0; + col := ACol(n+1,k,i); + sig := new MutableList from 0..(#col-1); + l := 0; + for j to (#col-1) do( + if (col#j == 0) then ( + sig#j = rowOfId(numCols,l); + l = l+1; + continue; + ); + sig#j = zeroes; + ); + new List from sig + ) + +degenMapi = (n,i,C) -> ( + maxK := min(max C,n); + preMat := fold(directSum,for k from 0 to maxK list ( + promoteMaptoComplex(degenMapik(n,k,i),k,C) + ) + ); + preMat || map(C_(n+1),source preMat,0) + ) + +-- Sign of the permutation that sorts L into ascending order. O(#L^2), fine for small d. +signOfSort = L -> ( + s := 1; + for i from 0 to #L-2 do + for j from i+1 to #L-1 do + if L_i > L_j then s = -s; + s + ) + +-- Fast exterior power for matrices with at most one nonzero entry per column +-- (i.e. every column is either zero or a scalar multiple of a standard basis +-- vector). Dold-Kan face maps d_k (k > 0) and degeneracy maps s_k have this +-- shape exactly, yet M2's generic exteriorPower iterates all binomial(m,d) x +-- binomial(n,d) minors; on the (6, k) face maps with d = 3, that is ~4.4M +-- minors per call. Walking source d-subsets directly costs binomial(n, d). +-- +-- Returns null if f doesn't have the required shape, so callers can fall back. +fastExteriorPowerSparse = (d, f) -> ( + R := ring f; + m := numrows f; + n := numcols f; + if d <= 0 then return exteriorPower(d, f); + if d > n or d > m then + return map(exteriorPower(d, target f), exteriorPower(d, source f), 0); + ents := entries f; + -- colInfo_j = (rowIdx, value) for the unique nonzero, or (-1, 0_R) if the + -- column is zero. Bail out (return null) if any column has more than one + -- nonzero entry. + colInfo := for j from 0 to n-1 list ( + nz := for i from 0 to m-1 list + if ents_i_j != 0 then (i, ents_i_j) else continue; + if #nz > 1 then return null; + if #nz == 0 then (-1, 0_R) else nz_0 + ); + srcSubs := subsets(n, d); + tgtSubs := subsets(m, d); + tgtIdx := hashTable for i to #tgtSubs - 1 list tgtSubs_i => i; + triples := for sIdx from 0 to #srcSubs - 1 list ( + cols := srcSubs_sIdx; + infos := for c in cols list colInfo_c; + if any(infos, ri -> ri_0 == -1) then continue; + rows := infos / first; + if #(unique rows) < d then continue; + vals := infos / last; + sg := signOfSort rows; + valProd := fold((x, y) -> x * y, 1_R, vals); + (tgtIdx#(sort rows), sIdx) => sg * valProd + ); + map(exteriorPower(d, target f), exteriorPower(d, source f), triples) + ) + +-- Fast Schur functor for matrices with at most one nonzero entry per column. +-- Mirrors the logic of schur(List, Matrix) from SchurFunctors: +-- schur(lambda, f) = gN * exteriorPower(mu, f) * gM +-- where mu = conjugate(lambda) and gM, gN are the Schur change-of-basis +-- matrices. The bottleneck in the generic code is exteriorPower(mu, f), +-- which calls exteriorPower(mu_i, f) for each part and tensors them. +-- We replace those with fastExteriorPowerSparse. +-- +-- Returns null if f doesn't have the required shape (fallback signal). +fastSchurSparse = (lambda, f) -> ( + R := ring f; + m := numrows f; + n := numcols f; + -- Quick sparsity check: every column must have at most one nonzero. + ents := entries f; + for j from 0 to n-1 do ( + nzCount := 0; + for i from 0 to m-1 do if ents_i_j != 0 then nzCount = nzCount + 1; + if nzCount > 1 then return null; + ); + -- Build Schur modules for source and target (these depend only on + -- lambda and module rank, not on the matrix entries). + SM := schurModule(lambda, source f); + SN := schurModule(lambda, target f); + mu := toList conjugate new Partition from lambda; + -- Compute exteriorPower(mu, f) using fast sparse per part, then tensor. + fastExts := for p in mu list ( + r := fastExteriorPowerSparse(p, f); + if r === null then return null; + r + ); + F := fold((a, b) -> a ** b, fastExts); + gM := SM.cache#"Schur"_1; + gN := SN.cache#"Schur"_0; + result := gN * F * gM; + (source result).cache#"Schur" = SM.cache#"Schur"; + (target result).cache#"Schur" = SN.cache#"Schur"; + result + ) + + + + + + diff --git a/M2/Macaulay2/packages/SimplicialModules/SimplicialModule.m2 b/M2/Macaulay2/packages/SimplicialModules/SimplicialModule.m2 new file mode 100644 index 00000000000..57cebe47723 --- /dev/null +++ b/M2/Macaulay2/packages/SimplicialModules/SimplicialModule.m2 @@ -0,0 +1,1410 @@ + needsPackage "Complexes" + + SimplicialModule = new Type of MutableHashTable -- + -- note: we make this mutable in order to construct the + -- differential as a morphism of Simplicial modules (in the style of Complexes) + -- BUT: after construction, it is should be IMMUTABLE!! + -- at some point, we might want to allow lazy determination of the modules and maps + -- but for now, we insist that all modules and maps are explicit. + -- key: + -- ring + -- modules: hash table: ZZ => Module + -- differential: SimplicialModuleMap from C --> C, degree -1 + + + SimplicialModuleMap = new Type of HashTable + -- keys: + -- degree: ZZ + -- source: Simplicial module over a ring R + -- target: simplicial module over the same ring R + -- maps themselves (HashTable of Matrices), keys lying in the concentration period of the source. + -- not all of the keys maps#i, need be present. + -- missing ones are presumed to be zero maps. + -- cache: a CacheTable + -- cache.isCommutative: whether this map commutes with the face/degeneracy maps + -- not set until needed. unset means we have not checked yet, + -- and the user hasn't declared it to be true/false yet. + +SimplicialModule.synonym = "Simplicial Module" +SimplicialModuleMap.synonym = "Map of Simplicial Modules" + +topDegree = method(); +topDegree SimplicialModule := ZZ => S -> S.topDegree +topDegree SimplicialModuleMap := ZZ => f -> max(topDegree source f, topDegree target f) + +ring SimplicialModule := Ring => S -> S.ring + +moduleMaker = (C,d) -> ( + moduleList := new MutableHashTable; + maxK := min (d, length C); + for k to maxK do ( + moduleList#(d,k) = directSum toList( + binomial(d,k):(C_k)); + ); + for i in (sort keys moduleList) list (i,moduleList#i) + ) + +mapMaker = (phi,d) -> ( + mapList := new MutableHashTable; + maxK := min (d, max(length source phi,length target phi)); + for k to maxK do ( + mapList#(d,k) = directSum toList( + binomial(d,k):(phi_k)); + ); + for i in (sort keys mapList) list (i,mapList#i) + ) + + + + +--H1 is the face maps, H2 is the degeneracy maps +simplicialModule = method(Options => {Base=>0,Degeneracy => false}) +simplicialModule(Complex,HashTable,HashTable,ZZ) := SimplicialModule => opts -> (C,H1,H2,d) -> ( + spots := sort keys H1; + if #spots === 0 then + error "expected at least one map"; + R := ring C; + moduleList := new MutableHashTable; + for b to d do ( + maxK := min (b, max C); + for k to maxK do ( + modwComps := directSum toList(binomial(b,k):(C_k)); + modwComps.cache.components = flatten toList(binomial(b,k):(components (C_k))); + moduleList#(b,k) = modwComps + ); + ); + S := new SimplicialModule from { + symbol ring => R, + symbol topDegree => d, + symbol module => new HashTable from moduleList, + symbol cache => new CacheTable, + symbol complexLength => max C, + symbol complex => C + }; + S.dd = map(S,S,H1,Degree=>-1); + S.ss = map(S,S,H2,Degree=>1); + S + ) + +--H1 is the face maps +simplicialModule(Complex,HashTable,ZZ) := SimplicialModule => opts -> (C,H1,d) -> (--print("made it here!"); + spots := sort keys H1; + if #spots === 0 then + error "expected at least one map"; + (lo, hi) := concentration C; + R := ring C; + moduleList := new MutableHashTable; + for b to d do ( + maxK := min (b, hi); + for k to maxK do ( + modwComps := directSum toList(binomial(b,k):(C_k)); + modwComps.cache.components = flatten toList(binomial(b,k):(components (C_k))); + moduleList#(b,k) = modwComps + ); + ); + S := new SimplicialModule from { + symbol ring => R, + symbol topDegree => d, + symbol module => new HashTable from moduleList, + symbol cache => new CacheTable, + symbol complexLength => hi, + symbol complex => C + }; + S.dd = map(S,S,H1,Degree=>-1); + S + ) + +simplicialModule(HashTable,HashTable,HashTable,ZZ) := SimplicialModule => opts -> (L,H1,H2,d) -> (--print("we got started"); + R := ring (L#((keys L)#0)); + S := new SimplicialModule from { + symbol ring => R, + symbol topDegree => d, + symbol module => L, + symbol cache => new CacheTable, + }; + S.dd = map(S,S,H1,Degree=>-1); + S.ss = map(S,S,H2,Degree=>1); + S + ) + +simplicialModule(HashTable,HashTable,ZZ) := SimplicialModule => opts -> (L,H1,d) -> (--print("we got started"); + R := ring (L#((keys L)#0)); + S := new SimplicialModule from { + symbol ring => R, + symbol topDegree => d, + symbol module => L, + symbol cache => new CacheTable, + }; + S.dd = map(S,S,H1,Degree=>-1); + S + ) + + +simplicialModule(Complex,ZZ) := SimplicialModule => opts -> (C,d) -> ( + --C is a chain complex, output is the Dold-Kan image of C in the category of simplicial modules + if not instance(opts.Base, ZZ) then + error "expected Base to be an integer"; + (lo, hi) := concentration C; + --if lo == hi then + if instance(C,Complex) then ( + if opts.Degeneracy === true then (degenmapHash := hashTable flatten for n from 0 to d-1 list ( + for i from 0 to n list ( + (n,i) => degenMapi(n,i,C) + ) + );); + facemapHash := hashTable flatten for n from 1 to d list ( + for i from 0 to n list ( + (n,i) => faceMapi(n,i,C) + ) + ); + --print("mde it here first"); + if opts.Degeneracy === true then break return simplicialModule(C,facemapHash,degenmapHash,d); + --print("made it here"); + return simplicialModule(C,facemapHash,d) + ); + ) + + simplicialModule(Complex) := SimplicialModule => opts -> C -> (simplicialModule(C,max C,Degeneracy => opts.Degeneracy)) + + + simplicialModule(Module,ZZ) := SimplicialModule => opts -> (M,d) -> (simplicialModule(complex M,d,Degeneracy => opts.Degeneracy)) + + simplicialModule(Ring,ZZ) := SimplicialModule => opts -> (R,d) -> (simplicialModule(R^1,d,Degeneracy => opts.Degeneracy)) + +simplicialModule(Ideal, ZZ) := SimplicialModule => opts -> (I,d) -> simplicialModule(module I, d, opts) + +simplicialModule(ComplexMap,ZZ) := SimplicialModuleMap => opts -> (phi,d) -> ( + deg := degree phi; + src := simplicialModule(source phi,d, opts); + trg := simplicialModule(target phi, d, opts); + if deg > 0 then return simplicialModule map((target phi)[deg], source phi, phi, Degree => 0); + if deg < 0 then return simplicialModule map(target phi, (source phi)[-deg], phi[-deg], Degree => 0); + result := map(trg,src,new HashTable from for i to d list i => directSum apply(mapMaker(phi,i),j->j_1),Degree => degree phi); + result.cache.complexMap = phi; + result + ) + +simplicialModule(ComplexMap) := SimplicialModuleMap => opts -> phi -> (tDeg := max((source phi).concentration_1,(target phi).concentration_1); + simplicialModule(phi,tDeg, opts) + ) + + + +isWellDefined SimplicialModule := Boolean => C -> ( + k := keys C; + -- check keys, check their types + if not instance(C.ring, Ring) then ( + if debugLevel > 0 then printerr "expected 'ring C' to be a ring"; + return false; + ); + (lo,hi) := (0, C.topDegree); + if not instance(hi,ZZ) or lo > hi then ( + if debugLevel > 0 then printerr "expected topDegree to be a nonnegative integer"; + return false; + ); + if not instance(C.module, HashTable) then ( + if debugLevel > 0 then printerr "expected C.module to be a HashTable"; + return false; + ); + if not instance(C.dd, SimplicialModuleMap) then ( + if debugLevel > 0 then printerr "expected dd^C to be a SimplicialModuleMap"; + return false; + ); + if not instance(C.cache, CacheTable) then ( + if debugLevel > 0 then printerr "expected 'C.cache' to be a CacheTable"; + return false; + ); + -- check ring matches modules + if not all(keys C.module, i -> instance(i,Sequence) and i_0 >= lo and i_0 <= hi) + and not all(keys C.module, i -> instance(i,ZZ) and i >= lo and i <= hi) then ( + if debugLevel > 0 then printerr("expected all keys of C.module to be sequences or integers with nonnegative first entry, bounded by the top degree", toString [lo,hi]); + return false; + ); + if not all(values C.module, m -> ring m === ring C) then ( + if debugLevel > 0 then printerr "expected all modules in C.module to be over 'ring C'"; + return false; + ); + -- check face maps + if ring C.dd =!= ring C then ( + if debugLevel > 0 then printerr "expected ring of the face maps to be the ring of the simplicial module"; + return false; + ); + if degree C.dd =!= -1 then ( + if debugLevel > 0 then printerr "expected degree of the face maps to be -1"; + return false; + ); + if not all(keys (dd^C).map, i -> instance(i,Sequence) and i_0 >= lo+1 and i_0 <= hi) then ( + if debugLevel > 0 then printerr "expected all maps of the face maps to be indexed by integers in the concentration [lo+1,hi]"; + return false; + ); + for i from lo+1 to hi do ( + f := dd^C_i; + if source f != C_i or target f != C_(i-1) + then ( + if debugLevel > 0 then ( + printerr "expected source and target of the face maps to be modules in the simplicial module"; + printerr(" face map at index ", toString i, " fails this condition"); + ); + return false; + ); + ); + if not(C.?ss) then ( + D := naiveNorm C; + if not isWellDefined D then ( + if debugLevel > 0 then printerr "expected naive normalization to be a well-defined complex"; + return false; + ); + ); + if C.?ss then ( + if not isSimplicialModule C then ( + if debugLevel > 0 then printerr "object fails to satisfy simplicial identities; run isSimplicialModule to see where it fails"; + return false; + ); + ); + true + ) + + +-- helpers for comparing map compositions (handles both free and non-free modules) +-- tries direct composition first; falls back to matrix-level entry comparison +mapsEq = (A, B, C, D) -> ( + -- checks if A*B == C*D as maps + if target B === source A and target D === source C + then try (A * B == C * D) else matEq(matrix A * matrix B, matrix C * matrix D) + else matEq(matrix A * matrix B, matrix C * matrix D) + ) +mapIsId = (A, B) -> ( + -- checks if A*B == identity + if target B === source A + then try (A * B == id_(source B)) else matIsId(matrix A * matrix B) + else matIsId(matrix A * matrix B) + ) +matEq = (A, B) -> entries A == entries B +matIsId = A -> entries A == entries id_((ring A)^(numrows A)) + +--this method checks if the simplicial identities hold for simplicial objects with degeneracy map keys +isSimplicialModule = method() +isSimplicialModule(SimplicialModule) := Boolean => S -> ( + if not S.?ss then error "expected S to have both face and degeneracy maps"; + faceMaps := S.dd; + degenMaps := S.ss; + t := S.topDegree; + for i from 1 to t do ( + for j from 2 to i do ( + for k from 1 to j-1 do ( + if not mapsEq(dd^S_(i-1,k), dd^S_(i,j), dd^S_(i-1,j-1), dd^S_(i,k)) + then ( + if debugLevel > 0 then ( + printerr "simplicial map identities fail for face/face compositions"; + printerr("face/face composition for indices ", toString (i,j,k), " fail"); + ); + return false; + ); + ); + ); + ); + for i from 0 to t-1 do ( + for j from 0 to i do ( + for k from 0 to j-1 do ( + if not mapsEq(dd^S_(i+1,k), ss^S_(i,j), ss^S_(i-1,j-1), dd^S_(i,k)) + then ( + if debugLevel > 0 then ( + printerr "simplicial map identities fail for face/degeneracy compositions"; + printerr("face/degeneracy composition for indices ", toString (i,j,k), " fail"); + ); + return false; + ); + ); + ); + ); + for i from 0 to t-1 do ( + for j from 0 to i do ( + if not mapIsId(dd^S_(i+1,j), ss^S_(i,j)) or not mapIsId(dd^S_(i+1,j+1), ss^S_(i,j)) + then ( + if debugLevel > 0 then ( + printerr "simplicial map identities fail for face/degeneracy compositions"; + printerr("face/degeneracy composition for indices ", toString (i,j,j), " fail"); + ); + return false; + ); + ); + ); + for i from 0 to t-1 do ( + for j from 0 to i do ( + for k from j+2 to i do ( + if not mapsEq(dd^S_(i+1,k), ss^S_(i,j), ss^S_(i-1,j), dd^S_(i,k-1)) + then ( + if debugLevel > 0 then ( + printerr "simplicial map identities fail for face/degeneracy compositions"; + printerr("face/degeneracy composition for indices ", toString (i,j,k), " fail"); + ); + return false; + ); + ); + ); + ); + for i from 0 to t-1 do ( + for j from 0 to i do ( + for k from 0 to j do ( + if not mapsEq(ss^S_(i+1,k), ss^S_(i,j), ss^S_(i+1,j+1), ss^S_(i,k)) + then ( + if debugLevel > 0 then ( + printerr "simplicial map identities fail for degeneracy/degeneracy compositions"; + printerr("degeneracy/degeneracy composition for indices ", toString (i,j,k), " fail"); + ); + return false; + ); + ); + ); + ); + true + ) + + + +isWellDefined SimplicialModuleMap := f -> ( + k := keys f; + -- source and target + if ring f.source =!= ring f.target then ( + if debugLevel > 0 then printerr "expected source and target to have the same ring"; + return false; + ); + if not isWellDefined f.source or not isWellDefined f.target then ( + if debugLevel > 0 then printerr "expected source and target to be well-defined simplicial modules"; + return false; + ); + if not instance(f.degree, ZZ) then ( + if debugLevel > 0 then printerr "expected degree of homomorphism to be an integer"; + return false; + ); + (lo,hi) := (0,f.source.topDegree); + if not all(keys f.map, i -> instance(i,ZZ) or instance(i, Sequence)) then ( + if debugLevel > 0 then printerr "expected all maps to be indexed by integers or sequences of two integers"; + return false; + ); + if all(keys f.map, i -> instance(i,ZZ)) then for i from lo to hi do ( + g := f_i; + if source g != f.source_i or target g != f.target_(i+f.degree) + then ( + if debugLevel > 0 then ( + printerr "expected source and target of maps to agree with those of simplicial module"; + printerr(" the map at index ", toString i, " fails this condition"); + ); + return false; + ); + ); + --print "here we are"; + if all(keys f.map, i -> instance(i,ZZ)) and f.cache.?isCommutative then ( + deg := degree f; + C := f.source; + D := f.target; + (loC,hiC) := (0, C.topDegree); + (loD,hiD) := (0, D.topDegree); + iscommutative := true; + for i from loC to hiC do ( + if i+deg-1 >= loD and i+deg-1 <= hiD then ( + for l from 0 to i do ( + if not (try (dd^D_(i+deg,l) * f_i == (-1)^deg * (f_(i-1) * dd^C_(i,l))) else matEq(matrix(dd^D_(i+deg,l)) * matrix(f_i), (-1)^deg * (matrix(f_(i-1)) * matrix(dd^C_(i,l))))) + then ( + iscommutative = false; + if f.cache.isCommutative then ( + if debugLevel > 0 then ( + printerr "the cache table incorrectly asserts that the maps commute with the differentials"; + printerr(" differential at index ", toString i, " fails this condition"); + ); + return false; + ); + ) + );)); + if iscommutative and not f.cache.isCommutative then ( + if debugLevel > 0 then printerr "the cache table incorrectly asserts that the maps do not commute with the differentials"; + return false; + ); + ); + true + ) + + +isCommutative SimplicialModuleMap := Boolean => f -> ( + if debugLevel == 0 and f.cache.?isCommutative then + return f.cache.isCommutative; + C := source f; + D := target f; + deg := degree f; + hasDegens := C.?ss and D.?ss; + (loC,hiC) := (0, C.topDegree); + (loD,hiD) := (0, D.topDegree); + for i from loC to hiC do ( + if i+deg-1 >= loD and i+deg-1 <= hiD then ( + for l from 0 to i do ( + if not (try (dd^D_(i+deg,l) * f_i == (-1)^deg * (f_(i-1) * dd^C_(i,l))) else matEq(matrix(dd^D_(i+deg,l)) * matrix(f_i), (-1)^deg * (matrix(f_(i-1)) * matrix(dd^C_(i,l))))) + or not (if hasDegens then (try (ss^D_(i+deg,l) * f_i == (-1)^deg * (f_(i+1) * ss^C_(i,l))) else matEq(matrix(ss^D_(i+deg,l)) * matrix(f_i), (-1)^deg * (matrix(f_(i+1)) * matrix(ss^C_(i,l))))) else true) + then ( + if debugLevel > 0 then printerr("block ", toString (i,i-1), " fails to commute"); + f.cache.isCommutative = false; + return false; + ) + ); + ) + ); + f.cache.isCommutative = true; + true + ) + +isSimplicialMorphism = method(TypicalValue => Boolean) +isSimplicialMorphism SimplicialModuleMap := (f) -> ( + if debugLevel > 0 and degree f =!= 0 then ( + printerr "the complex map has non-zero degree"; + return false; + ); + degree f === 0 and isCommutative f + ) + + +--this function forgets the data of the underlying complex of a simplicial module, if the simplicial module S +--is obtained as a Dold-Kan image + forgetComplex = method(Options => {RememberSummands => true}); + forgetComplex(SimplicialModule) := SimplicialModule => opts -> S -> ( + if not S.?complex then return S; + L := hashTable for i to S.topDegree list i => combineSFactors(S,i,RememberSummands => opts.RememberSummands); + -- rewrite maps so their source/target match the new combined modules + H1 := applyPairs(S.dd.map, (k, f) -> (k, map(L#(first k - 1), L#(first k), matrix f))); + if S.?ss then ( + H2 := applyPairs(S.ss.map, (k, f) -> (k, map(L#(first k + 1), L#(first k), matrix f))); + return simplicialModule(L, H1, H2, S.topDegree); + ); + D := simplicialModule(L, H1, S.topDegree); + D.cache.components = components S; + D + ) + +--totalizing operation +combineSFactors = method(Options => {RememberSummands => true}); +combineSFactors(SimplicialModule,ZZ) := Module => opts -> (S,d) -> ( + if d<0 or d > S.topDegree then return (ring S)^0; + modwComps := directSum for j in components S list directSum for i to min(d,j.complexLength) list (j.module)#(d,i); + if opts.RememberSummands then modwComps.cache.components = flatten flatten for j in components S list for i to min(d,S.complexLength) list components (S.module#(d,i)); + modwComps + ) + + +--forgets the data of degeneracy maps of a simplicial object + --useful for when the user wants to ignore degeneracy maps + --for the purposes of speeding up computations + forgetDegeneracy = method(); + forgetDegeneracy(SimplicialModule) := S -> ( + if not S.?ss then return S; + if S.?complex then return simplicialModule(S.complex,S.dd.map,S.topDegree); + simplicialModule(S.module,S.dd.map,S.topDegree) + ) + + +SimplicialModule _ Sequence := Module => (S,p) -> ( + if #p =!= 2 then + error ("Expected a pair of integer indices"); + if S.module#?(p#0,p#1) then S.module#(p#0,p#1) else (ring S)^0 + ) + + +SimplicialModule _ ZZ := Module => (S,n) -> (if S.module#?n then S.module#n else combineSFactors(S,n)) + + + +net SimplicialModule := S -> ( + (lo,hi) := (0,topDegree S); + if lo > hi then + error "in a simplicial module, top degree should be nonnegative" + --"0" + else if lo == hi and S_lo === 0 then + "0" + else if S.?complex then + (horizontalJoin (between(" <-- ", + for i from lo to hi list + stack (net directSum(for k from 0 to min(i,S.complexLength) list (S.module)#(i,k)), " ", net i)) | {"<-- ..."}) ) + else + horizontalJoin (between(" <-- ", + for i from lo to hi list + stack (net ((S.module)#i), " ", net i)) | {"<-- ..."}) + ) + + + Symbol ^ SimplicialModule := SimplicialModuleMap => (sym, C) -> ( + if sym === dd then return C.dd; + if sym === ss then C.ss + else error "expected symbol to be 'dd' or 'ss'" + ) + +lineOnTop := (s) -> concatenate(width s : "-") || s + +source SimplicialModuleMap := SimplicialModule => f -> f.source +target SimplicialModuleMap := SimplicialModule => f -> f.target +ring SimplicialModuleMap := SimplicialModule => f -> ring source f +degree SimplicialModuleMap := ZZ => f -> f.degree + +isHomogeneous SimplicialModuleMap := (f) -> all(values f.map, isHomogeneous) + +--simplicialModuleMap = method(Options => {Degeneracy => false}); + + + + +map(SimplicialModule, SimplicialModule, HashTable) := SimplicialModuleMap => opts -> (tar, src, maps) -> ( + if not(topDegree tar == topDegree src) then error "expected source and target to have the same top degree"; + R := ring tar; + if ring src =!= R or any(values maps, f -> ring f =!= R) then + error "expected source, target and maps to be over the same ring"; + deg := if opts.Degree === null + then 0 + else if instance(opts.Degree, ZZ) then + opts.Degree + else + error "expected integer degree"; + (lo,hi) := (0,topDegree tar); + maps' := hashTable for k in keys maps list ( + if instance(k, Sequence) then ( + f := maps#k; + -- note: we use != instead of =!= in the next 2 tests, + -- since we want to ignore any term order differences + --print(k); + --print(source f); + --print(src_(first k)); + -*if rank source f != rank src_( first k) then ( + error ("map with index "|toString(k)|" has inconsistent source"); + ); + if rank target f != rank tar_(first(k)+deg) then + error ("map with index "|toString(k)|" has inconsistent target"); + if first k < lo or first k > hi then continue else*- (k,f) + ) + else ( + f = maps#k; + -- note: we use != instead of =!= in the next 2 tests, + -- since we want to ignore any term order differences + --print(k); + --print(source f); + --print(src_(first k)); + -*if source f != src_(k) then ( + error ("map with index "|toString(k)|" has inconsistent source"); + ); + if target f != tar_(k+deg) then + error ("map with index "|toString(k)|" has inconsistent target"); + if k < lo or k > hi then continue else*- (k,f) + )); + new SimplicialModuleMap from { + symbol source => src, + symbol target => tar, + symbol degree => deg, + symbol map => maps', + symbol cache => new CacheTable + } + ) + +map(SimplicialModule, SimplicialModule, ZZ) := SimplicialModuleMap => opts -> (D, C, j) -> ( + if j === 0 then ( + result := map(D,C,hashTable{},opts); + result.cache.isCommutative = true; + return result + ); + if j === 1 then ( + if C == D and (opts.Degree === null or opts.Degree === 0) then + return id_C; + error "expected source and target to be the same"; + ); + error "expected integer to be zero or one"; + ) + +---this method is good for inducing maps on subcomplexes +map(SimplicialModule, SimplicialModule, SimplicialModuleMap) := SimplicialModuleMap => opts -> (tar, src, f) -> ( + deg := if opts.Degree === null then degree f else opts.Degree; + H := applyPairs(f.map, (k, v) -> (k, map(tar_(deg+k), src_k, v))); + map(tar,src,H, Degree=>deg) + ) + + + + +SimplicialModuleMap _ ZZ := Matrix => (f,i) -> ( + if f.map#?i then f.map#i else map((target f)_(i + degree f), (source f)_i, 0)) + +SimplicialModuleMap _ Sequence := Matrix => (f,s) -> ( + if f.map#?s then f.map#s else map((target f)_(s#0 + degree f), (source f)_(s#0), 0)) + + + + +expression SimplicialModuleMap := Expression => f -> ( + d := degree f; + s := sort keys f.map; + if #s === 0 then + new ZeroExpression from {0} + else if instance(s_0,Sequence) then new VerticalList from for i in s list + RowExpression {(i#0+d,i#1), ":", MapExpression { target f_i, source f_i, f_i }, ":", i} + else if instance(s_0,ZZ) then return new VerticalList from for i in s list + RowExpression {i+d, ":", MapExpression { target f_i, source f_i, f_i }, ":", i} + ) + + + + net SimplicialModuleMap := Net => f -> ( + v := between("", + for i in sort keys f.map list ( + if instance(i,Sequence) then (horizontalJoin( + net ((i#0+f.degree,i#1)), " : ", net target f_i, " <--", + lineOnTop net f_i, + "-- ", net source f_i, " : ", net i + )) + else (horizontalJoin( + net (i+f.degree), " : ", net target f_i, " <--", + lineOnTop net f_i, + "-- ", net source f_i, " : ", net i + )) + )); + if # v === 0 then net "0" + else stack v + ) + + + + +SimplicialModule == ZZ := (C,n) -> ( + if n =!= 0 then error "cannot compare Simplicial module to non-zero integer"; + (lo,hi) := (0,C.topDegree); + for i from lo to hi do if C_i != 0 then return false; + true + ) +ZZ == SimplicialModule := (n,C) -> C == n + +--as written, the code assumes one only takes direct sums of simplicial modules with same top degree +SimplicialModule.directSum = args -> ( + local D; + assert(#args > 0); + R := ring args#0; + if not all(args, C -> ring C === R) then error "expected all simplicial modules to be over the same ring"; + concentrations := for C in args list (0,C.topDegree); + --strip complex data to ensure consistent module objects in direct sums + allDegens := all(args, i -> i.?ss); + origArgs := args; + args = apply(args, i -> forgetComplex(i)); + lo := concentrations/first//min; + hi := concentrations/last//max; + if not(all(args,i->i.topDegree==hi)) then error "all objects should have the same top degree"; + S := first args; + LM := new HashTable from for i in keys S.module list i => directSum for j in args list if j.module#?i then j_i else continue; + faceHashM := new HashTable from for i in keys S.dd.map list i => directSum for j in args list if j.dd.map#?i then j.dd_i; + if allDegens then ( + degenMapHashM := new HashTable from for i in keys S.ss.map list i => directSum for j in args list if j.ss.map#?i then j.ss_i; + D = simplicialModule(LM,faceHashM,degenMapHashM,hi); + D.cache.components = toList origArgs; + return D; + ); + D = simplicialModule(LM,faceHashM,S.topDegree); + D.cache.components = toList origArgs; + D + ) +SimplicialModule ++ SimplicialModule := SimplicialModule => (C,D) -> directSum(C,D) +directSum SimplicialModule := C -> directSum(1 : C) + +components SimplicialModule := C -> if C.cache.?components then C.cache.components else {C} + +SimplicialModule#id = (C) -> ( + (lo,hi) := (0,C.topDegree); + maps := hashTable for i from lo to hi list i => id_(C_i); + result := map(C,C,maps); + result.cache.isCommutative = true; + result + ) + + +SimplicialModuleMap ^ ZZ := SimplicialModuleMap => (f,n) -> ( + tDeg := (source f).topDegree; + df := degree f; + if n === -1 then ( + maps := hashTable for i from 0 to tDeg list (i+df) => ( + f_i^(-1) + ); + result := map(source f, target f, maps, Degree=>-df); + if f.cache.?isCommutative then result.cache.isCommutative = f.cache.isCommutative; + result + ) + else if n < 0 then (f^-1)^(-n) + else if n === 0 then id_(source f) + else if n === 1 then f + else ( + if source f != target f then error "expected source and target to be the same"; + maps = hashTable for i from 0 to tDeg list i => ( + s := f_i; + j := 1; + while j < n do ( + s = f_(i+j*df) * s; + j = j+1; + ); + if s == 0 then continue else s + ); + result = map(source f, source f, maps, Degree=> n * df); + if f.cache.?isCommutative then result.cache.isCommutative = f.cache.isCommutative; + result + ) + ) + + +SimplicialModule ** SimplicialModule := SimplicialModule => (C,D) -> simplicialTensor(C,D) + +--it seemed more efficient to directly define the tensor product with a module +--as opposed to converting the module into a simplicial object then using simplicialTensor +Module ** SimplicialModule := SimplicialModule => (M,S) -> ( + if S.?complex then S = forgetComplex S; + LM := applyValues(S.module, x -> M**x); + faceHashM := applyPairs(S.dd.map, (i, v) -> (i, map(M ** S_(i_0-1), M ** S_(i_0), M**v))); + if S.?ss then ( + degenMapHashM := applyPairs(S.ss.map, (i, v) -> (i, map(M ** S_(i_0+1), M ** S_(i_0), M**v))); + return simplicialModule(LM,faceHashM,degenMapHashM,S.topDegree); + ); + simplicialModule(LM,faceHashM,S.topDegree) + ) + +SimplicialModule ** Module := SimplicialModule => (S,M) -> ( + if S.?complex then S = forgetComplex S; + LM := applyValues(S.module, x -> x**M); + faceHashM := applyPairs(S.dd.map, (i, v) -> (i, map(S_(i_0-1) ** M, S_(i_0) ** M, v**M))); + if S.?ss then ( + degenMapHashM := applyPairs(S.ss.map, (i, v) -> (i, map(S_(i_0+1) ** M, S_(i_0) ** M, v**M))); + return simplicialModule(LM,faceHashM,degenMapHashM,S.topDegree); + ); + simplicialModule(LM,faceHashM,S.topDegree) + ) + +SimplicialModule ** Matrix := SimplicialModuleMap => (S, f) -> ( + if S.?complex then S = forgetComplex S; + if ring S =!= ring f then error "expected Simplicial module and Matrix over the same ring"; + src := S ** source f; + tar := S ** target f; + map(tar, src, new HashTable from for i to S.topDegree list i => map(tar_i, src_i, S_i ** f)) + ) + +Matrix ** SimplicialModule := SimplicialModuleMap => (f, S) -> ( + if S.?complex then S = forgetComplex S; + if ring S =!= ring f then error "expected Simplicial module and Matrix over the same ring"; + src := (source f) ** S; + tar := (target f) ** S; + map(tar, src, new HashTable from for i to S.topDegree list i => map(tar_i, src_i, f ** S_i)) + ) + +SimplicialModule ** Ring := SimplicialModule => (S,R) -> ( + if S.?complex then S = forgetComplex S; + LM := applyValues(S.module, m -> R**m); + faceHashM := applyValues(S.dd.map, m -> R**m); + if S.?ss then ( + degenMapHashM := applyValues(S.ss.map, m -> R**m); + return simplicialModule(LM,faceHashM,degenMapHashM,S.topDegree); + ); + simplicialModule(LM,faceHashM,S.topDegree) + ) + +Ring ** SimplicialModule := SimplicialModule => (R,S) -> S ** R + +RingMap SimplicialModule := SimplicialModule => (phi,S) -> ( + if S.?complex then return simplicialModule(tensor(phi, S.complex), S.topDegree, Degeneracy => S.?ss); + LM := applyValues(S.module, m -> phi m); + faceHashM := applyValues(S.dd.map, f -> phi f); + if S.?ss then ( + degenMapHashM := applyValues(S.ss.map, f -> phi f); + return simplicialModule(LM,faceHashM, degenMapHashM, S.topDegree); + ); + simplicialModule(LM,faceHashM,S.topDegree) + ) + +tensor(RingMap, SimplicialModule) := SimplicialModule => {} >> opts -> (phi, S) -> ( + if source phi =!= ring S then error "expected the source of the ring map to be the ring of the simplicial module"; + if S.?complex then return simplicialModule(tensor(phi, S.complex), S.topDegree, Degeneracy => S.?ss); + LM := applyValues(S.module, m -> tensor(phi, m)); + faceHashM := applyValues(S.dd.map, m -> tensor(phi, m)); + if S.?ss then ( + degenMapHashM := applyValues(S.ss.map, m -> tensor(phi, m)); + return simplicialModule(LM,faceHashM,degenMapHashM,S.topDegree); + ); + simplicialModule(LM,faceHashM,S.topDegree) + ) +tensor(SimplicialModule, RingMap) := SimplicialModule => {} >> opts -> (S, phi) -> tensor(phi, S) + +RingMap ** SimplicialModule := SimplicialModule => (phi, S) -> tensor(phi, S) +SimplicialModule ** RingMap := SimplicialModule => (S, phi) -> tensor(phi, S) + + +SimplicialModuleMap | SimplicialModuleMap := SimplicialModuleMap => (f,g) -> ( + if target f != target g then error "expected targets to be the same"; + if (source f).topDegree =!= (source g).topDegree then error "expected sources to have same top degree"; + deg := degree f; + if deg =!= degree g then error "expected maps with the same degree"; + map(target f, source f ++ source g, new HashTable from for i to (source f).topDegree list i => (f_i|g_i), Degree=>deg) + ) + +SimplicialModuleMap || SimplicialModuleMap := SimplicialModuleMap => (f,g) -> ( + if source f != source g then error "expected sources to be the same"; + if (target f).topDegree =!= (target g).topDegree then error "expected targets to have same top degree"; + deg := degree f; + if deg =!= degree g then error "expected maps with the same degree"; + map(target f ++ target g, source f, new HashTable from for i to (source f).topDegree list i => (f_i||g_i), Degree=>deg) + ) + +SimplicialModule == SimplicialModule := (C,D) -> ( + --note: we don't check for equality of keys since some operations + --on simplicial modules may add a key + if C === D then return true; + if topDegree C =!= topDegree D then return false; + if ring C =!= ring D then return false; + for i from 0 to C.topDegree do ( + if C_i != D_i then return false; + ); + for i in keys C.dd.map do ( + if not matEq(matrix C.dd.map#i, matrix D.dd.map#i) then return false; + ); + if C.?ss then for i in keys C.ss.map do ( + if not matEq(matrix C.ss.map#i, matrix D.ss.map#i) then return false; + ); + true + ) + +SimplicialModule == ZZ := (C,n) -> ( + if n =!= 0 then error "cannot compare simplicial module to non-zero integer"; + for i from 0 to C.topDegree do if C_i != 0 then return false; + true + ) +ZZ == SimplicialModule := (n,C) -> C == n + +transs := (C,v) -> ( + if C.cache.?indexComponents then ( + Ci := C.cache.indexComponents; + apply(v, i -> if Ci#?i then Ci#i else error "expected an index of a component of the direct sum")) + else ( + if not C.cache.?components then error "expected a direct sum of simplicialmodules"; + Cc := C.cache.components; + apply(v, i -> if not Cc#?i then error "expected an index of a component of the direct sum"); + v) + ) + + +SimplicialModule _ Array := SimplicialModuleMap => (C,v) -> ( + v = transs(C,v); + D := directSum apply(toList v, j -> C.cache.components#j); + Cc := if C.?complex then forgetComplex(C,RememberSummands => false) else C; + maps := hashTable for i from 0 to Cc.topDegree list i => map(C_i,D_i,Cc_i_v); + result := map(C,D,maps); + result.cache.isCommutative = true; + result + ) + +SimplicialModule ^ Array := SimplicialModuleMap => (C,v) -> ( + v = transs(C,v); + D := directSum apply(toList v, j -> C.cache.components#j); + Cc := if C.?complex then forgetComplex(C,RememberSummands => false) else C; + maps := hashTable for i from 0 to Cc.topDegree list i => map(D_i,C_i,Cc_i^v); + result := map(D,C,maps); + result.cache.isCommutative = true; + result + ) + + +SimplicialModuleMap == SimplicialModuleMap := (f,g) -> ( + if f === g then return true; + if source f != source g or target f != target g + then return false; + for i from 0 to (source f).topDegree do ( + if not (try (f_i == g_i) else matEq(matrix f_i, matrix g_i)) then return false; + ); + true + ) +SimplicialModuleMap == ZZ := Boolean => (f,n) -> ( + if n === 0 then + all(keys f.map, k -> f.map#k == 0) + else if n === 1 then ( + if source f != target f then return false; + if degree f =!= 0 then return false; + (lo,hi) := (0,(source f).topDegree); + for i from lo to hi do + if not (try (f_i == id_((source f)_i)) else matIsId(matrix f_i)) then return false; + f.cache.isCommutative = true; -- this is the identity, after all! + true + ) + else + error "cannot compare ComplexMap to integer other than 0 or 1" + ) +ZZ == SimplicialModuleMap := Boolean => (n,f) -> f == n + +RingElement * SimplicialModuleMap := (r,f) -> ( + df := degree f; + maps := hashTable for i to (source f).topDegree list i => ( + h := r * f_i; + if h == 0 then continue else h + ); + result := map(target f, source f, maps, Degree=>df); + result + ) + +SimplicialModuleMap * RingElement := (f,r) -> (r*f) + +Number * SimplicialModuleMap := (r,f) -> ( + try r = promote(r,ring f) else error "can't promote scalar to ring of complex homomorphism"; + r * f + ) + +SimplicialModuleMap * Number := (f,r) -> ( + try r = promote(r,ring f) else error "can't promote scalar to ring of complex homomorphism"; + f * r + ) + +- SimplicialModuleMap := (f) -> ( + result := (-1)*f; + if isCommutativeCached f then + result.cache.isCommutative = true; + result + ) + +SimplicialModuleMap + SimplicialModuleMap := (f,g) -> ( + df := degree f; + dg := degree g; + if source f != source g then error "expected simplicial module homomorphisms with the same source"; + if target f != target g then error "expected simplicial homomorphisms with the same target"; + if df =!= dg then error "expected complex homomorphisms with the same degree"; + maps := hashTable for i from 0 to (source f).topDegree list i => ( + h := f_i + g_i; + if h == 0 then continue else h + ); + result := map(target f, source f, maps, Degree=>df); + result + ) +SimplicialModuleMap + Number := +SimplicialModuleMap + RingElement := SimplicialModuleMap => (f,r) -> ( + if r == 0 then f + else ( + if source f != target f + then error "expected same source and target" + else f + r*id_(target f)) + ) +Number + SimplicialModuleMap := +RingElement + SimplicialModuleMap := SimplicialModuleMap => (r,f) -> f + r + +SimplicialModuleMap - Number := +SimplicialModuleMap - RingElement := +SimplicialModuleMap - SimplicialModuleMap := SimplicialModuleMap => (f,g) -> f + (-1)*g + +Number - SimplicialModuleMap := +RingElement - SimplicialModuleMap := SimplicialModuleMap => (r,f) -> -f + r + +SimplicialModuleMap * SimplicialModuleMap := (f,g) -> ( + --this is the case where just composing normal maps + if all(keys f.map, i-> instance(i, ZZ)) then ( + df := degree f; + dg := degree g; + src := source g; + tar := target f; + maps := hashTable for i from 0 to src.topDegree list i => ( + h := if target(g_i) === source(f_(dg + i)) + then f_(dg + i) * g_i + else map(tar_(df+dg+i), src_i, matrix(f_(dg + i)) * matrix(g_i)); + if h == 0 then continue else h + ); + result := map(tar, src, maps, Degree=>df+dg); + return result; + ); + --this is the case where face/degeneracy maps are being composed + --note: this case is not really used, since composition of face maps + --should be treated as a sort of totalized operation + if all(keys f.map, i-> instance(i, Sequence)) then ( + df = degree f; + dg = degree g; + maps = hashTable for i in keys g.map list i => ( + h := matrix(f_((dg + i_0, i_1))) * matrix(g_i); + if h == 0 then continue else h + ); + result = map(target f, source g, maps, Degree=>df+dg); + return result; + ); + ) + +--need to complete this +SimplicialModuleMap.directSum = args -> ( + -- args: sequence of SimplicialModuleMap's + -- args: f_i : C_i --> D_i, having same degree deg + -- result : sum(C_i) --> sum(D_i) + R := ring args#0; + deg := degree args#0; + if not all(args, f -> ring f === R) then + error "expected maps all over the same ring"; + if not all(args, f -> degree f === deg) then + error "expected maps to all have the same degree"; + -- WARNING: we call simplicialModule.directSum directly rather than using + -- just directSum to avoid getting a cached copy of the direct + -- sum. Otherwise the labels of the cached copies might get + -- changed (in Options.directSum). + src := SimplicialModule.directSum (args/source); + tar := SimplicialModule.directSum (args/target); + -- only keep matrices in the homomorphism that are non-zero + spots := unique flatten(args/(f -> keys f.map)); + maps := hashTable for i in spots list i => directSum(args/(f -> f_i)); + result := map(tar,src,maps,Degree=>deg); + result.cache.components = toList args; + result + ) + +SimplicialModuleMap ++ SimplicialModuleMap := SimplicialModuleMap => (f,g) -> directSum(f,g) +directSum SimplicialModuleMap := f -> directSum(1 : f) +components SimplicialModuleMap := f -> if f.cache.?components then f.cache.components else {f} +SimplicialModuleMap ^ Array := SimplicialModuleMap => (f,v) -> (target f)^v * f +SimplicialModuleMap _ Array := SimplicialModuleMap => (f,v) -> f * (source f)_v + + + + +-- the following method is not exported: +isCommutativeCached = method() +isCommutativeCached SimplicialModuleMap := Boolean => f -> f.cache.?isCommutative and f.cache.isCommutative + + +-------------------------------------------------------------------- +-- tensor products of simplicial maps ------------------------------ +-------------------------------------------------------------------- +tensor(SimplicialModuleMap, SimplicialModuleMap) := SimplicialModuleMap => {} >> opts -> (f,g) -> ( + -- f : C1 --> C2, g : D1 --> D2 + -- f**g : C1**D1 --> C2**D2 + -- (f**g)_i : sum_j(C1_j ** D1_(i-j) --> C2_(j+df) ** D2_(i-j+dg)) + df := degree f; + dg := degree g; + src := (source f) ** (source g); + tar := (target f) ** (target g); + -- for the i-th matrix src_i --> tar_(i+df+dg) + -- we make a table of matrices, and create a block matrix from that using "matrix" and "map" + (lo,hi) := (0,src.topDegree); + maps := hashTable for i from lo to hi list i => f_i**g_i; + result := map(tar, src, maps, Degree=>df+dg); + result + ) +SimplicialModuleMap ** SimplicialModuleMap := SimplicialModuleMap => (f,g) -> tensor(f,g) +SimplicialModule ** SimplicialModuleMap := SimplicialModuleMap => (C,g) -> id_C ** g +SimplicialModuleMap ** SimplicialModule := SimplicialModuleMap => (f,D) -> f ** id_D +Module ** SimplicialModuleMap := SimplicialModuleMap => (M,g) -> ( + map(M**(target g),M**(source g), applyValues(g.map, v -> M**v), Degree => degree g) + ) +SimplicialModuleMap ** Module := SimplicialModuleMap => (g,N) -> ( + map((target g) ** N,(source g) ** N, applyValues(g.map, v -> v**N), Degree => degree g) + ) + + +SimplicialModuleMap ** Ring := SimplicialModuleMap => (g,R) -> ( + map((target g)**R,(source g)**R, applyValues(g.map, v -> v**R), Degree => degree g) + ) +Ring ** SimplicialModuleMap := SimplicialModuleMap => (R,f) -> f ** R + +RingMap SimplicialModuleMap := SimplicialModuleMap => (phi,f) -> ( + map(phi (target f), phi (source f), applyValues(f.map, v -> phi v), Degree => degree f) + ) + +tensor(RingMap, SimplicialModuleMap) := SimplicialModuleMap => {} >> opts -> (phi, f) -> ( + if source phi =!= ring f then error "expected the source of the ring map to be the ring of the complex map"; + map(tensor(phi, target f), tensor(phi, source f), applyValues(f.map, v -> tensor(phi, v)), Degree => degree f) + ) +tensor(SimplicialModuleMap, RingMap) := SimplicialModuleMap => {} >> opts -> (f, phi) -> tensor(phi, f) + +RingMap ** SimplicialModuleMap := SimplicialModuleMap => (phi, f) -> tensor(phi, f) +SimplicialModuleMap ** RingMap := SimplicialModuleMap => (f, phi) -> tensor(phi, f) + +---------------------------------------------------------------------------------------- +------------- some functionality for complexes acting on simplicial modules ------------ + +Complex ** SimplicialModule := SimplicialModule => (C,S) -> (simplicialModule(C,S.topDegree)**S) + +SimplicialModule ** Complex := SimplicialModule => (S,C) -> (S**simplicialModule(C,S.topDegree)) + +ComplexMap ** SimplicialModuleMap := SimplicialModuleMap => (f,g) -> (tDeg := max((source g).topDegree,(target g).topDegree); + simplicialModule(f,tDeg)**g + ) + +SimplicialModuleMap ** ComplexMap := SimplicialModuleMap => (g,f) -> (tDeg := max((source g).topDegree,(target g).topDegree); + g**simplicialModule(f,tDeg) + ) + +Complex ** SimplicialModuleMap := SimplicialModuleMap => (C,f) -> (id_C**f) +SimplicialModuleMap ** Complex := SimplicialModuleMap => (f,C) -> (f**id_C) + + + +kernel SimplicialModuleMap := SimplicialModule => opts -> f -> ( + -- f : B --> C + local result; + B := source f; + modules := hashTable for i from 0 to B.topDegree list i => kernel f_i; + inducedMaps := hashTable for i from 0 to B.topDegree list i => inducedMap(B_i, modules#i); + facemaps := applyPairs(B.dd.map, (i, v) -> ( + b1 := v * inducedMaps#(i_0); + b2 := map(target b1, source inducedMaps#(i_0-1), inducedMaps#(i_0-1)); + (i, b1 // b2) + )); + if B.?ss then ( + degenmaps := applyPairs(B.ss.map, (i, v) -> ( + b1 := v * inducedMaps#(i_0); + b2 := map(target b1, source inducedMaps#(i_0+1), inducedMaps#(i_0+1)); + (i, b1 // b2) + )); + result = simplicialModule(modules,facemaps,degenmaps,B.topDegree); + result.cache.kernel = f; + return result; + ); + result = simplicialModule(modules,facemaps,B.topDegree); + result.cache.kernel = f; + result + ) +cokernel SimplicialModuleMap := SimplicialModule => f -> ( + -- f : B --> C + local result; + C := target f; + deg := degree f; + modules := hashTable for i from 0 to C.topDegree list i => cokernel f_(i-deg); + facemaps := applyPairs(C.dd.map, (i, v) -> (i, map(modules#(i_0-1), modules#(i_0), matrix v))); + if C.?ss then ( + degenmaps := applyPairs(C.ss.map, (i, v) -> (i, map(modules#(i_0+1), modules#(i_0), matrix v))); + result = simplicialModule(modules,facemaps,degenmaps,C.topDegree); + result.cache.cokernel = f; + return result; + ); + result = simplicialModule(modules,facemaps,C.topDegree); + result.cache.cokernel = f; + result + ) + +image SimplicialModuleMap := SimplicialModule => f -> ( + -- f : B --> C + local result; + B := source f; + C := target f; + deg := degree f; + modules := hashTable for i from 0 to C.topDegree list i => image f_(i-deg); + inducedMaps := hashTable for i from 0 to B.topDegree list i => inducedMap(C_i, modules#i); + facemaps := applyPairs(C.dd.map, (i, v) -> ( + b1 := v * inducedMaps#(i_0); + b2 := map(target b1, source inducedMaps#(i_0-1), inducedMaps#(i_0-1)); + (i, b1 // b2) + )); + if C.?ss then ( + degenmaps := applyPairs(C.ss.map, (i, v) -> ( + b1 := v * inducedMaps#(i_0); + b2 := map(target b1, source inducedMaps#(i_0+1), inducedMaps#(i_0+1)); + (i, b1 // b2) + )); + result = simplicialModule(modules,facemaps,degenmaps,C.topDegree); + result.cache.image = f; + return result; + ); + result = simplicialModule(modules,facemaps,C.topDegree); + result.cache.image = f; + result + ) + +coimage SimplicialModuleMap := SimplicialModule => f -> ( + -- f : B --> C + local result; + B := source f; + modules := hashTable for i from 0 to B.topDegree list i => coimage f_(i); + facemaps := applyPairs(B.dd.map, (i, v) -> (i, map(modules#(i_0-1), modules#(i_0), matrix v))); + if B.?ss then ( + degenmaps := applyPairs(B.ss.map, (i, v) -> (i, map(modules#(i_0+1), modules#(i_0), matrix v))); + result = simplicialModule(modules,facemaps,degenmaps,B.topDegree); + result.cache.coimage = f; + return result; + ); + result = simplicialModule(modules,facemaps,B.topDegree); + result.cache.coimage = f; + result + ) + +--this function takes the tensor product of a direct sum, but caches the components +--of the resulting tensor product based on the component indices of the original modules. +--useful for accessing induced maps on components of tensor products of direct sums +tensorwithComponents = method(); +tensorwithComponents(Module,Module) := (M,N) -> ( + T := if M.cache.?indexComponents then flatten table(keys (M.cache.indexComponents),toList(0..length components N-1),(u,v) -> {u,v}) + else flatten table(toList(0..length components M-1),toList(0..length components N-1),(u,v) -> {u,v}); + result := M**N; + if M.cache.?indexComponents then result.cache.components = apply(T,i->(M.cache.components#(M.cache.indexComponents#(i_0)))**(N.cache.components#(i_1))) + else result.cache.components = apply(T,i->((components M)#(i_0))**((components N)#(i_1))); + result.cache.indexComponents = hashTable for i to length(T)-1 list flatten (T_i) => i; + result.cache.indices = for i to length(T)-1 list flatten (T_i); + result + ) + +tensorwithComponents(Matrix,Matrix) := (A,B) -> ( + srcA := source A; + srcB := source B; + trgA := target A; + trgB := target B; + map(tensorwithComponents(trgA,trgB),tensorwithComponents(srcA,srcB),A**B) + ) + +tensorwithComponents List := L -> ( + if length L == 2 then return tensorwithComponents(L_0,L_1); + Ln := for i from 2 to length L-1 list L_i; + tensorwithComponents({tensorwithComponents(L_0,L_1)}|Ln) + ) + + +homology(SimplicialModule) := SimplicialModule => opts -> C -> ( + t := topDegree C; + simplicialModule( HH normalize C, t, Degeneracy => C.?ss) + ) + +homology(SimplicialModuleMap) := SimplicialModuleMap => opts -> f -> ( + t := topDegree f; + degens := (source f).?ss and (target f).?ss; + simplicialModule( HH normalize f, t, Degeneracy => degens) + ) + + +SimplicialModule Array := (C, L) -> (t := topDegree C; + simplicialModule( (normalize C) L, t, Degeneracy => C.?ss ) + ) + +SimplicialModuleMap Array := (f, L) -> (t := topDegree f; + simplicialModule( (normalize f) L, t, Degeneracy => (source f).?ss) + ) + +minimalPresentation SimplicialModule := +prune SimplicialModule := SimplicialModule => opts -> (cacheValue symbol minimalPresentation)(C -> ( + if C.?complex then C = forgetComplex C; + R := ring C; + -- opts is ignored here + -- to be cached: in the input C: cache the result D + -- in the result: cache pruningMap: D --> C + faceKeys := keys C.dd.map; + if C.?ss then degenKeys := keys C.ss.map; + prunedMods := new MutableHashTable from for i to C.topDegree list i => prune C_i; + prunedFaceMaps := new MutableHashTable from for i in faceKeys list i => map(prune C_(i_0-1),prune C_(i_0),0); + if C.?ss then prunedDegenMaps := new MutableHashTable from for i in degenKeys list i=>map(prune C_(i_0+1),prune C_(i_0),0); + nonzeros := select(0..C.topDegree, i -> minimalPresentation C_i != 0); + D := if #nonzeros === 0 + then ( + simplicialModule((ring C)^0,C.topDegree) + ) + else ( + lo := min nonzeros; + hi := max nonzeros; + for i from lo to hi do prunedMods#i = minimalPresentation(C_i); + for i in select(faceKeys,i->(i_0>=lo and i_0<=hi)) do prunedFaceMaps#i = minimalPresentation C.dd_i; + if C.?ss then ( + for i in select(degenKeys,i->(i_0>=lo and i_0 C + pruning := hashTable for i from 0 to C.topDegree list i => (minimalPresentation C_i).cache.pruningMap; + D.cache.pruningMap = map(C,D,pruning); + D.cache.pruningMap.cache.isCommutative = true; + D + )) + + +minimalPresentation SimplicialModuleMap := +prune SimplicialModuleMap := SimplicialModuleMap => opts -> f -> ( + if all(keys f.map, i -> instance(i, Sequence)) then return (if degree f == -1 then (prune source f).dd else (prune source f).ss); + C := source f; + if not C.cache.?pruningMap then f = f * (minimalPresentation C).cache.pruningMap; + D := target f; + if not D.cache.?pruningMap then f = (minimalPresentation D).cache.pruningMap^-1 * f; + f + ) + +inducedMap(SimplicialModule, SimplicialModule) := SimplicialModuleMap => opts -> (D,C) -> ( + -- compute f : C --> D the map induced by the identity matrix. + deg := if opts.Degree === null then 0 else opts.Degree; + (loC,hiC) := (0,C.topDegree); + (loD,hiD) := (0,D.topDegree); + maps := hashTable for i from max(loC,loD-deg) to min(hiC,hiD-deg) list i => inducedMap(D_(i+deg),C_i, Verify => opts.Verify); + map(D,C,maps,Degree=>deg) + ) + + +randomSimplicialMap = method(Options=>{ + Degree => 0, + InternalDegree => null, + Cycle => false, + Boundary => false + }) +randomSimplicialMap(SimplicialModule, SimplicialModule) := SimplicialModuleMap => opts -> (C,D) -> ( + simplicialModule(randomComplexMap(normalize C, normalize D, opts), min(C.topDegree, D.topDegree), Degeneracy => C.?ss) + ) + +basis(List, Complex) := Complex => opts -> (deg, C) -> ( + (a,b) := concentration C; + L := for i from a+1 to b list basis(deg, C.dd_i, opts); + complex(L, Base => a)) + +basis(ZZ, Complex) := Complex => opts -> (deg, C) -> ( + (a,b) := concentration C; + L := for i from a+1 to b list basis(deg, C.dd_i, opts); + complex(L, Base => a)) + +basis(List, SimplicialModule) := SimplicialModule => opts -> (L, S) -> ( + if S.?complex then return simplicialModule(basis(L,S.complex), S.topDegree, Degeneracy => S.?ss); + mods := applyValues(S.module, m -> image basis(L, m, opts)); + H1 := applyValues(S.dd.map, f -> basis(L, f, opts)); + if S.?ss then H2 := applyValues(S.ss.map, f -> basis(L, f, opts)); + if S.?ss then return simplicialModule(mods, H1, H2, S.topDegree); + simplicialModule(mods, H1, S.topDegree) + ) + +basis(ZZ, SimplicialModule) := SimplicialModule => opts -> (L, S) -> ( + if S.?complex then return simplicialModule(basis(L,S.complex), S.topDegree, Degeneracy => S.?ss); + mods := applyValues(S.module, m -> image basis(L, m, opts)); + H1 := applyValues(S.dd.map, f -> basis(L, f, opts)); + if S.?ss then H2 := applyValues(S.ss.map, f -> basis(L, f, opts)); + if S.?ss then return simplicialModule(mods, H1, H2, S.topDegree); + simplicialModule(mods, H1, S.topDegree) + ) + +basis(List, SimplicialModuleMap) := SimplicialModuleMap => opts -> (L, phi) -> ( + map(basis(L, target phi, opts), basis(L, source phi, opts), hashTable for i in keys phi.map list i => ( + if (f := basis(L, phi_i, opts)) == 0 then continue else f) ) + ) + +basis(ZZ, SimplicialModuleMap) := SimplicialModuleMap => opts -> (L, phi) -> ( + map(basis(L, target phi, opts), basis(L, source phi, opts), hashTable for i in keys phi.map list i => ( + if (f := basis(L, phi_i, opts)) == 0 then continue else f) ) + ) + + +truncate(List, SimplicialModule) := SimplicialModule => {} >> opts -> (L, S) -> ( + if S.?complex then return simplicialModule(truncate(L, S.complex, opts), S.topDegree, Degeneracy => S.?ss); + mods := applyValues(S.module, m -> truncate(L, m, opts)); + H1 := applyValues(S.dd.map, f -> truncate(L, f, opts)); + if S.?ss then H2 := applyValues(S.ss.map, f -> truncate(L, f, opts)); + if S.?ss then return simplicialModule(mods, H1, H2, S.topDegree); + simplicialModule(mods, H1, S.topDegree) + ) +truncate(ZZ, SimplicialModule) := SimplicialModule => {} >> opts -> (e, S) -> truncate({e}, S) + +truncate(List, SimplicialModuleMap) := SimplicialModuleMap => {} >> opts -> (L, phi) -> ( + map(truncate(L, target phi, opts), truncate(L, source phi, opts), hashTable for i in keys phi.map list i => ( + if (f := truncate(L, phi_i, opts)) == 0 then continue else f) ) + ) + +truncate(ZZ, SimplicialModuleMap) := SimplicialModuleMap => {} >> opts -> (L, phi) -> truncate({L}, phi, opts) + + + + +isShortExactSequence(SimplicialModuleMap, SimplicialModuleMap) := Boolean => (g, f) -> ( + -- f : A --> B, g : B --> C + -- the SES is 0 --> A --> B --> C --> 0. + isWellDefined g and + isWellDefined f and + isSimplicialMorphism g and + isSimplicialMorphism f and + g*f == 0 and + -- check exactness degree by degree (image and kernel may have + -- different generator representations, so comparing as simplicial + -- modules can fail even when the underlying submodules agree) + all(0..topDegree(source g), i -> image f_(i - degree f) == kernel g_i) and + kernel f == 0 and + coker g == 0 + ) diff --git a/M2/Macaulay2/packages/SimplicialModules/SimplicialModuleDOC.m2 b/M2/Macaulay2/packages/SimplicialModules/SimplicialModuleDOC.m2 new file mode 100644 index 00000000000..3d86a2220c2 --- /dev/null +++ b/M2/Macaulay2/packages/SimplicialModules/SimplicialModuleDOC.m2 @@ -0,0 +1,4177 @@ +doc /// + Key + SimplicialModules + Headline + A package for creating and computing objects in the category of Simplicial R-Modules + Description + Text + A simplicial R-module is a presheaf on the so-called Simplex Category, with values in the category of R-modules. Concretely, such objects can be viewed as nonnegatively graded R-modules + equipped with certain face and degeneracy operators satisfying the simplicial identities. As an example, every R-module M can be converted into a simplicial R-module whose degree + n piece is equal to M for all n, and with face/degeneracy operators simply given by the identity. + Example + S = simplicialModule(ZZ^2,3,Degeneracy => true) --the integer 3 specifies a top degree, the option Degeneracy specifies whether or not to compute degeneracy maps + Text + The output string for a simplicial module is meant to indicate that this object is infinite in general, + and the user can only compute a finite snapshot of the object. + The face/degeneracy maps can be accessed using the keys {\tt dd} and {\tt ss}, respectively. In order to verify that the resulting face/degeneracy maps satisfy the simplicial + identities, one can use the @TO isSimplicialModule@ command. + Example + S.dd + S.ss + assert isSimplicialModule S + Text + In general, simplicial objects are infinite objects. Because of this, the user can specify a top degree for the resulting simplicial object. If no top degree is specified, then + the default top degree is determined by the input object. + Text + @SUBSECTION "The Dold-Kan Correspondence"@ + Text + The category of simplicial R-modules is equivalent to the category of nonnegatively graded + chain complexes via an equivalence known as the Dold-Kan correspondence. + + This means that there is a functor that converts a chain complex into a simplicial object, known as the Dold-Kan functor. In practice, the image of the Dold-Kan functor is + highly nontrivial to compute by hand. However, this package uses an algorithm of Kock-Sakuranath to compute Dold-Kan images quite efficiently by using the @TO simplicialModule@ + command: + Example + R = ZZ/101[x_1..x_3]; + K = koszulComplex vars R + simplicialModule(K) --defaults to top degree 3 + elapsedTime simplicialModule(K,6) --specify top degree 6 + elapsedTime S' = simplicialModule(K,6, Degeneracy => true) + isWellDefined S' --this method checks the simplicial identities as well as many other basic checks + Text + The other piece of the Dold-Kan correspondence is the functor that converts a simplicial module into a chain complex. This functor is often referred to as the normalization functor, + and is also implemented as the command @TO normalize@. If a simplicial R-module is computed as the Dold-Kan image of some complex, then this complex is cached and the @TO normalize@ command + returns this cached complex. If the user wants the normalization to be computed without accessing this cached value, one can use the {\tt CheckComplex => false} option. + By default, the normalization function does not prune the output, so the user should use @TO prune@ to get a nicer looking output. + Example + R = ZZ/101[x_1..x_3]; + K = koszulComplex vars R + Kn = normalize(simplicialModule(K,4), CheckComplex => false) + Kn.dd + K == prune Kn + Text + As the above example shows, applying the normalization to the Dold-Kan image recovers the original complex. + Text + @SUBSECTION "Canonical Extensions of Functors to the Category of Chain Complexes"@ + Text + One of the main utilities of the Dold-Kan correspondence from the perspective of a commutative algebraist is as a method of canonically extending any + endofunctor of R-modules to an endofunctor of nonnegatively chain complexes. This is because endofunctors of R-modules naturally extend to endofunctors + of simplicial R-modules by just applying the functor degree-wise. Since any nonnegatively graded chain complex can be converted into a simplicial R-module + (and vice versa) this yields a canonical way to extend endofunctors of R-modules to chain complexes in a way that preserves homotopy equivalence. + + This extension is often called the Dold-Puppe extension, and was introduced in pioneering work on the theory of derived nonlinear functors. + In general, the Dold-Puppe extension of a functor looks quite different from the classically defined functor, and in most cases may not be quasi-isomorphic over arbitrary base rings! + Example + Q = ZZ/101[x_1,x_2]; + K1 = complex {matrix{{x_1}}}; + K2 = complex {matrix{{x_2}}}; + T1 = K1**K2 + T1.dd + T2 = prune simplicialTensor({K1,K2}) + T2.dd + phi1 = extend(T1,T2,id_(T1_0)) + phi2 = extend(T2,T1,id_(T1_0)) + phi1*phi2 == id_T1 + isNullHomotopic(phi2*phi1 - id_T2) + Text + The above example allows us to directly verify that the classically defined tensor product is homotopy equivalent to the Dold-Kan version. This is true over arbitrary base rings, + but there are other functors such as the symmetric/exterior power functors that have classical definitions for chain complexes that are not necessarily + homotopy equivalent to the Dold-Puppe extensions: + Example + needsPackage "ChainComplexOperations" + Q = ZZ/2[x_1..x_3]; + K = koszulComplex vars Q; + W1 = wedge2 K --the classical "naively" defined exterior power functor + W2 = prune extPower(2,K) --the simplicial version of the exterior power functor + prune HH W1 + prune HH W2 + Text + Notice that the two definitions of exterior power yield complexes that are not even quasi-isomorphic, and hence certainly not homotopy equivalent. Moreover, the "naive" + definition yields a complex that does not even have finite length homology, while the Dold-Puppe extension applied to a complex with finite length homology is guaranteed + to have finite length homology. This preservation of "good behavior" for Dold-Puppe extensions of functors was the main motivation of the authors of CITE for using + simplicial techniques to prove the Total Rank Conjecture in characteristic 2. The @TO extPower@ command along with Schur functors in general have also been implemented in a + functorial way and can take morphisms of chain complexes as inputs: + Example + Q=ZZ/101[x_1,x_2]; + K = koszulComplex vars Q; + F = complex res ((ideal vars Q)^2); + phi = extend(K,F,id_(K_0)) + e2phi = prune extPower(2,phi) --induced map on the exterior powers of these complexes + assert isCommutative e2phi + ephi = prune extPower(3,phi,TopDegree => 4); --the TopDegree option specifies how many homological degrees to compute + assert isCommutative ephi; + ephi_1 + ephi_2; + prune schurMap({2},phi) --the second symmetric power + sphi = schurMap({2,1},phi,TopDegree => 3); --schur functor corresponding to partition {2,1} applied to phi + sphi_1 + Text + This package thus lays the groundwork for computing with Dold-Puppe extensions of nonlinear functors, deriving nonlinear functors, and also allows any endofunctor of R-modules + implemented in Macaulay2 to immediately be canonically extended to an endofunctor at the level of chain complexes. Moreover, almost all methods + are implemented with an eye toward user accessibility and comprehension of the outputs. This is illustrated + in many of the documentation nodes below. + Text + @SUBSECTION "Getting started:"@ + Text + @UL { + TO "The Dold-Kan Correspondence in Macaulay2", + TO "Making simplicial modules", + TO "Making maps between simplicial modules", + TO "Basic invariants and properties", + TO "face/degeneracy maps of a simplicial module" + }@ + Text + @SUBSECTION "Contributors"@ + Text + The following people have generously contributed code or improved existing code: + Text + @SUBSECTION "Acknowledgments"@ + Text + This package began its life during the 2023 Macaulay2 workshop in Minneapolis. Many documentation + nodes for the basic constructors not specific to the {\tt SimplicialModule} type have been + repurposed from existing documentation coming from the @TO Complexes@ package. +/// + + +doc /// + Key + SimplicialModule + complexLength + Degeneracy + Headline + the class of all simplicial modules + Description + Text + A simplicial R-module is a presheaf on the so-called Simplex Category, with values in the category of R-modules. Concretely, such objects can be viewed as nonnegatively graded R-modules + equipped with certain face and degeneracy operators satisfying the simplicial identities. As an example, every R-module M can be converted into a simplicial R-module whose degree + n piece is equal to M for all n, and with face/degeneracy operators simply given by the identity. + Example + S = simplicialModule(ZZ^2,3,Degeneracy => true) --the integer 3 specifies a top degree, the option Degeneracy specifies whether or not to compute degeneracy maps + Text + The output string for a simplicial module is meant to indicate that this object is infinite in general, + and the user can only compute a finite snapshot of the object. + The face/degeneracy maps can be accessed using the keys {\tt dd} and {\tt ss}, respectively. In order to verify that the resulting face/degeneracy maps satisfy the simplicial + identities, one can use the @TO isSimplicialModule@ command. + Example + keys S + SeeAlso + "The Dold-Kan Correspondence in Macaulay2" + "Making simplicial modules" +/// + + +doc /// + Key + "The Dold-Kan Correspondence in Macaulay2" + (simplicialModule, Complex, ZZ) + (simplicialModule, Complex) + summandSurjection + Headline + compute the image of a non-negatively graded complex under the Dold-Kan correspondence + Usage + simplicialModule(C,d) + simplicialModule(C) + Inputs + C : Complex + d : ZZ + an integer specifying the top degree of the output SimplicialModule (if d is not specified, the top degree is the length of the complex C) + Description + Text + Given any non-negatively graded chain complex, the Dold-Kan correspondence is an equivalence of + categories: + $$\text{Ch}_{\geq 0} (R) \leftrightarrow \text{Simplicial R-modules}.$$ + If the simplicial module is obtained as a Dold-Kan image of some complex $C$, + then for each $i$ there is a decomposition + $$S_i = C_0 \oplus C_1^{\binom{i}{1}} \oplus \cdots \oplus C_j^{\binom{i}{j}} \oplus \cdots.$$ + To be technically correct, each of the direct summands $C_j$ of $S_i$ should be thought of as being + parametrized by an order preserving surjection $f : [i] \to [j]$ (where $[n] := \{ 0 ,\dots , n \}$). To deduce which surjection + corresponds to a given summand, first notice that order preserving surjections $f : [i] \to [j]$ + are in bijection with compositions of $i+1$ into $j+1$ parts by just listing the sizes of the + fibers of the map $f$. For instance, the composition $(2,2,1)$ corresponds to the surjection + $f : [4] \to [2]$ defined via + $$f(0) = f(1) = 0, \quad f(2) = f(3) = 1, \quad f(4) = 2.$$ + With this in mind, one can deduce the surjection corresponding to a summand as follows: + Example + R = ZZ/101[a..c] + C = koszulComplex vars R + S = simplicialModule(C, 4, Degeneracy => true) + S_2 + components S_2 --these are all the modules showing up + S_(2,1) + components S_(2,1) --these are only the components of C_1^2 + sort(select(compositions(2,3), i -> all(i, j -> j>0))) + Text + The ordering of the surjections corresponding to a summand agrees with the lexicographic + ordering of compositions via the bijection mentioned above. Thus the first summand of $C_1$ appearing + in $S_2$ corresponds to the surjection $f : [2] \to [1]$ given by $f(0) = 0$ and $f(1) = f(2) = 1$, + and the second summand corresponds to the surjection $f: [2] \to [1]$ with $f(0) = f(1) = 0$ and $f(2) = 1$. + These surjections may be accessed more directly using the {\tt summandSurjection} command: + Example + summandSurjection(2,0) --the surjection parametrizing C_0 + summandSurjection(2,1) --the surjections parametrizing C_1 + summandSurjection(2,2) --the surjection parametrizing C_2 + Text + Notice that the individual terms $C_j^{\binom{i}{j}}$ may be accessed as the $(i,j)$-component + of the simplicial module. There are moreover explicit formulas for the face and degeneracy + maps in terms of the differentials of $C$. These can also be accessed: + Example + S.dd --face maps + S.ss --degeneracy maps + S.dd_(2,0) + Text + If you want to restrict/project a face/degeneracy map to a particular summand group, this can be done + using the array notation. The $k$-th summand group of $S_i$ corresponds to $C_k^{\binom{i}{k}}$: + Example + (S.dd_(2,0))_[1]^[0] --restrict to C_1 summand group of S_2, project onto C_0 summand of S_1 + (S.dd_(2,0))_[1]^[1] --restrict to C_1 summand group of S_2, project onto C_1 summand of S_1 + (S.dd_(2,0))_[2]^[1] --restrict to C_2 summand of S_2, project onto C_1 summand of S_1 + Text + The first computation tells us that the component of the face map $d_{2,0}$ mapping + $$C_1^{\oplus 2} \to C_0$$ + is given by the differential of the original complex $C$ applied to the first copy of $C_1$ + (we use the compositions corresponding to the surjections to label the free modules). + The second computation shows the component $C_1^{\oplus 2} \to C_1$ + is simply the identity map on the second copy of $C_1$. + The third computation shows the component + $$C_2 \to C_1$$ + is also given by the differential of $C$. + SeeAlso + "Making simplicial modules" + "Making maps between simplicial modules" +/// + + +doc /// + Key + "Making simplicial modules" + Headline + information about the basic constructors + Description + Text + @SUBSECTION "Basic constructors"@ + Text + @UL { + TO (simplicialModule, HashTable, HashTable, HashTable, ZZ), + TO (simplicialModule, Module, ZZ), + TO (symbol SPACE, SimplicialModule, Array), + TO (isWellDefined, SimplicialModule) + }@ + Text + @SUBSECTION "Important computations creating new simplicial modules"@ + Text + @UL { + TO (simplicialModule, Complex), + TO (simplicialTensor, Complex, Complex) + }@ + Text + @SUBSECTION "More advanced constructors"@ + Text + @UL { + TO (symbol++, SimplicialModule, SimplicialModule), + TO (symbol**, SimplicialModule, SimplicialModule), + TO (symbol SPACE, RingMap, SimplicialModule), + TO (symbol **, RingMap, SimplicialModule), + TO (minimalPresentation, SimplicialModule), + TO (truncate, List, SimplicialModule) + }@ + Text + @SUBSECTION "Extracting simplicial modules from simplicial maps"@ + Text + @UL { + TO (source, SimplicialModuleMap), + TO (target, SimplicialModuleMap), + TO (kernel, SimplicialModuleMap), + TO (cokernel, SimplicialModuleMap), + TO (image, SimplicialModuleMap), + TO (coimage, SimplicialModuleMap) + }@ + SeeAlso + "Making maps between simplicial modules" + "Basic invariants and properties" +/// + +doc /// + Key + "Basic invariants and properties" + Headline + information about accessing basic features + Description + Text + @SUBSECTION "Predicates for simplicial modules and simplicial module maps"@ + Text + @UL { + TO (isWellDefined, SimplicialModule), + TO (isWellDefined, SimplicialModuleMap), + TO (isCommutative, SimplicialModuleMap), + TO (isSimplicialMorphism, SimplicialModuleMap), + TO (isShortExactSequence, SimplicialModuleMap, SimplicialModuleMap) + }@ + Text + @SUBSECTION "Other invariants for simplicial modules"@ + Text + @UL { + TO (ring, SimplicialModule), + TO (topDegree, SimplicialModule), + TO (components, SimplicialModule) + }@ + Text + @SUBSECTION "Other invariants for simplicial module maps"@ + Text + @UL { + TO (source, SimplicialModuleMap), + TO (target, SimplicialModuleMap), + TO (degree, SimplicialModuleMap), + TO (ring, SimplicialModuleMap), + TO (isHomogeneous, SimplicialModuleMap), + TO (components, SimplicialModuleMap) + }@ + SeeAlso + "Making simplicial modules" + "Making maps between simplicial modules" +/// + + + +doc /// + Key + (ring, SimplicialModule) + (ring, SimplicialModuleMap) + Headline + access the ring of a simplicial module or a simplicial module map + Usage + ring C + Inputs + C:SimplicialModule + or a @TO "SimplicialModuleMap"@ + Outputs + :Ring + Description + Text + Every simplicial module or simplicial module map has a base ring. This + function accesses that information. + Example + S = ZZ/101[a,b,c,d]; + C = simplicialModule freeResolution coker vars S + ring C + assert(ring C === S) + ring id_C + assert(ring id_C === S) + SeeAlso + "Basic invariants and properties" + ring +/// + + +doc /// + Key + topDegree + (topDegree, SimplicialModule) + (topDegree, SimplicialModuleMap) + Headline + top degree of a simplicial module + Usage + topDegree C + Inputs + C:SimplicialModule + Outputs + d:ZZ + an integer specifying the top degree to which the + face/degeneracy maps of a simplicial module have been computed + Description + Text + In this package, each simplicial module necessarily has a top degree which is a + finite bound up to which Macaulay2 will compute the face and/or degeneracy maps. + This function is mainly used in programming, to loop over all + non-zero modules or maps in the simplicial module. + Example + S = ZZ/101[a..c]; + C = simplicialModule(freeResolution coker vars S, 8) + topDegree C + C' = simplicialModule(freeResolution coker vars S, 6) + assert(topDegree C' == 6) + Text + Indices that are outside of the top degree automatically + return the zero object. + Example + C_-1 + C_9 + C'_7 + Text + The function {\tt topDegree} does no computation, and just accesses + a cached value. + SeeAlso + "Basic invariants and properties" + (symbol _, SimplicialModule, ZZ) +/// + + +doc /// + Key + simplicialModule + (simplicialModule, HashTable, HashTable, HashTable, ZZ) + (simplicialModule, HashTable, HashTable, ZZ) + (simplicialModule, Complex, HashTable, HashTable, ZZ) + (simplicialModule, Complex, HashTable, ZZ) + Headline + make a simplicial module + Usage + simplicialModule(H1, H2, H3, d) + Inputs + H1:HashTable + or @TO Complex@, used to determine the modules in each degree + H2:HashTable + used to specify the face maps + H3:HashTable + optional, used to specify degeneracy maps if needed + d:ZZ + an integer, specifying the top degree to compute the simplicial module up to + Degeneracy => Boolean + option specifying whether or not to also compute the degeneracy maps + Outputs + :SimplicialModule + Description + Text + A simplicial module is a sequence of objects (e.g. modules), + connected by maps called face/degeneracy maps denoted by $d$ and $s$, respectively. + These maps satisfy the simplicial identities: + 1. For face maps: + \[ d_j d_i = d_i d_{j-1} \text{ for } 0 \leq i < j \leq n \] + 2. For face and degeneracy maps: + \[ d_i s_j = s_{j-1} d_i \text{ for } i < j \] + \[ d_j s_j = \text{id} \] + \[ d_{j+1} s_j = \text{id} \] + \[ d_k s_j = s_j d_{k-1} \text{ for } k > j+1 \] + 3. For degeneracy maps: + \[ s_j s_i = s_i s_{j+1} \text{ for } i \leq j \] + + This constructor is the most basic constructor for building a simplicial module, + and is called by all of the more user friendly constructors. It is highly recommended + that the user sees @TO (simplicialModule, Complex)@ to quickly build simplicial modules. + Example + S = ZZ/101[a..d] + moduleHash = hashTable { 0 => S^1, + 1 => S^1++S^2, + 2 => S^1++S^2++S^2++S^1} + faceHash = hashTable {(1,0) => matrix {{1, a, b}}, + (1,1) => matrix {{1_S, 0, 0}}, + (2,0) => matrix {{1, a, b, 0, 0, 0}, + {0, 0, 0, 1, 0, -b}, + {0, 0, 0, 0, 1, a}}, + (2,1) => matrix {{1_S, 0, 0, 0, 0, 0}, + {0, 1, 0, 1, 0,0}, + {0, 0, 1, 0, 1, 0}}, + (2,2) => matrix {{1_S, 0, 0, 0, 0, 0}, + {0, 1, 0, 0, 0, 0}, + {0, 0, 1, 0, 0, 0}}} + degenHash = hashTable {(0,0) => matrix {{1_S}, {0}, {0}}, + (1,0) => matrix {{1_S, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {0, 0, 0}}, + (1,1) => matrix {{1_S, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}} + T = simplicialModule(moduleHash, faceHash, degenHash, 2) + T.module + T.dd + T.ss + T' = simplicialModule(moduleHash, faceHash, 2) + T'.?ss + Text + In the above, notice that if the user does not provide a hash table + specifying the degeneracy maps, then the simplicial module is still constructed + using only the data of the face maps. This feature is intentional since for the + purposes of efficiency, storing the data of the degeneracy maps may slow down + computations (and one can compute normalizations using only the face maps). In general, + the method @TO forgetDegeneracy@ allows the user to ignore the data of the degeneracy maps + if needed. + Text + In the following example, we see that one can construct simplicial modules + that are not well-defined. The user should use @TO (isWellDefined, SimplicialModule)@ + in order to check that a simplicial module is indeed well-defined. + Example + H1 = hashTable {0 => S^1, 1 => S^1, 2 => S^1} + H2 = hashTable {(1,0) => map(S^1, S^1, 0), + (1,1) => map(S^1, S^1, 0), + (2,0) => map(S^1, S^1, 0), + (2,1) => map(S^1, S^1, 0), + (2,2) => map(S^1, S^1, 0)} + H3 = hashTable {(0,0) => map(S^1, S^1, 0), + (1,0) => map(S^1, S^1, 0), + (1,1) => map(S^1, S^1, 0)} + U = simplicialModule(H1,H2,H3,2) + U.dd + U.ss + isWellDefined U + Caveat + This constructor minimizes computation + and does very little error checking. To verify that a complex + is well constructed, use @TO (isWellDefined, SimplicialModule)@. + SeeAlso + "Making simplicial modules" + (isWellDefined, SimplicialModule) + (simplicialModule, HashTable, HashTable, HashTable, ZZ) + (simplicialModule, Module, ZZ) + (symbol SPACE, SimplicialModule, Array) +/// + + +doc /// + Key + (simplicialModule, Module, ZZ) + (simplicialModule, Ideal, ZZ) + (simplicialModule, Ring, ZZ) + Headline + make a simplicial module associated to a complex concentrated in degree 0 + Usage + simplicialModule(M, d) + Inputs + M:Module + or @TO "Ideal"@, or @TO "Ring"@. + d:ZZ + top degree to compute the simplicial module up to + Outputs + :SimplicialModule + returns the complex whose 0-th component is {\tt M}. + Description + Text + In contrast to @TO (simplicialModule,HashTable,HashTable,HashTable,ZZ)@ or @TO + (simplicialModule,Complex,ZZ)@, this constructor provides a convenient + method to construct a simplicial module from a single module/ring/ideal. + + We illustrate this with a free module. + Example + S = ZZ/101[a..d] + C0 = simplicialModule( S^2, 6, Degeneracy => true) + f = dd^C0 + source f, target f + f == 0 + isWellDefined C0 + C0 == 0 + topDegree C0 + Example + C1 = simplicialModule(complex(S^2, Base=>3), 6, Degeneracy => true) + C1_3 + C1_0 + Text + A ring or an ideal will be converted to a module first. + Example + C2 = simplicialModule( S, 5, Degeneracy => true) + I = ideal(a^2-b, c^3) + C3 = simplicialModule( I, 7, Degeneracy => true) + C4 = simplicialModule( (S/I), 8, Degeneracy => true) + (ring C3, ring C4) + Text + The zero simplicial module over a ring {\tt S} is most conveniently + created by giving the zero module. + Example + C5 = simplicialModule(S^0, 8, Degeneracy => true) + C5 == 0 + dd^C5 == 0 + ss^C5 == 0 + C5_0 + SeeAlso + "Making simplicial modules" + (isWellDefined, SimplicialModule) + (simplicialModule, HashTable, HashTable, HashTable, ZZ) +/// + +doc /// + Key + (isWellDefined, SimplicialModule) + Headline + whether a simplicial module is well-defined + Usage + isWellDefined C + Inputs + C:SimplicialModule + Outputs + :Boolean + that is true when {\tt C} determines a well defined simplicial module + Description + Text + This routine checks that the face/degeneracy maps of {\tt C} satisfy the simplicial identities + (see @TO isSimplicialModule@ for a list of these identities). + If there are no degeneracy maps stored, it instead checks that the naive normalization of C + is a well-defined complex. + Additionally, it checks that the underlying data in {\tt C} is a properly formed + @TO SimplicialModule@ object in Macaulay2. If the variable {\tt debugLevel} is set to a value greater than zero, + then information about the nature of any failure is displayed. + Text + As a first example, we construct the Dold-Kan image of a free resolution and verify that it is + well-defined. + Example + R = QQ[a..d]; + f0 = matrix {{-b^2+a*c, b*c-a*d, -c^2+b*d}} + f1 = map(source f0,, {{d, c}, {c, b}, {b, a}}) + C = simplicialModule(complex {f0, f1}, 3, Degeneracy => true) + isWellDefined C + dd^C + ss^C + dd^C*ss^C --if C is well-defined, this should be all identity maps + Text + The zero simplicial module is well-defined. + Example + C = simplicialModule(R^0, 6, Degeneracy => true) + isWellDefined C + SeeAlso + (isWellDefined, SimplicialModuleMap) + map +/// + + +doc /// + Key + (symbol _, SimplicialModule, ZZ) + (symbol _, SimplicialModule, Sequence) + Headline + access individual objects of a simplicial module + Usage + C_i + Inputs + C:SimplicialModule + i:ZZ + or @TO Sequence@, indicating a term in some degree + Outputs + :Module + the {\tt i}-th object + Description + Text + The data of the modules of a simplicial module is stored in two different ways, + depending on whether the simplicial module $S$ is obtained as the Dold-Kan image + of some complex. If the simplicial module is obtained as a Dold-Kan image of some complex $C$, + then for each $i$ there is a decomposition + $$S_i = C_0 \oplus C_1^{\binom{i}{1}} \oplus \cdots \oplus C_j^{\binom{i}{j}} \oplus \cdots.$$ + Thus the individual terms $C_j^{\binom{i}{j}}$ may be accessed as the $(i,j)$-component + of the simplicial module. + If $S$ is not obtained as a Dold-Kan image, then each term is considered singly indexed with no additional components. + Example + S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker vars S, 4, Degeneracy => true) + C.?complex + C.complex + C_(1,0) == C.complex_0 + C_(1,1) == C.complex_1 + C_(2,1) == C.complex_1 ++ C.complex_1 + tC = C ** C + tC.?complex + tC_2 + Text + If the simplicial object is obtained as a Dold-Kan image, then using an integer subscript + will automatically take the direct sum over all the terms of the complex appearing in that degree + (and cache the components of that direct sum accordingly). + Example + C_3 + components C_3 + (C_3)_[1,2,3] --extract the inclusion of (C.complex_1)^3 + Text + Indices that are outside of the top degree automatically + return the zero object. + Example + C_(-1,3) + C_-7 + SeeAlso + (symbol _, SimplicialModuleMap, ZZ) +/// + + +doc /// + Key + (symbol ==, SimplicialModule, SimplicialModule) + (symbol ==, SimplicialModule, ZZ) + (symbol ==, ZZ, SimplicialModule) + Headline + whether two simplicial modules are equal + Usage + C == D + C == 0 + Inputs + C:SimplicialModule + D:SimplicialModule + Outputs + :Boolean + that is true when {\tt C} and {\tt D} are equal + Description + Text + Two simplicial modules are equal if the corresponding + objects and corresponding maps at each index are equal. + Example + S = ZZ/101[a..c] + C = simplicialModule(K = freeResolution coker vars S, 4) + D = image id_C; + C === D + C == D + Text + Both the maps and the objects must be equal. + Example + E = simplicialModule(complex for i from 1 to 3 list 0*dd^K_i, 4) + dd^E + C == E + E == 0 + Text + A simplicial module is equal to zero if all the objects and maps are zero. + This could require computation to determine if something that + is superficially not zero is in fact zero. + Example + f = id_C + D = coker f + D == 0 + Example + C0 = simplicialModule( S^0, 5, Degeneracy => true) + C1 = simplicialModule(complex(S^0, Base => 2), 5, Degeneracy => true) + topDegree C0 == topDegree C1 + C0 == C1 + C0 == 0 + C1 == 0 + Caveat + Testing for equality is not the same as testing for isomorphism. + SeeAlso +/// + + +doc /// + Key + "face/degeneracy maps of a simplicial module" + (symbol^, Symbol, SimplicialModule) + ss + Headline + access the face and degeneracy maps of a simplicial module + Usage + dd^C + ss^C + Inputs + C:SimplicialModule + Outputs + :SimplicialModuleMap + a map of degree -1 or 1, respectively + Description + Text + A simplicial module is a sequence of modules connected + by homomorphisms, called face/degeneracy maps, such that + these maps satisfy a set of identities known as the simplicial identities. + The face maps are accessed via the symbol @TT "dd"@ (the same symbol used + for the differentials of a complex), and the degeneracy maps via @TT "ss"@, + after the conventional letter $s$ (or $\sigma$) used for degeneracy maps + in the literature. + + More precisely we should have the following equalities: + 1. For face maps: + \[ d_j d_i = d_i d_{j-1} \text{ for } 0 \leq i < j \leq n \] + 2. For face and degeneracy maps: + \[ d_i s_j = s_{j-1} d_i \text{ for } i < j \] + \[ d_j s_j = \text{id} \] + \[ d_{j+1} s_j = \text{id} \] + \[ d_k s_j = s_j d_{k-1} \text{ for } k > j+1 \] + 3. For degeneracy maps: + \[ s_j s_i = s_i s_{j+1} \text{ for } i \leq j \] + Text + The face/degeneracy maps are considered as double indexed in this package. This is because + for a fixed integer $n$, the module $S_n$ comes equipped with $(n+1)$ face and degeneracy maps: + $$d_{n,i} : S_n \to S_{n-1}, \quad \text{and} \quad s_{n,i} : S_n \to S_{n+1}.$$ + The above maps can be accessed as follows: + Example + R = QQ[a..d]; + I = ideal(a*d-b*c, b^2-a*c, c^2-b*d); + C = simplicialModule(freeResolution(R^1/I), 4, Degeneracy => true) + isWellDefined C + dd^C + C.dd + ss^C + C.ss + assert(dd^C === C.dd) + assert(source dd^C === C) + assert(target dd^C === C) + assert(degree dd^C === -1) + assert(source ss^C === C) + assert(target ss^C === C) + assert(degree ss^C === 1) + Text + The individual maps between terms are indexed by their + source. + Example + dd^C_(2,0) + assert(source dd^C_2 === C_2) + assert(target dd^C_2 === C_1) + SeeAlso + "Making maps between simplicial modules" + (symbol_, SimplicialModuleMap, ZZ) + (symbol_, SimplicialModule, ZZ) + (source, SimplicialModuleMap) + (target, SimplicialModuleMap) + (degree, SimplicialModuleMap) +/// + + + +doc /// + Key + (symbol_, SimplicialModule, Array) + (symbol^, SimplicialModule, Array) + Headline + the canonical inclusion or projection map of a direct sum + Usage + i = C_[name] + p = C^[name] + Inputs + C:SimplicialModule + name: + Outputs + :SimplicialModuleMap + {\tt i} is the canonical inclusion and {\tt p} is + the canonical projection + Description + Text + The direct sum is an n-ary operator with projection and + inclusion maps from each component satisfying appropriate + identities. + + One can access these maps as follows. + Example + S = ZZ/101[a,b,c]; + C1 = simplicialModule(freeResolution coker vars S, 5, Degeneracy => true) + C2 = simplicialModule(complex (ideal(a,b,c)) , 5, Degeneracy => true) + D = C1 ++ C2 + D_[0] + isCommutative D_[0] + D_[1] + D^[0] * D_[0] == 1 + D^[1] * D_[1] == 1 + D^[0] * D_[1] == 0 + D^[1] * D_[0] == 0 + D_[0] * D^[0] + D_[1] * D^[1] == 1 + Text + The default names for the components are the non-negative + integers. However, one can choose any name. + Example + E = (chicken => C1) ++ (nuggets => C2) + E_[chicken] + E_[nuggets] + E^[chicken] * E_[chicken] == 1 + E^[nuggets] * E_[nuggets] == 1 + E^[chicken] * E_[nuggets] == 0 + E^[nuggets] * E_[chicken] == 0 + E_[chicken] * E^[chicken] + E_[nuggets] * E^[nuggets] == 1 + Text + One can also access inclusion and projection maps of sub-direct sums. + Example + F = directSum(C1, C2, simplicialModule(complex(S^2, Base => 1), 5, Degeneracy => true)) + prune (F^[0,1]) + isSimplicialMorphism oo + prune (F_[0,2]) + isSimplicialMorphism oo + SeeAlso + (directSum, SimplicialModule) + (components, SimplicialModule) + indices +/// + +doc /// + Key + (components, SimplicialModule) + Headline + list the components of a direct sum + Usage + components C + Inputs + C:SimplicialModule + Outputs + :List + the component simplicial modules of a direct sum (of simplicial modules) + Description + Text + A simplicial module which has been constructed as a direct sum + stores its component simplicial modules. + Example + S = ZZ/101[a,b,c]; + C1 = simplicialModule freeResolution coker vars S + C2 = simplicialModule(complex (ideal(a,b,c)), 3) + D = C1 ++ C2 + L = components D + L_0 === C1 + L_1 === C2 + E = (peanut => C1) ++ (butter => C2) + components E + Text + The names of the component simplicial modules are called indices, + and are used to access the relevant inclusion and projection maps. + Example + indices D + D^[0] + indices E + E_[butter] + SeeAlso + (directSum, SimplicialModule) + indices + (symbol_, SimplicialModule, Array) + (symbol^, SimplicialModule, Array) +/// + + +doc /// + Key + (symbol**, SimplicialModule, SimplicialModule) + (symbol**, Complex, SimplicialModule) + (symbol**, SimplicialModule, Complex) + (symbol**, SimplicialModule, Module) + (symbol**, Module, SimplicialModule) + Headline + tensor product of simplicial modules + Usage + D = C1 ** C2 + Inputs + C1:SimplicialModule + or @ofClass Module@ + C2:SimplicialModule + or @ofClass Module@ + Outputs + D:SimplicialModule + tensor product of {\tt C1} and {\tt C2} + Description + Text + The tensor product is a simplicial module $D$ whose $i$th component is + the tensor product of the degree i components of $C1$ and $C2$. The face/degeneracy maps + are given by the tensor products of the face and degeneracy maps of the original objects. + + As the next example illustrates, the simplicial tensor product in general does not + normalize to give an object that is isomorphic to the classically defined tensor product + of complexes. + Example + S = ZZ/101[a..c] + Ca = simplicialModule(complex {matrix{{a}}}, 3) + Cb = simplicialModule(complex {matrix{{b}}}, 3) + Cc = simplicialModule(complex {matrix{{c}}}, 3) + Cab = Cb ** Ca + dd^Cab + (prune normalize Cab).dd + assert isWellDefined Cab + Cabc = Cc ** Cab + Cc ** Cb ** Ca + dd^(nC = prune normalize Cabc) + assert isWellDefined nC + Text + If one of the arguments is a module, it is considered as a complex concentrated in homological degree 0. + Example + Cabc ** (S^1/(a,b,c)); + S^2 ** Cabc + Text + Because the tensor product can be regarded as the total complex of a double complex, + each term of the tensor product comes with pairs of indices, labelling the summands. + Example + indices Cabc_1 + components Cabc_1 + Cabc_1_[{1,0}] + indices Cabc_2 + components Cabc_2 + Cabc_2_[{0,2}] + SeeAlso + indices + components + directSum +/// + + + +doc /// + Key + (truncate, List, SimplicialModule) + (truncate, ZZ, SimplicialModule) + Headline + truncation of a simplicial module at a specified degree or set of degrees + Usage + truncate(d, C) + Inputs + d:List + or @TO "ZZ"@, if the underlying ring $R$ is singly graded. + C:SimplicialModule + that is homogeneous over $R$ + Outputs + :SimplicialModule + a simplicial module whose terms consist of all elements of component-wise degree at least {\tt d}. + Description + Text + Truncation of homogeneous (graded) modules induces a natural + operation on simplicial modules. + Text + In the singly graded case, the truncation of a homogeneous + module $M$ at degree $d$ is generated by all homogeneous + elements of degree at least $d$ in $M$. This method applies + this operation to each term in a simplicial module. + Example + R = QQ[a,b,c]; + I = ideal(a*b, a*c, b*c) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D = truncate(3,C) + assert isWellDefined D + Text + Truncating at a degree less than the minimal generators + is the identity operation. + Example + assert(C == truncate(0, C)) + Text + In the multi-graded case, the truncation of a homogeneous module at + a list of degrees is generated by all homogeneous elements of degree + that are component-wise greater than or equal to at least one + of the degrees. + Example + A = ZZ/101[x_0, x_1, y_0, y_1, y_2, Degrees => {2:{1,0}, 3:{0,1}}]; + I = intersect(ideal(x_0, x_1), ideal(y_0, y_1, y_2)) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D1 = prune truncate({{1,1}}, C) + D2 = truncate({{1,0}}, C) + D3 = truncate({{0,1}}, C) + D4 = truncate({{1,0},{0,1}}, C); + D5 = truncate({{2,2}}, C); + assert all({D1,D2,D3,D4,D5}, isWellDefined) + SeeAlso + "Making simplicial modules" + "Truncations :: truncate(List,Module)" + (truncate, List, SimplicialModuleMap) +/// + + +doc /// + Key + (symbol SPACE, RingMap, SimplicialModule) + Headline + apply a ring map to a simplicial module + Usage + phi C + Inputs + phi:RingMap + whose source is a ring $R$, and whose target is a ring $S$ + C:SimplicialModule + over the ring $R$ + Outputs + :SimplicialModule + over the ring $S$ + Description + Text + We illustrate the image of a simplicial module under a ring map. + Example + R = QQ[x,y,z] + S = QQ[s,t] + phi = map(S, R, {s, s+t, t}) + I = ideal(x^3, x^2*y, x*y^4, y*z^5) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D = phi C + isWellDefined D + dd^D + Text + When the ring map doesn't preserve homogeneity, + the @TO "DegreeMap"@ option is needed to determine + the degrees of the image free modules in the simplicial module. + Example + R = ZZ/101[a..d] + S = ZZ/101[s,t] + phi = map(S, R, {s^4, s^3*t, s*t^3, t^4}, DegreeMap => i -> 4*i) + C = simplicialModule(freeResolution coker vars R, 4, Degeneracy => true) + D = phi C + assert isWellDefined D + Caveat + Every term in the simplicial module must be free or a submodule of a free module. + Otherwise, use @TO (tensor, RingMap, SimplicialModule)@. + SeeAlso + (symbol SPACE, RingMap, SimplicialModuleMap) + (symbol **, RingMap, SimplicialModule) +/// + + +doc /// + Key + (symbol**, RingMap, SimplicialModule) + (symbol**, SimplicialModule, RingMap) + (tensor, RingMap, SimplicialModule) + (tensor, SimplicialModule, RingMap) + (symbol**, SimplicialModule, Ring) + (symbol**, Ring, SimplicialModule) + Headline + tensor a simplicial module along a ring map + Usage + phi ** C + tensor(phi, C) + S ** C + C ** S + Inputs + phi:RingMap + whose source is a ring $R$ and whose target is a ring $S$ + C:SimplicialModule + over the ring $R$ + Outputs + :SimplicialModule + over the ring $S$ + Description + Text + These methods implement the base change of rings. As input, one can either + give a ring map $\phi$, or the ring $S$ (when there is a canonical map + from $R$ to $S$). + Text + We illustrate the tensor product of a simplicial module along a ring map. + Example + R = QQ[x,y,z]; + S = QQ[s,t]; + phi = map(S, R, {s, s+t, t}) + I = ideal(x^3, x^2*y, x*y^4, y*z^5) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D = phi ** C + assert isWellDefined D + dd^D + ss^D + Text + If a ring is used rather than a ring map, then the implicit + map from the underlying ring of the complex to the given ring + is used. + Example + A = R/(x^2+y^2+z^2); + C ** A + assert(map(A,R) ** C == C ** A) + Text + The commutativity of tensor product is witnessed as follows. + Example + assert(D == C ** phi) + assert(C ** A == A ** C) + Text + When the modules in the complex are not free modules, + this is different than the image of a complex + under a ring map. + Example + use R + I = ideal(x*y, x*z, y*z); + J = I + ideal(x^2, y^2); + g = inducedMap(module J, module I) + assert isWellDefined g + C = simplicialModule(complex {g}, 3, Degeneracy => true) + D1 = phi C + assert isWellDefined D1 + D2 = phi ** C + assert isWellDefined D2 + prune D1 + prune D2 + Text + When the ring map doesn't preserve homogeneity, + the @TO "DegreeMap"@ option is needed to determine + the degrees of the image free modules in the complex. + Example + R = ZZ/101[a..d]; + S = ZZ/101[s,t]; + f = map(S, R, {s^4, s^3*t, s*t^3, t^4}, DegreeMap => i -> 4*i) + C = simplicialModule(freeResolution coker vars R, 3, Degeneracy => true) + D = f ** C + D == f C + assert isWellDefined D + SeeAlso + (symbol **, RingMap, SimplicialModuleMap) + (symbol SPACE, RingMap, SimplicialModule) +/// + + + +doc /// + Key + (minimalPresentation, SimplicialModule) + (prune, SimplicialModule) + (prune, SimplicialModuleMap) + (minimalPresentation, SimplicialModuleMap) + Headline + minimal presentation of all terms in a simplicial module + Usage + D = minimalPresentation C + D = prune C + h = minimalPresentation f + h = prune f + Inputs + C:SimplicialModule + or $f$ @ofClass SimplicialModuleMap@ + Exclude => + unused + Outputs + D:SimplicialModule + isomorphic to the input, where each term is replaced + by a minimally presented model, or $h$ @ofClass SimplicialModuleMap@ + where the source and target are minimally presented + Consequences + Item + The isomorphism $g : D \to C$ is available as + @TT "g = D.cache.pruningMap"@. The inverse isomorphism + can be obtained as @TT "g^-1"@ + Description + Text + This is frequently useful to make the output of certain + operations readable or understandable. This operation + is functorial, applying both to simplicial modules and simplicial module maps. + Text + In particular, images/kernels/cokernels of maps need to be pruned to + be understood. For instance, this is useful + for recognizing when terms given by subquotient modules + are actually zero. + Example + S = ZZ/101[a,b,c,d,e]; + I = ideal(a,b) * ideal(c,d,e) + F = simplicialModule((dual freeResolution I)[-4], 2, Degeneracy => true) + C = HH F + D = prune C + g = D.cache.pruningMap + assert isWellDefined g + assert isSimplicialMorphism g + assert (target g == C) + assert (source g == D) + g^-1 + assert(g*g^-1 == 1 and g^-1*g == 1) + Text + The image of a map of simplicial modules also becomes more + understandable via pruning. + Example + S = ZZ/101[a,b,c]; + I = ideal(a^2,b^2,c^2); + J = I + ideal(a*b*c); + FI = simplicialModule(freeResolution I, Degeneracy => true) + FJ = simplicialModule(freeResolution J, Degeneracy => true) + f = randomSimplicialMap(FJ, FI ** S^{-1}, Cycle => true) + C = image f + D = prune C + g = D.cache.pruningMap + assert isWellDefined g + assert isSimplicialMorphism g + assert (target g == C) + assert (source g == D) + g^-1 + assert(g*g^-1 == 1 and g^-1*g == 1) + Text + One can directly prune the map of simplicial modules $f$. + Example + h = prune f + assert(source h === prune source f) + assert(target h === prune target f) + SeeAlso + "Making simplicial modules" + (minimalPresentation, Module) + randomSimplicialMap + isSimplicialMorphism +/// + + +doc /// + Key + "Making maps between simplicial modules" + Headline + information about the basic constructors + Description + Text + @SUBSECTION "Basic constructors"@ + Text + @UL { + TO (map, SimplicialModule, SimplicialModule, HashTable), + TO (map, SimplicialModule, SimplicialModule, ZZ), + TO (map, SimplicialModule, SimplicialModule, SimplicialModuleMap), + TO (id, SimplicialModule), + TO "face/degeneracy maps of a simplicial module", + TO (symbol SPACE, SimplicialModuleMap, Array), + TO (isWellDefined, SimplicialModuleMap) + }@ + Text + @SUBSECTION "Important computations creating new simplicial module maps"@ + Text + @UL { + TO (symbol**, SimplicialModule, Matrix) + }@ + Text + @SUBSECTION "Canonical maps between simplicial modules"@ + Text + Some simplicial modules come with canonical maps. + These are best accessed using @TO inducedMap@. + Text + @UL { + TO (kernel, SimplicialModuleMap), + TO (cokernel, SimplicialModuleMap), + TO (image, SimplicialModuleMap), + TO (coimage, SimplicialModuleMap), + TO (inducedMap, SimplicialModule, SimplicialModule) + }@ + Text + @SUBSECTION "Random maps of simplicial modules"@ + Text + The method @TO (randomSimplicialMap, SimplicialModule, SimplicialModule)@ + allows one to construct random simplicial module maps, + random morphisms between simplicial modules, and random + null homotopies between simplicial modules. + Text + @UL { + TO (isCommutative, SimplicialModuleMap), + TO (isSimplicialMorphism, SimplicialModuleMap) + }@ + Text + @SUBSECTION "Elementary operations on simplicial module maps"@ + Text + @UL { + TO "arithmetic with simplicial module maps", + TO (symbol +, SimplicialModuleMap, SimplicialModuleMap), + TO (symbol |, SimplicialModuleMap, SimplicialModuleMap), + TO (symbol ||, SimplicialModuleMap, SimplicialModuleMap), + TO (symbol ++, SimplicialModuleMap, SimplicialModuleMap), + TO (symbol **, SimplicialModuleMap, SimplicialModuleMap), + TO (symbol _, SimplicialModuleMap, Array), + TO (symbol ^, SimplicialModuleMap, Array), + TO (truncate, List, SimplicialModuleMap), + TO (symbol SPACE, RingMap, SimplicialModuleMap), + TO (symbol **, RingMap, SimplicialModuleMap) + }@ + SeeAlso + "Making simplicial modules" + "Basic invariants and properties" +/// + + +doc /// + Key + SimplicialModuleMap + complexMap + Headline + the class of all maps between simplicial modules + Description + Text + @LITERAL ////////@ + + A map of simplicial modules $f \colon C \rightarrow D$ of degree $d$ is a + sequence of maps $f_i \colon C_i \rightarrow D_{d+i}$. + No relationship between the maps $f_i$ + and the face/degeneracy maps of either $C$ or $D$ is assumed. If a simplicial module map + is obtained as the image of a morphism of complexes under the Dold-Kan functor, + the key {\tt complexMap} will be stored for more efficient normalization computations. + + The usual algebraic operations are available: addition, + subtraction, scalar multiplication, and composition. The + identity map from a simplicial module to itself can be produced with + @TO "id"@. An attempt to add (subtract, or compare) a ring + element to a simplicial module map will result in the ring element being + multiplied by the appropriate identity map. + SeeAlso + SimplicialModule + "Making maps between simplicial modules" + "arithmetic with simplicial module maps" +/// + + +doc /// + Key + (map, SimplicialModule, SimplicialModule, HashTable) + Headline + make a map of simplicial modules + Usage + f = map(D, C, H) + Inputs + C:SimplicialModule + D:SimplicialModule + H:HashTable + whose keys are integers, and whose values are the maps between + the corresponding terms + Outputs + f:SimplicialModuleMap + Description + Text + A map of simplicial modules $f : C \rightarrow D$ of degree $d$ is a + sequence of maps $f_i : C_i \rightarrow D_{d+i}$. + No relationship between the maps $f_i$ and + the face/degeneracy maps of either $C$ or $D$ is assumed. + + We construct a map of simplicial modules by specifying the + individual maps between the terms. Note that this constructor is typically + used more behind the scenes, and using the @TO simplicialModule@ or basic map constructors + is typically much more efficient than typing the maps by hand. + Example + R = ZZ/101[a,b,c]; + C = simplicialModule(F = freeResolution coker matrix{{a^2-b^2,b^3-c^3,c^4}}, Degeneracy => true) + D = simplicialModule(G = freeResolution coker vars R, Degeneracy => true) + H = hashTable { 0 => map(D_0, C_0, 1), + 1 => map(D_1, C_1, {{1, 0, 0, 0}, {0, a, 0, 0}, {0, -b, b^2, 0}, {0, 0, -c^2, c^3}}), + 2 => map(D_2, C_2, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, a, 0, 0, 0, 0,0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0}, + {0, 0, 0, 0, -b, b^2, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3}}), + 3 => map(D_3, C_3, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, -b, b^2, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0,0, 0, 0, 0, 0, 0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0, 0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-a*c^2, a*c^3, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0,0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0, 0,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2,a*c^3, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2*c^3}}) + } + f = map(D, C, H) + assert isWellDefined f + assert isHomogeneous f + assert(degree f == 0) + assert isSimplicialMorphism f + Text + The keys in the hash table index the terms in the source of the + map. If a key is missing, that map is taken to be the zero map. + We illustrate this by constructing the 0 map as follows: + Example + h = map(C, C, hashTable {}) + h == 0 + Text + This is the primary constructor used by all of the more user friendly + methods for constructing a simplicial module map. + Caveat + This constructor minimizes computation + and does very little error checking. To verify that a simplicial module map + is well constructed, use @TO (isWellDefined, SimplicialModuleMap)@. + SeeAlso + SimplicialModuleMap + (isWellDefined, SimplicialModuleMap) + (isHomogeneous, SimplicialModuleMap) + (degree, SimplicialModuleMap) + (isSimplicialMorphism, SimplicialModuleMap) + (isCommutative, SimplicialModuleMap) + (source, SimplicialModuleMap) + (target, SimplicialModuleMap) +/// + + +doc /// + Key + (map, SimplicialModule, SimplicialModule, ZZ) + Headline + make the zero map or identity between simplicial modules + Usage + f = map(D, C, 0) + f = map(C, C, 1) + Inputs + C:SimplicialModule + D:SimplicialModule + 0:ZZ + or 1 + Outputs + f:SimplicialModuleMap + the zero map from $C$ to $D$ or the identity map from $C$ to $C$ + Description + Text + A map of simplicial modules $f : C \rightarrow D$ of degree $d$ is a + sequence of maps $f_i : C_i \rightarrow D_{d+i}$. + + We construct the zero map between two + simplicial modules. + Example + R = QQ[a,b,c] + C = simplicialModule(freeResolution coker vars R, Degeneracy => true) + D = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}, Degeneracy => true) + f = map(D, C, 0) + assert isWellDefined f + assert isSimplicialMorphism f + g = map(C, C, 0, Degree => 13) + assert isWellDefined g + assert(degree g == 13) + assert not isSimplicialMorphism g + assert isCommutative g + assert isHomogeneous g + assert(source g == C) + assert(target g == C) + Text + Using this function to create the identity map + is the same as using @TO (id, SimplicialModule)@. + Example + assert(map(C, C, 1) === id_C) + SeeAlso + SimplicialModuleMap + (isWellDefined, SimplicialModuleMap) + (isHomogeneous, SimplicialModuleMap) + (degree, SimplicialModuleMap) + (isSimplicialMorphism, SimplicialModuleMap) + (isCommutative, SimplicialModuleMap) + (source, SimplicialModuleMap) + (target, SimplicialModuleMap) + (id, SimplicialModule) +/// + +doc /// + Key + (map, SimplicialModule, SimplicialModule, SimplicialModuleMap) + Headline + make a new map of simplicial modules, induced from an existing one + Usage + g = map(D, C, f) + Inputs + C:SimplicialModule + D:SimplicialModule + f:SimplicialModuleMap + regarded as providing matrices which induce maps between the terms of $C$ and $D$ + Outputs + g:SimplicialModuleMap + Description + Text + A map of simplicial modules $f : C' \rightarrow D'$ is a + sequence of maps $f_i : C'_i \rightarrow D'_{d'+i}$. + The new map $g : C \rightarrow D$ is the sequence of maps $g_i : C_i \rightarrow D_{d+i}$ + induced by the matrix of $f_i$. + + One use for this function is to get the new map of simplicial modules induced by + forgetting the underlying complexes of the source and target, assuming that the + source and target are obtained as Dold-Kan images. + Example + R = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars R, Degeneracy => true) + f = map(forgetComplex C, forgetComplex C, id_C) + assert isWellDefined f + assert(degree f == 0) + assert isCommutative f + assert isSimplicialMorphism f + normalize f --notice how the normalization is not already pruned + normalize id_C + prune normalize f == normalize id_C + SeeAlso + SimplicialModuleMap + (isWellDefined, SimplicialModuleMap) + (degree, SimplicialModuleMap) + (isSimplicialMorphism, SimplicialModuleMap) + (isCommutative, SimplicialModuleMap) + (symbol SPACE, SimplicialModuleMap, Array) +/// + + + +doc /// + Key + (id, SimplicialModule) + Headline + the identity map of a simplicial module + Usage + f = id_C + Inputs + C:SimplicialModule + Outputs + f:SimplicialModuleMap + the identity map from $C$ to itself + Description + Text + The simplicial modules together with simplicial morphisms + forms a category. In particular, every simplicial module has an identity map. + Example + R = ZZ/101[x,y]/(x^3, y^3) + C = simplicialModule(freeResolution(coker vars R, LengthLimit=>6), 6, Degeneracy => true) + f = id_C; + assert isWellDefined f + assert isSimplicialMorphism f + SeeAlso + (map, SimplicialModule, SimplicialModule, ZZ) + (isWellDefined, SimplicialModuleMap) + (isSimplicialMorphism, SimplicialModuleMap) +/// + +doc /// + Key + (isWellDefined, SimplicialModuleMap) + Headline + whether a map of simplicial modules is well-defined + Usage + isWellDefined f + Inputs + f:SimplicialModuleMap + Outputs + :Boolean + that is true when {\tt f} determines a well defined simplicial module map + Description + Text + A map of simplicial modules $f : C \to D$ of degree $d$ is a sequence of + maps $f_i : C_i \to D_{d+i}$. No relationship is required between + these maps and the face/degeneracy maps in the source and target. + + This routine checks that $C$ and $D$ are well-defined + simplicial modules, and that, for each $f_i$, the source and + target equal $C_i$ and $D_{d+i}$, respectively. If the + variable {\tt debugLevel} is set to a value greater than + zero, then information about the nature of any failure is + displayed. + Text + Unlike the @TO2((isWellDefined, SimplicialModule), + "corresponding function for SimplicialModules")@, + the basic constructors for simplicial module maps are all but + assured to be well defined. The only case that could cause + a problem is if one constructs the source or target + complex, and those are not well defined. + Example + R = ZZ/101[a,b,c]; + C = simplicialModule(F = freeResolution coker matrix{{a^2-b^2,b^3-c^3,c^4}}, Degeneracy => true) + D = simplicialModule(G = freeResolution coker vars R, Degeneracy => true) + H = hashTable { 0 => map(D_0, C_0, 1), + 1 => map(D_1, C_1, {{1, 0, 0, 0}, {0, a, 0, 0}, {0, -b, b^2, 0}, {0, 0, -c^2, c^3}}), + 2 => map(D_2, C_2, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, a, 0, 0, 0, 0,0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0}, + {0, 0, 0, 0, -b, b^2, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3}}), + 3 => map(D_3, C_3, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, -b, b^2, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0,0, 0, 0, 0, 0, 0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0, 0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-a*c^2, a*c^3, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0,0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0, 0,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2,a*c^3, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2*c^3}}) + } + f = map(D, C, H) + assert isWellDefined f + assert isHomogeneous f + assert(degree f == 0) + assert isSimplicialMorphism f + Text + We construct two random maps of simplicial modules, + and check to see that, as should be the case, + both are well defined. + Example + g = randomSimplicialMap(D,C); --outputs are large + normalize g + assert isWellDefined g + assert not isCommutative g + Example + h = randomSimplicialMap(D,C, Cycle => true); + normalize h + assert isWellDefined h + assert isSimplicialMorphism h + Text + This method also checks the following aspects of + the data structure: + Text + @UL { + TEX "The underlying hash table has exactly the expected keys, + namely, {\\tt source, target, degree, map, cache}", + "The ring of the source and target are the same", + "The source and target are well defined simplicial modules", + "The degree is an integer", + TEX "All keys in the {\\tt map} field are integers or sequences, + in the range of the top degree of the source", + TEX "The source and target of each $f_i$ is as expected", + TEX "If the {\\tt isCommutative} key is present in the cache + table, then commutativity of the map with the face/degeneracy maps + is checked" + }@ + SeeAlso + (isWellDefined, SimplicialModule) + (isCommutative, SimplicialModuleMap) + (isSimplicialMorphism, SimplicialModuleMap) + (map, SimplicialModule, SimplicialModule, HashTable) +/// + +doc /// + Key + (source, SimplicialModuleMap) + Headline + get the source of a map of simplicial modules + Usage + C = source f + Inputs + f:SimplicialModuleMap + Outputs + C:SimplicialModule + Description + Text + Given a simplicial module map $f : C \to D$ + this method returns the simplicial module $C$. + Example + R = ZZ/101[a..d] + I = ideal(a^2, b^2, c^2) + J = I + ideal(a*b*c) + FI = simplicialModule(freeResolution I, Degeneracy => true) + FJ = simplicialModule(freeResolution J, Degeneracy => true) + f = randomSimplicialMap(FJ, FI, Cycle=>true) + source f + assert isWellDefined f + assert isSimplicialMorphism f + assert(source f == FI) + assert(target f == FJ) + Text + The face/degeneracy map in a simplicial module is considered to have type SimplicialModuleMap. + Example + kk = coker vars R + F = simplicialModule(freeResolution kk, Degeneracy => true) + source dd^F == F + target dd^F == F + degree dd^F == -1 + SeeAlso + "Making simplicial modules" + (target, SimplicialModuleMap) + (randomSimplicialMap, SimplicialModule, SimplicialModule) +/// + +doc /// + Key + (target, SimplicialModuleMap) + Headline + get the target of a map of simplicial modules + Usage + C = target f + Inputs + f:SimplicialModuleMap + Outputs + C:SimplicialModule + Description + Text + Given a simplicial module map $f : C \to D$ + this method returns the simplicial module $D$. + Example + R = ZZ/101[a..d] + I = ideal(a^2, b^2, c^2) + J = I + ideal(a*b*c) + FI = simplicialModule(freeResolution I, Degeneracy => true) + FJ = simplicialModule(freeResolution J, Degeneracy => true) + f = randomSimplicialMap(FJ, FI, Cycle=>true) + source f + assert isWellDefined f + assert isSimplicialMorphism f + assert(source f == FI) + assert(target f == FJ) + Text + The face/degeneracy map in a simplicial module is considered to have type SimplicialModuleMap. + Example + kk = coker vars R + F = simplicialModule(freeResolution kk, Degeneracy => true) + source dd^F == F + target dd^F == F + degree dd^F == -1 + SeeAlso + "Making simplicial modules" + (source, SimplicialModuleMap) + (freeResolution, Ideal) + (randomSimplicialMap, SimplicialModule, SimplicialModule) +/// + +doc /// + Key + (degree, SimplicialModuleMap) + Headline + get the degree of a map of simplicial modules + Usage + degree f + Inputs + f:SimplicialModuleMap + Outputs + :ZZ + Description + Text + A simplicial module map $f : C \to D$ of degree $d$ is a sequence + of maps $f_i : C_i \to D_{i+d}$. + This method returns $d$. + Text + The degree of the face/degeneracy map of a simplicial module is -1 or 1, respectively. + Example + R = ZZ/101[a..d]; + I = ideal(a^2, b^2, c^2) + FI = simplicialModule(freeResolution I, Degeneracy => true) + assert(degree dd^FI == -1) + assert(degree ss^FI == 1) + Example + FJ = simplicialModule(freeResolution (I + ideal(a*b*c)), Degeneracy => true) + f = randomSimplicialMap(FJ, FI, Cycle=>true, Degree => -2); + normalize f + SeeAlso + "Basic invariants and properties" + (source, SimplicialModuleMap) + (target, SimplicialModuleMap) + (randomSimplicialMap, SimplicialModule, SimplicialModule) +/// + + +doc /// + Key + (symbol _, SimplicialModuleMap, ZZ) + (symbol _, SimplicialModuleMap, Sequence) + Headline + access individual matrices in a simplicial module map + Usage + f_i + Inputs + f:SimplicialModuleMap + i:ZZ + the degree index or a sequence + Outputs + :Matrix + the {\tt i}-th map + Description + Text + A simplicial module map $f : C \to D$ of degree $d$ is a sequence of maps $f_i : C_i \to D_{i+d}$. + This method allows one to access the individual $f_i$. + Example + S = ZZ/101[a..c]; + C = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}, Degeneracy => true) + D = simplicialModule(freeResolution coker vars S, Degeneracy => true) + f = randomSimplicialMap(D, C) + f_2 + f_0 + Text + The face/degeneracy maps of a simplicial module are indexed by sequences $(a,b)$ of integers, + where $b \leq a$. + Example + dd^C_(2,0) + dd^C_(2,1) + dd^C_(2,2) + ss^C_(0,0) + ss^C_(1,0) + ss^C_(1,1) + Text + Indices that are outside of the top degree or range of sequences return 0 by default. + Example + topDegree f + f_-1 + f_3 + f_4 + ss^D_(2,3) + ss^D_(4,0) + SeeAlso + (symbol_, SimplicialModule, ZZ) +/// + +doc /// + Key + (isHomogeneous, SimplicialModuleMap) + Headline + whether a map of simplicial modules is homogeneous + Usage + isHomogeneous f + Inputs + f:SimplicialModuleMap + Outputs + :Boolean + that is true when $f_i$ is homogeneous, for all $i$ + Description + Text + A map of simplicial modules $f \colon C \to D$ is homogeneous + (graded) if its underlying ring is graded, and all the + component maps $f_i \colon C_i \to D_{d+i}$ are graded of + degree zero, where $f$ has degree $d$. + Example + S = ZZ/101[a,b,c,d]; + I = minors(2, matrix{{a,b,c},{b,c,d}}) + C = simplicialModule(freeResolution (S^1/I), Degeneracy => true) + assert isHomogeneous dd^C + f = randomSimplicialMap(C, C, Degree => -1) + assert isHomogeneous f + f = randomSimplicialMap(C, C, InternalDegree => 2) + Text + A map of simplicial modules may be homogeneous even if the + source or the target is not homogeneous. + Example + phi = map(S, S, {1,b,c,d}) + D = phi C + dd^D + assert not isHomogeneous dd^D + SeeAlso + "Basic invariants and properties" + isHomogeneous + (randomSimplicialMap, SimplicialModule, SimplicialModule) +/// + + + +doc /// + Key + (components, SimplicialModuleMap) + Headline + list the components of a direct sum + Usage + components f + Inputs + f:SimplicialModuleMap + Outputs + :List + the component maps of a direct sum of maps of simplicial modules + Description + Text + A map of simplicial modules stores its component maps. + Example + S = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + g1 = id_C + g2 = randomSimplicialMap(C[1], C[2], Boundary => true) + f = g1 ++ g2 + assert isWellDefined f + L = components f + L_0 === g1 + L_1 === g2 + indices f + Text + The names of the components are called indices, and are + used to access the relevant inclusion and projection maps. + Example + f^[0] + f_[0] + SeeAlso + (directSum, SimplicialModuleMap) + (components, SimplicialModule) + indices + (symbol_, SimplicialModuleMap, Array) + (symbol^, SimplicialModuleMap, Array) +/// + +doc /// + Key + (symbol*, SimplicialModuleMap, SimplicialModuleMap) + Headline + composition of homomorphisms of simplicial modules + Usage + f = h * g + Inputs + h:SimplicialModuleMap + if a ring element or integer, then we multiply the ring element + by the appropriate identity map + g:SimplicialModuleMap + Outputs + f:SimplicialModuleMap + the composition of $g$ followed by $h$ + Description + Text + If $g_i : C_i \rightarrow D_{d+i}$, and $h_j : D_j \rightarrow E_{e+j}$, + then the composition corresponds to + $f_i := h_{d+i} * g_i : C_i \rightarrow E_{i+d+e}$. In particular, + the degree of the composition $f$ is the sum of the degrees of + $g$ and $h$. + Example + R = ZZ/101[a..d] + C = simplicialModule(freeResolution coker vars R, Degeneracy => true) + 3 * dd^C + 0 * dd^C + dd^C * ss^C --if C is a well-defined simplicial object, these should all be identity maps + SeeAlso + "Making maps between simplicial modules" + "arithmetic with simplicial module maps" +/// + +doc /// + Key + (symbol ^, SimplicialModuleMap, ZZ) + Headline + the n-fold composition + Usage + f^n + Inputs + f:SimplicialModuleMap + whose source and target are the same simplicial module + n:ZZ + Outputs + :SimplicialModuleMap + the composition of $f$ with itself $n$ times. + Description + Text + A simplicial module map $f : C \to C$ can be composed with itself. + This method produces these new maps of simplicial modules. + Text + The face/degeneracy map always composes with itself to give the + zero map. + Example + S = ZZ/101[a..c]; + C = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}, Degeneracy => true) + f = dd^C + f^2 + assert(source f == target f) + assert(degree f == -1) + assert(degree f^2 == -2) + Example + g = randomSimplicialMap(C, C) + g^2 + g^3 + Text + The zero-th power returns the identity map + Example + f^0 == id_(C[1]) + g^0 == id_C + Text + When $n$ is negative, the result is the $n$-fold power + of the inverse simplicial module map, if it exists. + Example + h = randomSimplicialMap(C, C) + h^-1 + assert(h * h^-1 == id_C) + h^-4 + assert(h^-4 * h^4 == id_C) + SeeAlso + (symbol^, Matrix, ZZ) +/// + +doc /// + Key + (symbol ==, SimplicialModuleMap, SimplicialModuleMap) + (symbol ==, SimplicialModuleMap, ZZ) + (symbol ==, ZZ, SimplicialModuleMap) + Headline + whether two simplicial module maps are equal + Usage + f == g + f == 0 + f == 1 + Inputs + f:SimplicialModuleMap + or 0, or 1. + g:SimplicialModuleMap + or 0, or 1. + Outputs + :Boolean + that is true when {\tt f} and {\tt g} are equal + Description + Text + Two simplicial module maps are equal if they have the same source, + the same target, and $f_i = g_i$ for all $i$. + Example + S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + f = id_C + assert(f == 1) + Text + A simplicial module map is equal to zero if all the maps are zero. + This could require computation to determine if something that + is superficially not zero is in fact zero. + Example + assert(0 * id_C == 0) + Example + g = randomSimplicialMap(C, C) + h = inducedMap(coker g, target g) + assert(h == 0) + Text + Testing whether a map is equal to 1 is a shorthand for determining + if the simplicial module map is the identity. + Although the matrices may appear to be the identity, the map is not the + identity when the source and target are not equal. + Example + g = randomSimplicialMap(C, C, InternalDegree=>1, Cycle=>true) + h = inducedMap(coker g, target g) + assert(h != 1) + Text + Testing for equality is not the same as testing for isomorphism. + In particular, different presentations of a simplicial module need not be equal. + Example + D = prune image g + p = D.cache.pruningMap + p == 1 + assert(coker p == 0 and ker p == 0) + assert(prune p == 1) + SeeAlso + (symbol ==, SimplicialModule, SimplicialModule) + (symbol SPACE, SimplicialModuleMap, Array) + randomSimplicialMap + (prune, SimplicialModule) +/// + +doc /// + Key + (isCommutative, SimplicialModuleMap) + Headline + whether a simplicial module map commutes with the face/degeneracy maps + Usage + isCommutative f + Inputs + f:SimplicialModuleMap + Outputs + :Boolean + that is true when $f$ commutes with the face/degeneracy maps + Description + Text + For a simplicial module map $f : C \to D$, this method + checks whether, for all $i$, we have + $dd^D_{i} * f_i = f_{i-1} * dd^C_i$ and, if the source at target are equipped with + degeneracy maps, it also checks the equality $ss^D_{i} * f_i = f_{i+1} * ss^C_i$. + Text + We first construct a random simplicial module map which commutes with the face/degeneracy map. + Example + S = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D = C ** C + isWellDefined D + f1 = prune randomSimplicialMap(D, C, Cycle => true, InternalDegree => 1); + prune normalize f1 + isCommutative oo + isCommutative f1 + assert(degree f1 == 0) + Text + We next generate a simplicial module map that is commutative and (likely) + induces a nontrivial map on homology. + Example + f2 = randomSimplicialMap(D, C, Cycle => true); + isCommutative f2 + assert(degree f2 == 0) + assert isSimplicialMorphism f2 + Text + If the @TO "debugLevel"@ is greater than zero, then + the location of the first failure of commutativity is displayed. + SeeAlso + isSimplicialMorphism + randomSimplicialMap +/// + +doc /// + Key + (isSimplicialMorphism, SimplicialModuleMap) + isSimplicialMorphism + Headline + whether a simplicial module map is a morphism of simplicial modules + Usage + isSimplicialMorphism f + Inputs + f:SimplicialModuleMap + Outputs + :Boolean + that is true when $f$ commutes with the face/degeneracy maps and has degree $0$ + Description + Text + For a simplicial module map $f : C \to D$ of degree $d$, this method + checks whether $d = 0$ and, for all $i$, we have + $dd^D_{i+d} * f_i = f_{i-1} * dd^C_i$ and if the simplicial module + also has degeneracy maps, it checks that $ss^D_{i} * f_i = f_{i+1} * ss^C_i$. + Text + We first construct a random simplicial module morphism. + Example + S = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D = C ** C + f1 = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 1); + isSimplicialMorphism f1 + assert(degree f1 == 0) + Text + We next generate a complex morphism that (likely) + induces a nontrivial map on homology. + Example + f2 = randomSimplicialMap(D, C, Cycle => true); + isSimplicialMorphism f2 + assert(degree f2 == 0) + assert isSimplicialMorphism f2 + prune HH f2 + Text + If the @TO "debugLevel"@ is greater than zero, then + information about the failure is displayed. + SeeAlso + (isCommutative, SimplicialModuleMap) + randomSimplicialMap +/// + + +doc /// + Key + (symbol**, SimplicialModule, Matrix) + (symbol**, Matrix, SimplicialModule) + Headline + create the tensor product of a simplicial module and a map of modules + Usage + h = C ** f + h = f ** C + Inputs + C:SimplicialModule + over a ring $R$ + f:Matrix + defining a homomorphism from the $R$-module $M$ to the $R$-module $N$ + Outputs + h:SimplicialModuleMap + from $C \otimes M$ to $C \otimes N$ + Description + Text + For any simplicial module $C$, a map $f \colon M \to N$ of $R$-modules induces a + morphism $C \otimes f$ of simplicial modules + from $C \otimes M$ to $C \otimes N$. This method returns this map of simplicial modules. + Example + R = ZZ/101[a..d]; + I = ideal(c^2-b*d, b*c-a*d, b^2-a*c) + J = ideal(I_0, I_1) + C = simplicialModule(koszulComplex vars R, Degeneracy => true) + f = map(R^1/I, R^1/J, 1) + C ** f; + isSimplicialMorphism oo + f ** C; + isSimplicialMorphism oo + f' = random(R^2, R^{-1, -1, -1}) + C ** f'; + f' ** C; + assert isWellDefined(C ** f') + assert isWellDefined(f' ** C) + Text + Tensoring with a simplicial module defines a functor from the category + of $R$-modules to the category of simplicial modules over $R$. + Example + f'' = random(source f', R^{-2,-2}) + assert((C ** f') * (C ** f'') == C ** (f' * f'')) + assert(C ** id_(R^{-1,-2,-3}) == id_(C ** R^{-1,-2,-3})) + SeeAlso + "Making maps between simplicial modules" + (symbol**, SimplicialModule, SimplicialModule) +/// + +doc /// + Key + (symbol**, SimplicialModuleMap, SimplicialModuleMap) + (tensor, SimplicialModuleMap, SimplicialModuleMap) + (symbol**, SimplicialModule, SimplicialModuleMap) + (symbol**, SimplicialModuleMap, SimplicialModule) + (symbol**, ComplexMap, SimplicialModuleMap) + (symbol**, SimplicialModuleMap, ComplexMap) + (symbol**, Complex, SimplicialModuleMap) + (symbol**, SimplicialModuleMap, Complex) + (symbol**, SimplicialModuleMap, Module) + (symbol**, Module, SimplicialModuleMap) + Headline + the map of simplicial modules between tensor simplicial modules + Usage + h = f ** g + h = tensor(f, g) + Inputs + f:SimplicialModuleMap + g:SimplicialModuleMap + Outputs + h:SimplicialModuleMap + Description + Text + The maps $f : C \to D$ and $g : E \to F$ of simplicial modules induces the map + $h = f \otimes g : C \otimes E \to D \otimes F$ defined by $c \otimes e \mapsto f(c) \otimes g(e)$. + Example + S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D = simplicialModule((freeResolution coker matrix{{a^2,a*b,b^3}})[-1], Degeneracy => true) + f = randomSimplicialMap(D,C) + E = simplicialModule((dual C.complex)[-3], Degeneracy => true) + F = simplicialModule((dual D.complex)[-3], 3, Degeneracy => true) + g = randomSimplicialMap(F,E) + h = f ** g; + assert isWellDefined h + assert(prune source h == C ** E) + assert(prune target h == D ** F) + Text + If one argument is a SimplicialModule or Module, + then the identity map of the corresponding complex is used. + Example + fE = f ** E; + assert(fE == f ** id_E) + k = coker vars S + gk = g ** k; + Text + This routine is functorial. + Example + D' = simplicialModule((freeResolution coker matrix{{a^2,a*b,c^3}})[-1], 3, Degeneracy => true) + f' = randomSimplicialMap(D', D) + (f' * f) ** g == (f' ** g) * (f ** id_E) + (f' * f) ** g == (f' ** id_F) * (f ** g) + F' = simplicialModule(dual (freeResolution coker matrix{{a^2,a*b,a*c,b^3}})[-3], Degeneracy => true) + g' = randomSimplicialMap(F', F) + f ** (g' * g) == (f ** g') * (id_C ** g) + f ** (g' * g) == (id_D ** g') * (f ** g) + SeeAlso + (symbol**, SimplicialModule, SimplicialModule) + (randomSimplicialMap, SimplicialModule, SimplicialModule) +/// + +doc /// + Key + (truncate, List, SimplicialModuleMap) + (truncate, ZZ, SimplicialModuleMap) + Headline + truncation of a simplicial module map at a specified degree or set of degrees + Usage + truncate(d, f) + Inputs + d:List + or @TO "ZZ"@, if the underlying ring $R$ is singly graded. + f:SimplicialModuleMap + that is homogeneous over $R$ + Outputs + :SimplicialModuleMap + a simplicial module map over $R$ whose terms in the source and target + consist of all elements of component-wise degree at least {\tt d}. + Description + Text + Truncation of homogeneous (graded) maps induces a natural + operation on maps of simplicial modules. + Text + In the singly graded case, the truncation of a homogeneous + module $M$ at degree $d$ is generated by all homogeneous + elements of degree at least $d$ in $M$. The truncation of + a map between homogeneous modules is the induced map + between the truncation of the source and the truncation of + the target. This method applies this operation to each + term in a map of simplicial modules. + Example + R = QQ[a,b,c]; + C = simplicialModule(freeResolution ideal(a*b, a*c, b*c), 3, Degeneracy => true) + D = simplicialModule((freeResolution ideal(a*b, a*c, b*c, a^2-b^2))[-1], 3, Degeneracy => true) + f = randomSimplicialMap(D,C, Cycle => true) + g = truncate(3,f); + assert isWellDefined g + assert (source g == truncate(3, source f)) + assert (target g == truncate(3, target f)) + Text + Truncating at a degree less than the minimal generators + is the identity operation. + Example + assert(f == truncate(0, f)) + Text + In the multi-graded case, the truncation of a homogeneous module at + a list of degrees is generated by all homogeneous elements of degree + that are component-wise greater than or equal to at least one + of the degrees. As in the singly graded case, this induces a map between + the truncations of the source and target. + Example + A = ZZ/101[x_0, x_1, y_0, y_1, y_2, Degrees => {2:{1,0}, 3:{0,1}}]; + I = intersect(ideal(x_0, x_1), ideal(y_0, y_1, y_2)) + C = simplicialModule(freeResolution I, 4, Degeneracy => true) + J = intersect(ideal(x_0^2, x_1^2), ideal(y_0^2, y_1^2, y_2^2)) + D = simplicialModule(freeResolution J, 4, Degeneracy => true) + f = simplicialModule(extend(C.complex, D.complex, id_(A^1)), 2) + g1 = prune truncate({{1,1}}, f); + g1_0 + g1_1 + g2 = truncate({{1,0}}, f); + g2_1 + g3 = truncate({{0,1}}, f); + g4 = truncate({{1,0},{0,1}}, f); + g4_1 + g5 = truncate({{2,2}}, f); + assert all({g1,g2,g3,g4,g5}, isWellDefined) + SeeAlso + "Making maps between simplicial modules" + "Truncations :: truncate(List,Matrix)" + (truncate, List, SimplicialModule) +/// + + +doc /// + Key + (symbol SPACE, RingMap, SimplicialModuleMap) + Headline + apply a ring map to a map of simplicial modules + Usage + phi f + Inputs + phi:RingMap + whose source is a ring $R$, and whose target is a ring $S$ + f:SimplicialModuleMap + over the ring $R$ + Outputs + :SimplicialModuleMap + over the ring $S$ + Description + Text + We illustrate the image of a simplicial module map along a ring map. + Example + R = QQ[a,b,c,d]; + S = QQ[s,t]; + phi = map(S, R, {s, s+t, t, s-t}) + I = ideal(a*b, b*c, c*d) + J = I + ideal(a^2, b^2, c^2, d^2) + CI = simplicialModule(freeResolution I, 4, Degeneracy => true) + CJ = simplicialModule(freeResolution J, Degeneracy => true) + f = simplicialModule(extend(CJ.complex, CI.complex, map(CJ_0, CI_0, 1)), Degeneracy => true) + assert isWellDefined f + g = phi f + assert isWellDefined g + dd^(source g) + ss^(source g) + dd^(target g); + prune HH normalize g + SeeAlso + (symbol SPACE, RingMap, SimplicialModule) + (symbol **, RingMap, SimplicialModuleMap) +/// + +doc /// + Key + (symbol**, RingMap, SimplicialModuleMap) + (symbol**, Ring, SimplicialModuleMap) + (symbol**, SimplicialModuleMap, RingMap) + (symbol**, SimplicialModuleMap, Ring) + (tensor, RingMap, SimplicialModuleMap) + (tensor, SimplicialModuleMap, RingMap) + Headline + tensor a map of simplicial modules along a ring map + Usage + phi ** f + tensor(phi, f) + S ** f + f ** S + Inputs + phi:RingMap + whose source is a ring $R$, and whose target is a ring $S$ + f:SimplicialModuleMap + over the ring $R$ + Outputs + :SimplicialModuleMap + over the ring $S$ + Description + Text + These methods implement the base change of rings. As input, one can either + give a ring map $\phi$, or the ring $S$ (when there is a canonical map + from $R$ to $S$). + Text + We illustrate the tensor product of a map of simplicial modules along a ring map. + Example + R = QQ[a,b,c,d]; + S = QQ[s,t]; + phi = map(S, R, {s, s+t, t, s-t}) + I = ideal(a*b, b*c, c*d) + J = I + ideal(a^2, b^2, c^2, d^2) + CI = simplicialModule(freeResolution I, 4, Degeneracy => true) + CJ = simplicialModule(freeResolution J, Degeneracy => true) + f = simplicialModule(extend(CJ.complex, CI.complex, map(CJ_0, CI_0, 1)), Degeneracy => true) + assert isWellDefined f + g = phi ** f + assert isWellDefined g + dd^(source g) + ss^(source g) + dd^(target g); + simplicialModule prune HH normalize g + isSimplicialMorphism oo + SeeAlso + (symbol **, RingMap, SimplicialModule) + (symbol SPACE, RingMap, SimplicialModuleMap) +/// + +doc /// + Key + (inducedMap, SimplicialModule, SimplicialModule) + Headline + make the map of simplicial modules induced at each term by the identity map + Usage + f = inducedMap(D, C) + Inputs + C:SimplicialModule + D:SimplicialModule + Outputs + f:SimplicialModuleMap + Description + Text + Let $d$ be the value of the optional argument {\tt + Degree}, or zero, if not given. For each $i$, the terms + $D_{i+d}$ and $C_i$ must be subquotients of the same + ambient free module. This method returns the simplicial module map + induced by the identity on each of these free modules. + + If {\tt Verify => true} is given, then this method + also checks that these identity maps induce well-defined + maps. This can be a relatively expensive computation. + Text + We illustrate this method by truncating a free resolution + at two distinct internal degrees. We check that + the various induced maps compose to give another + induced map. + Example + needsPackage "Truncations" + kk = ZZ/32003 + R = kk[a,b,c] + F = simplicialModule(freeResolution (ideal gens R)^2, 2, Degeneracy => true) + C1 = truncate(3, F); + prune normalize C1 + C2 = truncate(4, F); + prune normalize C2 + assert isWellDefined C1 + assert isWellDefined C2 + f = inducedMap(C1, C2); + prune normalize f + assert isWellDefined f + f1 = inducedMap(F, C1) + f2 = inducedMap(F, C2) + assert isWellDefined f1 + assert isWellDefined f2 + assert(f2 == f1 * f) + SeeAlso + (inducedMap, Module, Module) + (truncate, List, SimplicialModule) +/// + + +doc /// + Key + "arithmetic with simplicial module maps" + (symbol+, SimplicialModuleMap, SimplicialModuleMap) + (symbol+, RingElement, SimplicialModuleMap) + (symbol+, Number, SimplicialModuleMap) + (symbol+, SimplicialModuleMap, RingElement) + (symbol+, SimplicialModuleMap, Number) + (symbol-, SimplicialModuleMap) + (symbol-, SimplicialModuleMap, SimplicialModuleMap) + (symbol-, RingElement, SimplicialModuleMap) + (symbol-, Number, SimplicialModuleMap) + (symbol-, SimplicialModuleMap, RingElement) + (symbol-, SimplicialModuleMap, Number) + (symbol*, RingElement, SimplicialModuleMap) + (symbol*, Number, SimplicialModuleMap) + (symbol*, SimplicialModuleMap, RingElement) + (symbol*, SimplicialModuleMap, Number) + Headline + perform arithmetic operations on simplicial module maps + Usage + f + g + a + f + f + a + -f + f - g + a - f + f - a + a * f + Inputs + f:SimplicialModuleMap + g:SimplicialModuleMap + a:RingElement + that is, an element in the underlying ring or a number + Outputs + :SimplicialModuleMap + Description + Text + The set of simplicial module maps forms a module over the underlying @TO2((ring, SimplicialModuleMap), "ring")@. + These methods implement the basic operations of addition, subtraction, and scalar multiplication. + Example + R = ZZ/101[a..d]; + C = simplicialModule(freeResolution coker matrix{{a*b, a*c^2, b*c*d^3, a^3}}, Degeneracy => true) + D = simplicialModule(freeResolution coker matrix{{a*b, a*c^2, b*c*d^3, a^3, a*c*d}}, 3, Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true); + prune normalize f + g = randomSimplicialMap(D, C, Boundary => true); + prune normalize g + Example + f+g; + isSimplicialMorphism oo + f-g; + isSimplicialMorphism oo + -f; + 3*f; + 0*f + a*f; + assert(0*f == 0) + assert(1*f == f) + assert((-1)*f == -f) + assert(-(f-g) == g-f) + assert((a+b)*f == a*f + b*f) + assert(a*(f+g) == a*f + a*g) + assert isSimplicialMorphism (f+g) + Text + Adding or subtracting a scalar is the same as adding or subtracting the + scalar multiple of the identity. In particular, the source and target must be equal. + Example + h = randomSimplicialMap(C, C); + prune normalize h + prune normalize(h+1) + assert(h+1 == h + id_C) + assert(h+a == h + a*id_C) + assert(1-h == id_C - h) + assert(b-c*h == -c*h + b*id_C) + assert(b-h*c == -h*c + id_C*b) + SeeAlso + "Making maps between simplicial modules" + randomSimplicialMap + (map, SimplicialModule, SimplicialModule, SimplicialModuleMap) +/// + +doc /// + Key + (symbol|, SimplicialModuleMap, SimplicialModuleMap) + Headline + join or concatenate maps horizontally + Usage + f | g + Inputs + f:SimplicialModuleMap + g:SimplicialModuleMap + Outputs + :SimplicialModuleMap + Description + Text + Given simplicial module maps with the same target, + this method constructs the associated map + from the direct sum of the sources to the target. + + First, we define some non-trivial maps of simplicial modules. + Example + R = ZZ/101[a..d]; + C1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], Degeneracy => true) + C2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, Degeneracy => true) + D = simplicialModule(freeResolution coker matrix{{a^2,b^2,c*d}}, Degeneracy => true) + f = randomSimplicialMap(D, C1) + g = randomSimplicialMap(D, C2) + Example + h = f|g + assert isWellDefined h + assert(source h === source f ++ source g) + assert(target h === target f) + SeeAlso + (symbol++, SimplicialModule, SimplicialModule) + (symbol++, SimplicialModuleMap, SimplicialModuleMap) + (symbol||, SimplicialModuleMap, SimplicialModuleMap) + (symbol|, Matrix, Matrix) +/// + +doc /// + Key + (symbol||, SimplicialModuleMap, SimplicialModuleMap) + Headline + join or concatenate maps vertically + Usage + f || g + Inputs + f:SimplicialModuleMap + g:SimplicialModuleMap + Outputs + :SimplicialModuleMap + Description + Text + Given simplicial module maps with the same source, + this method constructs the associated map + from the source to the direct sum of the targets. + + First, we define some non-trivial maps of simplicial modules. + Example + R = ZZ/101[a..d]; + D1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], Degeneracy => true) + D2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, Degeneracy => true) + C = simplicialModule(freeResolution coker matrix{{a^2,b^2,c*d}}, Degeneracy => true) + f = randomSimplicialMap(D1, C) + g = randomSimplicialMap(D2, C) + Example + h = f||g + assert isWellDefined h + assert(target h === target f ++ target g) + assert(source h === source f) + SeeAlso + (symbol++, SimplicialModule, SimplicialModule) + (symbol++, SimplicialModuleMap, SimplicialModuleMap) + (symbol|, SimplicialModuleMap, SimplicialModuleMap) + (symbol||, Matrix, Matrix) +/// + +doc /// + Key + (symbol++, SimplicialModuleMap, SimplicialModuleMap) + (directSum, SimplicialModuleMap) + Headline + direct sum of simplicial module maps + Usage + h = f ++ g + h = directSum(f,g,...) + h = directSum(name1 => f, name2 => g, ...) + Inputs + f:SimplicialModuleMap + g:SimplicialModuleMap + Outputs + h:SimplicialModuleMap + that is the direct sum of the input simplicial module maps + Description + Text + The direct sum of two simplicial module maps is a simplicial module map + from the direct sum of the sources to the direct sum of + the targets. + + First, we define some non-trivial maps of simplicial modules. + Example + R = ZZ/101[a..d]; + C1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], Degeneracy => true) + C2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, Degeneracy => true) + D1 = simplicialModule((freeResolution coker matrix{{a,b,c}}),2, Degeneracy => true) + D2 = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}[-1], 2, Degeneracy => true) + f = randomSimplicialMap(D1, C1, Cycle => true) + g = randomSimplicialMap(D2, C2, Cycle => true) + Example + h = f ++ g + assert isWellDefined h + assert isSimplicialMorphism h + Text + The direct sum of any sequence of simplicial module maps can be + computed as follows. + Example + directSum(f, g, f[2]) + h2 = directSum(peanut => f, butter => g, jelly => f[2]) + indices h2 + h2_[butter,jelly] + assert(source oo == C2 ++ C1[2]) + Text + One can easily obtain the compositions with canonical + injections and surjections. + Example + h_[0]^[0] == f + h_[1]^[1] == g + h_[0]^[1] == 0 + h_[1]^[0] == 0 + Example + h_[0] == h * (C1 ++ C2)_[0] + h_[1] == h * (C1 ++ C2)_[1] + h^[0] == (D1 ++ D2)^[0] * h + h^[1] == (D1 ++ D2)^[1] * h + SeeAlso + (symbol++, SimplicialModule, SimplicialModule) + (symbol**, SimplicialModuleMap, SimplicialModuleMap) + (symbol_, SimplicialModuleMap, Array) +/// + + +doc /// + Key + (image, SimplicialModuleMap) + Headline + make the image of a map of simplicial modules + Usage + E = image f + Inputs + f : SimplicialModuleMap + Outputs + E : SimplicialModule + Description + Text + If $f : C \to D$ is a map of simplicial modules of degree $d$, + then the image is the simplicial module $E$ whose $i$-th term is $image(f_{i-d})$, + and whose face/degeneracy map is induced from the face/degeneracy map + on the target. + Text + In the following example, we first construct a random + simplicial morphism $f : C \to D$. + Example + S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d),3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 0) + prune image f + prune normalize oo + i = inducedMap(forgetComplex target f, image f) + isSimplicialMorphism i + normalize i + Text + There is a canonical map of simplicial modules from the image to the target. + Example + g1 = inducedMap(target f, image f) + ker g1 == 0 + image g1 == image f + SeeAlso + "Making simplicial modules" + "Making maps between simplicial modules" + image + (coimage, SimplicialModuleMap) + (kernel, SimplicialModuleMap) + (cokernel, SimplicialModuleMap) +/// + +doc /// + Key + (coimage, SimplicialModuleMap) + Headline + make the coimage of a map of simplicial modules + Usage + coimage f + Inputs + f : SimplicialModuleMap + Outputs + : SimplicialModule + Description + Text + The coimage of a simplicial module map $f : C \to D$ + is the simplicial module $E$ whose $i$-th term is $coimage(f_i)$, + and whose face/degeneracy map is induced from the face/degeneracy map + on the source. + Text + In the following example, we first construct a random + simplicial morphism $f : C \to D$. + Example + S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d), 3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 0) + g1 = inducedMap(coimage f, source f) + coimage g1 == coimage f + coker g1 == 0 + Caveat + The coimage is more computationally intensive than @TO (image, SimplicialModuleMap)@ + because, unlike {\tt image}, it computes kernels of maps of modules. + SeeAlso + "Making simplicial modules" + "Making maps between simplicial modules" + coimage + (image, SimplicialModuleMap) + (kernel, SimplicialModuleMap) + (cokernel, SimplicialModuleMap) +/// + +doc /// + Key + (kernel, SimplicialModuleMap) + Headline + make the kernel of a map of simplicial modules + Usage + kernel f + ker f + Inputs + f : SimplicialModuleMap + Outputs + : SimplicialModule + Description + Text + The kernel of a simplicial module map $f : C \to D$ + is the simplicial module $E$ whose $i$-th term is $kernel(f_i)$, + and whose face/degeneracy map is induced from the face/degeneracy map + on the source. + Text + In the following example, we first construct a random + simplicial morphism $f : C \to D$. + Example + S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d), 3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Boundary => true, InternalDegree => 0) + prune ker f + h1 = inducedMap(source f, ker f) + ker f == image h1 + ker h1 == 0 + SeeAlso + "Making simplicial modules" + "Making maps between simplicial modules" + ker + (image, SimplicialModuleMap) + (coimage, SimplicialModuleMap) + (cokernel, SimplicialModuleMap) +/// + +doc /// + Key + (cokernel, SimplicialModuleMap) + Headline + make the cokernel of a map of simplicial modules + Usage + cokernel f + coker f + Inputs + f : SimplicialModuleMap + Outputs + : SimplicialModule + Description + Text + If $f : C \to D$ is a map of simplicial modules of degree $d$, + then the cokernel is the simplicial module $E$ whose $i$-th term is $cokernel(f_{i-d})$, + and whose face/degeneracy map is induced from the face/degeneracy map + on the target. + Text + In the following example, we first construct a random + simplicial morphism $f : C \to D$. + Example + S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d), 3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 0) + prune coker f + prune normalize oo + prune HH coker f + g1 = inducedMap(coker f, target f) + coker f == image g1 + coker g1 == 0 + SeeAlso + "Making simplicial modules" + "Making maps between simplicial modules" + cokernel + (image, SimplicialModuleMap) + (coimage, SimplicialModuleMap) + (kernel, SimplicialModuleMap) +/// + + +doc /// + Key + (symbol^, SimplicialModuleMap, Array) + (symbol_, SimplicialModuleMap, Array) + Headline + the composition with the canonical inclusion or projection map + Usage + i = f_[name] + p = f^[name] + Inputs + f:SimplicialModuleMap + name: + Outputs + :SimplicialModuleMap + {\tt i} is the composition of {\tt f} with the canonical inclusion and {\tt p} is + the composition of the canonical projection with {\tt f} + Description + Text + The direct sum is an n-ary operator with projection and + inclusion maps from each component satisfying appropriate + identities. + + One can access these maps as follows. First, we define + some non-trivial maps of simplicial modules. + Example + R = ZZ/101[a..d]; + C1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], 3, Degeneracy => true) + C2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, 3, Degeneracy => true) + D1 = simplicialModule((freeResolution coker matrix{{a,b,c}}), Degeneracy => true) + D2 = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}[-1], 3, Degeneracy => true) + f = randomSimplicialMap(D1, C1, Cycle => true) + g = randomSimplicialMap(D2, C2, Cycle => true) + Example + h = f ++ g; + Text + The four basic maps are the inclusion from each summand of the source + and the projection to each summand of the target. + Example + h_[0] == h * (C1 ++ C2)_[0] + h_[1] == h * (C1 ++ C2)_[1] + h^[0] == (D1 ++ D2)^[0] * h + h^[1] == (D1 ++ D2)^[1] * h + Text + These can be combined to obtain the blocks of the map of simplicial modules. + Example + h_[0]^[0] == f + h_[1]^[1] == g + h_[0]^[1] == 0 + h_[1]^[0] == 0 + Text + The default names for the components are the non-negative + integers. However, one can choose any name. + Example + h = (chicken => f) ++ (nuggets => g); + indices h + h_[chicken]^[chicken] == f + h_[nuggets]^[nuggets] == g + SeeAlso + (symbol++, SimplicialModule, SimplicialModule) + (symbol^, SimplicialModule, Array) + (symbol_, SimplicialModule, Array) + (directSum, SimplicialModule) + (components, SimplicialModule) + indices +/// + +doc /// + Key + (randomSimplicialMap, SimplicialModule, SimplicialModule) + randomSimplicialMap + [randomSimplicialMap, Boundary] + [randomSimplicialMap, Cycle] + [randomSimplicialMap, Degree] + [randomSimplicialMap, InternalDegree] + Cycle + Boundary + InternalDegree + Headline + a random map of simplicial modules + Usage + f = randomSimplicialMap(C,D) + Inputs + C:SimplicialModule + D:SimplicialModule + Boundary => Boolean + whether the constructed {\tt f} is a simplicial null homotopy + Cycle => Boolean + whether the constructed {\tt f} commutes with the face/degeneracy maps + Degree => ZZ + the degree of the constructed map of simplicial modules + InternalDegree => List + or @ofClass ZZ@ + Outputs + f:SimplicialModuleMap + Description + Text + A random simplicial module map $f : C \to D$ is obtained from choosing a random + map of the underlying normalizations, which uses the @TO randomComplexMap@ command. + Example + S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker matrix{{a*b, a*c, b*c}}, 3, Degeneracy => true) + D = simplicialModule(freeResolution coker vars S, Degeneracy => true) + f = randomSimplicialMap(D,C) + assert isWellDefined f + assert not isCommutative f + Text + When the random element is chosen with option {\tt Cycle => true}, the associated map of simplicial modules commutes + with the face/degeneracy map. + Example + g = randomSimplicialMap(D,C, Cycle => true) + assert isWellDefined g + assert isCommutative g + assert isSimplicialMorphism g + Text + When the random element is chosen with option {\tt Boundary => true}, the associated map of simplicial modules is a + simplicial null homotopy. + Example + h = randomSimplicialMap(D,C, Boundary => true) + assert isWellDefined h + assert isCommutative h + assert isSimplicialMorphism h + assert isNullHomotopic normalize h + nullHomotopy normalize h + Text + When the degree of the random element is chosen with a specific degree, + the associated map of simplicial modules will be a well-defined degree 0 simplicial + morphism mapping to the Dold-Kan image of the shift of the normalization. Thus, + even when specifying nonzero degree this constructor will still yield a simplicial + morphism. + Example + p = randomSimplicialMap(D, C, Cycle => true, Degree => -1) + assert isWellDefined p + assert isCommutative p + assert isSimplicialMorphism p + Text + Given an internal degree, the random element is constructed as maps of modules with this degree. + Example + q = randomSimplicialMap(D, C, Boundary => true, InternalDegree => 2); + assert isCommutative q + assert isSimplicialMorphism q + source q === C + target q === D + assert isNullHomotopic normalize q +/// + + +doc /// + Key + (isShortExactSequence, SimplicialModuleMap, SimplicialModuleMap) + Headline + whether a pair of simplicial module maps forms a short exact sequence + Usage + isShortExactSequence(g, f) + Inputs + f:SimplicialModuleMap + g:SimplicialModuleMap + Outputs + :Boolean + that is @TO true@ if these form a short exact sequence + Description + Text + A short exact sequence of simplicial modules + \[ 0 \to B \xrightarrow{f} C \xrightarrow{g} D \to 0\] + consists of two morphisms of simplicial modules + $f \colon B \to C$ and $g \colon C \to D$ such that + $g f = 0$, $\operatorname{image} f = \operatorname{ker} g$, + $\operatorname{ker} f = 0$, and $\operatorname{coker} g = 0$. + Text + From a simplicial morphism $h \colon B \to C$, one obtains a + short exact sequence + \[ 0 \to \operatorname{image} h \to C \to \operatorname{coker} h \to 0. \] + Example + R = ZZ/101[a,b,c]; + B = simplicialModule(freeResolution coker matrix{{a^2*b, a*b*c, c^3}}, Degeneracy => true) + C = simplicialModule(freeResolution coker vars R, 2, Degeneracy => true) + h = randomSimplicialMap(C, B, Cycle => true) + f = inducedMap(C, image h) + g = inducedMap(coker h, C) + assert isShortExactSequence(g,f) + Text + A short exact sequence of modules gives rise to a short + exact sequence of simplicial modules. These simplicial modules arise + as free resolutions of the modules. + Example + I = ideal(a^3, b^3, c^3) + J = I + ideal(a*b*c) + K = I : ideal(a*b*c) + SES = complex{ + map(comodule J, comodule I, 1), + map(comodule I, (comodule K) ** R^{-3}, {{a*b*c}}) + } + assert isWellDefined SES + assert isShortExactSequence(dd^SES_1, dd^SES_2) + (g,f) = (horseshoeResolution SES)/simplicialModule; + assert isShortExactSequence(g,f) + SeeAlso + "Basic invariants and properties" +/// + +doc /// + Key + (symbol SPACE, SimplicialModule, Array) + (symbol SPACE, SimplicialModuleMap, Array) + Headline + shift a simplicial module or simplicial module map + Usage + D = C[i] + g = f[i] + Inputs + C:SimplicialModule + or {\tt f}, a @TO SimplicialModuleMap@ + :Array + {\tt [i]}, where {\tt i} is an integer + Outputs + D:SimplicialModule + or {\tt g}, a @TO SimplicialModuleMap@. + Description + Text + The shifted simplicial module $D$ is not as simple to define as the + shift in the category of chain complexes. This method naively normalizes the + given simplicial module/map, applies the shift in the category of chain complexes, + then applies the Dold-Kan functor to the result. + + As the following example shows, this is not the same thing as simply shifting + all terms of the simplicial module. + Example + S = ZZ/101[a..d] + D = simplicialModule(S^1, 5) + D[-1] + oo.dd + D[-2] + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + dd^C_(3,0) + ss^C_(2,0) + D = C[1] + assert isWellDefined D + dd^D_(2,0) + ss^D_(1,0) + Text + Notice that the face maps of the shifted simplicial module are not simply the negation + of the face maps of the original. The shift operator is functorial, as illustrated below. + Example + C2 = simplicialModule(freeResolution (S^1/(a^2, b^2, c^2, d^2)), Degeneracy => true) + C3 = simplicialModule(freeResolution (S^1/(a^2, b^3, c^4, d^5)), Degeneracy => true) + f2 = simplicialModule(extend(C.complex, C2.complex, map(C_0, C2_0, 1))); + f3 = simplicialModule(extend(C2.complex, C3.complex, map(C2_0, C3_0, 1))); + isWellDefined (f2[-1]) + isSimplicialMorphism (f2[-1]) + isSimplicialMorphism (f3[-2]) + SeeAlso + "Making simplicial modules" +/// + +doc /// + Key + (directSum, SimplicialModule) + (symbol++, SimplicialModule, SimplicialModule) + Headline + direct sum of simplicial modules + Usage + D = C1 ++ C2 + D = directSum(C1,C2,...) + D = directSum(name1 => C1, name2 => C2, ...) + Inputs + Ci:SimplicialModule + Outputs + D:SimplicialModule + the direct sum of the input simplicial modules + Description + Text + The direct sum of two simplicial modules is another simplicial module. + Example + S = ZZ/101[a,b,c]; + C1 = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D1 = C1 ++ simplicialModule(complex(S^13)[-2], 3, Degeneracy => true) + isWellDefined D1 + D1.?ss --knows to cache degeneracy maps if inputs have degeneracy maps + C2 = simplicialModule(ideal(a,b,c), 3, Degeneracy => true) + C1 ++ C2 + assert isWellDefined(C1 ++ C2) + Text + The direct sum of a sequence of simplicial modules can be computed as follows. + Example + C3 = directSum(C1,C2,C2[-2]) + assert isWellDefined C3 + Text + The direct sum is an n-ary operator with projection and + inclusion maps from each component satisfying appropriate + identities. + Example + C4 = directSum(first => C1, second => C2) + C4_[first] -- inclusion map C1 --> C4 + C4^[first] -- projection map C4 --> C1 + C4^[first] * C4_[first] == 1 + C4^[second] * C4_[second] == 1 + C4^[first] * C4_[second] == 0 + C4^[second] * C4_[first] == 0 + C4_[first] * C4^[first] + C4_[second] * C4^[second] == 1 + Text + There are two short exact sequences associated to a direct sum. + Example + isShortExactSequence(C4^[first], C4_[second]) + isShortExactSequence(C4^[second], C4_[first]) + Text + Given a simplicial module which is a direct sum, we obtain the component + simplicial modules and their names (indices) as follows. + Example + components C3 + indices C3 + components C4 + indices C4 + SeeAlso + (components,SimplicialModule) + indices + (symbol^, SimplicialModule, Array) + (symbol_, SimplicialModule, Array) + (isShortExactSequence, SimplicialModuleMap, SimplicialModuleMap) +/// + + + +doc /// + Key + (isSimplicialModule, SimplicialModule) + isSimplicialModule + Headline + check if the simplicial identities hold for a simplicial object with degeneracy map keys + Usage + isSimplicialModule(S) + Inputs + S:SimplicialModule + A simplicial module with face maps `dd` and degeneracy maps `ss`. + Outputs + :Boolean + true if the simplicial identities hold, false otherwise + Description + Text + This function checks if the simplicial identities hold for a given simplicial object `S` with face maps `dd` and degeneracy maps `ss`. The simplicial identities are a set of equations that need to be satisfied by the face and degeneracy maps of the simplicial object. + Text + Specifically, the function verifies the following identities: + 1. For face maps: + \[ d_j d_i = d_i d_{j-1} \text{ for } 0 \leq i < j \leq n \] + 2. For face and degeneracy maps: + \[ d_i s_j = s_{j-1} d_i \text{ for } i < j \] + \[ d_j s_j = \text{id} \] + \[ d_{j+1} s_j = \text{id} \] + \[ d_k s_j = s_j d_{k-1} \text{ for } k > j+1 \] + 3. For degeneracy maps: + \[ s_j s_i = s_i s_{j+1} \text{ for } i \leq j \] + Text + If any of these identities fail, the function returns `false`. If all identities are satisfied, the function returns `true`. + Example + R = QQ[a..d]; + f0 = matrix {{-b^2+a*c, b*c-a*d, -c^2+b*d}} + f1 = map(source f0,, {{d, c}, {c, b}, {b, a}}) + C = simplicialModule(complex {f0, f1}, 3, Degeneracy => true) + isSimplicialModule C + dd^C + ss^C + dd^C*ss^C --if C is simplicial, this should be all identity maps + Text + The zero simplicial module is well-defined. + Example + C = simplicialModule(R^0, 6, Degeneracy => true) + isSimplicialModule C + SeeAlso + isWellDefined +/// + + +doc /// + Key + (forgetComplex, SimplicialModule) + forgetComplex + RememberSummands + Headline + forget the underlying complex data of a simplicial module obtained as a Dold-Kan image + Usage + forgetComplex(S) + Inputs + S:SimplicialModule + A simplicial module, which is assumed to be obtained as a Dold-Kan image. + RememberSummands => Boolean + Default value is true. Indicates whether to remember the component summands of the simplicial module when creating the new simplicial module. + Outputs + :SimplicialModule + A new simplicial module that is no longer viewed as a Dold-Kan image of some complex + Description + Text + This function removes the data of the underlying complex from a simplicial module $S$ that is obtained as a Dold-Kan image. + The function checks if the simplicial module has an associated complex and, if so, it reconstructs the simplicial module without the complex data + while preserving the face and degeneracy maps. + Text + If the option `RememberSummands` is set to true (the default), the function will remember the summands + of the simplicial module when reconstructing it. The face and degeneracy maps of the original simplicial module + are preserved in the new simplicial module. + This function is good for testing that the normalization of the Dold-Kan functor recovers the original complex, + since the @TO normalize@ command by default first checks if a simplicial module is obtained as a Dold-Kan image + before attempting a more costly computation. + Example + R = ZZ/101[x_1..x_3]; + K = koszulComplex vars R + S = simplicialModule(K,4, Degeneracy => true) + S.?complex + fS = forgetComplex S + components fS_3 + ffS = forgetComplex(S, RememberSummands => false) + components ffS_3 + Kn = normalize fS + Knn = normalize ffS + Kn.dd + K == prune Kn + SeeAlso + normalize + forgetDegeneracy +/// + + + +doc /// + Key + (forgetDegeneracy, SimplicialModule) + forgetDegeneracy + Headline + forget the data of degeneracy maps of a simplicial object + Usage + forgetDegeneracy(S) + Inputs + S:SimplicialModule + A simplicial module whose degeneracy maps are to be forgotten. + Outputs + :SimplicialModule + The simplicial module S, but with no degeneracy maps stored. + Description + Text + This function removes the data of degeneracy maps from a simplicial module `S`. + It is useful when the user wants to ignore degeneracy maps for the purpose of speeding up computations or simplifying the simplicial object. + Example + Q = ZZ/101[a..d] + K = koszulComplex vars Q + S = simplicialModule(K, 6, Degeneracy => true) + elapsedTime S**S + fS = forgetDegeneracy S + elapsedTime fS**fS --faster when degeneracy is ignored + Text + The change in speed becomes much more noticeable as ranks get larger. + SeeAlso + forgetComplex +/// + + + + +doc /// + Key + tensorwithComponents + (tensorwithComponents, Module, Module) + (tensorwithComponents, Matrix, Matrix) + (tensorwithComponents, List) + Headline + compute the tensor product of direct summands, caching the components for easy access + Usage + tensorwithComponents(M, N) + Inputs + M:Module + N:Module + Two modules whose tensor product of direct summands is to be computed. + Outputs + :Module + The tensor product of M and N, with cached components for easier access + Description + Text + This function computes the tensor product of two modules M and N, but additionally caches the components based on the component indices of the original modules. + This caching is particularly useful for easily accessing induced maps on components of tensor products of direct sums. + Text + If M and N have cached index components, then this function will use those indices. This function + is mostly used for extracting components of the simplicial tensor product, since the full + face/degeneracy maps are typically much too large to be displayed on their own. + Example + Q = ZZ/101[a..b] + M = Q^2++Q^3 + N = Q^3++Q^4 + components(M**N) --all components of M and N have been forgotten + T = tensorwithComponents(M,N) + components T + indices T + Text + This method also works for lists of modules, for purposes of iterating: + Example + tensorwithComponents {M,M,M} + indices oo + Text + This method is also functorial, which makes it easy to restrict maps + to tensor summands: + Example + L = directSum {Q^2, Q^3, Q^4} + f = L_[0,2] + phi = tensorwithComponents(f, f) + phi^[{0,2}]_[{0,1}] +/// + + + +doc /// + Key + (naiveNorm, SimplicialModule, ZZ) + (naiveNorm, SimplicialModule) + (naiveNorm, Complex, ZZ) + naiveNorm + Headline + compute the naive normalization of a simplicial object + Usage + naiveNorm(S, n) + Inputs + S:SimplicialModule + A simplicial module or @TO Complex@ + n:ZZ + and an integer n specifying the top degree for normalization. + Outputs + :Complex + The naive normalization complex of S, where the differential is the alternating sum of face maps. + Description + Text + This function computes the naive normalization of a simplicial object S. The naive normalization is a complex built from the modules of the simplicial object, + with a differential that is the alternating sum of the face maps. In general the naive normalization + is homotopy equivalent to the normalization (see @TO normalize@), but is much bigger in general. + Example + Q = ZZ/101[a..d] + K = koszulComplex vars Q + S = simplicialModule(K, Degeneracy => true) + nK = naiveNorm(S) + isWellDefined nK + prune HH nK + Text + Note that in the above, the naive normalization will always be an infinite complex, + so there will always be extraneous homology at the tail end. Note in this case that + the homology of the naive normalization is precisely the homology of {\tt K}, as it should + be (in fact, it is homotopy equivalent to {\tt K}). + SeeAlso + normalize +/// + + + +doc /// + Key + schurMap + (schurMap, List, SimplicialModule) + (schurMap, List, SimplicialModuleMap) + (schurMap, List, Complex) + (schurMap, List, ComplexMap) + TopDegree + Headline + construct the Dold-Puppe extension of the Schur functor + Usage + schurMap(lambda, S, Options => {Degeneracy => false, TopDegree => null}) + Inputs + lambda: List + A list representing a partition. + S: SimplicialModule + or @TO SimplicialModuleMap@, or @TO Complex@, or @TO ComplexMap@ to apply the Schur functor extension. + Degeneracy => Boolean + Default value is false. Indicates whether to include degeneracy maps in the construction. + TopDegree => ZZ + Default is null. If provided, specifies the top degree for the construction of the simplicial module. + Outputs + : SimplicialModule + The Dold-Puppe extension of the Schur functor applied to the simplicial module S. + Description + Text + This function constructs the Dold-Puppe extension of the Schur functor to the category of simplicial modules, + applied to a simplicial module S or a complex (or maps thereof). The construction involves + creating a simplicial module where each component is a Schur functor applied to the corresponding component of S. + Text + By default, degeneracy maps are not computed in this method since it adds additional computation time + that is not necessary for computing the normalization or many of the invariants of interest. However, + if the user is interested in having degeneracy maps, use the option {\tt Degeneracy => true}. + Example + Q = ZZ/101[a..b] + K = koszulComplex vars Q; + S = simplicialModule(K, 4, Degeneracy => true) + S2S = elapsedTime schurMap({2}, S) + elapsedTime schurMap({2}, S, Degeneracy => true) + Text + In general, if a polynomial functor has degree $d$, the Dold-Puppe extension of a functor + applied to a chain complex of length $t$ will have length at most $d \cdot t$. We can see this + explicitly in the following example: + Example + S = simplicialModule(K, 5) + S2 = schurMap({2}, S) + prune normalize S2 --notice the output has length 4 + minimize oo + Text + If the input is a complex, then the default top degree is taken to be the degree of the + Schur functor multiplied by the length of the complex. Computationally, this upper bound + is often too big to be computed at the moment, so the user may need to specify a top degree + by using the {\tt TopDegree => d} option. + Example + Q = ZZ/101[a..c] + K = koszulComplex vars Q; + S2K = elapsedTime prune schurMap({2}, K, TopDegree => 4) + (minimize S2K).dd --ignore the last differential + --S21K = elapsedTime prune schurMap({2,1}, K, TopDegree => 3) --top degree 4 takes ~1 minute + Text + These functors are particularly interesting in the modular setting, i.e., when the characteristic + of the underlying field is small relative to the degree of the Schur functor. In this case, + the induced complexes will have different homotopy classes as the characteristic varies. + Example + needsPackage "ChainComplexOperations" + Q = ZZ/2[a,b] + K = koszulComplex vars Q; + S2K = minimize prune schurMap({2}, K) + S2K' = sym2 K --the "naive" sym2 functor + S2K.dd + S2K'.dd + prune HH S2K + prune HH S2K' --not quasi-isomorphic! + Q = ZZ/3[a,b]; + K = koszulComplex vars Q; + prune HH schurMap({2}, K) + prune HH sym2 K --quasi-isomorphic in all other characteristics + --S3K = elapsedTime minimize prune schurMap({3}, K) --takes 23 seconds + --S21K = elapsedTime minimize prune schurMap({2,1}, K) --takes 17 seconds + --S21K.dd + --prune HH S21K + Text + This method is also implemented in a functorial way. + Example + Q = ZZ/2[a,b] + K = koszulComplex vars Q; + F = freeResolution( (ideal vars Q)^2) + phi = extend(K, F, id_(K_0)) + f = elapsedTime prune schurMap({2}, phi) + isCommutative f + isWellDefined f + prune HH source f + prune HH target f + prune HH f + Caveat + As many of the above examples show, this method can quickly become + computationally infeasible when the modules in the complex have too big of rank. + This seems to stem more from the efficiency of the @TO schur@ method, since methods such + as the tensor product still run very quickly even with huge matrices. + SeeAlso + extPower + simplicialTensor + symmetricQuotient +/// + + + +doc /// + Key + extPower + (extPower, ZZ, SimplicialModule) + (extPower, ZZ, SimplicialModuleMap) + (extPower, ZZ, Complex) + (extPower, ZZ, ComplexMap) + Headline + compute the Dold-Puppe extension of the exterior power functor to simplicial modules + Usage + extPower(d, S, Options => {Degeneracy => false, TopDegree => null}) + Inputs + d: ZZ + An integer representing the degree of the exterior power. + S: SimplicialModule + A simplicial module to which the exterior power functor extension is applied. + Degeneracy => Boolean + Default value is false. Indicates whether to include degeneracy maps in the construction. + TopDegree => ZZ + Default is null. If provided, specifies the top degree for the construction of the simplicial module. + Outputs + : SimplicialModule + The Dold-Puppe extension of the exterior power functor applied to the simplicial module S. + Description + Text + This function computes the Dold-Puppe extension of the exterior power functor to the category of chain complexes. + It can be applied to a simplicial module, a complex, or maps thereof. This method is equivalent + to using @TO schurMap@ for the partition $(1, 1, \dots , 1)$, but is generally much faster. + Example + Q = ZZ/2[a,b] + K = koszulComplex vars Q + w3K = elapsedTime prune extPower(3, K) + --elapsedTime prune schurMap({1,1,1}, K) --takes much longer + (minimize w3K).dd + prune HH w3K + Text + This method is also implemented in a functorial way: + Example + F = freeResolution( (ideal vars Q)^2) + phi = extend(K, F, id_(K_0)) + f = elapsedTime prune extPower(2, phi) + prune HH f + Text + Since this method runs significantly faster than using {\tt schurMap}, we can compute the + full exterior power complex of a longer complex: + Example + Q = ZZ/2[a..c]; + K = koszulComplex vars Q + w2K = prune extPower(2, K) + (minimize w2K).dd + prune HH w2K --has more interesting homology than in the nonmodular case + needsPackage "ChainComplexOperations" + Q = ZZ/3[a..c] + K = koszulComplex vars Q + prune HH wedge2 K --notice: homology concentrated in odd degrees + Caveat + This method may take a very long time to run if the user inputs a large/long complex. + Use the option {\tt TopDegree => d} to only run the computation up to a certain degree. + SeeAlso + schurMap + exteriorInclusion + symmetricQuotient + simplicialTensor +/// + + + +doc /// + Key + simplicialTensor + (simplicialTensor, List) + (simplicialTensor, Complex, Complex) + (simplicialTensor, ComplexMap, ComplexMap) + (simplicialTensor, ZZ, Complex) + (simplicialTensor, ZZ, SimplicialModule) + Headline + compute the simplicial tensor product and cache direct sum indices for easy access + Usage + simplicialTensor(T, Options => {Degeneracy => false, TopDegree => null}) + Inputs + T: List + A list of simplicial modules or complexes to compute the tensor product. + Degeneracy => Boolean + Default value is false. Indicates whether to include degeneracy maps in the construction. + TopDegree => ZZ + Default is null. If provided, specifies the top degree for the construction of the simplicial module. + Outputs + : SimplicialModule + The simplicial tensor product of the components in T, with cached direct sum indices. + Description + Text + This function computes the simplicial tensor product of a list T, which can consist of simplicial modules or complexes. + It caches direct sum indices using @TO tensorwithComponents@ so that the user can easily access + components of the resulting face/degeneracy maps on particular direct summands of the tensor product. + + The simplicial tensor product of complexes is built as the Dold-Kan extension of the tensor product + functor on the category of R-modules. In general, this complex looks quite different from the + standard tensor product of complexes. In fact, the simplicial tensor product is always homotopy + equivalent to the classically defined tensor product. + Example + Q = ZZ/101[x_1,x_2]; + K1 = complex {matrix{{x_1}}}; + K2 = complex {matrix{{x_2}}}; + T1 = K1**K2 + T1.dd + T2 = prune simplicialTensor({K1,K2}) + T2.dd + phi1 = extend(T1,T2,id_(T1_0)) + phi2 = extend(T2,T1,id_(T1_0)) + phi1*phi2 == id_T1 + isNullHomotopic(phi2*phi1 - id_T2) + Text + Here is how to access specific components of the face maps for a tensor product of simplicial + modules: + Example + S1 = simplicialModule(K1, 4) + S2 = simplicialModule(K2, 4) + S12 = S1**S2 + indices S12_4 --lists the indices of the summands + netList flatten for i in indices S12_4 list ( + for j in indices S12_3 list ( if (dd^S12_(4,0))_[i]^[j]==0 then continue else + horizontalJoin {net (dd^S12_(4,0))_[i]^[j], " : ", net i, " --> " , net j} )) + Text + One reason for using the simplicial tensor product + is that its components as a simplicial module are more canonically built, and thus naturally + extend functorial maps on the category of R-modules to the category of chain complexes. + + We can see actually see this in an example. The exterior power functor admits a canonical + comultiplication map + $$\bigwedge^{i+j} \to \bigwedge^i \otimes \bigwedge^j.$$ + This means that for any chain complex $C$ there is a canonical inclusion of complexes + $$\bigwedge^{i+j} C \hookrightarrow \bigwedge^i C \otimes \bigwedge^j C.$$ + Constructing this inclusion directly using the naive definition of the exterior power functor + on complexes would be extremely unnatural, but filtering through the simplicial category + makes this a very easy task: + Example + Q = ZZ/101[a,b]; + K = koszulComplex vars Q + SK = simplicialModule(K,6) --want top degree 6 since the resulting complexes should have length 6 + w21K = extPower(2, SK) ** SK + w3K = extPower(3, SK) + H = hashTable for i from 0 to 6 list i => dual wedgeProduct(2,1, dual SK_i); + inclusion = map(w21K, w3K, H); + isWellDefined inclusion + isCommutative inclusion + prune ker inclusion --should be 0 since it is an inclusion + S21' = complex { matrix {{a, b, 0, 0, -b}, {0, 0, a, b, a}}, matrix {{-b, b, 0, 0, + b, 0}, {a, -a, 0, 0, 0, b}, {0, 0, -b, b, -a, 0}, {0, 0, a, -a, 0, -a}, {0, 0, 0, 0, a, b}}, + matrix {{a, b, 0, -b, -b}, {a, b, 0, 0, -2*b}, {0, a, b, a, 0}, {0, a, b, 0, a}, {0, 0, 0, + -b, b}, {0, 0, 0, a, -a}}, matrix {{3*b, 0}, {-a, 2*b}, {0, -3*a}, {a, b}, {a, b}}} + S21 = S21'[-1] --this is the S^(2,1) schur functor; note the ranks + prune HH S21 --note the homology + Text + Just for sake of illustration, let us see how the above example changes in the modular + setting; note that the only characteristic for which the inclusion + $$\bigwedge^3 \hookrightarrow \bigwedge^2 \otimes \bigwedge^1$$ + is not canonically split is for characteristic 3, so we should expect the Dold-Kan + extension of the Schur functor $\mathbb{S}^{(2,1)} (K)$ to look different in characteristic 3. + (the resulting minimization of $\mathbb{S}^{(2,1)} (K)$ should not have coefficients, for instance). + Example + Q = ZZ/3[a,b]; + K = koszulComplex vars Q + SK = simplicialModule(K,6) --want top degree 6 since the resulting complexes should have length 6 + w21K = extPower(2, SK) ** SK + w3K = extPower(3, SK) + H = hashTable for i from 0 to 6 list i => dual wedgeProduct(2,1, dual SK_i); + inclusion = map(w21K, w3K, H); + --inc = prune normalize inclusion; + --next commands are commented out since they are longer computations + --S21K = prune coker inc --the char 3 simplicial schur functor + --(minimize S21K).dd --complex is longer! All other cases have ranks (2,5,6,5,2) + --prune HH S21K --more homology as well! + Text + Note that the above method of computing the Schur functor $\mathbb{S}^{(2,1)} (K)$ is + significantly faster than the {\tt schurMap} command. + + This method is also implemented functorially, so it can be applied to simplicial maps + and complex maps. + Example + F = freeResolution( (ideal vars Q)^2) + phi = extend(K, F, id_(K_0)) + prune simplicialTensor(phi, phi) + Caveat + The user should remember to prune the output upon normalizing a simplicial + tensor product. + SeeAlso + extPower + symmetricQuotient +/// + + + +doc /// + Key + (normalize, SimplicialModule, ZZ) + (normalize, SimplicialModule) + (normalize, SimplicialModuleMap, ZZ) + (normalize, SimplicialModuleMap) + CheckSum + CheckComplex + [(normalize, SimplicialModule, ZZ), CheckSum] + [(normalize, SimplicialModule, ZZ), CheckComplex] + [(normalize, SimplicialModule), CheckSum] + [(normalize, SimplicialModule), CheckComplex] + [(normalize, SimplicialModuleMap, ZZ), CheckSum] + [(normalize, SimplicialModuleMap, ZZ), CheckComplex] + [(normalize, SimplicialModuleMap), CheckSum] + [(normalize, SimplicialModuleMap), CheckComplex] + Headline + normalization functor from simplicial modules to nonnegatively-graded chain complexes + Usage + normalize(S, d) + normalize S + Inputs + S: SimplicialModule + or @TO SimplicialModuleMap@, the object to be normalized. + d: ZZ + An integer specifying the degree up to which the normalization should be computed. + CheckSum => Boolean + Default value is true. If true and S has more than one component, computes the direct sum of normalized components. + CheckComplex => Boolean + Default value is true. If true and S contains a cached simplicial module or map, it outputs this cached value. + Outputs + : Complex + or @TO ComplexMap@, the normalized nonnegatively-graded chain complex resulting from the normalization process. + Description + Text + This function computes the normalization functor from the category of simplicial modules + to the category of nonnegatively-graded chain complexes. It is implemented in a functorial way, + applying both to simplicial modules and simplicial module maps. + + The normalization of a simplicial module $S$ is by definition equal to the complex $N (S)$ with: + $$N(S)_n := \bigcap_{i=1}^n \ker d^S_{(n,i)},$$ + with differential induced by the face map $d_{(n,0)}^S$. As currently implemented, the normalization + does not prune the output; the user should use {\tt prune} to obtain the best looking output. + + The {\tt normalize} command is implemented so that it first checks whether a simplicial module + has been obtained as the Dold-Kan image of some complex. If it has, then it returns that complex + without doing any additional computation. If the user prefers that the normalization is computed + by definition instead of accessing this cached value, use the option {\tt CheckComplex => false}. + Example + R = ZZ/101[x_1..x_3]; + K = koszulComplex vars R + S = simplicialModule(K,10, Degeneracy => true) + keys S + K == normalize S + Kn = normalize(S, CheckComplex => false) + Kn.dd + K == Kn + K == prune Kn + Text + For computational efficiency, this method also checks if the simplicial module has been constructed as a direct sum of simplicial + modules. If it has, then it returns the direct sum of the normalizations of each component. If the + user prefers that this shortcut is not taken, use {\tt CheckSum => false}. + Example + S10 = directSum toList(10: forgetComplex S) + elapsedTime prune normalize S10 + elapsedTime prune normalize(S10, CheckSum => false) --about 3-4 times slower; becomes significant for larger ranks + Text + The user may also specify the top homological degree to compute the normalization up to. Note that + this can help speed up computational time; if the user knows the normalization should have + a shorter length, then they should specify this upper bound in the syntax: + Example + elapsedTime prune normalize(S10, 3, CheckSum => false) --MUCH FASTER! + Text + Again, this method is functorial, and when combined with other methods in this package + can be a particularly powerful way of obtaining nontrivial morphisms of complexes. We use this + method to obtain the image of the inclusion + $$\bigwedge^3 K \to \bigwedge^2 K \otimes K$$ + for a Koszul complex K. Constructing this map directly using the naive definitions of + tensor products/exterior powers of complexes is not possible in full generality. Taking + advantage of the Dold-Kan correspondence and simplicial methods allows us to obtain + this inclusion explicitly. + Example + Q = ZZ/3[a,b]; + K = koszulComplex vars Q + SK = simplicialModule(K,6) --want top degree 6 since the resulting complexes should have length 6 + w21K = extPower(2, SK) ** SK + w3K = extPower(3, SK) + H = hashTable for i from 0 to 6 list i => dual wedgeProduct(2,1, dual SK_i); + inclusion = map(w21K, w3K, H); + inc = prune normalize(inclusion,3); + isWellDefined inc + isCommutative inc + Text + Taking the cokernel of the above morphism yields a canonically + defined Schur functor $\mathbb{S}^{(2,1)} (K)$; constructing this complex + using the classical Schur complex definition will yield a complex that does not + have finite length homology (and hence isn't even homotopically equivalent). + Example + S21K = prune coker inc --this is only a snapshot; should go up to degree 6 + (minimize S21K).dd + prune HH S21K --free part is because we truncated + SeeAlso + (directSum, SimplicialModule) + naiveNorm + extPower + simplicialTensor +/// + + +doc /// + Key + exteriorInclusion + (exteriorInclusion, SimplicialModule) + (exteriorInclusion, Complex, ZZ) + (exteriorInclusion, Complex) + (exteriorInclusion, Module) + Headline + computes the image of the 2nd exterior power into the tensor product + Usage + exteriorInclusion(S) + Inputs + M:SimplicialModule + or @TO Complex@ or @TO Module@, optional integer argument specifies top degree + Outputs + :SimplicialModuleMap + or @TO Matrix@, the map representing the image of the 2nd exterior power of $S$ into the tensor product $S \otimes S$. + Description + Text + Given a simplicial module $S$ (or a complex), this function computes the map + $$\bigwedge^2 S \to S \otimes S,$$ + The cokernel of + this map is by definition the second symmetric power of $S$. This method + is mainly used in conjunction with the @TO tensorLES@ command to compute induced + maps on homology for canonical short exact sequences. + Example + Q = ZZ/101[a,b,c] + K = koszulComplex vars Q + --elapsedTime schurMap({2}, K) --takes some time + phi = elapsedTime exteriorInclusion(K,3); --specify top degree 3 + isWellDefined phi + isCommutative phi + prune coker phi + Text + All of the homology of the second symmetric/exterior powers are guaranteed to be concentrated + in degrees $0$ to $3$ in the above example, so it suffices to compute only $4$ terms to + understand all of the homology. + Example + for i to 3 list prune HH_i source phi + for i to 3 list prune HH_i (coker phi) + Text + Notice that since the tensor square splits outside of characteristic 2, the symmetric power + picks up the even degree homology and the exterior square picks up the odd homology. In + characteristic 2 this changes: + Example + Q = ZZ/2[a,b,c] + K = koszulComplex vars Q + phi = elapsedTime exteriorInclusion(K,3); --specify top degree 3 + isWellDefined phi + isCommutative phi + for i to 2 list prune HH_i source phi + for i to 2 list prune HH_i (coker phi) + SeeAlso + symmetricQuotient + extPower + simplicialTensor +/// + + + + +doc /// + Key + symmetricQuotient + (symmetricQuotient, SimplicialModule) + (symmetricQuotient, Complex, ZZ) + (symmetricQuotient, Complex) + (symmetricQuotient, Module) + Headline + computes the image of the surjection from the simplicial tensor product onto the second symmetric power of a simplicial module + Usage + symmetricQuotient(S) + Inputs + S: SimplicialModule + or @TO Complex@ or @TO Module@, optional integer argument specifies top degree + Outputs + : SimplicialModuleMap + The induced map representing the image of the surjection from the simplicial tensor product onto the second symmetric power of S. + Description + Text + This function computes the induced map on the cokernel of the map constructed by + the @TO exteriorInclusion@ method, which is explicitly giving the map + $$S \otimes S \to \operatorname{Sym}^2 (S).$$ + Let us see some examples: + Example + Q = ZZ/2[a,b]; + K = koszulComplex vars Q; + phi = prune symmetricQuotient(K,4) + isWellDefined phi + isCommutative phi + prune coker phi == 0 + prune HH phi + prune coker oo --the induced map on homology is NOT surjective, in contrast to the case when 2 is a unit + SeeAlso + tensorLES + exteriorInclusion + simplicialTensor +/// + + + + +doc /// + Key + tensorLES + (tensorLES, Complex, ZZ) + Headline + computes the long exact sequence of homology induced by the canonical short exact sequence of complexes + Usage + tensorLES(C, d) + Inputs + C: Complex + A complex. + d: ZZ + An integer specifying the top degree for constructing the simplicial module. + Outputs + : Complex + The long exact sequence of homology induced by the canonical short exact sequence \( 0 \to \wedge^2 C \to T^2 C \to Sym^2 C \to 0 \). + Description + Text + This function computes the long exact sequence of homology associated with the canonical short exact sequence of complexes: + \( 0 \to \bigwedge^2 C \to C \otimes C \to \operatorname{Sym}^2 C \to 0 \). + Text + This function first computes the exterior inclusion map on the complex C up to degree d using the @TO exteriorInclusion@ function. + Then it constructs the induced map on the cokernel of this exterior inclusion map, which is the surjection to the second symmetric power of C. + Finally, it computes the long exact sequence of homology using the @TO longExactSequence@ function on the pruned maps of the induced maps. + + This long exact sequence is only interesting in characteristic 2, since the above sequence is split whenever 2 is a unit, + so there are no interesting connecting homomorphisms. Let's see some examples in characteristic 2 of the + connecting homomorphisms: + Example + Q = ZZ/2[a,b] + K = koszulComplex vars Q + prune tensorLES(K,4) + oo.dd_6 --nontrivial connecting homomorphism + F = freeResolution( (ideal vars Q)^3) + prune tensorLES(F,4) + L = complex {K.dd_1, map(source K.dd_1,target K.dd_2 ,K.dd_2*K.dd_1), K.dd_2} + hL = elapsedTime prune tensorLES(L,4) + netList {hL.dd_3, hL.dd_6, hL.dd_9, hL.dd_12, hL.dd_15} --two nontrivial connecting homs + SeeAlso + exteriorInclusion + simplicialTensor + /// + +-- Option doc stubs + +doc /// + Key + [simplicialModule, Degeneracy] + Headline + whether to compute degeneracy maps + Usage + simplicialModule(..., Degeneracy => true) + Description + Text + If {\tt Degeneracy => true}, the degeneracy maps are also computed + alongside the face maps. By default, degeneracy maps are not computed. + SeeAlso + simplicialModule +/// + +doc /// + Key + [forgetComplex, RememberSummands] + Headline + whether to remember direct sum structure + Usage + forgetComplex(..., RememberSummands => true) + Description + Text + If {\tt RememberSummands => true}, the direct sum structure + of the terms is remembered when forgetting the underlying complex. + SeeAlso + forgetComplex +/// + +doc /// + Key + [extPower, Degeneracy] + Headline + whether to compute degeneracy maps + Usage + extPower(..., Degeneracy => true) + Description + Text + If {\tt Degeneracy => true}, the degeneracy maps are also computed + alongside the face maps. By default, degeneracy maps are not computed. + SeeAlso + extPower +/// + +doc /// + Key + [extPower, TopDegree] + Headline + top degree for the construction + Usage + extPower(..., TopDegree => n) + Description + Text + Specifies the top simplicial degree for the resulting simplicial module. + SeeAlso + extPower +/// + +doc /// + Key + [schurMap, Degeneracy] + Headline + whether to compute degeneracy maps + Usage + schurMap(..., Degeneracy => true) + Description + Text + If {\tt Degeneracy => true}, the degeneracy maps are also computed + alongside the face maps. By default, degeneracy maps are not computed. + SeeAlso + schurMap +/// + +doc /// + Key + [schurMap, TopDegree] + Headline + top degree for the construction + Usage + schurMap(..., TopDegree => n) + Description + Text + Specifies the top simplicial degree for the resulting simplicial module. + SeeAlso + schurMap +/// + +doc /// + Key + [simplicialTensor, Degeneracy] + Headline + whether to compute degeneracy maps + Usage + simplicialTensor(..., Degeneracy => true) + Description + Text + If {\tt Degeneracy => true}, the degeneracy maps are also computed + alongside the face maps. By default, degeneracy maps are not computed. + SeeAlso + simplicialTensor +/// + +doc /// + Key + [simplicialTensor, TopDegree] + Headline + top degree for the construction + Usage + simplicialTensor(..., TopDegree => n) + Description + Text + Specifies the top simplicial degree for the resulting simplicial module. + SeeAlso + simplicialTensor +/// + + +doc /// + Key + [minimalPresentation, Exclude] + Headline + exclude option for minimal presentation + Description + Text + This option is inherited from the core @TO minimalPresentation@ method. + SeeAlso + (minimalPresentation, SimplicialModule) +/// + +doc /// + Key + [map, Degree] + Headline + specify the degree of a map of simplicial modules + Description + Text + When constructing a map of simplicial modules, this option + specifies the degree $d$ of the resulting map. A map of + degree $d$ consists of component maps $f_i : C_i \to D_{i+d}$. + By default, the degree is 0. + SeeAlso + (map, SimplicialModule, SimplicialModule, HashTable) +/// + +doc /// + Key + [map, DegreeLift] + Headline + unused option for maps of simplicial modules + Description + Text + This option is inherited from the core map method + and is not used for maps of simplicial modules. +/// + +doc /// + Key + [map, DegreeMap] + Headline + unused option for maps of simplicial modules + Description + Text + This option is inherited from the core map method + and is not used for maps of simplicial modules. +/// + +doc /// + Key + [inducedMap, Degree] + Headline + specify the degree of an induced map of simplicial modules + Description + Text + When constructing an induced map of simplicial modules, + this option specifies the degree of the resulting map. + By default, the degree is 0. + SeeAlso + (inducedMap, SimplicialModule, SimplicialModule) +/// + + diff --git a/M2/Macaulay2/packages/SimplicialModules/SimplicialModuleTESTS1.m2 b/M2/Macaulay2/packages/SimplicialModules/SimplicialModuleTESTS1.m2 new file mode 100644 index 00000000000..9e32b7b43a8 --- /dev/null +++ b/M2/Macaulay2/packages/SimplicialModules/SimplicialModuleTESTS1.m2 @@ -0,0 +1,1348 @@ +TEST /// +---basic test arising from ring documentation +S = ZZ/101[a,b,c,d]; + C = simplicialModule freeResolution coker vars S + ring C + assert(ring C === S) + ring id_C + assert(ring id_C === S) +/// + + +TEST /// +--test for topdeg from doc +S = ZZ/101[a..c]; + C = simplicialModule(freeResolution coker vars S, 8) + assert(topDegree C == 8) + C' = simplicialModule(freeResolution coker vars S, 6) + assert(topDegree C' == 6) + + S = ZZ/101[a..d] + C0 = simplicialModule( S^2, 6, Degeneracy => true) + f = dd^C0 + assert(source f == target f) + f == 0 + isWellDefined C0 + C0 == 0 + assert(topDegree C0 == 6) + C5 = simplicialModule(S^0, 8, Degeneracy => true) + assert(C5 == 0) + assert(dd^C5 == 0) + assert(ss^C5 == 0) + C5_0 +/// + + +TEST /// +R = QQ[a..d]; + I = ideal(a*d-b*c, b^2-a*c, c^2-b*d); + C = simplicialModule(freeResolution(R^1/I), 4, Degeneracy => true) + dd^C + C.dd + ss^C + C.ss + assert(dd^C === C.dd) + assert(source dd^C === C) + assert(target dd^C === C) + assert(degree dd^C === -1) + assert(source ss^C === C) + assert(target ss^C === C) + assert(degree ss^C === 1) + dd^C_(2,0) + assert(source dd^C_2 === C_2) + assert(target dd^C_2 === C_1) +/// + + +TEST /// +--direct sum testing +S = ZZ/101[a,b,c]; + C1 = simplicialModule(freeResolution coker vars S, 5, Degeneracy => true) + C2 = simplicialModule(complex (ideal(a,b,c)) , 5, Degeneracy => true) + D = C1 ++ C2 + assert isWellDefined D_[0] + assert isCommutative D_[0] + assert isWellDefined D_[1] + assert isCommutative D_[1] + assert(D^[0] * D_[0] == 1) + assert(D^[1] * D_[1] == 1) + assert(D^[0] * D_[1] == 0) + assert(D^[1] * D_[0] == 0) + assert(D_[0] * D^[0] + D_[1] * D^[1] == 1) +/// + +TEST /// +S = ZZ/101[a..d] + moduleHash = hashTable { 0 => S^1, + 1 => S^1++S^2, + 2 => S^1++S^2++S^2++S^1} + faceHash = hashTable {(1,0) => matrix {{1, a, b}}, + (1,1) => matrix {{1_S, 0, 0}}, + (2,0) => matrix {{1, a, b, 0, 0, 0}, + {0, 0, 0, 1, 0, -b}, + {0, 0, 0, 0, 1, a}}, + (2,1) => matrix {{1_S, 0, 0, 0, 0, 0}, + {0, 1, 0, 1, 0,0}, + {0, 0, 1, 0, 1, 0}}, + (2,2) => matrix {{1_S, 0, 0, 0, 0, 0}, + {0, 1, 0, 0, 0, 0}, + {0, 0, 1, 0, 0, 0}}} + degenHash = hashTable {(0,0) => matrix {{1_S}, {0}, {0}}, + (1,0) => matrix {{1_S, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {0, 0, 0}}, + (1,1) => matrix {{1_S, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}} + T = simplicialModule(moduleHash, faceHash, degenHash, 2) + assert(T.?module) + assert T.?dd + assert T.?ss + T' = simplicialModule(moduleHash, faceHash, 2) + H1 = hashTable {0 => S^1, 1 => S^1, 2 => S^1} + H2 = hashTable {(1,0) => map(S^1, S^1, 0), + (1,1) => map(S^1, S^1, 0), + (2,0) => map(S^1, S^1, 0), + (2,1) => map(S^1, S^1, 0), + (2,2) => map(S^1, S^1, 0)} + H3 = hashTable {(0,0) => map(S^1, S^1, 0), + (1,0) => map(S^1, S^1, 0), + (1,1) => map(S^1, S^1, 0)} + U = simplicialModule(H1,H2,H3,2) + assert U.?dd + assert U.?ss + assert not isWellDefined U +/// + +TEST /// +S = ZZ/101[a..d] + C0 = simplicialModule( S^2, 6, Degeneracy => true) + f = dd^C0 + source f, target f + f == 0 + assert isWellDefined C0 + C0 == 0 + assert(topDegree C0 ==6) + C2 = simplicialModule( S, 5, Degeneracy => true) + I = ideal(a^2-b, c^3) + C3 = simplicialModule( I, 7, Degeneracy => true) + C4 = simplicialModule( (S/I), 8, Degeneracy => true) + assert isSimplicialModule C2 + assert isSimplicialModule C3 + assert isSimplicialModule C4 + C5 = simplicialModule(S^0, 8, Degeneracy => true) + assert(C5 == 0) + assert (dd^C5 == 0) + assert(ss^C5 == 0) + C5_0 +/// + +TEST /// +S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker vars S, 4, Degeneracy => true) + assert C.?complex + assert isWellDefined C.complex + assert(C_(1,0) == C.complex_0) + assert(C_(1,1) == C.complex_1) + assert(C_(2,1) == C.complex_1 ++ C.complex_1) + tC = C ** C + assert not tC.?complex + tC_2 +/// + + +TEST /// +S = ZZ/101[a..c] + C = simplicialModule(K = freeResolution coker vars S, 4) + D = image id_C; + assert(not(C === D)) + assert(C == D) + E = simplicialModule(complex for i from 1 to 3 list 0*dd^K_i, 4) + assert isWellDefined dd^E + assert not(C == E) + assert not(E == 0) + f = id_C + D = coker f + assert(D == 0) + C0 = simplicialModule( S^0, 5, Degeneracy => true) + C1 = simplicialModule(complex(S^0, Base => 2), 5, Degeneracy => true) + topDegree C0 == topDegree C1 + assert(C0 == C1) + assert(C0 == 0) + assert(C1 == 0) +/// + + +TEST /// +R = QQ[a..d]; + I = ideal(a*d-b*c, b^2-a*c, c^2-b*d); + C = simplicialModule(freeResolution(R^1/I), 4, Degeneracy => true) + isWellDefined C + dd^C + C.dd + ss^C + C.ss + assert(dd^C === C.dd) + assert(source dd^C === C) + assert(target dd^C === C) + assert(degree dd^C === -1) + assert(source ss^C === C) + assert(target ss^C === C) + assert(degree ss^C === 1) + dd^C_(2,0) + assert(source dd^C_2 === C_2) + assert(target dd^C_2 === C_1) +/// + + +TEST /// +S = ZZ/101[a,b,c]; + C1 = simplicialModule(freeResolution coker vars S, 5, Degeneracy => true) + C2 = simplicialModule(complex (ideal(a,b,c)) , 5, Degeneracy => true) + D = C1 ++ C2 + D_[0] + assert isCommutative D_[0] + D_[1] + assert(D^[0] * D_[0] == 1) + assert(D^[1] * D_[1] == 1) + assert(D^[0] * D_[1] == 0) + assert(D^[1] * D_[0] == 0) + assert(D_[0] * D^[0] + D_[1] * D^[1] == 1) + E = (chicken => C1) ++ (nuggets => C2) + E_[chicken] + E_[nuggets] + assert(E^[chicken] * E_[chicken] == 1) + assert(E^[nuggets] * E_[nuggets] == 1) + assert(E^[chicken] * E_[nuggets] == 0) + assert(E^[nuggets] * E_[chicken] == 0) + assert(E_[chicken] * E^[chicken] + E_[nuggets] * E^[nuggets] == 1) + F = directSum(C1, C2, simplicialModule(complex(S^2, Base => 1), 5, Degeneracy => true)) + prune (F^[0,1]) + assert isSimplicialMorphism oo + prune (F_[0,2]) + assert isSimplicialMorphism oo +/// + + +TEST /// +S = ZZ/101[a,b,c]; + C1 = simplicialModule freeResolution coker vars S + C2 = simplicialModule(complex (ideal(a,b,c)), 3) + D = C1 ++ C2 + L = components D + assert(L_0 === C1) + assert(L_1 === C2) + E = (peanut => C1) ++ (butter => C2) + components E +/// + + +TEST /// +S = ZZ/101[a..c] + Ca = simplicialModule(complex {matrix{{a}}}, 3) + Cb = simplicialModule(complex {matrix{{b}}}, 3) + Cc = simplicialModule(complex {matrix{{c}}}, 3) + Cab = Cb ** Ca + dd^Cab + (prune normalize Cab).dd + assert isWellDefined Cab + Cabc = Cc ** Cab + Cc ** Cb ** Ca + dd^(nC = prune normalize Cabc) + assert isWellDefined nC + Cabc ** (S^1/(a,b,c)); + assert isWellDefined oo + S^2 ** Cabc + assert isWellDefined oo +/// + +TEST /// +R = QQ[a,b,c]; + I = ideal(a*b, a*c, b*c) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D = truncate(3,C) + assert isWellDefined D + assert(C == truncate(0, C)) + A = ZZ/101[x_0, x_1, y_0, y_1, y_2, Degrees => {2:{1,0}, 3:{0,1}}]; + I = intersect(ideal(x_0, x_1), ideal(y_0, y_1, y_2)) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D1 = prune truncate({{1,1}}, C) + D2 = truncate({{1,0}}, C) + D3 = truncate({{0,1}}, C) + D4 = truncate({{1,0},{0,1}}, C); + D5 = truncate({{2,2}}, C); + assert all({D1,D2,D3,D4,D5}, isWellDefined) +/// + + +TEST /// +R = QQ[x,y,z] + S = QQ[s,t] + phi = map(S, R, {s, s+t, t}) + I = ideal(x^3, x^2*y, x*y^4, y*z^5) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D = phi C + assert isWellDefined D + dd^D + R = ZZ/101[a..d] + S = ZZ/101[s,t] + phi = map(S, R, {s^4, s^3*t, s*t^3, t^4}, DegreeMap => i -> 4*i) + C = simplicialModule(freeResolution coker vars R, 4, Degeneracy => true) + D = phi C + assert isWellDefined D +/// + +TEST /// +R = QQ[x,y,z]; + S = QQ[s,t]; + phi = map(S, R, {s, s+t, t}) + I = ideal(x^3, x^2*y, x*y^4, y*z^5) + C = simplicialModule(freeResolution I, 3, Degeneracy => true) + D = phi ** C + assert isWellDefined D + assert isWellDefined dd^D + assert isWellDefined ss^D + A = R/(x^2+y^2+z^2); + C ** A + assert(map(A,R) ** C == C ** A) + use R + I = ideal(x*y, x*z, y*z); + J = I + ideal(x^2, y^2); + g = inducedMap(module J, module I) + assert isWellDefined g + C = simplicialModule(complex {g}, 3, Degeneracy => true) + D1 = phi C + assert isWellDefined D1 + D2 = phi ** C + assert isWellDefined D2 + prune D1 + prune D2 + R = ZZ/101[a..d]; + S = ZZ/101[s,t]; + f = map(S, R, {s^4, s^3*t, s*t^3, t^4}, DegreeMap => i -> 4*i) + C = simplicialModule(freeResolution coker vars R, 3, Degeneracy => true) + D = f ** C + D == f C + assert isWellDefined D +/// + + +TEST /// +S = ZZ/101[a,b,c,d,e]; + I = ideal(a,b) * ideal(c,d,e) + F = simplicialModule((dual freeResolution I)[-4], 2, Degeneracy => true) + C = HH F + D = prune C + g = D.cache.pruningMap + assert isWellDefined g + assert isSimplicialMorphism g + assert (target g == C) + assert (source g == D) + g^-1 + assert(g*g^-1 == 1 and g^-1*g == 1) + S = ZZ/101[a,b,c]; + I = ideal(a^2,b^2,c^2); + J = I + ideal(a*b*c); + FI = simplicialModule(freeResolution I, Degeneracy => true) + FJ = simplicialModule(freeResolution J, Degeneracy => true) + f = randomSimplicialMap(FJ, FI ** S^{-1}, Cycle => true) + C = image f + D = prune C + g = D.cache.pruningMap + assert isWellDefined g + assert isSimplicialMorphism g + assert (target g == C) + assert (source g == D) + g^-1 + assert(g*g^-1 == 1 and g^-1*g == 1) + h = prune f + assert(source h === prune source f) + assert(target h === prune target f) +/// + + +TEST /// +R = ZZ/101[a,b,c]; + C = simplicialModule(F = freeResolution coker matrix{{a^2-b^2,b^3-c^3,c^4}}, Degeneracy => true) + D = simplicialModule(G = freeResolution coker vars R, Degeneracy => true) + H = hashTable { 0 => map(D_0, C_0, 1), + 1 => map(D_1, C_1, {{1, 0, 0, 0}, {0, a, 0, 0}, {0, -b, b^2, 0}, {0, 0, -c^2, c^3}}), + 2 => map(D_2, C_2, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, a, 0, 0, 0, 0,0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0}, + {0, 0, 0, 0, -b, b^2, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3}}), + 3 => map(D_3, C_3, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, -b, b^2, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0,0, 0, 0, 0, 0, 0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0, 0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-a*c^2, a*c^3, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0,0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0, 0,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2,a*c^3, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2*c^3}}) + } + f = map(D, C, H) + assert isWellDefined f + assert isHomogeneous f + assert(degree f == 0) + assert isSimplicialMorphism f + h = map(C, C, hashTable {}) + assert(h == 0) +/// + + +TEST /// +R = QQ[a,b,c] + C = simplicialModule(freeResolution coker vars R, Degeneracy => true) + D = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}, Degeneracy => true) + f = map(D, C, 0) + assert isWellDefined f + assert isSimplicialMorphism f + g = map(C, C, 0, Degree => 13) + assert isWellDefined g + assert(degree g == 13) + assert not isSimplicialMorphism g + assert isCommutative g + assert isHomogeneous g + assert(source g == C) + assert(target g == C) + assert(map(C, C, 1) === id_C) +/// + + +TEST /// +R = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars R, Degeneracy => true) + f = map(forgetComplex C, forgetComplex C, id_C) + assert isWellDefined f + assert(degree f == 0) + assert isCommutative f + assert isSimplicialMorphism f + normalize f --notice how the normalization is not already pruned + normalize id_C + prune normalize f == normalize id_C +/// + + +TEST /// +R = ZZ/101[x,y]/(x^3, y^3) + C = simplicialModule(freeResolution(coker vars R, LengthLimit=>6), 6, Degeneracy => true) + f = id_C; + assert isWellDefined f + assert isSimplicialMorphism f +/// + +TEST /// +R = ZZ/101[a,b,c]; + C = simplicialModule(F = freeResolution coker matrix{{a^2-b^2,b^3-c^3,c^4}}, Degeneracy => true) + D = simplicialModule(G = freeResolution coker vars R, Degeneracy => true) + H = hashTable { 0 => map(D_0, C_0, 1), + 1 => map(D_1, C_1, {{1, 0, 0, 0}, {0, a, 0, 0}, {0, -b, b^2, 0}, {0, 0, -c^2, c^3}}), + 2 => map(D_2, C_2, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, a, 0, 0, 0, 0,0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0}, + {0, 0, 0, 0, -b, b^2, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3}}), + 3 => map(D_3, C_3, {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0}, + {0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, a, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, -b, b^2, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,-c^2, c^3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0,0, 0, 0, 0, 0, 0, -b, b^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0, 0, 0, -c^2, c^3, 0, 0, 0, 0, 0, 0,0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0,0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-a*c^2, a*c^3, 0, 0, 0, 0, 0, 0, 0,0}, + {0, 0, 0, 0, 0, 0,0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0, 0,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2,a*c^3, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, b*c^2, -b*c^3, b^2*c^3, 0, 0, 0, 0}, + {0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2, 0, 0, 0}, + {0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -a*c^2, a*c^3,0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b*c^2, -b*c^3, b^2*c^3, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, a*b^2*c^3}}) + } + f = map(D, C, H) + assert isWellDefined f + assert isHomogeneous f + assert(degree f == 0) + assert isSimplicialMorphism f + g = randomSimplicialMap(D,C); --outputs are large + normalize g + assert isWellDefined g + assert not isCommutative g + h = randomSimplicialMap(D,C, Cycle => true); + normalize h + assert isWellDefined h + assert isSimplicialMorphism h +/// + + +TEST /// +R = ZZ/101[a..d] + I = ideal(a^2, b^2, c^2) + J = I + ideal(a*b*c) + FI = simplicialModule(freeResolution I, Degeneracy => true) + FJ = simplicialModule(freeResolution J, Degeneracy => true) + f = randomSimplicialMap(FJ, FI, Cycle=>true) + source f + assert isWellDefined f + assert isSimplicialMorphism f + assert(source f == FI) + assert(target f == FJ) + kk = coker vars R + F = simplicialModule(freeResolution kk, Degeneracy => true) + assert(source dd^F == F) + assert(target dd^F == F) + assert(degree dd^F == -1) +/// + +TEST /// +R = ZZ/101[a..d]; + I = ideal(a^2, b^2, c^2) + FI = simplicialModule(freeResolution I, Degeneracy => true) + assert(degree dd^FI == -1) + assert(degree ss^FI == 1) +/// + + +TEST /// +S = ZZ/101[a,b,c,d]; + I = minors(2, matrix{{a,b,c},{b,c,d}}) + C = simplicialModule(freeResolution (S^1/I), Degeneracy => true) + assert isHomogeneous dd^C + f = randomSimplicialMap(C, C, Degree => -1) + assert isHomogeneous f + f = randomSimplicialMap(C, C, InternalDegree => 2) + phi = map(S, S, {1,b,c,d}) + D = phi C + dd^D + assert not isHomogeneous dd^D +/// + + +TEST /// +S = ZZ/101[a..c]; + C = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}, Degeneracy => true) + f = dd^C + f^2 + assert(source f == target f) + assert(degree f == -1) + assert(degree f^2 == -2) + g = randomSimplicialMap(C, C) + assert isWellDefined g^2 + assert isWellDefined g^3 + assert not(f^0 == id_(C[1])) + assert(g^0 == id_C) + h = randomSimplicialMap(C, C) + assert isWellDefined h^-1 + assert(h * h^-1 == id_C) + assert isWellDefined h^-4 + assert(h^-4 * h^4 == id_C) +/// + + +TEST /// +S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + f = id_C + assert(f == 1) + assert(0 * id_C == 0) + g = randomSimplicialMap(C, C) + h = inducedMap(coker g, target g) + assert(h == 0) + g = randomSimplicialMap(C, C, InternalDegree=>1, Cycle=>true) + h = inducedMap(coker g, target g) + assert(h != 1) + D = prune image g + p = D.cache.pruningMap + p == 1 + assert(coker p == 0 and ker p == 0) + assert(prune p == 1) +/// + + +TEST /// +S = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D = C ** C + isWellDefined D + f1 = prune randomSimplicialMap(D, C, Cycle => true, InternalDegree => 1); + g1 = prune normalize f1 + assert(isCommutative g1) + assert(isCommutative f1) + assert(degree f1 == 0) + --f2 = randomSimplicialMap(D, C, Cycle => true); + --isCommutative f2 + --assert(degree f2 == 0) + --assert isSimplicialMorphism f2 +/// + +TEST /// +S = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D = C ** C + f1 = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 1); + assert isSimplicialMorphism f1 + assert(degree f1 == 0) +/// + +TEST /// +R = ZZ/101[a..d]; + I = ideal(c^2-b*d, b*c-a*d, b^2-a*c) + J = ideal(I_0, I_1) + C = simplicialModule(koszulComplex vars R, Degeneracy => true) + f = map(R^1/I, R^1/J, 1) + C ** f; + assert isSimplicialMorphism oo + f ** C; + assert isSimplicialMorphism oo + f' = random(R^2, R^{-1, -1, -1}) + C ** f'; + f' ** C; + assert isWellDefined(C ** f') + assert isWellDefined(f' ** C) + f'' = random(source f', R^{-2,-2}) + assert((C ** f') * (C ** f'') == C ** (f' * f'')) + assert(C ** id_(R^{-1,-2,-3}) == id_(C ** R^{-1,-2,-3})) +/// + +TEST /// +S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D = simplicialModule((freeResolution coker matrix{{a^2,a*b,b^3}})[-1], Degeneracy => true) + f = randomSimplicialMap(D,C) + E = simplicialModule((dual C.complex)[-3], Degeneracy => true) + F = simplicialModule((dual D.complex)[-3], 3, Degeneracy => true) + g = randomSimplicialMap(F,E) + h = f ** g; + assert isWellDefined h + assert(prune source h == C ** E) + assert(prune target h == D ** F) + fE = f ** E; + assert(fE == f ** id_E) + k = coker vars S + gk = g ** k; + D' = simplicialModule((freeResolution coker matrix{{a^2,a*b,c^3}})[-1], 3, Degeneracy => true) + f' = randomSimplicialMap(D', D) + assert((f' * f) ** g == (f' ** g) * (f ** id_E)) + assert((f' * f) ** g == (f' ** id_F) * (f ** g)) + F' = simplicialModule(dual (freeResolution coker matrix{{a^2,a*b,a*c,b^3}})[-3], Degeneracy => true) + g' = randomSimplicialMap(F', F) + assert(f ** (g' * g) == (f ** g') * (id_C ** g)) + assert(f ** (g' * g) == (id_D ** g') * (f ** g)) +/// + + +TEST /// +R = QQ[a,b,c]; + C = simplicialModule(freeResolution ideal(a*b, a*c, b*c), 3, Degeneracy => true) + D = simplicialModule((freeResolution ideal(a*b, a*c, b*c, a^2-b^2))[-1], 3, Degeneracy => true) + f = randomSimplicialMap(D,C, Cycle => true) + g = truncate(3,f); + assert isWellDefined g + assert (source g == truncate(3, source f)) + assert (target g == truncate(3, target f)) + assert(f == truncate(0, f)) + A = ZZ/101[x_0, x_1, y_0, y_1, y_2, Degrees => {2:{1,0}, 3:{0,1}}]; + I = intersect(ideal(x_0, x_1), ideal(y_0, y_1, y_2)) + C = simplicialModule(freeResolution I, 4, Degeneracy => true) + J = intersect(ideal(x_0^2, x_1^2), ideal(y_0^2, y_1^2, y_2^2)) + D = simplicialModule(freeResolution J, 4, Degeneracy => true) + f = simplicialModule(extend(C.complex, D.complex, id_(A^1)), 2) + g1 = prune truncate({{1,1}}, f); + g1_0 + g1_1 + g2 = truncate({{1,0}}, f); + g2_1 + g3 = truncate({{0,1}}, f); + g4 = truncate({{1,0},{0,1}}, f); + g4_1 + g5 = truncate({{2,2}}, f); + assert all({g1,g2,g3,g4,g5}, isWellDefined) +/// + +TEST /// +R = QQ[a,b,c,d]; + S = QQ[s,t]; + phi = map(S, R, {s, s+t, t, s-t}) + I = ideal(a*b, b*c, c*d) + J = I + ideal(a^2, b^2, c^2, d^2) + CI = simplicialModule(freeResolution I, 4, Degeneracy => true) + CJ = simplicialModule(freeResolution J, Degeneracy => true) + f = simplicialModule(extend(CJ.complex, CI.complex, map(CJ_0, CI_0, 1)), Degeneracy => true) + assert isWellDefined f + g = phi ** f + assert isWellDefined g + assert isWellDefined dd^(source g) + assert isWellDefined ss^(source g) + dd^(target g); + simplicialModule prune HH normalize g + assert isSimplicialMorphism oo +/// + +TEST /// +needsPackage "Truncations" + kk = ZZ/32003 + R = kk[a,b,c] + F = simplicialModule(freeResolution (ideal gens R)^2, 2, Degeneracy => true) + C1 = truncate(3, F); + prune normalize C1 + C2 = truncate(4, F); + prune normalize C2 + assert isWellDefined C1 + assert isWellDefined C2 + f = inducedMap(C1, C2); + prune normalize f + assert isWellDefined f + f1 = inducedMap(F, C1) + f2 = inducedMap(F, C2) + assert isWellDefined f1 + assert isWellDefined f2 + assert(f2 == f1 * f) +/// + + +TEST /// +R = ZZ/101[a..d]; + C = simplicialModule(freeResolution coker matrix{{a*b, a*c^2, b*c*d^3, a^3}}, Degeneracy => true) + D = simplicialModule(freeResolution coker matrix{{a*b, a*c^2, b*c*d^3, a^3, a*c*d}}, 3, Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true); + prune normalize f + g = randomSimplicialMap(D, C, Boundary => true); + prune normalize g + f+g; + assert isSimplicialMorphism oo + f-g; + assert isSimplicialMorphism oo + -f; + 3*f; + 0*f + a*f; + assert(0*f == 0) + assert(1*f == f) + assert((-1)*f == -f) + assert(-(f-g) == g-f) + assert((a+b)*f == a*f + b*f) + assert(a*(f+g) == a*f + a*g) + assert isSimplicialMorphism (f+g) + h = randomSimplicialMap(C, C); + prune normalize h + prune normalize(h+1) + assert(h+1 == h + id_C) + assert(h+a == h + a*id_C) + assert(1-h == id_C - h) + assert(b-c*h == -c*h + b*id_C) + assert(b-h*c == -h*c + id_C*b) +/// + + +TEST /// +R = ZZ/101[a..d]; + C1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], Degeneracy => true) + C2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, Degeneracy => true) + D = simplicialModule(freeResolution coker matrix{{a^2,b^2,c*d}}, Degeneracy => true) + f = randomSimplicialMap(D, C1) + g = randomSimplicialMap(D, C2) + h = f|g + assert isWellDefined h + assert(source h === source f ++ source g) + assert(target h === target f) +/// + +TEST /// +R = ZZ/101[a..d]; + D1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], Degeneracy => true) + D2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, Degeneracy => true) + C = simplicialModule(freeResolution coker matrix{{a^2,b^2,c*d}}, Degeneracy => true) + f = randomSimplicialMap(D1, C) + g = randomSimplicialMap(D2, C) + h = f||g + assert isWellDefined h + assert(target h === target f ++ target g) + assert(source h === source f) +/// + +TEST /// +R = ZZ/101[a..d]; + C1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], Degeneracy => true) + C2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, Degeneracy => true) + D1 = simplicialModule((freeResolution coker matrix{{a,b,c}}),2, Degeneracy => true) + D2 = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}[-1], 2, Degeneracy => true) + f = randomSimplicialMap(D1, C1, Cycle => true) + g = randomSimplicialMap(D2, C2, Cycle => true) + h = f ++ g + assert isWellDefined h + assert isSimplicialMorphism h + directSum(f, g, f[2]) + h2 = directSum(peanut => f, butter => g, jelly => f[2]) + indices h2 + h2_[butter,jelly] + assert(source oo == C2 ++ C1[2]) + assert(h_[0]^[0] == f) + assert(h_[1]^[1] == g) + assert(h_[0]^[1] == 0) + assert(h_[1]^[0] == 0) + assert(h_[0] == h * (C1 ++ C2)_[0]) + assert(h_[1] == h * (C1 ++ C2)_[1]) + assert(h^[0] == (D1 ++ D2)^[0] * h) + assert(h^[1] == (D1 ++ D2)^[1] * h) +/// + +TEST /// +S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d),3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 0) + assert isCommutative f + assert isWellDefined prune image f + prune normalize oo + i = inducedMap(forgetComplex target f, image f) + isSimplicialMorphism i + normalize i + g1 = inducedMap(target f, image f) + assert(ker g1 == 0) + assert(image g1 == image f) +/// + + +TEST /// +S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d), 3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 0) + assert isCommutative f + g1 = inducedMap(coimage f, source f) + assert(coimage g1 == coimage f) + assert(coker g1 == 0) +/// + +TEST /// +S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d), 3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Boundary => true, InternalDegree => 0) + assert isCommutative f + assert isWellDefined prune ker f + h1 = inducedMap(source f, ker f) + assert(ker f == image h1) + assert(ker h1 == 0) +/// + + +TEST /// +S = ZZ/101[a,b,c,d]; + C = simplicialModule(freeResolution ideal(b^2-a*c, b*c-a*d, c^2-b*d), 3, Degeneracy => true) + D = simplicialModule(freeResolution ideal(a,b,c), Degeneracy => true) + f = randomSimplicialMap(D, C, Cycle => true, InternalDegree => 0) + assert isCommutative f + assert isWellDefined prune coker f + prune normalize oo + prune HH coker f + g1 = inducedMap(coker f, target f) + assert(coker f == image g1) + assert(coker g1 == 0) +/// + + +TEST /// +R = ZZ/101[a..d]; + C1 = simplicialModule((freeResolution coker matrix{{a,b,c}})[1], 3, Degeneracy => true) + C2 = simplicialModule(freeResolution coker matrix{{a*b,a*c,b*c}}, 3, Degeneracy => true) + D1 = simplicialModule((freeResolution coker matrix{{a,b,c}}), Degeneracy => true) + D2 = simplicialModule(freeResolution coker matrix{{a^2, b^2, c^2}}[-1], 3, Degeneracy => true) + f = randomSimplicialMap(D1, C1, Cycle => true) + assert isCommutative f + g = randomSimplicialMap(D2, C2, Cycle => true) + assert isCommutative g + h = f ++ g; + assert(h_[0] == h * (C1 ++ C2)_[0]) + assert(h_[1] == h * (C1 ++ C2)_[1]) + assert(h^[0] == (D1 ++ D2)^[0] * h) + assert(h^[1] == (D1 ++ D2)^[1] * h) + assert(h_[0]^[0] == f) + assert(h_[1]^[1] == g) + assert(h_[0]^[1] == 0) + assert(h_[1]^[0] == 0) + h = (chicken => f) ++ (nuggets => g); + indices h + assert(h_[chicken]^[chicken] == f) + assert(h_[nuggets]^[nuggets] == g) +/// + + +TEST /// +S = ZZ/101[a..c] + C = simplicialModule(freeResolution coker matrix{{a*b, a*c, b*c}}, 3, Degeneracy => true) + D = simplicialModule(freeResolution coker vars S, Degeneracy => true) + f = randomSimplicialMap(D,C) + assert isWellDefined f + assert not isCommutative f + g = randomSimplicialMap(D,C, Cycle => true) + assert isWellDefined g + assert isCommutative g + assert isSimplicialMorphism g + h = randomSimplicialMap(D,C, Boundary => true) + assert isWellDefined h + assert isCommutative h + assert isSimplicialMorphism h + assert isNullHomotopic normalize h + nullHomotopy normalize h + p = randomSimplicialMap(D, C, Cycle => true, Degree => -1) + assert isWellDefined p + assert isCommutative p + assert isSimplicialMorphism p + q = randomSimplicialMap(D, C, Boundary => true, InternalDegree => 2); + assert isCommutative q + assert isSimplicialMorphism q + assert(source q == C) + assert(target q == D) + assert isNullHomotopic normalize q +/// + +TEST /// +R = ZZ/101[a,b,c]; + B = simplicialModule(freeResolution coker matrix{{a^2*b, a*b*c, c^3}}, Degeneracy => true) + C = simplicialModule(freeResolution coker vars R, 2, Degeneracy => true) + h = randomSimplicialMap(C, B, Cycle => true) + f = inducedMap(C, image h) + assert isCommutative f + g = inducedMap(coker h, C) + assert isCommutative g + assert isShortExactSequence(g,f) + I = ideal(a^3, b^3, c^3) + J = I + ideal(a*b*c) + K = I : ideal(a*b*c) + SES = complex{ + map(comodule J, comodule I, 1), + map(comodule I, (comodule K) ** R^{-3}, {{a*b*c}}) + } + assert isWellDefined SES + assert isShortExactSequence(dd^SES_1, dd^SES_2) + (g,f) = (horseshoeResolution SES)/simplicialModule; + assert isShortExactSequence(g,f) +/// + + +TEST /// +S = ZZ/101[a..d] + D = simplicialModule(S^1, 5) + D[-1] + oo.dd + D[-2] + C = simplicialModule(freeResolution coker vars S, Degeneracy => true) + dd^C_(3,0) + ss^C_(2,0) + D = C[1] + assert isWellDefined D + assert isWellDefined dd^D_(2,0) + ss^D_(1,0) + C2 = simplicialModule(freeResolution (S^1/(a^2, b^2, c^2, d^2)), Degeneracy => true) + C3 = simplicialModule(freeResolution (S^1/(a^2, b^3, c^4, d^5)), Degeneracy => true) + f2 = simplicialModule(extend(C.complex, C2.complex, map(C_0, C2_0, 1))); + f3 = simplicialModule(extend(C2.complex, C3.complex, map(C2_0, C3_0, 1))); + assert isWellDefined (f2[-1]) + assert isSimplicialMorphism (f2[-1]) + assert isSimplicialMorphism (f3[-2]) +/// + + +TEST /// +S = ZZ/101[a,b,c]; + C1 = simplicialModule(freeResolution coker vars S, Degeneracy => true) + D1 = C1 ++ simplicialModule(complex(S^13)[-2], 3, Degeneracy => true) + assert isWellDefined D1 + assert(D1.?ss) --knows to cache degeneracy maps if inputs have degeneracy maps + C2 = simplicialModule(ideal(a,b,c), 3, Degeneracy => true) + C1 ++ C2 + assert isWellDefined(C1 ++ C2) + C3 = directSum(C1,C2,C2[-2]) + assert isWellDefined C3 + C4 = directSum(first => C1, second => C2) + assert isCommutative C4_[first] -- inclusion map C1 --> C4 + assert isCommutative C4^[first] -- projection map C4 --> C1 + assert(C4^[first] * C4_[first] == 1) + assert(C4^[second] * C4_[second] == 1) + assert(C4^[first] * C4_[second] == 0) + assert(C4^[second] * C4_[first] == 0) + assert(C4_[first] * C4^[first] + C4_[second] * C4^[second] == 1) + assert isShortExactSequence(C4^[first], C4_[second]) + assert isShortExactSequence(C4^[second], C4_[first]) +/// + + +TEST /// +Q = ZZ/101[a..d] + K = koszulComplex vars Q + S = simplicialModule(K, Degeneracy => true) + nK = naiveNorm(S) + assert isWellDefined nK + prune HH nK +/// + +TEST /// +Q = QQ[x_1..x_3]; + K = koszulComplex vars Q; + assert isWellDefined simplicialModule(K, 4) --no degeneracy here + S = simplicialModule(K,4, Degeneracy => true) + S.dd**Q^1 + assert isWellDefined oo + assert isWellDefined S + Sdegen = simplicialModule(K,4,Degeneracy => true) + S.dd + assert Sdegen.?ss --this is the version that actually has degeneracy maps cached + simplicialModule(K, 5) + C = complex(Q^1, Base => 1) + simplicialModule(C, 3) + assert isWellDefined oo + simplicialModule(complex(Q^2, Base => 2), 6, Degeneracy => true) + assert isWellDefined oo --nice, now we can be confident the objects we are messing with actually make any sense + assert oo.?ss + assert isSimplicialMorphism id_S + phi = exteriorInclusion(S); + assert isWellDefined phi + assert isCommutative phi + assert isWellDefined prune phi + assert isCommutative prune phi + assert isWellDefined prune coker phi + K = koszulComplex {x_1,x_2} + Sn = simplicialModule(K, 4, Degeneracy => true) + phi = exteriorInclusion(K) + assert isCommutative phi + assert isWellDefined phi + assert isCommutative prune phi + assert isWellDefined prune phi + sphi = exteriorInclusion(Sn); + psphi = prune sphi; + assert isWellDefined (source psphi).cache.pruningMap + assert isCommutative (source psphi).cache.pruningMap + assert isSimplicialMorphism (source psphi).cache.pruningMap --pruningMaps are well-defined morphisms + S = simplicialModule(K,6) + + + p = randomComplexMap(K, K, Degree => 1, InternalDegree => 1) + sp = simplicialModule p + assert isWellDefined sp + assert not isCommutative sp + normalize(sp, CheckComplex => false) + prune oo ---reobtain p + p' = randomComplexMap(K, K, Degree => -1) + sp' = simplicialModule p' + assert isWellDefined sp' + assert not isCommutative sp' + normalize(sp', CheckComplex => false) + prune oo --reobtain p' + p = randomComplexMap(K, K, Degree => 1, Cycle => true, InternalDegree => 2) + sp = simplicialModule p + assert isWellDefined sp + assert isCommutative sp + normalize(sp, CheckComplex => false) + prune oo ---reobtain p + diffs = simplicialModule K.dd + isWellDefined diffs + assert isCommutative diffs + + assert(image(id_S) == S) + assert(simplicialModule(id_K, 6) == id_S) + assert isSimplicialMorphism id_S + simplicialModule(complex(Q^0), 6, Degeneracy => true) -- should be able to input 0 and get a well-defined output + assert isWellDefined oo + phi = randomSimplicialMap(S, S, Cycle => true, InternalDegree => 1) + assert isWellDefined phi + assert isCommutative phi + + bS = basis(0, forgetComplex S) + prune bS.dd + assert isWellDefined bS + assert isWellDefined prune bS + bid = basis(0, id_(forgetComplex S)) + assert isWellDefined bid + assert isCommutative bid + assert isWellDefined prune bid + assert isCommutative prune bid + + fS = forgetComplex S + assert not fS.?ss + + bS = basis(1, forgetComplex S) + assert isWellDefined bS + prune bS.dd + assert isWellDefined oo + --prune bS.ss + --assert isWellDefined oo + normalize bS + prune oo + prune basis(1, K) + + bS = basis(3, forgetComplex S) + assert isWellDefined bS + prune bS.dd; + assert isWellDefined oo + --prune bS.ss; + --assert isWellDefined oo + normalize bS + prune oo + prune basis(3, koszulComplex vars Q) + + tS = truncate(1, S) + assert isWellDefined tS + prune tS + assert isWellDefined prune tS + tS = truncate(1, fS) + assert isWellDefined tS + prune tS + assert isWellDefined prune tS + assert isCommutative truncate(2, id_S) + assert isCommutative truncate(2, id_fS) + + K = koszulComplex {x_1,x_2} + assert isWellDefined (Sc = schurMap({1,1}, K)) + prune extPower(2, K) + --assert isWellDefined prune schurMap({2,1}, K, TopDegree => 4) +/// + + +TEST /// +Q = ZZ/2[a,b] + K = koszulComplex vars Q; + F = freeResolution( (ideal vars Q)^2) + phi = extend(K, F, id_(K_0)) + f = elapsedTime prune schurMap({2}, phi) + assert isCommutative f + assert isWellDefined f + prune HH source f + prune HH target f + prune HH f +/// + + +-- commented out: this test takes ~6 minutes due to extPower/wedgeProduct at top degree 6 +-* +TEST /// +Q = ZZ/101[x_1,x_2]; + K1 = complex {matrix{{x_1}}}; + K2 = complex {matrix{{x_2}}}; + T1 = K1**K2 + T1.dd + T2 = prune simplicialTensor({K1,K2}) + T2.dd + phi1 = extend(T1,T2,id_(T1_0)) + phi2 = extend(T2,T1,id_(T1_0)) + assert(phi1*phi2 == id_T1) + assert isNullHomotopic(phi2*phi1 - id_T2) + Q = ZZ/101[a,b]; + K = koszulComplex vars Q + SK = simplicialModule(K,6) --want top degree 6 since the resulting complexes should have length 6 + w21K = extPower(2, SK) ** SK + w3K = extPower(3, SK) + assert isWellDefined w3K + H = hashTable for i from 0 to 6 list i => dual wedgeProduct(2,1, dual SK_i); + inclusion = map(w21K, w3K, H); + assert isWellDefined inclusion + assert isCommutative inclusion +/// +*- + +TEST /// +R = ZZ/101[x_1..x_3]; + K = koszulComplex vars R + S = simplicialModule(K,10, Degeneracy => true) + keys S + assert(K == normalize S) + Kn = normalize(S, CheckComplex => false) + Kn.dd + assert not(K == Kn) + assert(K == prune Kn) + Q = ZZ/3[a,b]; + K = koszulComplex vars Q + SK = simplicialModule(K,4) --want top degree 6 since the resulting complexes should have length 6 + w21K = extPower(2, SK) ** SK + w3K = extPower(3, SK) + H = hashTable for i from 0 to 6 list i => dual wedgeProduct(2,1, dual SK_i); + inclusion = map(w21K, w3K, H); + assert isWellDefined inclusion + assert isCommutative inclusion + Q = ZZ/2[a,b,c] + K = koszulComplex vars Q + phi = elapsedTime exteriorInclusion(K,3); --specify top degree 3 + assert isWellDefined phi + assert isCommutative phi +/// + +-- Tests for bugs fixed in the polishing session. +-- These exercise edge cases around non-free modules, quotient rings, +-- and consistency of face map source/target in the Dold-Kan construction. + +-- Test: isWellDefined and isSimplicialModule over a quotient ring +-- Exercises the mapsEq/mapIsId helpers that were added to handle +-- non-free modules where matrix(A)*matrix(B) != matrix(A*B). +TEST /// + R = ZZ/101[x,y]/(x^3, y^3); + C = simplicialModule(freeResolution(coker vars R, LengthLimit=>5), 5, Degeneracy => true); + assert isWellDefined C + assert isSimplicialModule C + f = id_C; + assert isWellDefined f + assert isSimplicialMorphism f + assert isCommutative f + assert(f == 1) +/// + +-- Test: prune on a simplicial module that retains .complex +-- This verifies the forgetComplex fix at the start of prune. +TEST /// + R = QQ[a,b,c]; + I = ideal(a*b, a*c, b*c); + C = simplicialModule(freeResolution I, 3, Degeneracy => true); + assert C.?complex + D = prune C; + assert isWellDefined D + g = D.cache.pruningMap; + assert isWellDefined g + assert isSimplicialMorphism g + assert(g * g^-1 == 1) + assert(g^-1 * g == 1) +/// + +-- Test: forgetComplex produces a well-defined simplicial module +-- and preserves the normalization. +TEST /// + R = ZZ/101[a,b,c]; + K = koszulComplex vars R; + S = simplicialModule(K, 4, Degeneracy => true); + assert S.?complex + fS = forgetComplex S; + assert(not fS.?complex) + assert isWellDefined fS + assert isSimplicialModule fS + assert(K == prune normalize fS) +/// + +-- Test: faceMap0Direct source/target consistency at topDegree >= 3 +-- Verifies that face maps at all levels have consistent source/target +-- for simplicial modules built from complexes of length >= 3. +TEST /// + R = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars R, 3); + assert isWellDefined C + -- Also test with the horseshoe resolution (produces length-3 complexes) + I = ideal(a^3, b^3, c^3); + J = I + ideal(a*b*c); + K = I : ideal(a*b*c); + SES = complex{ + map(comodule J, comodule I, 1), + map(comodule I, (comodule K) ** R^{-3}, {{a*b*c}}) + }; + (g0,f0) = horseshoeResolution SES; + sg = simplicialModule g0; + sf = simplicialModule f0; + assert isWellDefined(source sg) + assert isWellDefined(source sf) + assert isWellDefined(target sg) + assert isWellDefined(target sf) +/// + +-- Test: isShortExactSequence with image/kernel having +-- different generator representations (the degree-by-degree fix). +TEST /// + R = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars R, 2, Degeneracy => true); + B = simplicialModule(freeResolution coker matrix{{a^2*b, a*b*c, c^3}}, Degeneracy => true); + h = randomSimplicialMap(C, B, Cycle => true); + f = inducedMap(C, image h); + g = inducedMap(coker h, C); + assert isShortExactSequence(g, f) + -- Verify the components individually + assert(g*f == 0) + assert(kernel f == 0) + assert(coker g == 0) +/// + +-- Test: normalize on a direct sum built via forgetComplex +-- Exercises the forgetComplex map source/target rewriting. +TEST /// + R = ZZ/101[a,b,c]; + K = koszulComplex vars R; + S = simplicialModule(K, 3, Degeneracy => true); + fS = forgetComplex S; + D = fS ++ fS; + assert isWellDefined D + N = prune normalize D; + assert isWellDefined N + assert(N == K ++ K) +/// + +-- Test: truncate on a simplicial module over a multigraded ring +-- with the complex early-return path. +TEST /// + A = ZZ/101[x_0, x_1, y_0, y_1, y_2, Degrees => {2:{1,0}, 3:{0,1}}]; + I = intersect(ideal(x_0, x_1), ideal(y_0, y_1, y_2)); + C = simplicialModule(freeResolution I, 3, Degeneracy => true); + D = truncate({{1,1}}, C); + assert isWellDefined D + assert(C == truncate({{0,0}}, C)) +/// + +-- Test: composition of simplicial module maps where source/target +-- involve non-free modules (exercises the direct composition fix). +TEST /// + R = ZZ/101[a,b,c]; + C = simplicialModule(freeResolution coker vars R, 2, Degeneracy => true); + f = id_C; + g = id_C; + h = f * g; + assert(h == 1) + assert isWellDefined h + -- Also test composition of random cycle maps + D = simplicialModule(freeResolution coker matrix{{a^2,b^2,c^2}}, 2, Degeneracy => true); + f1 = randomSimplicialMap(D, C, Cycle => true); + f2 = randomSimplicialMap(C, D, Cycle => true); + h1 = f1 * f2; + assert isWellDefined h1 + assert isCommutative h1 +/// + +-- Test: RingMap applied to a non-free complex simplicial module +-- Exercises the tensor(phi, S.complex) fix. +TEST /// + R = ZZ/101[x,y,z]; + S = ZZ/101[a,b,c]; + phi = map(S, R, {a,b,c}); + I = ideal(x*y, x*z, y*z); + J = I + ideal(x^2, y^2); + g = inducedMap(module J, module I); + C = simplicialModule(complex {g}, 3, Degeneracy => true); + D = phi C; + assert isWellDefined D +/// + diff --git a/M2/Macaulay2/packages/SpaceCurves.m2 b/M2/Macaulay2/packages/SpaceCurves.m2 index e22e44b5811..130fcae5091 100644 --- a/M2/Macaulay2/packages/SpaceCurves.m2 +++ b/M2/Macaulay2/packages/SpaceCurves.m2 @@ -14,7 +14,7 @@ newPackage( }, Headline => "space curves", Keywords => {"Examples and Random Objects"}, - PackageImports => {"OldChainComplexes"}, + PackageImports => {"Complexes"}, DebuggingMode => false, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry", @@ -379,7 +379,7 @@ minimalCurveBetti Module := M -> ( continue ) ); - betti chainComplex {random(R^1,(target (Q.dd_3)^cols)**R^{-h}),(Q.dd_3)^cols**R^{-h}, + betti complex {random(R^1,(target (Q.dd_3)^cols)**R^{-h}),(Q.dd_3)^cols**R^{-h}, Q.dd_4**R^{-h}, Q.dd_5**R^{-h}} ) minimalCurveBetti Ideal := I -> minimalCurveBetti raoModule I @@ -566,7 +566,7 @@ curve (ZZ,ZZ,Ring) := (d,g,R) -> ( --generates a random curve of degree d and genus g in a given ring if 2*g == (d-1)*(d-2) then return ideal(random(1,R),random(d,R)); L := smoothDivisors(d,g,R); - if L != {} then return curve first random L + if L != {} then return curve randomElement L else print "No smooth curve with this degree and genus exists!"; ) curve (ZZ,ZZ) := (d,g) -> ( diff --git a/M2/Macaulay2/packages/SparseResultants.m2 b/M2/Macaulay2/packages/SparseResultants.m2 index feb9558d7d7..8f3de48cee5 100644 --- a/M2/Macaulay2/packages/SparseResultants.m2 +++ b/M2/Macaulay2/packages/SparseResultants.m2 @@ -449,7 +449,7 @@ dualizedChowForm (RingMap) := o -> (phi) -> ( kerPhi := kernel phi; if dim kerPhi =!= r+1 then error("hypothesis not satisfied by the set of monomials (the dimension of the associated toric variety is less than "|toString(r)|")"); mnr := o.AffineChartGrass; - if mnr === true then mnr = (random toList(0..n))_{0..r}; + if mnr === true then mnr = shuffle(toList(0..n), r + 1); try assert(ring matrix{mnr} === ZZ and min mnr >=0 and max mnr <=n and # unique mnr == r+1 and # mnr == r+1) else error("bad value for option AffineChartGrass: expected either 'true' or list of "|toString(r+1)|" distinct integers between 0 and "|toString(n)); mnr = sort mnr; x := local x; u := local u; diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds.m2 index 805bacbcd2d..422c7834d8e 100644 --- a/M2/Macaulay2/packages/SpecialFanoFourfolds.m2 +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds.m2 @@ -1,22 +1,21 @@ -* - Copyright 2020, Giovanni Staglianò. + Copyright 2020-2026, Giovanni Staglianò. You may redistribute this file under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of - the License, or any later version. + the License, or (at your option) any later version. *- -if version#"VERSION" < "1.21" then error "this package requires Macaulay2 version 1.21 or newer"; - newPackage( "SpecialFanoFourfolds", - Version => "2.7.1", - Date => "May 10, 2023", + Version => "2.8", + Date => "Apr 13, 2026", Authors => {{Name => "Giovanni Staglianò", Email => "giovanni.stagliano@unict.it" }}, Headline => "Hodge-special fourfolds", Keywords => {"Algebraic Geometry"}, - PackageImports => {"PrimaryDecomposition"}, + AuxiliaryFiles => true, + PackageImports => {"PrimaryDecomposition","TangentCone"}, PackageExports => {"MultiprojectiveVarieties"}, DebuggingMode => false, Reload => false, @@ -47,37 +46,42 @@ if MultiprojectiveVarieties.Options.Version < requiredMultiprojectiveVarietiesVe ); export{ - "HodgeSpecialFourfold", - "specialFourfold", - "SpecialCubicFourfold", - "specialCubicFourfold", - "SpecialGushelMukaiFourfold", - "specialGushelMukaiFourfold", - "CongruenceOfCurves", - "detectCongruence", - "ambientFivefold", - "surface", - "curve", - "NumNodes", - "InputCheck", - "parameterCount", - "normalSheaf", - "unirationalParametrization", - "toGrass", - "isAdmissible", - "isAdmissibleGM", - "mirrorFourfold", - "Singular", - "associatedK3surface", - "associatedCastelnuovoSurface", - "building", - "trisecantFlop", - "GMtables", - "fanoFourfold", - "parametrizeFanoFourfold", - "fromOrdinaryToGushel", - "IntersectionOfThreeQuadricsInP7", - "beauvilleMap" + "HodgeSpecialFourfold", + "specialFourfold", + "CubicFourfold", + "cubicFourfold", + "GushelMukaiFourfold", + "gushelMukaiFourfold", + "DoublySpecialCubicFourfold", + "IntersectionOfThreeQuadricsInP7", + "CongruenceOfCurves", + "detectCongruence", + "ambientFivefold", + "surface", + "surfaces", + "NumNodes", + "InputCheck", + "FanoMapType", + "parameterCount", + "normalSheaf", + "unirationalParametrization", + "toGrass", + "isAdmissible", + "isAdmissibleGM", + "mirrorFourfold", + "Singular", + "associatedK3surface", + "associatedCastelnuovoSurface", + "building", + "trisecantFlop", + "GMtables", + "fanoFourfold", + "parametrizeFanoFourfold", + "fromOrdinaryToGushel", + "beauvilleMap", + "polarizedK3surface", + "latticePolarization", + "swap" } needsPackage "IntegralClosure"; -- for method: normalization @@ -87,3855 +91,34 @@ needsPackage("RationalMaps",DebuggingMode=>false); -- for method: inverse3 debug SparseResultants debug MultiprojectiveVarieties ------------------------------------------------------------------------- ------------------------ Hodge-special fourfolds ------------------------ ------------------------------------------------------------------------- - -HodgeSpecialFourfold = new Type of EmbeddedProjectiveVariety; - -globalAssignment HodgeSpecialFourfold; - -HodgeSpecialFourfold.synonym = "Hodge-special fourfold"; - -specialFourfold = method(TypicalValue => HodgeSpecialFourfold, Options => {NumNodes => null, InputCheck => 1, Verbose => false}); - -specialFourfold (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X,V) -> ( - if not (ring ideal S === ring ideal X and ring ideal X === ring ideal V) then error "expected varieties in the same ambient space"; - if dim X != 4 then error "expected a fourfold"; - if not (dim V == 5 and isSubset(X,V)) then error "expected a fivefold containing the fourfold"; - if dim S != 2 then error "expected a surface"; - i := o.InputCheck; - if not(instance(i,ZZ) and i >= -1) then error("option InputCheck expects a nonnegative integer:"|newline|"0: no check is done about the smoothness of the fourfold and of the surface"|newline|"1: just the smoothness of the fourfold is checked"|newline|"2: both the smoothness of the fourfold and the smoothness of the surface are checked"); - if i >= 0 then ( - if not isSubset(S,X) then error "the given surface is not contained in the fourfold"; - if not isSmooth V then error "the ambient fivefold is not smooth"; - ); - if i >= 1 then if not isSmooth X then error "expected a smooth fourfold"; - if i >= 2 then ( - if not isSmooth S then error "expected a smooth surface"; - if o.Verbose then <<"-- smoothness of the surface verified (assuming equidimensionality)"<= 4 then ( - if S != top S then error "expected an irreducible reduced surface"; - if o.Verbose then <<"-- equidimensionality of the surface verified"< (S,X) -> ( - if ring ideal S =!= ring ideal X then error "expected varieties in the same ambient space"; - if dim S != 2 then error "expected a surface"; - if dim X == 4 and dim ambient X == 5 and degrees X == {({3},1)} and degree X == 3 then return specialCubicFourfold(S,X,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim X == 4 and dim ambient X == 8 and degrees X == {({2},6)} and degree X == 10 then return specialGushelMukaiFourfold(S,X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim X == 5 and (degrees X == {({2},2)} or degrees X == {({3},1)}) then return specialFourfold(S,X * random(2,S),X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim X != 4 then error "expected a fourfold"; - if dim ambient X == 7 and degrees X == {({2},3)} and degree X == 8 then return specialFourfold(S,X,random({{2},{2}},X),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim ambient X == 6 and degrees X == {({2},1),({3},1)} and degree X == 6 then return specialFourfold(S,X,random(3,X),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim ambient X == 5 and degrees X == {({4},1)} and degree X == 4 then return specialFourfold(S,X,ambient X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if o.InputCheck >= 0 then if not isSubset(S,X) then error "the given surface is not contained in fourfold"; - if o.InputCheck >= 1 then if not isSmooth X then error "expected a smooth fourfold"; - Fourfold := new HodgeSpecialFourfold from X; - if Fourfold#?"SurfaceContainedInTheFourfold" then Fourfold#"SurfaceContainedInTheFourfold" = prepend(S,Fourfold#"SurfaceContainedInTheFourfold") else Fourfold#"SurfaceContainedInTheFourfold" = {S}; - Fourfold -); - -specialFourfold (Ideal,Ideal) := o -> (idS,idX) -> specialFourfold(projectiveVariety idS,projectiveVariety idX,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialFourfold (Ideal,RingElement) := o -> (idS,C) -> specialFourfold(idS,ideal C,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialFourfold EmbeddedProjectiveVariety := o -> S -> ( - if dim S == 4 then ( - if S#?"SurfaceContainedInTheFourfold" then - return specialFourfold(first S#"SurfaceContainedInTheFourfold",S,InputCheck=>o.InputCheck,Verbose=>o.Verbose) - else - return specialFourfold(S * random({2:{1}},0_S),S,InputCheck=>o.InputCheck,Verbose=>o.Verbose) - ); - if dim S != 2 then error "expected a surface"; - if dim ambient S == 5 then return specialCubicFourfold(S,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim ambient S == 6 then return specialFourfold(S,random({{2},{3}},S),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim ambient S == 7 then return specialFourfold(S,random({{2},{2},{2}},S),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if dim ambient S == 8 or dim ambientVariety S == 5 or dim ambientVariety S == 6 then return specialGushelMukaiFourfold(S,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - error "no valid input provided"; -); - -specialFourfold Ideal := o -> idS -> specialFourfold(makeSubvariety idS,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialFourfold (String,Ring) := o -> (str,K) -> ( - try return specialGushelMukaiFourfold(str,K,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - try return specialCubicFourfold(str,K,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - local X; local S; - if str === "plane in PP^7" then ( - X = specialFourfold(surface({1},K,ambient=>7),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - assert(recognize X === "planeInPP7"); - return X; - ); - if str === "internal projection of K3 surface of genus 8" then ( - G := GG(K,1,5); - G = (parametrize random({6:{1}},0_G))^^ G; - X = specialFourfold((rationalMap point G) G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - assert(recognize X === "internal-projection-K3-genus-8"); - return X; - ); - error ///string may not be valid, permitted strings are: -*** cubic fourfolds *** -"quartic scroll", -"3-nodal septic scroll", -"one-nodal septic del Pezzo surface", -"6-nodal octic scroll", -"general cubic 4-fold of discriminant 32", -"general cubic 4-fold of discriminant 38", -"general cubic 4-fold of discriminant 42", -"8-nodal nonic scroll", -"general cubic 4-fold of discriminant 44", -"cubic 4-fold of discriminant 48" -*** GM fourfolds *** -"sigma-plane", -"rho-plane", -"tau-quadric", -"cubic scroll", -"quintic del Pezzo surface", -"K3 surface of genus 8 with class (9,5)", -"general GM 4-fold of discriminant 20", -"1","2",...,"21", -"nodal surface of degree 11 and genus 3 with class (7,4)", -"surface of degree 11 and genus 3 with class (7,4)", -"GM 4-fold of discriminant 26('')", -"GM 4-fold of discriminant 28", -"GM 4-fold of discriminant 34(')", -"triple projection of K3 surface of degree 26" -*** Complete intersections of 3 quadrics in PP^7 *** -"plane in PP^7", -"internal projection of K3 surface of genus 8"///; -); - -specialFourfold String := o -> str -> specialFourfold(str,ZZ/65521,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialFourfold (String,ZZ) := o -> (str,i) -> (prebuiltExamplesOfRationalFourfolds()) (str,i); - -expression HodgeSpecialFourfold := X -> ( - E := if dim ambient X == 4 - then "a " - else if degrees X == {({2},1),({3},1)} - then "complete intersection of type (2,3) in " - else if degrees X == {({4},1)} - then "quartic hypersurface in " - else "fourfold in "; - E = E|"PP^"|toString(dim ambient X)|" containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X); - expression E -); - -describe HodgeSpecialFourfold := X -> ( - S := surface X; - d := degree S; g := sectionalGenus S; - degs := flatten degrees ideal S; - if instance(X,IntersectionOfThreeQuadricsInP7) then recognize X; - descr := if dim ambient X == 4 - then "Four-dimensional projective space" - else if degrees X == {({2},3)} - then "Complete intersection of 3 quadrics in PP^7" - else if degrees X == {({2},1),({3},1)} - then "Complete intersection of type (2,3) in PP^6" - else if degrees X == {({4},1)} - then "Quartic hypersurface in PP^5" - else "Hodge-special fourfold in PP^"|toString(dim ambient X); - a := flatten degrees ideal X; r := codim X; - if r > 0 and #a == r then ( - disc := discriminant X; - A := X.cache#(S,"LatticeIntersectionMatrix"); - descr = descr|newline|toString("of discriminant "|(toString disc)|" = det"|(net A)); - ); - descr = descr|newline|"containing a "; - n := if S.cache#?"FiniteNumberOfNodes" or S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" or (S.cache#?"fitVariety" and (S.cache#"fitVariety").cache#?"nonSaturatedSingularLocus") then numberNodes S else -1; - descr = descr|(if n > 0 then toString(n)|"-nodal " else (if n == 0 then "smooth " else "")); - descr = descr|"surface of degree "|toString(d)|" and sectional genus "|toString(g)|newline; - descr = descr|(if # unique degs == 1 then "cut out by "|toString(#degs)|" hypersurfaces of degree "|toString(first degs) else "cut out by "|toString(#degs)|" hypersurfaces of degrees "|toString(toSequence degs)); - if instance(X,IntersectionOfThreeQuadricsInP7) then ( - if member(recognize X,{"surf-5-7-0-1","surf-5-10-1","internal-projection-K3-genus-8","surf-4-3-1-external","surf-5-6-2-nodal","surf-7-1-9"}) then descr = descr|newline|"(This is a rational fourfold discovered in August 2022)"; - if recognize X === "planeInPP7" then descr = descr|newline|"(This is a classical example of rational fourfold)"; - ); - net expression descr -); - -ambientFivefold = method(TypicalValue => EmbeddedProjectiveVariety); +load "./SpecialFanoFourfolds/HodgeSpecialFourfolds.m2"; -ambientFivefold HodgeSpecialFourfold := X -> ( - if X.cache#?"AmbientFivefold" then return X.cache#"AmbientFivefold"; - if codim X == 1 then return X.cache#"AmbientFivefold" = ambient X; - if instance(X,SpecialGushelMukaiFourfold) then return X.cache#"AmbientFivefold" = varietyDefinedBylinearSyzygies X; - error "no ambient fivefold found"; -); - -associatedMapFromFivefold = X -> ( - if (surface X).cache#?("AssociatedMapFromFivefold",ambientFivefold X) then return (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X); - if dim ambient X == 5 then return (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap(ideal surface X,degreeHypersurface X); - if degreeHypersurface X == 2 then ( - S := ideal surface X; - J1 := select(S_*,s -> degree s == {1}); - if #J1 > 0 then J1 = (ideal J1) * (ideal vars ring S) else J1 = ideal ring S; - J2 := select(S_*,s -> degree s == {2}); - if #J2 > 0 then J2 = ideal J2 else J2 = ideal ring S; - return (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap trim sub(J1+J2,ring ambientFivefold X); - ); - (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap(idealOfSubvariety((surface X)%(ambientFivefold X)),degreeHypersurface X) -); +load "./SpecialFanoFourfolds/CubicFourfolds.m2"; -map HodgeSpecialFourfold := o -> X -> associatedMapFromFivefold X; - -degreeHypersurface = method(); - -degreeHypersurface HodgeSpecialFourfold := X -> ( - if instance(X,SpecialCubicFourfold) then return 3; - if instance(X,SpecialGushelMukaiFourfold) or instance(X,IntersectionOfThreeQuadricsInP7) then return 2; - I := flatten degrees trim sub(ideal X,ring ambientFivefold X); - if #I != 1 then error "degree is not defined"; - first I -); +load "./SpecialFanoFourfolds/GushelMukai.m2"; -recognize = method(); +load "./SpecialFanoFourfolds/IntersectionOfThreeQuadrics.m2"; -recognize HodgeSpecialFourfold := X -> ( - if X.cache#?(surface X,"label") then return X.cache#(surface X,"label"); - if instance(X,SpecialCubicFourfold) then return X.cache#(surface X,"label") = recognizeCubicFourfold X; - if instance(X,SpecialGushelMukaiFourfold) then return X.cache#(surface X,"label") = recognizeGMFourfold X; - if instance(X,IntersectionOfThreeQuadricsInP7) then return X.cache#(surface X,"label") = recognize3QuadricsP7 X; - "NotRecognized" -); +load "./SpecialFanoFourfolds/DSCF.m2"; -parameterCount = method(Options => {Verbose => false}) +load "./SpecialFanoFourfolds/FanoMaps.m2"; -parameterCount (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X) -> ( - isSing := true; - if S.cache#?"isSmooth" then isSing = not isSmooth S else ( - if S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" then isSing = dim singLocus S >= 0 else ( - if S.cache#?"FiniteNumberOfNodes" then isSing = numberNodes S >= 1; - ); - ); - if ring ambient S =!= ring ambient X then error "expected varieties in the same ambient space"; - d := first first degrees ideal X; - c := codim X; - if not ({{d}} === unique degrees ideal X and c == # degrees ideal X) then error "the second argument must be a complete intersection of hypersurfaces of the same degree"; - r := dim S; - if (r <= 0) then error "the first argument must be a positive dimensional scheme"; - if not isSubset(S,X) then error "expected the first scheme to be a subscheme of the second one"; - if o.Verbose then <<"S: "|toString(? ideal S)< 1 and o.Verbose then <<"dim GG("|toString(c-1)|","|toString(m-1)|") = "|toString(M)< 1 then "dim GG("|toString(c-1)|","|toString(m-1)|")" else toString(m-1))|" = "|toString(h0N + M)<= "|toString(h0N + M - h0NX)< 1 then "dim GG("|toString(c-1)|",P(H^0(O_(P^"|toString(n)|")("|toString(d)|")))) = " else "dim P(H^0(O_(P^"|toString(n)|")("|toString(d)|"))) = ")|toString(c * (binomial(n+d,d) - c))< X -> ( - Y := ambientFivefold X; - S := surface X; - a := degreeHypersurface X; - if o.Verbose then <<"S: "|toString(? ideal S)<= "|toString(h0N + m-1 - h0NX)< F -> F.variety.cache#"embedded projective variety"; -CoherentSheafOnEmbeddedProjectiveVariety#{WebApp,AfterPrint} = CoherentSheafOnEmbeddedProjectiveVariety#{WebApp,AfterNoPrint} = -CoherentSheafOnEmbeddedProjectiveVariety#{Standard,AfterPrint} = CoherentSheafOnEmbeddedProjectiveVariety#{Standard,AfterNoPrint} = F -> (<< endl << concatenate(interpreterDepth:"o") << lineNumber << " : Coherent sheaf on " << projectiveVariety F << endl); +load "./SpecialFanoFourfolds/contractionMaps.m2"; -normalSheaf = method(TypicalValue => CoherentSheaf); -normalSheaf MultiprojectiveVariety := normalSheaf EmbeddedProjectiveVariety := X -> ( - if X.cache#?("normalSheaf",ambientVariety X) then return X.cache#("normalSheaf",ambientVariety X); - I := idealOfSubvariety X; - R := (ring I)/I; - N := sheaf Hom((module I) ** R,R); - if N.variety.ring =!= R then error "internal error encountered"; - N.variety.cache#"embedded projective variety" = X; - X.cache#("normalSheaf",ambientVariety X) = new CoherentSheafOnEmbeddedProjectiveVariety from N -); -normalSheaf (MultiprojectiveVariety,MultiprojectiveVariety) := normalSheaf (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := (X,Y) -> ( - Z := ambientVariety X; - N := normalSheaf makeSubvariety(X,Y); - makeSubvariety(X,Z); - N -); +load "./SpecialFanoFourfolds/mirrorFourfolds.m2"; -rankHH = method(); -rankHH (ZZ,CoherentSheafOnEmbeddedProjectiveVariety) := (i,F) -> ( - X := projectiveVariety F; - if X.cache#?("rank HH",i,F) then return X.cache#("rank HH",i,F); - X.cache#("rank HH",i,F) = rank HH^i F -); +load "./SpecialFanoFourfolds/LatticePolarizedK3.m2"; -unirationalParametrization = method(); - -unirationalParametrization HodgeSpecialFourfold := (cacheValue "unirationalParametrization") (X -> ( - error "not implemented yet"; -)); - -parametrize HodgeSpecialFourfold := X -> ( - if X.cache#?"rationalParametrization" then return X.cache#"rationalParametrization"; - Psi := fanoMap X; - X.cache#"rationalParametrization" = inverse3(Psi|X) -); - -surface = method(TypicalValue => EmbeddedProjectiveVariety); - -surface HodgeSpecialFourfold := X -> first X#"SurfaceContainedInTheFourfold"; - -surface (VisibleList,Ring,Option,Option) := (L,K,oN,oA) -> ( - oN = toList oN; oA = toList oA; - if first oN === ambient and first oA === NumNodes then (oN,oA) = (oA,oN); - if first oN =!= NumNodes and first oA =!= ambient then error "NumNodes and ambient are the only available options for surface(VisibleList)"; - if not (#L>0 and all(L,i->instance(i,ZZ) and i>=0)) then error "expected a list of nonnegative integers"; - P := for i from 1 to #L-1 list for j from 1 to L_i list (i,point PP_K^2); - B := if #L==1 or all(take(L,-(#L-1)),i->i==0) then 0_(PP_K^2) else ⋃ apply(flatten P,p -> p_0 * p_1); - f := rationalMap(B,L_0,Dominant=>true); - if last oN > 0 then f = f * rationalMap((linearSpan apply(last oN,i -> point linearSpan {f point source f,f point source f}))_(target f),Dominant=>true); - if last oA =!= null then f = rationalMap(f << PP_K^(last oA),Dominant=>true); - S := target f; - S.cache#"rationalParametrization" = f; - S.cache#"linear system on PP^2" = L; - S.cache#"points on PP^2" = P; - if dim linearSpan S >= 5 and last oN >= 0 then S.cache#"FiniteNumberOfNodes" = last oN; - S.cache#"takeCurve" = ((D,d) -> ( - if not(instance(D,ZZ) and D>0) then error "expected a positive integer for the degree of a plane curve"; - if not (instance(d,VisibleList) and #d==#L-1 and all(#d,i->instance(d_i,ZZ) and d_i>=0 and d_i<=L_(i+1))) - then error("expected a list of "|toString(#L-1)|" nonnegative integers representing a curve on the surface"|toString(L)); - local C; local pt; - if sum toList d == 0 then ( - pt = point PP_K^2; - C = random(D,pt); - ) else ( - pts := flatten for a to #d-1 list take(apply(P_a,last),d_a); - C = random(D,⋃ pts); - pt = first pts; - ); - if D == 2 then C.cache#"rationalParametrization" = inverse(rationalMap(pt%C,1),Verify=>true); - fC := f C; - makeSubvariety(fC,S,Verify=>true); - if not (dim fC == 1 and degree fC == D*L_0 - sum(#d,i->(i+1)*d_i)) then error "something went wrong when taking the curve"; - fC.cache#"plane representation" = (D,d); - if D <= 2 then fC.cache#"rationalParametrization" = check rationalMap((parametrize C)*f,fC); - return fC; - )); - return S; -); -surface (VisibleList,Ring,Option) := (L,K,opt) -> ( - o := first toList opt; - if o === ambient - then return surface(L,K,NumNodes=>0,opt) - else if o === NumNodes - then return surface(L,K,opt,ambient=>null); - error "NumNodes and ambient are the only available options for surface(VisibleList)"; -); -surface(VisibleList,Option,Option) := (L,opt1,opt2) -> surface(L,ZZ/65521,opt1,opt2); -surface(VisibleList,Option) := (L,opt) -> surface(L,ZZ/65521,opt); -surface (VisibleList,Ring) := (L,K) -> surface(L,K,NumNodes=>0,ambient=>null); -surface List := L -> surface(L,ZZ/65521,NumNodes=>0,ambient=>null); - ---------------------------------------------------------- -HodgeSpecialSurface = new Type of WeightedProjectiveVariety; -globalAssignment HodgeSpecialSurface; -HodgeSpecialSurface.synonym = "Hodge-special surface"; -surface (MultiprojectiveVariety,MultiprojectiveVariety) := (C,S) -> ( - if ring ideal C =!= ring ideal S then error "expected varieties in the same ambient space"; - if dim S != 2 then error "expected a surface"; - if abs dim C != 1 then error "expected a curve"; - if not isSubset(C,S) then error "the given curve is not contained in the surface"; - newS := new HodgeSpecialSurface of (class S) from S; - if newS#?"CurveContainedInTheSurface" then newS#"CurveContainedInTheSurface" = prepend(C,newS#"CurveContainedInTheSurface") else newS#"CurveContainedInTheSurface" = {C}; - if not newS.cache#?"hyperplane" then newS.cache#"hyperplane" = random(1,0_S); - newS -); -map (HodgeSpecialSurface,ZZ,ZZ) := o -> (S,a,b) -> ( - if S.cache#?("map",a,b) then return S.cache#("map",a,b); - H := S.cache#"hyperplane"; - C := first S#"CurveContainedInTheSurface"; - if a < 0 then error "expected a nonnegative integer"; - S.cache#("map",a,b) = if b >= 0 then mapDefinedByDivisor(S,{(H,a),(C,b)}) else rationalMap(C_S,a,-b) -); -HodgeSpecialSurface Sequence := (S,ab) -> ( - if not(#ab == 2 and instance(first ab,ZZ) and instance(last ab,ZZ)) then error "expected a sequence of two integers"; - (a,b) := ab; - image map(S,a,b) -); -map HodgeSpecialSurface := o -> S -> ( - if S.cache#?"doubleCover" then return S.cache#"doubleCover"; - S.cache#"doubleCover" = quadricFibration(map(S,1,0),Verify=>true) -); -curve = method(); -curve HodgeSpecialSurface := U -> first U#"CurveContainedInTheSurface"; -discriminant HodgeSpecialSurface := ZZ => o -> S -> ( - if S.cache#?(curve S,"discriminantSurface") then return last S.cache#(curve S,"discriminantSurface"); - if not member(o.Algorithm, {null, 1, 2}) then error "the Algorithm option accepts the values 1 and 2"; - C := curve S; - if dim C != 1 then error "expected a Hodge-special surface"; - if o.Algorithm === 2 then return discriminant2 S; - f := map(S,0,1); - if dim target f <= 0 then (if o.Algorithm === 1 then error "the option Algorithm=>1 is not available for this case" else return discriminant2 S); - C' := f^* random(1,0_(target f)); - if dim(C * C') != 0 then error "something went wrong :("; - C2 := degree(C * C'); - H := S.cache#"hyperplane"; - H2 := S * H * random(1,0_S); - if dim H2 != 0 then error "something went wrong :("; - H2 = degree H2; - HC := S * H * C; - if dim HC != 0 then error "something went wrong :("; - HC = degree HC; - disc := det(S.cache#(curve S,"LatticeIntersectionMatrix") = matrix {{H2,HC},{HC,C2}}); - S.cache#(curve S,"discriminantSurface") = (C2,disc); - disc -); -discriminant2 = method(); -discriminant2 HodgeSpecialSurface := S -> ( - if not (codim S == 1 and numgens ideal S == 1) then error "the option Algorithm=>2 is not available for this case"; - if S.cache#?(curve S,"discriminantSurface") then return last S.cache#(curve S,"discriminantSurface"); - C := curve S; - H := S.cache#"hyperplane"; - HC := H * C; - if dim HC != 0 then error "something went wrong :("; - HC = degree HC; - a := degree S; - g := sectionalGenus image segreEmbedding C; - e := sum degrees ring ambient S; - if #e != 1 then error "something went wrong :("; - C2 := (e_0 - a)*HC + 2*g -2; - H2 := S * H * random(1,0_S); - if dim H2 != 0 then error "something went wrong :("; - H2 = degree H2; - disc := det(S.cache#(curve S,"LatticeIntersectionMatrix") = matrix {{H2,HC},{HC,C2}}); - S.cache#(curve S,"discriminantSurface") = (C2,disc); - disc -); -HodgeSpecialSurface#{WebApp,AfterPrint} = HodgeSpecialSurface#{WebApp,AfterNoPrint} = -HodgeSpecialSurface#{Standard,AfterPrint} = HodgeSpecialSurface#{Standard,AfterNoPrint} = S -> ( - d := degree S; - str := "ProjectiveVariety, "|(if d <= 9 then ({"linear", "quadratic", "cubic", "quartic", "quintic", "sextic", "septic", "octic", "nonic"}_(d-1))|" surface" else "surface of degree "|toString(d)); - str = str|" in "|(toString expression ambient S); - try discriminant S; - if S.cache#?(curve S,"LatticeIntersectionMatrix") then ( - M := S.cache#(curve S,"LatticeIntersectionMatrix"); - str = (str|" with rank 2 lattice")||("defined by the intersection matrix "|net(M)|" (det: "|(toString discriminant S)|")"); - ); - << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << str << endl; -); -parameterCount HodgeSpecialSurface := o -> S -> ( - if not(codim S == 1 and numgens ideal S == 1) then error "expected a surface in a three-dimensional weighted projective space"; - a := first degree (ideal S)_0; - C := curve S; - if dim C != 1 then error "expected a Hodge-special surface"; - if o.Verbose then <<"C: "|toString(? C)<= "|toString(h0N + m-1 - h0NS)<0) -); - -random HodgeSpecialFourfold := o -> X -> ( - if instance(X,SpecialGushelMukaiFourfold) then ( - X' := specialGushelMukaiFourfold(surface X,random(2,surface X) * ambientFivefold X,InputCheck=>-1); - X'.cache#"AmbientFivefold" = ambientFivefold X; - return X'; - ); - if instance(X,SpecialCubicFourfold) then return specialCubicFourfold(surface X,InputCheck=>-1); - specialFourfold(surface X,random(degreeHypersurface X,surface X) * ambientFivefold X,ambientFivefold X,InputCheck=>-1) -); - -toExternalString HodgeSpecialFourfold := X -> ( - x := local x; - K := coefficientRing X; - n := dim ambient X; - R := K[x_0..x_n]; - s := ///needsPackage "SpecialFanoFourfolds";///|newline; - s = s|"(i -> (x := local x;"|newline; - s = s|"R := "|toExternalString(K)|"[x_0..x_"|toString(n)|"];"|newline; - s = s|"S := projectiveVariety "|toString sub(ideal surface X,vars R)|";"|newline; - if codim ambientFivefold X == 0 then s = s|"V := ambient S;"|newline else s = s|"V := projectiveVariety "|toString sub(ideal ambientFivefold X,vars R)|";"|newline; - s = s|"X := projectiveVariety "|toString sub(ideal X,vars R)|";"|newline; - if instance(X,SpecialCubicFourfold) - then s = s|"X = specialFourfold(S,X,NumNodes=>"|toString(numberNodes surface X)|",InputCheck=>0);"|newline else - if instance(X,SpecialGushelMukaiFourfold) - then s = s|"X = specialFourfold(S,X,InputCheck=>0);"|newline|///X.cache#"AmbientFivefold" = V;///|newline else - s = s|"X = specialFourfold(S,X,V,InputCheck=>0);"|newline; - if (surface X).cache#?"euler" then s = s|///(surface X).cache#"euler" = ///|toString(euler surface X)|";"|newline; - if (surface X).cache#?"FiniteNumberOfNodes" then s = s|///(surface X).cache#"FiniteNumberOfNodes" = ///|toString(numberNodes surface X)|";"|newline; - N := numgens target map X -1; - if N >= 6 and (map X)#"idealImage" =!= null then ( - z := local z; Z := K[z_0..z_N]; - phi := sub(map X,R,Z); - s = s|"z := local z; Z := "|toExternalString(K)|"[z_0..z_"|toString(N)|"];"|newline; - s = s|///(surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap(ring V,Z,///|(toString entries phi)|");"|newline; - s = s|"forceImage(map X,"|(toString image phi)|");"|newline; - ); - if (surface X).cache#?("fanoMap",ambientFivefold X) then ( - mu := fanoMap X; - m := numgens ambient target mu -1; - y := local y; - T := K[y_0..y_m]; - mu = sub(mu,R,T); - s = s|"y := local y; T := "|toExternalString(K)|"[y_0..y_"|toString(m)|"];"|newline; - if m > 4 then s = s|"T = T/"|toString ideal target mu|";"|newline; - s = s|"mu := rationalMap map(ring V,T,"|toString entries mu|");"|newline; - s = s|"forceImage(mu,ideal(0_(target mu)));"|newline; - s = s|///(mu#"map").cache#"multiplicityFanoMap" = ///|toString(((fanoMap X)#"map").cache#"multiplicityFanoMap")|";"|newline; - s = s|///(surface X).cache#("fanoMap",ambientFivefold X) = mu;///|newline; - if (surface X).cache#?("surfaceDeterminingInverseOfFanoMap",ideal X) then ( - U := surfaceDeterminingInverseOfFanoMap X; - s = s|"U := projectiveVariety("|toString sub(ideal U,vars T)|",Saturate=>false);"|newline; - s = s|///(surface X).cache#("surfaceDeterminingInverseOfFanoMap",ideal X) = U;///|newline; - if U.cache#?"exceptionalCurves" then ( - (L,C) := exceptionalCurves X; - s = s|"L := "|(if dim L >= 0 then "projectiveVariety("|toString sub(ideal L,vars T)|",Saturate=>false)" else "0_U")|";"|newline; - s = s|"C := "|(if dim C >= 0 then "projectiveVariety("|toString sub(ideal C,vars T)|",Saturate=>false)" else "0_U")|";"|newline; - s = s|///U.cache#"exceptionalCurves" = (L%U,C%U);///|newline; - ); - ); - ); - s = s|"X))()"; - return s; -); - ------------------------------------------------------------------------- ---------------------------- Cubic fourfolds ---------------------------- ------------------------------------------------------------------------- - -SpecialCubicFourfold = new Type of HodgeSpecialFourfold; - -globalAssignment SpecialCubicFourfold; - -SpecialCubicFourfold.synonym = "special cubic fourfold"; - -specialCubicFourfold = method(TypicalValue => SpecialCubicFourfold, Options => {NumNodes => null, InputCheck => 1, Verbose => false}); - -specialCubicFourfold (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X) -> ( - if ring ideal S =!= ring ideal X then error "expected varieties in the same ambient space"; - if not (dim ambient X == 5 and degrees X == {({3},1)}) then error "expected a cubic fourfold"; - if dim S != 2 then error "expected a surface"; - i := o.InputCheck; - if not(instance(i,ZZ) and i >= -1) then error("option InputCheck expects a nonnegative integer:"|newline|"0: no check is done about the smoothness of the fourfold and of the (normalization of the) surface"|newline|"1: just the smoothness of the fourfold is checked"|newline|"2: the smoothness of the fourfold and of a general hyperplane section of the surface are checked"|newline|"3: as above and furthermore the smoothness of the normalization of the surface is checked"); - if i >= 0 then if not isSubset(S,X) then error "the given surface is not contained in the cubic fourfold"; - if i >= 1 then if not isSmooth X then error "expected a smooth cubic fourfold"; - n := o.NumNodes; - if n === null then n = numberNodes(S,Verbose=>o.Verbose); - if not(instance(n,ZZ) and n >= 0) then error "option NumNodes expects a nonnegative integer or null"; - if S.cache#?"FiniteNumberOfNodes" then if n =!= S.cache#"FiniteNumberOfNodes" then error "the number of nodes is wrong"; - if i == 2 or (i >= 3 and n > 0) then ( - if not isSmooth(S * random(1,0_S)) then error "expected a surface with at most a finite number of nodes"; - if o.Verbose then <<"-- smoothness in codimension 1 of the surface verified"<= 3 then ( - if n > 0 then ( - q = normalization(S,Verbose=>o.Verbose); - if not isSmooth(Var source q) then error "expected a surface with smooth normalization"; - if o.Verbose then <<"-- smoothness of the normalization of the surface verified (assuming equidimensionality)"<o.Verbose) then error "the number of nodes is wrong"; - if o.Verbose then <<"-- number of nodes (partially) verified"<0)"; - if o.Verbose then <<"-- smoothness of the surface verified (assuming equidimensionality)"<= 4 then ( - if S != top S then error "expected an irreducible reduced surface"; - if o.Verbose then <<"-- equidimensionality of the surface verified"< (idS,idX) -> specialCubicFourfold(projectiveVariety idS,projectiveVariety idX,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialCubicFourfold (Ideal,RingElement) := o -> (idS,C) -> specialCubicFourfold(idS,ideal C,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialCubicFourfold EmbeddedProjectiveVariety := o -> S -> ( - if dim ambient S == 5 and codim S == 1 and degrees S === {({3},1)} then return specialCubicFourfold(S * random({2:{1}},0_S),S,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if not(dim ambient S == 5 and dim S == 2) then error "expected a surface in P^5"; - specialCubicFourfold(S,random({3},S),NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose) -); - -specialCubicFourfold Ideal := o -> idS -> specialCubicFourfold(projectiveVariety idS,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialCubicFourfold (String,Ring) := o -> (str,K) -> ( - if o.NumNodes =!= null then error "the option NumNodes is ignored, the number of nodes is determined automatically"; - local X; - if str === "quintic del Pezzo surface" then ( - X = specialCubicFourfold(surface({3,4},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "quinticDelPezzoSurface"; - return X; - ); - if str === "quartic scroll" then ( - X = specialCubicFourfold(surface({3,1,1},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "quarticScrollSurface"; - return X; - ); - if str === "general cubic 4-fold of discriminant 38" or str === "C38" then ( - X = specialCubicFourfold(surface({10,0,0,10},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "C38Coble"; - return X; - ); - if str === "6-nodal octic scroll" then ( - X = specialCubicFourfold("C38",K,InputCheck=>0,Verbose=>o.Verbose); - X = specialCubicFourfold(((top baseLocus fanoMap X) * X)\surface X,X,NumNodes=>6,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "6NodalOcticSrollC38"; - return X; - ); - if str === "3-nodal septic scroll" or str === "Farkas-Verra C26" then ( - t := gens ring PP_K^2; - f := multirationalMap rationalMap(ring PP_K^2,ring PP_K^8,{t_0^5, t_0^4*t_1, t_0^3*t_1^2, t_0^2*t_1^3, t_0^4*t_2, t_0^3*t_1*t_2, t_0^2*t_1^2*t_2, t_0*t_1^3*t_2, t_1^4*t_2}); - f = f * rationalMap linearSpan apply(3,i -> point linearSpan {f point source f,f point source f}); - X = specialCubicFourfold(image f,NumNodes=>3,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "FarkasVerra"; - return X; - ); - if str === "one-nodal septic del Pezzo surface" then ( - g := multirationalMap rationalMap(ring PP_K^2,{3,2}); - g = g * rationalMap(ring target g,ring PP_K^5,gens ideal linearSpan {point target g,point linearSpan {g point source g,g point source g}}); - X = specialCubicFourfold(image g,NumNodes=>1,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "oneNodalSepticDelPezzoSurfaceC26"; - return X; - ); - if str === "general cubic 4-fold of discriminant 42" or str === "C42" then ( - X = specialCubicFourfold(last last randomS42data(K),NumNodes=>5,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "C42"; - return X; - ); - if str === "cubic 4-fold of discriminant 48" or str === "C48" then ( - X = specialCubicFourfold(randomS48 K,NumNodes=>6,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "C48"; - return X; - ); - if str === "general cubic 4-fold of discriminant 32" or str === "C32" then ( - X = specialCubicFourfold(surface({9,1,4,6},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "C32"; - return X; - ); - if str === "general cubic 4-fold of discriminant 44" or str === "C44" then ( -- Enriques surface (see e.g. https://arxiv.org/pdf/1210.1903.pdf, p. 7) - J := Var ideal jacobian ideal discriminant first genericPolynomials({2,-1,-1,-1},K); - X = specialCubicFourfold((parametrize random({{1},{1},{1},{1}},0_J))^* J,NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "C44"; - return X; - ); - if str === "8-nodal nonic scroll" then ( - X = LaiFarkasVerraC42(K,NumNodes=>8,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "LaiFarkasVerraC42"; - (surface X).cache#"euler" = -44; - return X; - ); - error(///not valid string, permitted strings are: -"quintic del Pezzo surface", -"quartic scroll", -"3-nodal septic scroll", -"one-nodal septic del Pezzo surface", -"6-nodal octic scroll", -"general cubic 4-fold of discriminant 32", -"general cubic 4-fold of discriminant 38", -"general cubic 4-fold of discriminant 42", -"8-nodal nonic scroll", -"general cubic 4-fold of discriminant 44", -"cubic 4-fold of discriminant 48"///); -); - -specialCubicFourfold String := o -> str -> specialCubicFourfold(str,ZZ/65521,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -expression SpecialCubicFourfold := X -> expression("cubic fourfold containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X)); - -describe SpecialCubicFourfold := X -> ( - S := surface X; - d := degree S; g := sectionalGenus S; - degs := flatten degrees ideal S; - discrX := discriminant X; - descr:="Special cubic fourfold of discriminant "|toString(discrX)|newline|"containing a "; - n := numberNodes surface X; - descr = descr|(if n > 0 then toString(n)|"-nodal " else "(smooth) "); - descr = descr|"surface of degree "|toString(d)|" and sectional genus "|toString(g)|newline; - descr = descr|(if # unique degs == 1 then "cut out by "|toString(#degs)|" hypersurfaces of degree "|toString(first degs) else "cut out by "|toString(#degs)|" hypersurfaces of degrees "|toString(toSequence degs)); - if recognize X === "LaiFarkasVerraC42" then descr = descr|newline|"(the surface is the 8-nodal nonic scroll studied by K.-W. Lai, G. Farkas and A. Verra,"|newline|"this implementation is due to M. Hoff)"; - net expression descr -); - -map SpecialCubicFourfold := o -> X -> associatedMapFromFivefold X; - -recognizeCubicFourfold = X -> ( - S := surface X; - d := discriminant X; - e := eulerCharacteristic S; - n := numberNodes surface X; - invS := (degree S,sectionalGenus S,euler hilbertPolynomial S); - degs := flatten degrees ideal S; - if (d == 14 and e == 7 and n == 0 and invS === (5,1,1) and degs == toList(5:2)) then return "quinticDelPezzoSurface"; - if (d == 14 and e == 4 and n == 0 and invS === (4,0,1) and degs == toList(6:2)) then return "quarticScrollSurface"; - if (d == 32 and e == 14 and n == 0 and invS === (10,6,1) and degs == toList(10:3)) then return "C32"; - if (d == 38 and e == 13 and n == 0 and invS === (10,6,1) and degs == toList(10:3)) then return "C38Coble"; - if (d == 38 and e == -32 and n == 6 and invS === (8,0,-5) and degs == toList(10:3)) then return "6NodalOcticSrollC38"; - if (d == 44 and e == 12 and n == 0 and invS === (10,6,1) and degs == toList(10:3)) then return "C44"; - if (d == 26 and e == -14 and n == 3 and invS === (7,0,-2) and degs == toList(13:3)) then return "FarkasVerra"; - if (d == 26 and e == -1 and n == 1 and invS === (7,1,0) and degs == toList(14:3)) then return "oneNodalSepticDelPezzoSurfaceC26"; - if (d == 42 and e == -23 and n == 5 and invS === (9,2,-4) and degs == toList(9:3)) then return "C42"; - if (d == 48 and e == -29 and n == 6 and invS === (9,2,-5) and degs == {2,3,3,3,3}) then return "C48"; - if (d == 14 and e == 46 and n == 0 and invS === (13,12,4) and degs == toList(7:3)) then return "hyperplane section of a conic bundle over PP2"; - "NotRecognized" -); - -parameterCount SpecialCubicFourfold := o -> X -> parameterCount(surface X,X,Verbose=>o.Verbose); - -isAdmissible = method(); - -isAdmissible ZZ := d -> ( - if d <= 6 then return false; - if d % 2 != 0 then return false; - if d % 4 == 0 then return false; - if d % 9 == 0 then return false; - for p from 3 to floor(d/2) do if (p % 3 == 2 and isPrime p and d % p == 0) then return false; - if d % 6 != 0 and d % 6 != 2 then error toString d; - return true; -); - -isAdmissible SpecialCubicFourfold := X -> isAdmissible discriminant X; - -unirationalParametrization (SpecialCubicFourfold,EmbeddedProjectiveVariety) := (X,L) -> ( - if not(isSubset(L,X) and dim L == 1 and degree L == 1) then error "expected a line in the cubic fourfold"; - ringP5 := ring ambient X; - K := coefficientRing X; - l := toRationalMap parametrize L; - K' := frac(source l); - ringP5' := K'[gens ringP5]; - p' := trim minors(2,(vars ringP5')||(matrix l)); - X' := sub(ideal X,ringP5'); - TpX' := trim ideal((vars ringP5') * sub(jacobian X',apply(gens ringP5',flatten entries coefficients parametrize p',(x,s) -> x => s))); - U := TpX' + ideal(last gens ringP5'); - u := parametrize U; - u = rationalMap(Grass(0,3,K',Variable=>"s"),source u) * u; - e := lcm apply(flatten entries sub(last coefficients matrix u,K'),denominator); - M := transpose((matrix l)||(e * matrix u)); - ringP1xP3 := (source l) ** K[gens source u]; - M = sub(M,ringP1xP3); - r := local r; - Kr := ringP1xP3[r]; - P := first first entries gens sub(ideal X,apply(gens ringP5,flatten entries(submatrix(sub(M,Kr),{0}) + r*submatrix(sub(M,Kr),{1})),(v,v') -> v => v')); - psi := rationalMap(ringP1xP3,ringP5,transpose(coefficient(r^3,P) * submatrix(M,{0}) - coefficient(r^2,P) * submatrix(M,{1}))); - Psi := multirationalMap({parametrize psi},X); - if not isSubset(Psi point source Psi,X) then error "internal error encountered"; - Psi -); - -unirationalParametrization SpecialCubicFourfold := (cacheValue "unirationalParametrization") (X -> unirationalParametrization(X,line X)); - -randomS42data = method(); -randomS42data Ring := K -> ( - p := apply(5,i -> point PP_K^2); - f := (rationalMap(p_0 + p_2 + p_3 + p_4,2)) | (rationalMap(p_0 + p_1,2)); - s := multirationalMap segre target f; - f = multirationalMap segre f; - L := linearSpan {f point source f,f point source f}; - j := parametrize random(1,L); - pr := rationalMap point (j^^ L); - B := pr(j^^ (image s)); - C := pr(j^^ (image f)); - phi := toRationalMap rationalMap(C,Dominant=>true); - forceInverseMap(phi,inverseMap phi); - Bs := baseLocus inverse phi; - Q := 0_Bs; - while select(degrees ideal Q,d -> d <= {2}) =!= {{1},{1},{1},{2}} do ( - u := parametrize random({{1},{1}},0_Bs); - v := (rationalMap {random(1,ring source u),random(1,ring source u)})|(u^^ Bs); - Q = Q + u support v^*((v (u^^ Bs))\support(v (u^^ Bs))); - ); - Q = Var trim ideal select(flatten entries gens ideal Q,d -> degree d <= {2}); - if not (dim Q == 2 and degree Q == 2 and dim singularLocus Q == -1) then error "internal error encountered"; - P1xP2 := phi^* Q; - w := Var trim lift(phi (rationalMap flatten entries syz gens ideal P1xP2)^-1 (ideal submatrix'(vars ring ambient P1xP2,{0})),ambient target phi); - e := super inverse(rationalMap(w%Q),Verify=>false); - w = e point source e; - (L1,L2) := toSequence decompose(Q * tangentSpace(w,Q)); - D := phi^* L1; - if not(dim D == 2 and degree D == 5 and sectionalGenus D == 1) then D = phi^* L2; - if not(dim D == 2 and degree D == 5 and sectionalGenus D == 1) then error "internal error encountered"; - psi := rationalMap(B,3); - T := psi D; - if not(dim T == 2 and codim T == 6 and degree T == 9 and sectionalGenus T == 2) then error "internal error encountered"; - eta := rationalMap((SchubertCycle22OnLinearSectionOfG14 image psi)%(image psi)); - S42 := eta T; - if not(dim S42 == 2 and degree S42 == 9 and sectionalGenus S42 == 2 and genera ideal S42 == {-5,2,8} and degrees S42 == {({3},9)}) then error "internal error encountered"; - S42.cache#"euler" = -23; S42.cache#"FiniteNumberOfNodes" = 5; - T.cache#"euler" = 7; T.cache#"FiniteNumberOfNodes" = 0; - ((psi,D),(super inverse3 eta,S42)) -); - -randomS48 = method(); -randomS48 Ring := K -> ( - (psi,D) := first randomS42data K; - p := psi point source psi; - V := coneOfLines(image psi,p); - j := parametrize linearSpan V; - V' := j^* V; - p' := j^* p; - h := rationalMap p'_V'; - e := point image h; - P := j h^* ((coneOfLines(image h,e))\e); - assert(dim P == 2 and degree P == 1 and isSubset(P,image psi)); - S := (psi * rationalMap P) D; - if not(dim S == 2 and degree S == 9 and sectionalGenus S == 2 and genera ideal S == {-6,2,8} and degrees S == {({2}, 1),({3}, 4)}) then error "internal error encountered"; - return S; -); - -LaiFarkasVerraC42 = method(Options => options specialCubicFourfold); -LaiFarkasVerraC42 Ring := o -> K -> ( --- ** --- The code of this function has been written by Michael Hoff --- ** --- 24.10.2019 --- The construction and notation follow Farkas and Verra: --- "THE UNIRATIONALITY OF THE MODULI SPACE OF K3 SURFACES OF DEGREE 42" - y := local y; - P2 := K[y_0..y_2]; - p := ideal(y_0,y_1); - z := local z; - P4 := K[z_0..z_4]; - blowup := map(P2,P4, gens p *matrix basis(2,p)); -- linear system |2h-p| - F1 := kernel blowup; -- Hirzebruch surface F1 - l := ideal(random(P4^1,P4^{3:-1})); -- line in P4 - t := apply(8, i-> (preimage_blowup(ideal(random(P2^1,P2^{2:-1}))))); -- 8 points on F1 - spantl := apply(#t,i->gens intersect(t_i,l)*matrix basis(1,intersect(t_i,l))); -- linear span of l and t_i - points16 := intersect (points2 := apply(#spantl,i->(saturate((ideal(spantl_i)+F1),t_i)))); -- 16 residual points on F1 - assert((dim points16, degree points16, genus points16) == (1, 16, 15)); - -- see FV end of page 4 - RF1 := P4/F1; - z = gens RF1; - E := ideal(z_0,z_1,z_3); - pointsOnF1 := sub(points16,RF1); - I := intersect(pointsOnF1,E); - C := ideal(gens I * random(source gens I, RF1^{1:-3})); - C = saturate(sub(C,P4)+F1,sub(E,P4)); - assert((dim C, degree C, genus C) == (2, 8, 4)); - projl := map(P4,P2,gens l); - octic := preimage_projl(C); -- plane octic curve with 8 + 9 singular points - singOctic := saturate ideal singularLocus octic; - assert((dim singOctic, degree singOctic, genus singOctic) == (1, 17, 16)); - points8 := saturate(preimage_projl(points16)); - points9 := saturate(singOctic, points8); - -- blow up of 9 points - x := local x; - P5 := K[x_0..x_5]; - blowup2 := map(P2,P5,gens points9*matrix basis(4,points9)); - T := kernel blowup2; - -- betti res T - gamma := preimage_blowup2(octic); -- hyperelliptic curve of degree 14 and arithmetic genus 12 - -- betti res gamma - singGamma := preimage_blowup2(points8); - -- betti (resSingGamma = res singGamma) - -- I take the canonical map to compute pairs of points which are involuted. - w := local w; - P3 := K[w_0..w_3]; - canMap := map(P2,P3,gens singOctic*matrix basis(5,singOctic)); - twistedCubic := preimage_canMap(octic); - betti (resTwistedC := res twistedCubic); - assert((dim twistedCubic, degree twistedCubic, genus twistedCubic) == (2, 3, 0)); - M := resTwistedC.dd_2; - -- I take many lines and compute the ideal where the generators stabilizes - pNip := apply(40, i-> saturate(canMap(ideal(random(K)*M_{0} + random(K)*M_{1})),singOctic)); - L := apply(#pNip,i->(ideal(gens(preimage_blowup2(pNip#i)))_{0,1,2,3})); - betti (R := intersect(L)); - R = ideal( (gens R)_{0..5,6..8}); -- the nonic scroll with 8 singularities - -- betti res R - assert((dim R, degree R, genus R) == (3, 9, -8) and gamma + R == gamma); - if o.NumNodes =!= 8 then error "the number of nodes is equal to 8"; - specialCubicFourfold(R,NumNodes=>8,Verbose=>o.Verbose,InputCheck=>o.InputCheck) -); - ------------------------------------------------------------------------- ---------------------------- GM fourfolds ------------------------------- ------------------------------------------------------------------------- - -SpecialGushelMukaiFourfold = new Type of HodgeSpecialFourfold; - -globalAssignment SpecialGushelMukaiFourfold; - -SpecialGushelMukaiFourfold.synonym = "special Gushel-Mukai fourfold"; - -specialGushelMukaiFourfold = method(TypicalValue => SpecialGushelMukaiFourfold, Options => {InputCheck => 1, Verbose => false}); - -specialGushelMukaiFourfold (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X) -> ( - if ring ideal S =!= ring ideal X then error "expected varieties in the same ambient space"; - if not (dim ambient X == 8 and degrees X == {({2},6)} and codim X == 4 and degree X == 10 and sectionalGenus X == 6) then error "expected a 4-dimensional subvariety of PP^8 of degree 10 and sectional genus 6 cut out by 6 quadrics"; - if dim S != 2 then error "expected a surface"; - i := o.InputCheck; - if not(instance(i,ZZ) and i >= -1) then error("option InputCheck expects a nonnegative integer:"|newline|"0: no check is done about the smoothness of the fourfold and of the surface"|newline|"1: just the smoothness of the fourfold is checked"|newline|"2: both the smoothness of the fourfold and the smoothness of the surface are checked"); - if i >= 0 then if not isSubset(S,X) then error "the given surface is not contained in the fourfold"; - if i >= 1 then if not isSmooth X then error "expected a smooth GM fourfold"; - if i >= 2 then ( - if not isSmooth S then error "expected a smooth surface"; - if o.Verbose then <<"-- smoothness of the surface verified (assuming equidimensionality)"<= 4 then ( - if S != top S then error "expected an irreducible reduced surface"; - if o.Verbose then <<"-- equidimensionality of the surface verified"< (idS,idX) -> specialGushelMukaiFourfold(projectiveVariety idS,projectiveVariety idX,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialGushelMukaiFourfold EmbeddedProjectiveVariety := o -> S -> ( - if dim ambient S == 8 and codim S == 4 and degrees S === {({2},6)} and degree S == 10 then return specialGushelMukaiFourfold(S * random({2:{1}},0_S),S,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - Y := ambientVariety S; - er := "expected a surface in a GM fourfold or del Pezzo fivefold or del Pezzo sixfold"; - if dim S != 2 then error er; - if not((dim ambient Y == 9 and dim Y == 6 and degrees Y === {({2},5)}) or - (dim ambient Y == 8 and dim Y == 5 and degrees Y === {({2},5)}) or - (dim ambient Y == 8 and dim Y == 4 and degrees Y === {({2},6)}) - ) then error er; - if dim Y == 4 then return specialGushelMukaiFourfold(S,Y,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - local X; - if dim Y == 5 then ( - X = specialGushelMukaiFourfold(S,Y * random(2,S),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#"AmbientFivefold" = Y; - return X; - ); - if dim Y == 6 then ( - if dim linearSpan S > 8 then error "expected linear span of the surface to be of dimension at most 8"; - j := parametrize random(1,linearSpan S); - T := makeSubvariety(j^^ S,j^^ ambientVariety S); - if S.cache#?"FiniteNumberOfNodes" and (not T.cache#?"FiniteNumberOfNodes") then T.cache#"FiniteNumberOfNodes" = S.cache#"FiniteNumberOfNodes"; - if S.cache#?"top" and (not T.cache#?"top") then (T.cache#"top" = j^^(S.cache#"top"); if top T == T then T.cache#"top" = T); - if S.cache#?"singularLocus" and (not T.cache#?"singularLocus") then T.cache#"singularLocus" = j^^(S.cache#"singularLocus"); - if S.cache#?"euler" and (not T.cache#?"euler") then T.cache#"euler" = S.cache#"euler"; - X = specialGushelMukaiFourfold(T,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - if instance(Y,GrassmannianVariety) then ( - j = j||Y; - (source j).cache#"toGrass" = j; - try assert(ideal ambientFivefold X == ideal source j) else error "internal error encountered"; - X.cache#"AmbientFivefold" = source j; - ); - return X; - ); -); - -specialGushelMukaiFourfold Ideal := o -> I -> specialGushelMukaiFourfold(makeSubvariety I,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -specialGushelMukaiFourfold (String,Ring) := o -> (str,K) -> ( - G := GG(K,1,4); - local X; local S; - if str === "sigma-plane" then ( - X = specialGushelMukaiFourfold(schubertCycle({3,1},G),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 6; - return X; - ); - if str === "rho-plane" then ( - X = specialGushelMukaiFourfold(schubertCycle({2,2},G),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 9; - return X; - ); - if str === "tau-quadric" then ( - X = specialGushelMukaiFourfold((schubertCycle({1,1},G) * random({{1},{1}},0_G))%G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 1; - return X; - ); - if str === "cubic scroll" then ( - X = specialGushelMukaiFourfold((schubertCycle({2,0},G) * random({{1},{1}},0_G))%G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 7; - return X; - ); - if str === "quintic del Pezzo surface" then ( - X = specialGushelMukaiFourfold((G * random({4:{1}},0_G))%G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 4; - return X; - ); - if str === "K3 surface of genus 8 with class (9,5)" then ( - G15 := Grass replace(1,5,Grass ring G); - pr := rationalMap(G15,ring G,select(gens ambient G15,g -> last last baseName g != 5)); - X = specialGushelMukaiFourfold(pr sub(ideal for i to 5 list random(1,ambient G15),G15),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 3; - return X; - ); - if str === "general GM 4-fold of discriminant 20" then ( - (g,T) := first randomS42data(K); - X = specialGushelMukaiFourfold((g T)%image(g),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = 17; - return X; - ); - if #str >= 1 and #str <= 2 then ( - Vstr := value str; - if instance(Vstr,ZZ) and Vstr >= 1 and Vstr <= 21 then return fourfoldFromTriple(Vstr,GMtables(Vstr,K),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - ); - if str === "nodal surface of degree 11 and genus 3 with class (7,4)" then ( - X = specialGushelMukaiFourfold([4,5,1,0],[3,5,1,0],"cubic scroll",(1,K),InputCheck=>o.InputCheck,Verbose=>false); - (surface X).cache#"FiniteNumberOfNodes" = 1; - X.cache#(surface X,"label") = "mukai26''"; - return X; - ); - if str === "nodal D44" then ( - X = specialGushelMukaiFourfold([2,0,0,0],[1,0,0,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); - (surface X).cache#"FiniteNumberOfNodes" = 1; - X.cache#(surface X,"label") = "nodal D44"; - return X; - ); - if str === "GM 4-fold of discriminant 26('')" then ( - X = specialGushelMukaiFourfold([4,5,1,0],[2,3,0,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); - (surface X).cache#"FiniteNumberOfNodes" = 0; - X.cache#(surface X,"label") = "october2021-26''"; - return X; - ); - if str === "GM 4-fold of discriminant 28" then ( - X = specialGushelMukaiFourfold([6,4,6,0],[3,3,5,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); - (surface X).cache#"FiniteNumberOfNodes" = 0; - X.cache#(surface X,"label") = "october2021-28"; - return X; - ); - if str === "GM 4-fold of discriminant 34(')" then ( - X = specialGushelMukaiFourfold([6,4,6,0],[3,1,6,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); - (surface X).cache#"FiniteNumberOfNodes" = 0; - X.cache#(surface X,"label") = "october2021-34'"; - return X; - ); - if str === "triple projection of K3 surface of degree 26" then ( - X = specialGushelMukaiFourfold([4,5,1],[2,3,0],"cubic scroll",K,InputCheck=>0,Verbose=>false); - B := projectiveVariety matrix fanoMap X; - interpolateTop(B,{2},Verbose=>false,cache=>true,"Deep"=>2); - P := (B\top B)\top B; - f := rationalMap(P % ambientFivefold X); - S = (X * f^* f surface X)\(surface X); - if not(dim S == 2 and degree S == 17 and sectionalGenus S == 11) then error "something went wrong"; - X = specialGushelMukaiFourfold(S,X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "gushel26''"; - return X; - ); - if str === "surface of degree 11 and genus 3 with class (7,4)" then ( - X = specialGushelMukaiFourfold([4,5,1],[2,3,0],"cubic scroll",K,InputCheck=>0,Verbose=>false); - S = (X * ((fanoMap X)^* first decompose first exceptionalCurves X))\surface X; - if not(dim S == 2 and degree S == 11 and sectionalGenus S == 3 and degrees S == {({2},16)}) then error "something went wrong"; - S.cache#"FiniteNumberOfNodes" = 1; - X = specialGushelMukaiFourfold(S,X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = "mukai26''"; - return X; - ); - error(///not valid string, permitted strings are: -"sigma-plane", -"rho-plane", -"tau-quadric", -"cubic scroll", -"quintic del Pezzo surface", -"K3 surface of genus 8 with class (9,5)", -"general GM 4-fold of discriminant 20", -"1","2",...,"21", -"nodal surface of degree 11 and genus 3 with class (7,4)", -"surface of degree 11 and genus 3 with class (7,4)", -"GM 4-fold of discriminant 26('')", -"GM 4-fold of discriminant 28", -"GM 4-fold of discriminant 34(')", -"triple projection of K3 surface of degree 26"///); -); - -specialGushelMukaiFourfold String := o -> str -> specialGushelMukaiFourfold(str,ZZ/65521,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - -expression SpecialGushelMukaiFourfold := X -> expression("GM fourfold containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X)|(if X.cache#?(surface X,"classSurfaceInG14") then (" with class "|toString(first cycleClass X)) else "")); - -describe SpecialGushelMukaiFourfold := X -> ( - S := surface X; - d := degree S; g := sectionalGenus S; - degs := flatten degrees ideal S; - (cS,ab) := cycleClass X; - (a,b) := ab; - recognize X; - discrX := discriminant X; - descr:="Special Gushel-Mukai fourfold of discriminant "|toString(discrX); - if discrX % 8 == 2 then ( - if even(a+b) and odd(b) then - descr = descr|"(')" - else - if odd(a+b) and even(b) then - descr = descr|"('')" - else error "internal error encountered" - ); - descr = descr|newline|"containing a "; - n := if S.cache#?"FiniteNumberOfNodes" then numberNodes S else -1; - if n > 0 then descr = descr|toString(n)|"-nodal " else if n == 0 then descr = descr|"smooth "; - descr = descr|"surface in PP^8 of degree "|toString(d)|" and sectional genus "|toString(g)|newline; - descr = descr|(if # unique degs == 1 then "cut out by "|toString(#degs)|" hypersurfaces of degree "|toString(first degs) else "cut out by "|toString(#degs)|" hypersurfaces of degrees "|toString(toSequence degs)); - descr = descr|newline|"and with class in G(1,4) given by "|toString(cS); - if dim singLocus ambientFivefold X >= 0 then descr = descr|newline|"Type: Gushel (not ordinary)" else descr = descr|newline|"Type: ordinary"; - if instance(recognize X,ZZ) then descr = descr|newline|"(case "|toString(recognize X)|" of Table 1 in arXiv:2002.07026)"; - if recognize X === "gushel26''" then ( - if dim singLocus ambientFivefold X >= 0 - then descr = descr|newline|"(case considered in Section 1 of arXiv:2003.07809)" - else descr = descr|newline|"(case discovered in November 2021; see also Section 1 of arXiv:2003.07809)"; - ); - if recognize X === "mukai26''" and dim singLocus ambientFivefold X >= 0 then descr = descr|newline|"(case considered in Section 2 of arXiv:2003.07809)"; - if recognize X === "mukai26''" and dim singLocus ambientFivefold X == -1 then descr = descr|newline|"(case considered in Section 3 of arXiv:2003.07809)"; - if recognize X === "october2021-26''" or recognize X === "october2021-28" or recognize X === "october2021-28-2" or recognize X === "october2021-34'" then descr = descr|newline|"(case discovered in October 2021)"; - if recognize X === "october2021-20" then descr = descr|newline|"(case discovered in October 2021; see also Table 1 of arXiv:2003.07809)"; - if recognize X === "april2022-GM42''" then descr = descr|newline|"(strange example discovered in October 2021)"; - net expression descr -); - -cycleClass SpecialGushelMukaiFourfold := X -> ( - if X.cache#?(surface X,"classSurfaceInG14") then return X.cache#(surface X,"classSurfaceInG14"); - S := surface X; - j := toGrass X; - cS := cycleClass makeSubvariety(j S,target j); - ab := toSequence flatten entries lift(transpose last coefficients(cS,Monomials=>vars ring cS),ZZ); - X.cache#(surface X,"classSurfaceInG14") = (cS,ab) -); - -map SpecialGushelMukaiFourfold := o -> X -> associatedMapFromFivefold X; - -recognizeGMFourfold = X -> ( - S := surface X; - d := discriminant X; - e := eulerCharacteristic S; - (a,b) := last cycleClass X; - invS := (degree S,sectionalGenus S,euler hilbertPolynomial S); - degs := flatten degrees ideal S; - if (d == 10 and e == 4 and a == 1 and b == 1 and invS == (2,0,1) and degs == {1, 1, 1, 1, 1, 2}) then return 1; - if (d == 10 and e == 4 and a == 3 and b == 1 and invS == (4,0,1) and degs == {1, 1, 1, 2, 2, 2, 2, 2, 2}) then return 2; - if (d == 10 and e == 24 and a == 9 and b == 5 and invS == (14,8,2) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 3; - if (d == 10 and e == 7 and a == 3 and b == 2 and invS == (5,1,1) and degs == {1, 1, 1, 2, 2, 2, 2, 2}) then return 4; - if (d == 10 and e == 11 and a == 5 and b == 4 and invS == (9,3,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 5; - if (d == 10 and e == 3 and a == 1 and b == 0 and invS == (1,0,1) and degs == {1, 1, 1, 1, 1, 1}) then return 6; - if (d == 12 and e == 4 and a == 2 and b == 1 and invS == (3,0,1) and degs == {1, 1, 1, 1, 2, 2, 2}) then return 7; - if (d == 12 and e == 9 and a == 4 and b == 3 and invS == (7,2,1) and degs == {1, 1, 2, 2, 2, 2, 2, 2, 2, 2}) then return 8; - if (d == 12 and e == 3 and a == 0 and b == 1 and invS == (1,0,1) and degs == {1, 1, 1, 1, 1, 1}) then return 9; - if (d == 16 and e == 12 and a == 6 and b == 4 and invS == (10,4,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 10; - if (d == 16 and e == 10 and a == 6 and b == 4 and invS == (10,3,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 11; - if (d == 16 and e == 24 and a == 8 and b == 6 and invS == (14,8,2) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 12; - if (d == 18 and e == 13 and a == 7 and b == 5 and invS == (12,5,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 13; - if (d == 18 and e == 8 and a == 5 and b == 3 and invS == (8,2,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 14; - if (d == 18 and e == 10 and a == 5 and b == 4 and invS == (9,3,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 15; - if (d == 18 and e == 13 and a == 7 and b == 4 and invS == (11,5,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 16; - if (d == 20 and e == 7 and a == 6 and b == 3 and invS == (9,2,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 17; - if (d == 20 and e == 4 and a == 4 and b == 3 and invS == (7,0,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 18; - if (d == 24 and e == 9 and a == 6 and b == 4 and invS == (10,3,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 19; - if (d == 24 and e == 3 and a == 2 and b == 2 and invS == (4,0,1) and degs == {1, 1, 1, 2, 2, 2, 2, 2, 2}) then return 20; - if (d == 26 and e == 12 and a == 7 and b == 5 and invS == (12,5,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 21; - -- - if (d == 26 and e == 25 and a == 11 and b == 6 and invS == (17,11,2) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "gushel26''"; - if ((d == 18 or d == 26) and e == 3 and a == 7 and b == 4 and invS == (11,3,0) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then if numberNodes(S,Verbose=>false) == 1 and discriminant X == 26 then return "mukai26''"; - -- - if (d == 20 and e == 14 and a == 8 and b == 5 and invS == (13,6,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-20"; - if (d == 26 and e == 7 and a == 5 and b == 4 and invS == (9,2,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-26''"; - if (d == 28 and e == 13 and a == 8 and b == 5 and invS == (13,6,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-28"; - if (d == 28 and e == 10 and a == 6 and b == 5 and invS == (11,4,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-28-2"; - if (d == 34 and e == 13 and a == 9 and b == 5 and invS == (14,7,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3}) then return "october2021-34'"; - if (d == 34 and e == 7 and a == 9 and b == 6 and invS == (15,7,0) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3}) then if numberNodes(S,Verbose=>false) == 1 and discriminant X == 42 then return "april2022-GM42''"; - "NotRecognized" -); - -toGrass = method(TypicalValue => MultirationalMap) - -toGrass EmbeddedProjectiveVariety := (cacheValue "toGrass") (X -> ( - K := coefficientRing X; - Y6 := GG(K,1,4); - if dim X == 6 and dim ambient X == 9 and degrees X == {({2},5)} and degree X == 5 - then return (findIsomorphism(X,Y6,Verify=>true))||Y6; - if dim X == 5 and dim ambient X == 8 and degrees X == {({2},5)} and degree X == 5 - then ( - if dim singLocus X == -1 then return ((findIsomorphism(X,Y5 K,Verify=>true))||(Y5 K)) * (mapY5 K); - p := sum decompose singLocus X; - if not isPoint p then error "expected a point to be the vertex of a cone"; - p' := Var ideal submatrix'(vars ring ambient X,{0}); - f := inverse findIsomorphism(p,p',Verify=>true); - X' := f^^ X; - j := toGrass Var sub(ideal X',K[flatten entries submatrix'(vars ring ambient X,{0})]); - y0 := local y0; - coneG := quotient sub(ideal target j,K[y0,gens ring ambient target j]); - J := multirationalMap( - rationalMap(ring X,ring X',matrix first factor inverse f) * - rationalMap(ring X',coneG,matrix{{first gens ring ambient X}}|sub(lift(matrix first factor j,ring ambient source j),ring ambient X)) - ); - if not(source J == X and degree(J,Strategy=>"random point") == 1) then error "internal error encountered"; - return J; - ); - if dim X == 4 and dim ambient X == 7 and degrees X == {({2},5)} and degree X == 5 - then return ((findIsomorphism(X,Y4 K,Verify=>true))||(Y4 K)) * (mapY4 K); - if dim X == 4 and dim ambient X == 8 and degrees X == {({2},6)} and degree X == 10 - then ( - Y := varietyDefinedBylinearSyzygies X; - psi := toGrass Y; - if dim singLocus Y == -1 then return psi|X; - return psi * rationalMap(ring target psi,ring Y6,submatrix'(vars ring ambient target psi,{0})); - ); - error "expected a Gushel-Mukai fourfold, or a del Pezzo fourfold/fivefold/sixfold"; -)); - -toGrass SpecialGushelMukaiFourfold := (cacheValue "toGrass") (X -> ( -- for better documentation - Y := ambientFivefold X; - psi := toGrass Y; - if dim singLocus Y == -1 then return psi|X; - K := coefficientRing X; - check multirationalMap((psi|X) * rationalMap(ring target psi,ring GG(K,1,4),submatrix'(vars ring ambient target psi,{0})),GG(K,1,4)) -)); - -isAdmissibleGM = method(); - -isAdmissibleGM ZZ := d -> ( - if d <= 8 then return false; - if d % 8 == 0 then return false; - if d % 8 != 2 and d % 8 != 4 then return false; - for p from 3 to floor(d/2) do if ((p % 4 == 0 or p % 4 == 2 or p % 4 == 3) and isPrime p and d % p == 0) then return false; - return true; -); - -isAdmissibleGM SpecialGushelMukaiFourfold := X -> isAdmissibleGM discriminant X; - -parameterCount SpecialGushelMukaiFourfold := o -> X -> ( - S := surface X; - Y := ambientFivefold X; - if o.Verbose then <<"S: "|toString(? ideal S)<= "|toString(h0N + m-1 - h0NX)< ( - f := toRationalMap toGrass X; - H := image(f,1); - Q := ideal random(2,makeSubvariety image f); - S := trim(Q + tangentialChowForm(chowEquations(H_0),0,1)); - return trim lift(f^* S,ambient source f); -); - -unirationalParametrization SpecialGushelMukaiFourfold := (cacheValue "unirationalParametrization") (X -> ( - K := coefficientRing X; - S := sigmaQuadric X; - s := parametrize S; - s = rationalMap(Grass(0,2,K,Variable=>"u"),source s) * s; - K' := frac(K[gens source s]); - ringP8' := K'[gens ring ambient X]; - p' := trim minors(2,vars ringP8' || sub(matrix s,K')); - Y := ideal ambientFivefold X; - V := ideal coneOfLines(Var sub(Y,ringP8'),Var p'); - j := parametrize((ideal select(V_*,v -> degree v == {1})) + (ideal first gens ring V)); - W := trim (map j) V; - P := plucker(W,2); while dim P <= 0 do P = plucker(W,2); P = trim sub(plucker P,vars ring W); - Q := trim quotient(W,P); - q := trim minors(2,vars ring W || transpose submatrix(coefficients parametrize(P+Q),,{0})); - f := (inverse(rationalMap trim sub(q,quotient Q),Certify=>true)) * j; - ringP2xP2 := (source s) ** K[gens source f]; - K'' := frac(ringP2xP2); - ringP8'' := K''[gens ring ambient X]; - X'' := sub(ideal X,ringP8''); - p'' := sub(p',ringP8''); - ringP1'' := Grass(0,1,K'',Variable=>"v"); - l := rationalMap(ringP1'',ringP8'', (vars ringP1'') * (sub(matrix s,K'') || sub(matrix f,K''))); - e := parametrize trim quotient(trim (map l) X'',trim (map l) p''); - el := rationalMap((map e) * (map l)); - el = rationalMap(source el,target el,(lcm apply(flatten entries sub(last coefficients matrix el,K''),denominator)) * (matrix el)); - psi := rationalMap(ringP2xP2,ring ambient X,sub(transpose coefficients el,ringP2xP2)); - Psi := multirationalMap({parametrize psi},X); - if not isSubset(Psi point source Psi,X) then error "internal error encountered"; - Psi -)); - ------------------------------------------------------------------------- ------------ Complete intersections of three quadrics in PP^7 ----------- ------------------------------------------------------------------------- - -IntersectionOfThreeQuadricsInP7 = new Type of HodgeSpecialFourfold; - -globalAssignment IntersectionOfThreeQuadricsInP7; - -IntersectionOfThreeQuadricsInP7.synonym = "complete intersection of three quadrics in PP^7"; - -expression IntersectionOfThreeQuadricsInP7 := X -> expression("complete intersection of three quadrics in PP^7 containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X)); - -recognize3QuadricsP7 = X -> ( - S := surface X; - d := discriminant X; - e := eulerCharacteristic S; - invS := (degree S,sectionalGenus S,euler hilbertPolynomial S); - degs := flatten degrees ideal S; - if (d == 31 and e == 3 and invS === (1,0,1) and degs == toList(5:1)) then return "planeInPP7"; - if (d == 47 and e == 11 and invS === (9,3,1) and degs == toList(12:2)) then return "surf-5-7-0-1"; - if (d == 55 and e == 14 and invS === (11,5,1) and degs == toList(10:2)) then return "surf-5-10-1"; - if (d == 55 and e == 25 and invS === (13,8,2) and degs == toList(9:2)) then return "internal-projection-K3-genus-8"; - if (d == 79 and e == 7 and invS === (9,2,1) and degs == {2,2,2,2,2,2,2,2,2,2,3}) then return "surf-4-3-1-external"; - if ((d == 71 or d == 87) and e == 5 and invS === (11,4,0) and degs == toList(9:2)) then if numberNodes(S,Verbose=>false) == 1 and discriminant X == 87 then return "surf-5-6-2-nodal"; - if (d == 96 and e == 13 and invS === (12,6,1) and degs == {2,2,2,2,2,2,2,2,2,3}) then return "surf-7-1-9"; - "NotRecognized" -); - -infoAboutParameterCountInAmbientP7 = (x,y) -> ( - -------------------------------------------------- - -- Conversion between parameter counts - -- X : c. i. of 3 quadrics in PP^7; Y = ambientFivefold X; S = surface X; - -- Suppose we have computed: - -- h^1(N_{S,Y}) = 0, h^0(N_{S,Y}) = a - -- h^1(O_S(2)) = 0, and h^0(I_{S,Y}(2)) = b = h^0(O_Y(2)) - \chi(O_S(2)); - -- h^0(N_{S,X}) = c, dim P(H^0(O_Y(2))) = 33 - -- codim{[X] : S ⊂ X ⊂ Y} <= 33 - (a + (b-1) - c) - -- parameterCount(X,Y) ----> (34-a-b+c, (b, a, c)) - -- Now from the exact sequence 0 -> N_(S,Y) -> N_(S,PP^7) -> N_(Y,PP^7)|_S -> 0 - -- that is 0 -> N_(S,Y) -> N_(S,PP^7) -> O_S(2)++O_S(2) -> 0 - -- we deduce h^0(N_(S,PP^7)) = h^0(N_{S,Y}) + h^0(O_S(2)++O_S(2)) = a + 2*(34 - b) = 68 + a - 2b - -- Further, from the exact sequence 0 -> I_(Y,PP^7) -> I_(S,PP^7) -> I_(S,Y) -> 0 - -- we deduce h^0(I_{S,PP^7}(2)) = h^0(I_{S,Y}(2)) + 2 = b + 2 - -- from which we have dim GG(2, PP(h^0(I_{S,PP^7}(2)))) = 3 * (b-1) - -- Since dim GG(2,P(H^0(O_(P^7)(2)))) = 99 we obtain - -- 99 - ( (68 + a - 2b) + 3*(b-1) - c ) = 31 - a + 2b - 3b + 3 + c = 34 - a - b + c - -- Therefore parameterCount(X) ----> (34-a-b+c, (b+2, 68 + a - 2b , c)) - -------------------------------------------------- - (b,a,c) := y; - if 34-a-b+c != x then <<"--warning: something went wrong"< ( - R := ring ambient X; - l := projectiveVariety ideal submatrix'(vars R,{0,1}); - j := rationalMap(((line X) ===> l)|X,Dominant=>true); - X' := target j; - if not isSubset(l,X') then error "something went wrong"; - K := coefficientRing R; - x := local x; P7 := projectiveVariety(K[x_0..x_7]); - t := local t; P2 := projectiveVariety(K[t_0..t_2]); - u := local u; P5 := projectiveVariety(K[u_0..u_5]); - Q := flatten entries sub(gens ideal X',vars ring P7); - S := (K[x_2..x_7])[x_0,x_1]; - L := apply(3,i -> coefficient((x_0)_S,sub(Q_i,S))); - M := apply(3,i -> coefficient((x_1)_S,sub(Q_i,S))); - F := apply(3,i -> coefficient(1_S,sub(Q_i,S))); - if F != apply(3,i -> sub(Q_i,S) - (x_0)_S * L_i - (x_1)_S * M_i) then error "something went wrong"; - P2xP5 := P2 ** P5; - A := sub(matrix {L,M,F},for i to 5 list (gens coefficientRing S)_i => u_i); - Z := projectiveVariety ideal det A; - Y := projectiveVariety ideal(((map last projections P2xP5) A) * transpose matrix {first P2xP5#"multigens"}); - fromXtoZ := j * rationalMap(ring X',ring P5,submatrix'(vars R,{0,1})); - if image fromXtoZ != Z then error "something went wrong"; - fromXtoZ = rationalMap(fromXtoZ,Z); - fromYtoZ := (last projections P2xP5)|Y; - if image fromYtoZ != Z then error "something went wrong"; - fromYtoZ = rationalMap(fromYtoZ,Z); - Y.cache#"projection maps" = {quadricFibration first projectionMaps Y, fromYtoZ}; - f := fromXtoZ * inverse fromYtoZ; - g := fromYtoZ * inverse fromXtoZ; - if not(f * g == 1 and g * f == 1) then error "something went wrong"; - forceInverseMap(f,g); - f -); - ------------------------------------------------------------------------- ------------------------------- Fano maps ------------------------------- ------------------------------------------------------------------------- - -fanoMap = method(Options => {Singular => null, RaiseError => true, Verbose => false}); - -fanoMap HodgeSpecialFourfold := o -> X -> ( - if (surface X).cache#?("fanoMap",ambientFivefold X) then return (surface X).cache#("fanoMap",ambientFivefold X); - if (surface X).cache#?("fanoMap",ambient X) and dim ambient X >= 5 then ( - Mu := (surface X).cache#("fanoMap",ambient X); - mu := toRationalMap(Mu|(ambientFivefold X)); - (mu#"map").cache#"multiplicityFanoMap" = (Mu#"map").cache#"multiplicityFanoMap"; - if mu#"idealImage" === null then forceImage(mu,ideal(0_(target mu))); -- - (surface X).cache#("fanoMap",ambientFivefold X) = mu; - return mu - ); - local e; sat := 1; - if member(recognize X,{"quinticDelPezzoSurface","quarticScrollSurface",1,6,"planeInPP7"}) - then e = 1 - else if member(recognize X,{"C38Coble","FarkasVerra","oneNodalSepticDelPezzoSurfaceC26",17,18,"october2021-26''"}) - then e = 2 - else if member(recognize X,{"C42","6NodalOcticSrollC38",3,"october2021-34'","mukai26''","october2021-20","surf-5-7-0-1"}) - then e = 3 - else if member(recognize X,{"surf-5-10-1","surf-7-1-9"}) - then e = 4 - else if member(recognize X,{"gushel26''","internal-projection-K3-genus-8", "hyperplane section of a conic bundle over PP2", "surf-4-3-1-external"}) - then e = 5 - else if member(recognize X,{"surf-5-6-2-nodal"}) - then e = 6 - else e = (detectCongruence(X,Verbose=>o.Verbose))#"degree"; - if o.Verbose then <<"-- detected degree of the curves of the congruence: "<o.Singular,RaiseError=>o.RaiseError,Verbose=>o.Verbose) -); - -fanoMap (HodgeSpecialFourfold,ZZ) := o -> (X,e) -> ( - sat := 1; - if member(recognize X,{"october2021-34'","gushel26''","surf-7-1-9"}) then sat = 2; - fanoMap(X,e,sat,Singular=>o.Singular,RaiseError=>o.RaiseError,Verbose=>o.Verbose) -); - -imageOfFanoMap = method(Options => {RaiseError => true, Verbose => false}); -imageOfFanoMap (HodgeSpecialFourfold,RationalMap) := o -> (X,mu) -> ( - M := mu#"dimAmbientSource"; - if M =!= mu#"dimSource" then error "expected target of map to be a projective space"; - if M <= 3 then error "Fano map not valid"; - if mu#"idealImage" =!= null then return image mu; - if o.Verbose then <<"-- computing/forcing image of map to PP^"<o.Verbose) - else if member(recognize X,{3, "internal-projection-K3-genus-8", "october2021-34'"}) - then interpolateImage(mu,{3},3,Verbose=>o.Verbose) - else if recognize X === "hyperplane section of a conic bundle over PP2" - then interpolateImage(mu,{2,2,2},2,Verbose=>o.Verbose) - else if M == 4 - then forceImage(mu,ideal(0_(target mu))) - else if M == 5 - then (try interpolateImage(mu,{2},2,Verbose=>o.Verbose) else try interpolateImage(mu,{3},3,Verbose=>o.Verbose)) - else if M == 6 - then (try interpolateImage(mu,{2,2},2,Verbose=>o.Verbose)) - else if M == 7 - then (try interpolateImage(mu,{2,2,2,2,2},2,Verbose=>o.Verbose) else try interpolateImage(mu,{2,2,2},2,Verbose=>o.Verbose)) - else if member(M,{8,9,10,11,12}) - then (try interpolateImage(mu,toList(binomial(M-4,2) : 2),2,Verbose=>o.Verbose)); - if mu#"idealImage" =!= null then ( - if dim image mu != 5 then error "something went wrong; the image of the Fano map is not of dimension 4"; - return image mu; - ); - if o.RaiseError then error "not implemented yet: image of Fano map for unrecognized fourfold"; -); - -fanoMap (HodgeSpecialFourfold,ZZ,ZZ) := o -> (X,e,sat) -> ( - if (surface X).cache#?("fanoMap",ambientFivefold X) then return (surface X).cache#("fanoMap",ambientFivefold X); - P := o.Singular; - if P === null then ( - P = findProgram("Singular","Singular --help",RaiseError=>false) =!= null; - if o.Verbose then P = P and findProgram("xterm","xterm -version",RaiseError=>false) =!= null; - ); - if not instance(P,Boolean) then error "option Singular expects true or false"; - if e == 1 then sat = 0; - local mu; - if P then ( - mu = fanoMapUsingSingular(X,e,sat,Verbose=>o.Verbose); - ) else ( - S := idealOfSubvariety((surface X)%(ambientFivefold X)); - a := degreeHypersurface X; - if o.Verbose then <<"-- computing power of ideal"< degree i <= {a*e-1}); - ); - W := imageOfFanoMap(X,mu,RaiseError=>o.RaiseError,Verbose=>o.Verbose); - if W =!= null then ( - mu = rationalMap(mu,Dominant=>true); - (mu#"map").cache#"multiplicityFanoMap" = e; - (surface X).cache#("fanoMap",ambientFivefold X) = mu; - ) else ( - <<"--error: not implemented yet: image of Fano map for unrecognized fourfold"< {Verbose => false}); -fanoMapUsingSingular (HodgeSpecialFourfold,ZZ,ZZ) := o -> (X,e,sat) -> ( - Singular := findProgram("Singular", "Singular --help"); - if o.Verbose then XTermSingular := findProgram("xterm", "xterm -version"); -- using xterm we see Singular messages instantly and not at the end of the calculation - V := ambientFivefold X; - S := surface X; - x := local x; - K := coefficientRing X; - if not (if char K == 0 then K === QQ else K === ZZ/(char K)) then error "expected coefficient ring of the form ZZ/p or QQ"; - R := Grass(0,dim ambient X,K,Variable=>x); - idV := sub(ideal V,vars R); - R = R/idV; - I := sub(idealOfSubvariety(S%V),vars R); - dir := temporaryFileName() | "/"; - mkdir dir; - if o.Verbose then <<"-- writing Singular code on file: "<<(dir|"input.singular")<=1; i=i-1) {if (deg(Q[i]) <= "<dir,RaiseError=>true,Verbose=>o.Verbose,KeepFiles=>false) - else runProgram(Singular,"-qc 'execute(read(\"input.singular\"));'",RunDirectory=>dir,RaiseError=>true,Verbose=>o.Verbose,KeepFiles=>false); - Out := "x := local x; x = gens Grass(0,"|toString(dim ambient X)|","|(toExternalString K)|",Variable=>x);" | newline | get(dir|"output.singular") | newline | "OUTPUTVALUE" | newline; - D := value Out; - D = if ring D === ZZ then sub(D,ring V) else sub(D,vars ring V); - rationalMap gens trim D -); - ------------------------------------------------------------------------- ------------------------------ Congruences ------------------------------ ------------------------------------------------------------------------- - -CongruenceOfCurves = new Type of HashTable; - -globalAssignment CongruenceOfCurves; - -CongruenceOfCurves.synonym = "congruence of curves"; - -detectCongruence = method(TypicalValue => CongruenceOfCurves, Options => {Verbose => false}); - -detectCongruence (SpecialCubicFourfold,ZZ) := o -> (X,e) -> congruenceOfCurves(X,e); - -detectCongruence (SpecialGushelMukaiFourfold,ZZ) := o -> (X,e) -> congruenceOfCurves(X,e); - -detectCongruence (HodgeSpecialFourfold,ZZ) := o -> (X,e) -> congruenceOfCurves(X,e); - -detectCongruenceInt = method(Options => {Verbose => false}); -detectCongruenceInt (EmbeddedProjectiveVariety,HodgeSpecialFourfold) := o -> (p,X) -> ( - a := degreeHypersurface X; - if not(isPoint p and ring ambient p === ring ambient X and isSubset(p,ambientFivefold X)) then error "expected a point in the ambient fivefold"; - phi := map X; - imageOfAssociatedMap X; -- image of phi - S := surface X; - secants := new MutableList from {null,0,0,0,0,0,0}; - phip := phi p; - E := coneOfLines(Var image phi,phip); - if dim E == 0 then ( - if o.Verbose then <<"no ("<1); - if not(dim E' == 0 and degree E' == degree E) then error "something went wrong"; - local n; local C; - P := apply(decompose E',q -> (assert(dim q == 0); C = phi^* pr'^* q; assert(dim C == 1 and dim(C*S) == 0); (degree q,C))); - for nC in P do ( - (n,C) = nC; - for e from 1 to 6 do - if degree(C) == n*e and degree(C*S) == n*(a*e-1) and all(delete(e,toList(1..6)),e' -> not(degree(C) == n*e' and degree(C*S) == n*(a*e'-1))) - then secants#e = secants#e + n; - ); - secants = toList take(secants,-(#secants-1)); - if sum secants =!= degree E then <<"--warning: something went wrong: "< a*(e+1)-1),(s,t) -> "number "|toString(t)|"-secant "|s); - if o.Verbose then for e to #secants-1 do if secants_e != 0 then < X -> detectCongruenceInt(point ambient X,X,Verbose=>o.Verbose); -detectCongruence SpecialGushelMukaiFourfold := o -> X -> detectCongruenceInt(pointOnLinearSectionOfG14 ambientFivefold X,X,Verbose=>o.Verbose); -detectCongruence HodgeSpecialFourfold := o -> X -> detectCongruenceInt(point ambientFivefold X,X,Verbose=>o.Verbose); - -congruenceOfCurves = method(); - -congruenceOfCurves (HodgeSpecialFourfold,ZZ) := (X,e) -> ( - a := degreeHypersurface X; - Y := ambientFivefold X; - phi := map X; - imageOfAssociatedMap X; -- image of phi - S := surface X; - f := method(); - f EmbeddedProjectiveVariety := p -> ( - if not isPoint p then error "expected a point"; - if not(ring ambient p === ring ambient Y and isSubset(p,Y)) then error "expected a point on the ambient fivefold containing the surface"; - phip := phi p; - E := coneOfLines(Var image phi,phip); - if dim E == 0 then error "no congruences detected"; - if dim E != 1 then error "expected cone of lines to be one dimensional"; - if degree E == 1 then ( - D := phi^* E; - if not(dim D == 1 and degree D == e and dim(D*S) == 0 and degree(D*S) == a*e-1) then error "no congruences detected"; - if sectionalGenus D != 0 then D = top D; - return makeSubvariety(D,Y); - ); - pr := rationalMap phip; - v := (rationalMap {random(1,ring target pr),random(1,ring target pr)}); - pr' := toRationalMap((pr * v)|E); - E' := Var kernel(map pr',SubringLimit=>1); - if not(dim E' == 0 and degree E' == degree E) then error "something went wrong"; - decE' := select(decompose E',q -> (dim q == 0 and degree q == 1)); - P := select(apply(decE',q -> phi^* pr'^* q),C -> (dim C == 1 and degree C == e and dim(C*S) == 0 and degree(C*S) == a*e-1)); - if #P != 1 then (if #P > 1 - then error "got more than one curve of the congruence that passes through the point" - else error "got no curve of the congruence that passes through the point"); - C := first P; - if sectionalGenus C != 0 then C = top C; - makeSubvariety(C,Y) - ); - try f (if instance(X,SpecialGushelMukaiFourfold) then pointOnLinearSectionOfG14 Y else point Y) else error "no congruences detected"; - new CongruenceOfCurves from { - "function" => f, - "fourfold" => X, - "degree" => e, - "string" => "of "|toString(a*e-1)|"-secant "|(if e == 1 then "lines" else if e == 2 then "conics" else if e == 3 then "cubic curves" else if e == 4 then "quartic curves" else if e == 5 then "quintic curves" else "curves of degree "|toString(e)) - } -); - -toString CongruenceOfCurves := net CongruenceOfCurves := f -> if hasAttribute(f,ReverseDictionary) then toString getAttribute(f,ReverseDictionary) else "a congruence "|f#"string"; -texMath CongruenceOfCurves := texMath @@ net; - -CongruenceOfCurves#{WebApp,AfterPrint} = CongruenceOfCurves#{WebApp,AfterNoPrint} = -CongruenceOfCurves#{Standard,AfterPrint} = CongruenceOfCurves#{Standard,AfterNoPrint} = f -> ( - S := surface f#"fourfold"; - Y := ambientFivefold f#"fourfold"; - e := f#"degree"; - << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << "Congruence " << f#"string" << " to "; - << if hasAttribute(S,ReverseDictionary) then toString getAttribute(S,ReverseDictionary) else "surface"; - <<" in "; - << if hasAttribute(Y,ReverseDictionary) then toString getAttribute(Y,ReverseDictionary) else (if codim Y > 0 then "a fivefold in PP^"|toString(dim ambient Y) else "PP^5"); - if S.cache#?("fanoMap",Y) and ((S.cache#("fanoMap",Y))#"map").cache#"multiplicityFanoMap" == e - then << "; parameter space: " << Var target S.cache#("fanoMap",Y); - << endl; -); - -CongruenceOfCurves EmbeddedProjectiveVariety := (f,p) -> f#"function" p; -CongruenceOfCurves Ideal := (f,Ip) -> idealOfSubvariety f projectiveVariety gens Ip; - -member(EmbeddedProjectiveVariety,CongruenceOfCurves) := (C,f) -> ( - L := (map f#"fourfold") C; - dim L == 1 and unique degrees ideal L === {{1}} -); - -map CongruenceOfCurves := o -> f -> ( - X := f#"fourfold"; - e := f#"degree"; - mu := fanoMap(X,e); - e' := (mu#"map").cache#"multiplicityFanoMap"; - if e' =!= e then error("the congruence seems not valid; try the command: detectCongruence(...,"|toString(e')|")"); - multirationalMap mu -); - -check (ZZ,CongruenceOfCurves) := o -> (n,f) -> ( - X := f#"fourfold"; - try for i to n-1 do f(point ambientFivefold X) else error "the congruence of curves is not valid"; - f -); -check CongruenceOfCurves := o -> f -> check(5,f); - ------------------------------------------------------------------------- ---------------------------- Discriminants ------------------------------ ------------------------------------------------------------------------- - -eulerCharacteristic = method(Options => {Algorithm => null}); - -eulerCharacteristic EmbeddedProjectiveVariety := o -> X -> ( - if X.cache#?"euler" then return X.cache#"euler"; - if codim linearSpan X > 0 then return X.cache#"euler" = eulerCharacteristic((parametrize linearSpan X)^^ X,Algorithm=>o.Algorithm); - if o.Algorithm === "Hodge" then return X.cache#"euler" = euler variety X; - if o.Algorithm === "CremonaCertifyTrue" then return euler(X,Verify=>true); - K := coefficientRing X; - if K === QQ then return X.cache#"euler" = eulerCharacteristic(X ** (ZZ/65521),Algorithm=>o.Algorithm); - if char K < 1000 and K === ZZ/(char K) then <<"--warning: base field too small to use probabilistic methods"<false); - if o.Algorithm === "CharacteristicClasses" then return X.cache#"euler" = Euler(ideal X,InputIsSmooth=>true); - if o.Algorithm === null then ( - X' := if max flatten degrees ideal X > 2 and dim X == 2 then isomorphicProjectionOfSurfaceInP5 X else X; - return X.cache#"euler" = if codim X' > 0 then Euler(ideal X',InputIsSmooth=>true) else euler(X',Verify=>false); - ); - error(///Algorithm option: Expected method to compute the topological Euler characteristic. -Possible methods are the following: -"Hodge" -- command: euler variety X -- package: Core; -"CremonaCertifyTrue" -- command: EulerCharacteristic(ideal X,Certify=>true) -- package: Cremona; -"CremonaCertifyFalse" -- command: EulerCharacteristic ideal X -- package: Cremona; -"CharacteristicClasses" -- command: Euler(ideal X,InputIsSmooth=>true) -- package: CharacteristicClasses///); -); - -isomorphicProjectionOfSurfaceInP5 = method(); -isomorphicProjectionOfSurfaceInP5 EmbeddedProjectiveVariety := X -> ( - if dim X != 2 then error "expected a surface"; - if codim linearSpan X > 0 then X = (parametrize linearSpan X)^^ X; - if dim ambient X <= 5 then return X; - pr := rationalMap apply(6,i -> random(1,ring ambient X)); - Var pr (ideal X) -); - -discriminant SpecialGushelMukaiFourfold := o -> X -> ( - if X.cache#?(surface X,"discriminantFourfold") then return last X.cache#(surface X,"discriminantFourfold"); - S := surface X; - degS := degree S; g := sectionalGenus S; chiOS := euler hilbertPolynomial S; - chiS := eulerCharacteristic(S,Algorithm=>if o.Algorithm === "Poisson" then null else o.Algorithm); - KS2 := 12*chiOS-chiS; - KSHS := 2*g-2-degS; - (a,b) := last cycleClass X; - n := if S.cache#?"FiniteNumberOfNodes" or S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" or (S.cache#?"fitVariety" and (S.cache#"fitVariety").cache#?"nonSaturatedSingularLocus") then numberNodes S else 0; - S2 := 3*a + 4*b + 2*KSHS + 2*KS2 - 12*chiOS + 2*n; - d := 4*S2 - 2*(b^2+(a-b)^2); - if S.cache#?"FiniteNumberOfNodes" then X.cache#(surface X,"discriminantFourfold") = (S2,d); - d -); - --- discriminant SpecialCubicFourfold := o -> X -> ( --- if X.cache#?(surface X,"discriminantFourfold") then return last X.cache#(surface X,"discriminantFourfold"); --- S := surface X; --- degS := degree S; g := sectionalGenus S; chiOS := euler hilbertPolynomial S; --- chiS := eulerCharacteristic(S,Algorithm=>if o.Algorithm === "Poisson" then null else o.Algorithm); --- KS2 := 12*chiOS-chiS; --- n := numberNodes S; --- S2 := 3*degS+6*g-12*chiOS+2*KS2+2*n-6; --- d := 3*S2 - degS^2; --- X.cache#(surface X,"discriminantFourfold") = (S2,d); --- d --- ); - -discriminant SpecialCubicFourfold := discriminant HodgeSpecialFourfold := o -> X -> ( - if X.cache#?(surface X,"discriminantFourfold") then return last X.cache#(surface X,"discriminantFourfold"); - r := codim X; - a := flatten degrees ideal X; - if #a != r then error "expected a special fourfold which is a complete intersection"; - S := surface X; - HS2 := degree S; - KSHS := 2*(sectionalGenus S)-2-HS2; - chiOS := euler hilbertPolynomial S; - c2TS := eulerCharacteristic(S,Algorithm=>if o.Algorithm === "Poisson" then null else o.Algorithm); - KS2 := 12*chiOS-c2TS; - n := if instance(X,SpecialCubicFourfold) or S.cache#?"FiniteNumberOfNodes" or S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" or (S.cache#?"fitVariety" and (S.cache#"fitVariety").cache#?"nonSaturatedSingularLocus") then numberNodes S else 0; - S2 := 2*n + (binomial(r+5,2) - (r+5)*(sum a) + (sum a)^2 - sum flatten for i to r-1 list for j from i+1 to r-1 list a_i*a_j) * HS2 + (r+5-sum a) * KSHS + KS2 - c2TS; - d := det(X.cache#(surface X,"LatticeIntersectionMatrix") = matrix {{degree X,HS2},{HS2,S2}}); - if S.cache#?"FiniteNumberOfNodes" then X.cache#(surface X,"discriminantFourfold") = (S2,d); - d -); - ------------------------------------------------------------------------- ------------------------- Associated K3 surfaces ------------------------ ------------------------------------------------------------------------- - -surfaceDeterminingInverseOfFanoMap = method(Options => {Verbose => false, Strategy => null}); -surfaceDeterminingInverseOfFanoMap HodgeSpecialFourfold := o -> X -> ( - if (surface X).cache#?("surfaceDeterminingInverseOfFanoMap",ideal X) then return (surface X).cache#("surfaceDeterminingInverseOfFanoMap",ideal X); - a := degreeHypersurface X; - Str := o.Strategy; - if Str === null then ( - Str = "Interpolate"; - if member(recognize X,{"planeInPP7", "quinticDelPezzoSurface", "quarticScrollSurface", 1, 6}) then Str = "Inverse"; - if member(recognize X,{"C38Coble", "FarkasVerra", 3, 17, "october2021-26''", 18}) and char coefficientRing X <= 65521 then Str = "F4"; - ); - mu := fanoMap X; - if Str === "Inverse" then ( - muInv := inverse3(mu|X); - if not X.cache#?"rationalParametrization" then X.cache#"rationalParametrization" = muInv; - return (surface X).cache#("surfaceDeterminingInverseOfFanoMap",ideal X) = projectiveVariety(if member(recognize X,{"planeInPP7", 1, 6}) then gens saturate ideal matrix muInv else matrix muInv); - ); - mu = super mu; - if Str === "Interpolate" then (W := Var image mu; iW := lift(3 - (2*(sectionalGenus W)-2)/(degree W),ZZ)); - m := numgens ambient target map X -1; - if o.Verbose then <<"-- needed "< - ( - if o.Verbose then <<"-- (step "<o.Verbose) - else error "unrecognized Strategy; available strategies are: \"DirectImage\", \"F4\", \"Interpolate\"" - ) - ); - if member(recognize X,{"NotRecognized", "FarkasVerra", 1, "surf-5-6-2-nodal"}) then U = top U; - if recognize X === "surf-7-1-9" then (if o.Verbose then <<"-- removing cubic scroll component from surface"< {Verbose => false, Strategy => null, "NumberLines" => infinity}); -exceptionalCurves HodgeSpecialFourfold := o -> X -> ( - NumLines := o#"NumberLines"; - if NumLines =!= infinity then if not(instance(NumLines,ZZ) and NumLines >= 0) then error "option NumberLines expects a nonnegative integer"; - if NumLines === infinity then ( - if member(recognize X,{"planeInPP7", "quarticScrollSurface", "oneNodalSepticDelPezzoSurfaceC26", "FarkasVerra", 3, "6NodalOcticSrollC38", 18, "gushel26''", "surf-5-6-2-nodal", "surf-4-3-1-external", "surf-7-1-9"}) then NumLines = 0; - if member(recognize X,{"surf-5-7-0-1", 17, 6, "mukai26''", "hyperplane section of a conic bundle over PP2"}) then NumLines = 1; - if recognize X === 1 then NumLines = 2; - if recognize X === "october2021-34'" then NumLines = 3; - if recognize X === "october2021-26''" then NumLines = 4; - if member(recognize X,{"quinticDelPezzoSurface", "C42", "october2021-20"}) then NumLines = 5; - if recognize X === "C38Coble" then NumLines = 10; - ); - a := degreeHypersurface X; - if o.Verbose then <<"-- computing the Fano map mu from "<<(if codim ambientFivefold X > 0 then "the fivefold in PP^"|toString(dim ambient X) else "PP^5")< 0 then "the fivefold in PP^"|toString(dim ambient X) else "PP^5")<<" to PP^"<<(numgens ambient target mu -1)<<" defined by the hypersurfaces"<o.Verbose,Strategy=>o.Strategy); - if U.cache#?"exceptionalCurves" then return U.cache#"exceptionalCurves"; - if o.Verbose then <<"-- computing the surface U' corresponding to another fourfold X'"<o.Verbose,Strategy=>o.Strategy); - if dim(U*U') <= 0 then return U.cache#"exceptionalCurves" = ((0_U)%U,(0_U)%U); - if dim(U*U') > 1 then error "something went wrong: dim(U*U') > 1"; - local LL; local L; - if instance(NumLines,ZZ) and NumLines > 0 and member(recognize X,{"quinticDelPezzoSurface", 1}) then ( - if o.Verbose then <<"-- computing the "<o.Verbose,"Deep"=>2); - if degree(U*U') =!= degree(L) then error "something went wrong"; - ) else if instance(NumLines,ZZ) and NumLines == 1 and recognize X === "mukai26''" then ( - if o.Verbose then <<"-- computing the "<o.Verbose,"Deep"=>2),Cu -> dim Cu == 1 and degree Cu == 1); - ) else if instance(NumLines,ZZ) and NumLines > 0 then ( - if o.Verbose then <<"-- computing the "<= 0 then Fano LL else 0_U; - ) else L = 0_U; - if not (isSubset(L,U) and isSubset(L,U')) then error "something went wrong"; - if degree(U*U') == degree(L) then return U.cache#"exceptionalCurves" = (L%U,(0_U)%U); - if degree((U*U')\L) =!= degree(U*U') - degree(L) then error "some exceptional line has multiplicity > 1"; - if o.Verbose then <<"-- computing the top components of (U*U')\\{exceptional lines} via interpolation"<o.Verbose,"Deep"=>2) - else C = interpolateTop(2,(U*U')\L,Verbose=>o.Verbose,"Deep"=>3); - U.cache#"exceptionalCurves" = (L%U,C%U) -); - -SurfaceAssociatedToRationalFourfold = new Type of EmbeddedProjectiveVariety; -WeightedSurfaceAssociatedToRationalFourfold = new Type of WeightedProjectiveVariety; - -globalAssignment SurfaceAssociatedToRationalFourfold; -globalAssignment WeightedSurfaceAssociatedToRationalFourfold; - -WeightedSurfaceAssociatedToRationalFourfold.synonym = SurfaceAssociatedToRationalFourfold.synonym = "surface associated to rational fourfold"; - -expression WeightedSurfaceAssociatedToRationalFourfold := expression SurfaceAssociatedToRationalFourfold := U -> ( - X := U.cache#"Hodge-special fourfold"; - (S,F) := if instance(X,SpecialCubicFourfold) - then ("K3 surface","cubic fourfold") - else if instance(X,SpecialGushelMukaiFourfold) - then ("K3 surface","GM fourfold") - else if instance(X,IntersectionOfThreeQuadricsInP7) - then ("Castelnuovo surface","complete intersection of 3 quadrics in PP^7") - else ("surface","fourfold"); - if dim U == -1 then S = "not-fully-calculated "|S; - A := if hasAttribute(X,ReverseDictionary) then toString getAttribute(X,ReverseDictionary) else (F|" of discriminant "|toString(discriminant X)); - expression(S|" associated to "|A) -); - -net WeightedSurfaceAssociatedToRationalFourfold := net SurfaceAssociatedToRationalFourfold := U -> ( - if hasAttribute(U,ReverseDictionary) then return toString getAttribute(U,ReverseDictionary); - if dim U >= 0 then return ? U; - "-* some calculations are missing *-" -); -texMath WeightedSurfaceAssociatedToRationalFourfold := texMath SurfaceAssociatedToRationalFourfold := texMath @@ net; - -makeSurfaceAssociated = (X,mu,U,C,f) -> ( - assert(instance(X,HodgeSpecialFourfold) and instance(mu,MultirationalMap) and instance(U,EmbeddedProjectiveVariety) and instance(C,List) and (instance(f,Nothing) or instance(f,MultirationalMap))); - S := if f =!= null then image f else projectiveVariety((coefficientRing X)[],Saturate=>false); - S = if instance(S,WeightedProjectiveVariety) then new WeightedSurfaceAssociatedToRationalFourfold from S else new SurfaceAssociatedToRationalFourfold from S; - S.cache#"construction of SurfaceAssociatedToRationalFourfold" = (mu,U,C,f); - S.cache#"Hodge-special fourfold" = X; - return S; -); - -building = method(); -building WeightedSurfaceAssociatedToRationalFourfold := building SurfaceAssociatedToRationalFourfold := S -> S.cache#"construction of SurfaceAssociatedToRationalFourfold"; - -associatedK3surface = method(Options => {Verbose => false, Strategy => null, Singular => null}); -associatedK3surface SpecialGushelMukaiFourfold := associatedK3surface SpecialCubicFourfold := o -> X -> ( - if (not X.cache#?(surface X,"label")) and o.Verbose then <<"-- trying to recognize the fourfold"<o.Singular,Verbose=>o.Verbose); - (L,C) := exceptionalCurves(X,Verbose=>o.Verbose,Strategy=>o.Strategy); - U := ambientVariety L; - mu := multirationalMap fanoMap X; - if U.cache#?"MapToMinimalK3Surface" then return makeSurfaceAssociated(X,mu,U,{L,C},U.cache#"MapToMinimalK3Surface"); - genK3 := lift((discriminant(X)+2)/2,ZZ); - f := null; H := random(1,0_U); local normU; - if member(recognize X,{"NotRecognized", "october2021-34'", "october2021-20"}) then ( - if o.Verbose then <<"-- skipping computation of the map f from U to the minimal K3 surface of degree "<false); - if o.Verbose then <<"-- inverting the normalization of U"<true); - if o.Verbose then <<"-- computing the map f from U to the minimal K3 surface of degree "<o.Verbose); - if o.Verbose then <<"-- computing the map f from U to the minimal K3 surface of degree "<true); - if o.Verbose then <<"-- computing normalization of the surface image"<false)); - if f#"image" === null then error "something went wrong"; - ) else ( - if o.Verbose then <<"-- computing the map f from U to the minimal K3 surface of degree "<o.Verbose); - ); - if f =!= null then ( - if dim target f =!= genK3 then error("expected map to PP^"|(toString genK3)|", but got map to PP^"|toString(dim target f)); - if char coefficientRing X <= 65521 then ( - if o.Verbose then <<"-- computing the image of f using the F4 algorithm"<o.Verbose); - ) else image f; - if f#"image" === null then error "something went wrong"; - if degrees image f =!= {({2},binomial(genK3-2,2))} then error "the degrees for the generators of the ideal of the K3 surface are not as expected"; - U.cache#"MapToMinimalK3Surface" = f; - ); - return makeSurfaceAssociated(X,mu,U,{L,C},f); -); - -associatedCastelnuovoSurface = method(Options => {Verbose => false, Strategy => null, Singular => null}); -associatedCastelnuovoSurface IntersectionOfThreeQuadricsInP7 := o -> X -> ( - if (not X.cache#?(surface X,"label")) and o.Verbose then <<"-- trying to recognize the fourfold"<o.Singular,Verbose=>o.Verbose); - (L,C) := exceptionalCurves(X,Verbose=>o.Verbose,Strategy=>o.Strategy); - U := ambientVariety L; - mu := multirationalMap fanoMap X; - if U.cache#?"MapToMinimalK3Surface" then return makeSurfaceAssociated(X,mu,U,{L,C},U.cache#"MapToMinimalK3Surface"); -- inappropriate key name - f := null; H := random(1,0_U); - if member(recognize X,{"NotRecognized", "surf-7-1-9"}) then ( - if o.Verbose then <<"-- skipping computation of the map f from U to the minimal Castelnuovo surface"<o.Verbose); - if o.Verbose then <<"-- computing the map f from U to the minimal Castelnuovo surface"<3); - f = multirationalMap inverse rationalMap(ring target h,ring target n,take(gens ring target h,5)); - if not((f * (inverse f) == 1 and (inverse f) * f == 1)) then error "something went wrong"; - ) else if recognize X === "surf-4-3-1-external" then ( - if o.Verbose then <<"-- computing the normalization of U"<false); - if o.Verbose then <<"-- inverting the normalization of U"< HodgeSpecialFourfold, Options => {Verbose => false, Strategy => null, Singular => null}); -mirrorFourfold SpecialCubicFourfold := mirrorFourfold SpecialGushelMukaiFourfold := mirrorFourfold IntersectionOfThreeQuadricsInP7 := o -> X -> ( - if X.cache#?(surface X,"associatedFourfold") then return X.cache#(surface X,"associatedFourfold"); - if o.Verbose then <<"-- computing the Fano map"<o.Singular,Verbose=>o.Verbose); - if o.Verbose then <<"-- computing the surface U corresponding to the given fourfold"<o.Verbose,Strategy=>o.Strategy); - W := specialFourfold(U,Var target mu,InputCheck=>0); - W.cache#(surface W,"associatedFourfold") = X; - X.cache#(surface X,"associatedFourfold") = W -); -mirrorFourfold HodgeSpecialFourfold := o -> X -> ( - if not X.cache#?(surface X,"associatedFourfold") then error "can't find associated fourfold"; - X.cache#(surface X,"associatedFourfold") -); - ------------------------------------------------------------------------- ----------------- arXiv:2002.07026 -------------------------------------- ------------------------------------------------------------------------- - -GMtables = method(Options => {Verify => true}); -GMtables (ZZ,Ring) := o -> (i,K) -> ( - if i < 1 or i > 21 then error "expected an integer between 1 and 21"; - t := gens ring PP_K^2; - s := multirationalMap rationalMap(ring PP_K^2,ring PP_K^5,{t_0^2,t_0*t_1,t_1^2,t_0*t_2,t_1*t_2,0}); - S := image s; - p0 := Var ideal(t_0,t_1); -- base point of s - assert(p0 == baseLocus s); - x := gens ring PP_K^5; - L0 := Var ideal(x_0,x_1,x_2,x_5); -- directrix line of S - u := gens ring PP_K^3; - b := multirationalMap rationalMap(ring PP_K^3,ring PP_K^5,{u_0^3*u_1-u_0*u_1^3, u_0^2*u_1^2-u_0*u_1^3+u_0^3*u_2-u_1^3*u_3, u_0^2*u_1*u_2-u_1^3*u_3, -u_0^2*u_1^2+u_0*u_1^3+u_0*u_1^2*u_2-u_1^3*u_3, u_0^2*u_1*u_3-u_1^3*u_3, u_0^2*u_1^2-u_0*u_1^3+u_0*u_1^2*u_3-u_1^3*u_3}); - B := image b; - E := (Var ideal(u_0,u_1), Var ideal(u_3,u_0), Var ideal(u_2,u_1), Var ideal(u_2-u_3,u_0-u_1)); -- base locus of b: E_0 triple line, E_1,E_2,E_3 simple lines - assert(⋃ {3*E_0,E_1,E_2,E_3} == baseLocus b); - pE := apply(E,L -> parametrize L); - curveOnS := e -> ( - assert(instance(e,ZZ) and e >= 1 and e <= 4); - local j; - if e == 1 then j = parametrize random({1},p0); - if e == 2 then j = parametrize random({1},0_(PP_K^2)); - if e == 3 then j = inverse rationalMap(p0_(random({2},p0))); - if e == 4 then ( - p1 := point PP_K^2; - j = inverse rationalMap(p1_(random({2},p1))); - ); - g := rationalMap(j * s,Dominant=>true); - (target g).cache#"rationalParametrization" = g; - return target g; - ); - curveOnB := e -> ( - assert(instance(e,ZZ) and e >= 1 and e <= 5); - local C; local p; local p'; local p''; local j; - if e == 1 then ( - p = point source pE_0; - C = random({{1},{1}},pE_0 p); - j = parametrize C; - ); - if e == 2 then ( - p = point source pE_0; - p' = point source pE_0; - C = random({{2},{1}},pE_0(p) + pE_0(p')); - j = inverse rationalMap((pE_0 p)_C); - ); - if e == 3 then ( - p = point source pE_0; - p' = point source pE_1; - p'' = point source pE_2; - C = random({{2},{1}},pE_0(p) + pE_1(p') + pE_2(p'')); - j = inverse rationalMap((pE_0 p)_C); - ); - if e == 4 then ( - p = point source pE_0; - p' = point source pE_1; - C = random({{2},{1}},pE_0(p) + pE_1(p')); - j = inverse rationalMap((pE_0 p)_C); - ); - if e == 5 then ( - p = point source pE_0; - C = random({{2},{1}},pE_0 p); - j = inverse rationalMap((pE_0 p)_C); - ); - g := rationalMap(j * b,Dominant=>true); - (target g).cache#"rationalParametrization" = g; - return target g; - ); - SB := {,S,S,S,S,B,S,S,B,S,B,B,S,S,S,S,S,S,B,B,S,S}; - degsV := {,2,2,8,4,3,1,3,2,1,4,4,8,7,4,6,6,5,4,4,4,7}; - sGenV := {,0,0,5,1,0,0,0,0,0,1,0,5,3,1,2,3,1,0,0,0,3}; - degsC := {,2,1,4,3,2,1,3,1,1,4,4,4,4,2,4,3,3,5,4,4,4}; - inters := {,0,0,0,0,7,0,0,5,0,4,6,0,0,0,0,0,0,3,6,0,0}; - verify := (i0,S',V',C') -> ( - if not o.Verify then return (S',V',C'); - try assert( - S' == SB_i0 and - dim V' == 2 and - degree V' == degsV_i0 and - sectionalGenus V' == sGenV_i0 and - dim C' == 1 and - degree C' == degsC_i0 and - sectionalGenus C' == 0 and - isSubset(C',S') and isSubset(C',V') and - degree((S' * V') \ C') == inters_i0 - ) else error ("something went wrong while constructing a triple for case "|toString(i)); - return (S',V',C'); - ); - local C; local p; local j; local D; local V; local v; local phi; - if i == 1 then ( - C = curveOnS 2; - V = random({{1},{1},{2}},C); - return verify(i,S,V,C); - ); - if i == 2 then ( - C = curveOnS 1; - V = random({{1},{1},{2}},C); - return verify(i,S,V,C); - ); - if i == 3 then ( - C = ⋃ {L0,curveOnS 1,curveOnS 1,curveOnS 1}; - V = random({{2},{2},{2}},C); - return verify(i,S,V,C); - ); - if i == 4 then ( - C = curveOnS 3; - V = random({{1},{2},{2}},C); - return verify(i,S,V,C); - ); - if i == 5 then ( - C = curveOnB 2; - D = curveOnS 2; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi S; - return verify(i,B,V,C); - ); - if i == 6 then ( - C = curveOnS 1; - V = random({{1},{1},{1}},C); - return verify(i,S,V,C); - ); - if i == 7 then ( - C = curveOnS 3; - D = curveOnS 3; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi S; - return verify(i,S,V,C); - ); - if i == 8 then ( - C = curveOnB 1; - D = random({{1},{1},{1},{1}},0_(PP_K^5)); - V = random({{1},{1},{2}},D); - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,B,V,C); - ); - if i == 9 then return verify(i,S,random({{1},{1},{1}},L0),L0); - if i == 10 then ( - C = curveOnB 4; - j = rationalMap((parametrize PP_K^(1,4)) << PP_K^5,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - V = random({{1},{2},{2}},D); - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,B,V,C); - ); - if i == 11 then ( - C = curveOnB 4; - p = for i to 1 list point PP_K^2; - v = rationalMap(p_0 + 2*p_1,3); - V = image v; - j = rationalMap((inverse rationalMap((p_1)_(random({2},p_1)))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,B,V,C); - ); - if i == 12 then ( - C = curveOnS 4; - V = random({{2},{2},{2}},C); - return verify(i,S,V,C); - ); - if i == 13 then ( - C = curveOnS 4; - p = for i to 9 list point PP_K^2; - v = rationalMap((⋃ take(p,9)) + 3*p_9,5); - V = image v; - j = rationalMap((inverse rationalMap((p_0)_(random({2},⋃{p_0,p_1,p_2,p_9})))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,S,V,C); - ); - if i == 14 then ( - C = curveOnS 2; - V = random({{1},{2},{2}},C); - return verify(i,S,V,C); - ); - if i == 15 then ( - C = curveOnS 4; - p = for i to 6 list point PP_K^2; - v = rationalMap((⋃ take(p,6)) + 2*p_6,4); - V = image v; - j = rationalMap((inverse rationalMap((p_0)_(random({2},⋃{p_0,p_1,p_6})))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,S,V,C); - ); - if i == 16 then ( - C = curveOnS 3; - p = for i to 9 list point PP_K^2; - v = rationalMap(⋃ p,4) << PP_K^5; - V = image v; - j = (inverse rationalMap((p_0)_(random({2},⋃ take(p,5))))) * v; - j = rationalMap(j,image j); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,S,V,C); - ); - if i == 17 then ( - C = curveOnS 3; - p = for i to 3 list point PP_K^2; - v = rationalMap(⋃ p,3); - V = image v; - j = rationalMap((parametrize random({1},0_(PP_K^2))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,S,V,C); - ); - if i == 18 then ( - C = curveOnB 5; - p = for i to 1 list point PP_K^2; - v = rationalMap(p_0 + 2*p_1,3); - V = image v; - j = rationalMap((inverse rationalMap((p_0)_(random({2},p_0)))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,B,V,C); - ); - if i == 19 then ( - C = curveOnB 4; - V = PP_K^(2,2); - v = parametrize V; - p = point source v; - j = rationalMap((inverse rationalMap(p_(random({2},p)))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,B,V,C); - ); - if i == 20 then ( - C = curveOnS 4; - V = PP_K^(2,2); - v = parametrize V; - p = point source v; - j = rationalMap((inverse rationalMap(p_(random({2},p)))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,S,V,C); - ); - if i == 21 then ( - C = curveOnS 4; - p = for i to 8 list point PP_K^2; - v = rationalMap(⋃ p,4); - V = image v; - j = rationalMap((inverse rationalMap((p_0)_(random({2},⋃ take(p,4))))) * v,Dominant=>true); - (target j).cache#"rationalParametrization" = j; - D = image j; - phi = findIsomorphism(D,C,Verify=>o.Verify); - V = phi V; - return verify(i,S,V,C); - ); -); - -GMtables (Ring,String) := o -> (K,name) -> ( -- store all data in a file -F := openOut (name|".dat"); -F <<"-- file created automatically using: GMtables("|toExternalString K|",\""|name|"\",Verify=>"<<(o.Verify)<<"); date: "|(toString get "!date")< j -> ("<o.Verify); - F = openOutAppend (name|".dat"); - F << " if j == "<false,MinimalGenerators=>false),"<false,MinimalGenerators=>false),"<false,MinimalGenerators=>false));"< i -> ( - if i < 1 or i > 21 then error "expected integer between 1 and 21"; - try value get "data_examples.dat" else error("file \"data_examples.dat\" not found. You can make it using GMtables(K,\"data_examples\")"); - GMtables i -); - -GMtables (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (B,V,C) -> ( - if not ((dim B == 2 or dim B == 3) and degree B == dim B + 1 and dim V == 2 and dim C == 1 and ring ambient C === ring ambient V and ring ambient C === ring ambient B) then error "invalid input for GMtables"; - psi := rationalMap(ideal B,Dominant=>2); - if o.Verify then <<"-- constructing random GM fourfold from the triple..."<(if o.Verify then 10 else 0),Verbose=>true); - if o.Verify then <<"-- GM fourfold correctly constructed."<= 1 and i <= 21) then error "something went wrong"; - <<"-- done (got case "<true); - <<"-- parameterCount successfully terminated (got: "<<(c,h)<<")"< {InputCheck => 1, Verbose => true}); -fourfoldFromTriple (ZZ,VisibleList) := o -> (i,E) -> ( - psi := rationalMap(ideal E_0,Dominant=>2); - X := specialGushelMukaiFourfold(psi ideal E_1,InputCheck=>o.InputCheck,Verbose=>o.Verbose); - X.cache#(surface X,"label") = i; - return X; -); - ------------------------------------------------------------------------- ----------------------- Prime Fano fourfolds ---------------------------- ------------------------------------------------------------------------- - -fanoFourfold = method(TypicalValue => EmbeddedProjectiveVariety, Options => {CoefficientRing => ZZ/65521}); -fanoFourfold (ZZ,ZZ) := o -> (d,g) -> ( - K := o.CoefficientRing; - if not (instance(K,Ring) and isField K) then error "CoefficientRing option expects a field"; - local Y; local j; local S; local psi; - local X; - dg := {(2,0),(3,1),(4,1),(5,1),(4,3),(6,4),(8,5),(10,6),(12,7),(14,8),(16,9),(18,10)}; - if not member((d,g),dg) then error("expected a pair of integers in the set "|toString(dg)); - if d == 2 and g == 0 then X = random({2},0_(PP_K^5)); - if d == 3 and g == 1 then X = random({3},0_(PP_K^5)); - if d == 4 and g == 1 then X = random({{2},{2}},0_(PP_K^6)); - if d == 5 and g == 1 then ( - Y = GG(K,1,4); - j = parametrize random({{1},{1}},0_Y); - X = j^* Y; - ); - if d == 4 and g == 3 then X = random({4},0_(PP_K^5)); - if d == 6 and g == 4 then X = random({{2},{3}},0_(PP_K^6)); - if d == 8 and g == 5 then X = random({3:{2}},0_(PP_K^7)); - if d == 10 and g == 6 then ( - Y = GG(K,1,4); - j = parametrize random({1},0_Y); - X = (j^* Y) * random({2},0_(source j)); - ); - if d == 12 and g == 7 then ( - S = surface({3,4},K); - psi = rationalMap(S * random({1},0_S),2); - j = parametrize random({1},0_(target psi)); - X = j^* image(2,psi); - ); - if d == 14 and g == 8 then ( - Y = GG(K,1,5); - j = parametrize random({4:{1}},0_Y); - X = j^* Y; - ); - if d == 16 and g == 9 then ( - S = surface({2},K,ambient=>6); - psi = rationalMap(S,3,2); - j = parametrize random({{1},{1}},0_(target psi)); - X = j^* image psi; - ); - if d == 18 and g == 10 then ( - -- p. 4 of [Kapustka and Ranestad - Vector Bundles On Fano Varieties Of Genus Ten] - w := gens ring PP_K^13; - M := matrix {{0,-w_5,w_4,w_6,w_7,w_8,w_0}, - {w_5,0,-w_3,w_12,w_13,w_9,w_1}, - {-w_4,w_3,0,w_10,w_11,-w_6-w_13,w_2}, - {-w_6,-w_12,-w_10,0,w_2,-w_1,w_3}, - {-w_7,-w_13,-w_11,-w_2,0,w_0,w_4}, - {-w_8,-w_9,w_6+w_13,w_1,-w_0,0,w_5}, - {-w_0,-w_1,-w_2,-w_3,-w_4,-w_5,0}}; - Y = Var trim pfaffians(4,M); - j = toRationalMap parametrize random({1},0_Y); - X = j^* Y; - ); - if not (dim X == 4 and degree X == d and sectionalGenus X == g) then error("something went wrong while computing random fourfold of degree "|toString(d)|" and sectional genus "|toString(g)); - return X; -); - -parametrizeFanoFourfold = method(TypicalValue => MultirationalMap, Options => {Strategy => 1}); -parametrizeFanoFourfold EmbeddedProjectiveVariety := o -> X -> ( - X#InverseMethod = inverse3; - if dim X != 4 then error "expected a fourfold"; - if degree X == 5 and sectionalGenus X == 1 then ( - if o.Strategy === 2 then return parametrize X; - if o.Strategy =!= 1 then error("the available strategies are:"|newline|"-- 1: projection from the plane spanned by a conic contained in the fourfold"|newline|"-- 2: projection from the unique sigma_(2,2) plane contained in the fourfold (Todd's result)"); - p := point X; - C := line(X,p) + line(X,p); - if degree C != 2 then error "something went wrong while finding conic on fourfold"; - return inverse3 multirationalMap rationalMap(sub(ideal C,ring X),1); - ); - if o.Strategy =!= 1 then error "strategy not available"; - parametrize X -); - ------------------------------------------------------------------------- ------------------------------------------------------------------------- ------------------------------------------------------------------------- - -inverse3 = method(); -inverse3 RationalMap := psi -> ( - if psi#"inverseRationalMap" =!= null then return psi#"inverseRationalMap"; - phi := rationalMap map inverseOfMap(map psi,CheckBirational=>false,AssumeDominant=>true,QuickRank=>false,MinorsLimit=>0,Verbosity=>0); - forceInverseMap(phi,psi); - phi -); -inverse3 MultirationalMap := Psi -> ( - if Psi#"inverse" =!= null then return Psi#"inverse"; - Phi := multirationalMap inverse3 toRationalMap Psi; - if ring source Phi =!= ring target Psi then error "internal error encountered"; - Phi#"source" = target Psi; - if ring target Phi =!= ring source Psi then error "internal error encountered"; - Phi#"target" = source Psi; - -- if Phi#"inverse" != Psi then error "internal error encountered"; - Phi#"inverse" = Psi; - return Phi; -); -inverse3 (RationalMap,Option) := (psi,opt) -> inverse3 psi; -inverse3 (MultirationalMap,Option) := (Psi,opt) -> inverse3 Psi; - -interpolateImage = method(Options => {Verbose => false, cache => true}); -interpolateImage (MultirationalMap,List,ZZ) := o -> (Phi,D,j) -> ( - if Phi#"image" =!= null then return Phi#"image"; - if Phi#"isDominant" === true then return target Phi; - if not all(D,d -> instance(d,ZZ)) then error "expected a list of integers"; - cont := 0; - W := Phi point source Phi; - while select(flatten degrees ideal W,d -> d <= j) =!= D do ( - if o.Verbose then <<"image "< first degree w <= j),MinimalGenerators=>true,Saturate=>false); - if flatten degrees ideal W =!= D then error "something went wrong: the degrees of the generators are wrong"; - if o.cache then ( - if Phi#"image" === null then Phi#"image" = W; - Phi#"isDominant" = Phi#"image" == target Phi; - if Phi#"isDominant" then Phi#"image" = target Phi; - return Phi#"image"; - ) else return W; -); -interpolateImage (RationalMap,List,ZZ) := o -> (Phi,D,j) -> ( - if Phi#"idealImage" =!= null then return Phi#"idealImage"; - W := interpolateImage(multirationalMap Phi,D,j,Verbose=>o.Verbose,cache=>false); - if o.cache then forceImage(Phi,ideal W); - ideal W -); - -interpolateTop = method(Options => {Verbose => false, cache => true, "Deep" => 3}); -interpolateTop (EmbeddedProjectiveVariety,List) := o -> (X,j) -> ( - if X.cache#?"top" then return X.cache#"top"; - assert(#j == 1 and instance(first j,ZZ)); - j = first j; - cont := 0; m := o#"Deep"; local f; - W := 0_X; - D := toList(1..m); - while # unique take(D,-m) != 1 do ( - cont = cont + 1; - if o.Verbose then <<"top "< d <= j)); - ); - gW := select(flatten entries gens ideal W,w -> first degree w <= j); - if # gW > 0 then ( - W = projectiveVariety(ideal gW,MinimalGenerators=>true,Saturate=>false); - if o.cache then X.cache#"top" = W; - return W; - ) else return ambient W; -); -interpolateTop (ZZ,EmbeddedProjectiveVariety) := o -> (i,X) -> ( - W := 0_X; - while not (dim W == dim X and degree W == degree X) do ( - i = i + 1; - W = interpolateTop(X,{i},Verbose=>o.Verbose,cache=>false,"Deep"=>o#"Deep"); - ); - if o.cache then X.cache#"top" = W else W -); -interpolateTop EmbeddedProjectiveVariety := o -> X -> interpolateTop(0,X,Verbose=>o.Verbose,cache=>o.cache,"Deep"=>o#"Deep"); - -mapDefinedByDivisor = method(); -mapDefinedByDivisor (QuotientRing,VisibleList) := (R,D) -> rationalMap(R,new Tally from apply(select(D,l -> last l > 0),d -> first d => last d)); -mapDefinedByDivisor (MultiprojectiveVariety,VisibleList) := (X,D) -> rationalMap(X,new Tally from apply(select(D,l -> last l > 0),d -> first d => last d)); - --- sufficient conditions for smoothness ('Y' is assumed to be equidimensional) -isSmooth EmbeddedProjectiveVariety := {} >> o -> (cacheValue "isSmooth") (Y -> ( - if Y.cache#?"singularLocus" or Y.cache#?"nonSaturatedSingularLocus" then return (dim singLocus Y == -1); - X := fitVariety Y; - isXsm := dim singLocus X == -1; - if isXsm then Y.cache#"singularLocus" = 0_Y; - isXsm -)); - -numberNodes = method(Options => {Verbose => true}); -numberNodes EmbeddedProjectiveVariety := o -> Y -> ( - if Y.cache#?"FiniteNumberOfNodes" then return Y.cache#"FiniteNumberOfNodes"; - X := if not(Y.cache#?"singularLocus" or Y.cache#?"nonSaturatedSingularLocus") then fitVariety Y else Y; - if dim singLocus X >= 1 then error "expected at most a finite number of nodes"; - n := if dim singLocus X == -1 then 0 else ( - if (singLocus X).cache#?"Support" then - degree support singLocus X - else - degree support (rationalMap random({{1},{1}},0_X)) singLocus X - ); - if o.Verbose then <<"-- calculated number of nodes (got "<< n <<" nodes)"< ( - if coefficientRing X === QQ then X = X ** (ZZ/nextPrime random(1000,11000000)); - if codim linearSpan X > 0 then X = (parametrize linearSpan X)^^ X; - n := dim ambient X; k := dim X; - if k > 0 and k <= 2 and 2*k+1 < n then ( - pr := rationalMap linearSpan apply(n-(2*k+1),i -> point X); - if dim target pr != 2*k+1 then error "internal error encountered"; - X = pr X; - ); - return X; -)); - -normalization = method(Options => {Verbose => true}); -normalization EmbeddedProjectiveVariety := o -> X -> ( - if X.cache#?"Normalization" then return X.cache#"Normalization"; - if o.Verbose then <<"-- computing normalization of "|toString(? ideal X)< {Verbose => true}); -experimentalNormalizationInv EmbeddedProjectiveVariety := o -> X -> ( - if X.cache#?"ExperimentalNormalizationInv" then return X.cache#"ExperimentalNormalizationInv"; - if not (dim X == 2 and dim linearSpan X > 5) then error "expected a surface with linear span of dimension > 5"; - if o.Verbose then <<"-- computing experimental normalization of "|toString(? ideal X)< point X); - pr := rationalMap((linearSpan pts)_X,Dominant=>true); - if degree target pr != degree X - (dim ambient X - 5) then error "something went wrong"; - n := multirationalMap normalization(target pr,Verbose=>o.Verbose); - if o.Verbose then <<"-- inverting normalization"< (n' pr'^* p,1))); - if dim ambient target h != dim ambient source h + (dim ambient X - 5) then error "something went wrong"; - N := pr * n' * h; - X.cache#"ExperimentalNormalizationInv" = N -); - -varietyDefinedBylinearSyzygies = method(); -varietyDefinedBylinearSyzygies EmbeddedProjectiveVariety := (cacheValue "varietyDefinedBylinearSyzygies") (Y -> ( - G := transpose syz gens ideal Y; - M := matrix select(entries G,g -> max flatten degrees ideal g == 1); - K := mingens kernel M; - I := unique apply(entries transpose K,g -> trim ideal g); - Var first select(I,i -> dim i >= 1) -)); - -toGushel = method(); -toGushel SpecialGushelMukaiFourfold := X -> ( - if dim singLocus ambientFivefold X >= 0 then return X; - j := toRationalMap toGrass X; - Y := local Y; - i := rationalMap(target j,(coefficientRing X)[Y,gens ambient target j],0|vars target j); - i = rationalMap(i,Dominant=>sub(ideal target j,target i)); - S := trim lift((j*i) ideal surface X,ambient target i); - Sv := intersect(S,ideal submatrix'(vars ambient target i,{0})); - try H := ideal random({{1},{1}},Var Sv) else error "not able to specialize to Gushel type"; - h := (parametrize H)||(target i); - specialGushelMukaiFourfold h^* S -); - -fromOrdinaryToGushel = method(); -fromOrdinaryToGushel SpecialGushelMukaiFourfold := X -> try toGushel X else error "not able to deform to Gushel type"; - -imageOfAssociatedMap = method(); -imageOfAssociatedMap HodgeSpecialFourfold := X -> ( - f := map X; - if f#"idealImage" =!= null then return image f; - e := if X.cache#?(surface X,"label") then X.cache#(surface X,"label") else "not recognized yet"; - if e === "quinticDelPezzoSurface" or e === "quarticScrollSurface" or e === "FarkasVerra" or e === "planeInPP7" then forceImage(f,image(f,2)); - if e === "C42" then forceImage(f,image(f,3)); - if instance(e,ZZ) and e >= 1 and e <= 21 and e != 3 and e != 21 then forceImage(f,image(f,2)); - if instance(e,ZZ) and (e == 3 or e == 21) then forceImage(f,trim lift(kernel(map rationalMap(f,Dominant=>2),SubringLimit=>1),ambient target f)); - if member(e,{"gushel26''", "hyperplane section of a conic bundle over PP2", "surf-5-6-2-nodal"}) then forceImage(f,trim kernel(map f,SubringLimit=>1)); - ch := char coefficientRing X; - if (coefficientRing X === ZZ/ch and ch <= 65521) then image(f,"F4") else image f -); - -mapY5 = memoize (K -> ( - X := GG(K,1,4); - h := (parametrize projectiveVariety ideal sum gens ring ambient X)||X; - -- assert(dim singLocus source h == -1); - h -)); -Y5 = K -> source mapY5 K; - -mapY4 = memoize (K -> ( - y := gens ring ambient source mapY5 K; - h := (parametrize projectiveVariety ideal(y_0-y_1+y_2-y_3+y_4-y_5+y_6))||(Y5 K); - h = h * (mapY5 K); - -- assert(dim singLocus source h == -1); - h -)); -Y4 = K -> source mapY4 K; - -singLocus = method(); -singLocus EmbeddedProjectiveVariety := X -> singularLocus(X,Saturate=>false); - -Var = method(Options => {MinimalGenerators => false}); -Var Ideal := o -> I -> projectiveVariety(I,MinimalGenerators=>o.MinimalGenerators,Saturate=>false); -Var Ring := o -> R -> projectiveVariety(R,MinimalGenerators=>o.MinimalGenerators,Saturate=>false); -Var Matrix := o -> M -> projectiveVariety(M,MinimalGenerators=>o.MinimalGenerators,Saturate=>false); - -HodgeSpecialFourfold ? HodgeSpecialFourfold := (X,Y) -> ( - if not uniform {X,Y} then return incomparable; - try (dX,dY) := (discriminant X,discriminant Y) else return incomparable; - if dX < dY then return symbol <; - if dX > dY then return symbol >; - if instance(X,SpecialGushelMukaiFourfold) and instance(Y,SpecialGushelMukaiFourfold) and dX == dY and dX % 8 == 2 then ( - (a,b) := last cycleClass X; (a',b') := last cycleClass Y; - if (even(a+b) and odd(b)) and (odd(a'+b') and even(b')) then return symbol <; - if (odd(a+b) and even(b)) and (even(a'+b') and odd(b')) then return symbol >; - ); - if X.cache#?(surface X,"parameterCount") and Y.cache#?(surface Y,"parameterCount") then ( - if first X.cache#(surface X,"parameterCount") < first Y.cache#(surface Y,"parameterCount") then return symbol <; - if first X.cache#(surface X,"parameterCount") > first Y.cache#(surface Y,"parameterCount") then return symbol >; - ); - if degree surface X < degree surface Y then return symbol <; - if degree surface X > degree surface Y then return symbol >; - if sectionalGenus surface X < sectionalGenus surface Y then return symbol <; - if sectionalGenus surface X > sectionalGenus surface Y then return symbol >; - if (surface X).cache#?"linear system on PP^2" and (surface Y).cache#?"linear system on PP^2" then return (((surface X).cache#"linear system on PP^2") ? ((surface Y).cache#"linear system on PP^2")); - if X == Y and surface X == surface Y then return symbol ==; - return incomparable; -); - ------------------------------------------------------------------------- --------------------------- Prebuilt Examples --------------------------- ------------------------------------------------------------------------- - -trisecantFlop = method(Options => {Verbose => false}); -trisecantFlop ZZ := o -> i -> ( - try needsPackage "TrisecantFlops" else ( - git := findProgram("git", "git --help"); - dir := temporaryFileName() | "/"; - mkdir dir; - <<"The package TrisecantFlops is not present."< dir); - runProgram(git, "checkout master", RunDirectory => dir | "/TrisecantFlops"); - if not fileExists(dir|"TrisecantFlops/TrisecantFlops.m2") then error "something went wrong in downloading the package TrisecantFlops"; - try needsPackage("TrisecantFlops",FileName => dir|"TrisecantFlops/TrisecantFlops.m2") else error "something went wrong in loading the package TrisecantFlops"; - <<"The package TrisecantFlops has been successfully loaded."< false,FileName => dir|"TrisecantFlops/TrisecantFlops.m2"); - ); - ); - if not member(value "TrisecantFlops",loadedPackages) then error "something went wrong"; - if (value "TrisecantFlops").Options.Version < "1.7" then ( - if o.Verbose then <<"-- removing old version of TrisecantFlops"<"|toString(o.Verbose)|")") -); - -prebuiltExamplesOfRationalFourfolds = memoize(() -> ( - try needsPackage "PrebuiltExamplesOfRationalFourfolds" else ( - curl := findProgram("curl", "curl -h"); - dir := temporaryFileName() | "/"; - mkdir dir; - <<"The package PrebuiltExamplesOfRationalFourfolds is not present."<dir); - if not fileExists(dir|"PrebuiltExamplesOfRationalFourfolds.m2") then error "something went wrong in downloading the package PrebuiltExamplesOfRationalFourfolds"; - try needsPackage("PrebuiltExamplesOfRationalFourfolds",FileName => dir|"PrebuiltExamplesOfRationalFourfolds.m2") else error "something went wrong in loading the package PrebuiltExamplesOfRationalFourfolds"; - <<"The package PrebuiltExamplesOfRationalFourfolds has been successfully loaded."< false,FileName => dir|"PrebuiltExamplesOfRationalFourfolds.m2"); - ); - ); - if not member(value "PrebuiltExamplesOfRationalFourfolds",loadedPackages) then error "something went wrong"; - if (value "PrebuiltExamplesOfRationalFourfolds").Options.Version < "1.1" then ( - <<"-- removing old version of PrebuiltExamplesOfRationalFourfolds"< {InputCheck => 1, Verbose => true, "Gluing" => "cubic scroll", Degrees => hashTable{1=>(1,infinity),2=>(0,infinity),3=>(0,1)}}); -makeGMfromCurveOnSurfaceInP6 EmbeddedProjectiveVariety := o -> C -> ( - S := ambientVariety C; - if not (dim C == 1 and dim S == 2 and dim ambient S == 6) then error "expected a curve on a surface in PP^6"; - B := if o#"Gluing" === "cubic scroll" - then glueScroll C - else if o#"Gluing" === "quartic scroll" - then glueScroll' C - else error "the option \"Gluing\" expects \"cubic scroll\" or \"quartic scroll\""; - psi := rationalMap B; - psi' := if S.cache#?"rationalParametrization" then (parametrize S) * psi else psi|S; - H := image(1,psi'); - if codim H == 0 then (if o.Verbose then (<<"got surface in GG(1,4) ⊂ PP^9 with linear span of dimension 9"< o.Degrees#1_1 then error "request on the degrees is not satisfied"; - -- V := if char coefficientRing C <= 65521 then image(psi',"F4") else image psi'; - V := image psi'; - if o.Verbose then <<"got surface in GG(1,4) ⊂ PP^9 with ideal generated in degrees "< d == 2) < 6 then error "the surface in G(1,4) is not contained in any GM fourfold"; - if # select(degs,d -> d > 3) > 0 or - # select(degs,d -> d == 2) < o.Degrees#2_0 or # select(degs,d -> d == 2) > o.Degrees#2_1 or - # select(degs,d -> d == 3) < o.Degrees#3_0 or # select(degs,d -> d == 3) > o.Degrees#3_1 - then error "request on the degrees is not satisfied"; - if o.InputCheck >= 2 then ( - if isIsomorphism rationalMap(psi|S,V) then ( - if S.cache#?"FiniteNumberOfNodes" and S.cache#"FiniteNumberOfNodes" == 0 then (V.cache#"top" = V; V.cache#"singularLocus" = 0_V); - if S.cache#?"FiniteNumberOfNodes" and S.cache#"FiniteNumberOfNodes" > 0 then V.cache#"FiniteNumberOfNodes" = S.cache#"FiniteNumberOfNodes"; - V.cache#"euler" = eulerCharacteristic S; - if o.Verbose then <<"isomorphism between surface in PP^6 and surface in GG(1,4): YES"<o.InputCheck); - X.cache#"Construction" = (B,C); - return X; -); - -glueScroll = method(); -- glue P^1xP^2 along a curve -glueScroll EmbeddedProjectiveVariety := C -> ( - if not (dim C == 1 and dim ambientVariety C == 2 and dim ambient C == 6) then error "expected a curve on a surface in PP^6"; - if not((degree C <= 5 and sectionalGenus C == 0) or (degree C == 5 and sectionalGenus C == 1)) then error "not implemented yet: expected a rational curve of degree at most 5 or an elliptic curve of degree 5"; - K := coefficientRing C; - (p,L,s) := CubicScroll K; - E := image s; - local D; - if degree C == 5 and sectionalGenus C == 0 then ( - D = image(PP_K^(1,3) << source s); - D = ((point D) ===> (point L)) D; - E = (s(D) ===> C) E; - ); - if degree C == 5 and sectionalGenus C == 1 then ( - f := super parametrize ambientVariety C; - g := f|(f^* C); - H := random(1,linearSpan C); - pr := inverse parametrize H; - g = g * pr; - j := inverse rationalMap((linearSpan {g point source g,g point source g})_(image g),Dominant=>true); - B := image((rationalMap lift(matrix j,ring ambient source j)) * inverse pr); - if not(dim B == 2 and degree B == 3 and sectionalGenus B == 0 and isSubset(C,B)) then error "something went wrong"; - E = (random(1,0_E) * E ===> B) E; - ); - if degree C == 4 and sectionalGenus C == 0 then ( - D = random({{2},{1}},0_(source s)); - assert(dim(D*(baseLocus s)) == -1); - E = (s(D) ===> C) E; - ); - if degree C == 3 and sectionalGenus C == 0 then ( - E = (E * random({{1},{1}},0_E) ===> C) E; - ); - if degree C == 2 and sectionalGenus C == 0 then ( - D = random({{1},{1}},0_(source s)); - assert(dim(D*(baseLocus s)) == -1); - E = (s(D) ===> C) E; - ); - if degree C == 1 and sectionalGenus C == 0 then ( - D = random({{1},{1}},point L); - assert(dim(D*(baseLocus s)) == 0); - E = (s(D) ===> C) E; - ); - if not(dim E == 3 and degree E == 3 and isSubset(C,E)) then error "something went wrong"; - E -); - -glueScroll' = method(); -- glue quartic scroll fourfold along a curve -glueScroll' EmbeddedProjectiveVariety := C -> ( - if not (dim C == 1 and dim ambientVariety C == 2 and dim ambient C == 6) then error "expected a curve on a surface in PP^6"; - if not(degree C <= 6 and sectionalGenus C == 0) then error "not implemented yet: expected rational curve of degree at most 6"; - K := coefficientRing C; - (P,s) := QuarticScroll K; - E := image s; - local D; - if degree C == 6 and sectionalGenus C == 0 then ( - D = image(PP_K^(1,3) << source s); - D = (sum(4,i -> point D) ===> sum(P,point)) D; - E = (s(D) ===> C) E; - ); - if degree C == 5 and sectionalGenus C == 0 then ( - D = image(PP_K^(1,3) << source s); - D = (sum(3,i -> point D) ===> sum {point P_0,point P_0,point P_1}) D; - E = (s(D) ===> C) E; - ); - if degree C == 4 and sectionalGenus C == 0 then ( - D = image(PP_K^(1,3) << source s); - D = (sum(4,i -> point D) ===> sum {point P_0,point P_0,point P_1,point P_2}) D; - E = (s(D) ===> C) E; - ); - if degree C == 3 and sectionalGenus C == 0 then ( - D = random({2:{1},{2}},0_(source s)); - D = (sum(3,i -> point D) ===> sum {point P_0,point P_1,point P_2}) D; - E = (s(D) ===> C) E; - ); - if degree C == 2 and sectionalGenus C == 0 then ( - D = random({2:{1},{2}},0_(source s)); - D = (sum(2,i -> point D) ===> sum {point P_0,point P_0}) D; - E = (s(D) ===> C) E; - ); - if degree C == 1 and sectionalGenus C == 0 then ( - D = random({3:{1}},point P_0); - E = (s(D) ===> C) E; - ); - if not(dim E == 4 and degree E == 4 and isSubset(C,E)) then error "something went wrong"; - E -); - -CubicScroll = memoize(K -> ( - s := (multirationalMap segre parametrize(PP_K^1 ** PP_K^2)) << PP_K^6; - p := (baseLocus s)\top baseLocus s; - L := top baseLocus s; - assert(baseLocus s == L + p and dim(L*p) == -1 and dim L == 1 and degree L == 1 and dim p == 0 and degree p == 1); - (p,L,s) -)); - -QuarticScroll = memoize(K -> ( - b := super parametrize(PP_K[1,1,1,1]); - b = b * rationalMap point target b; - P0 := ((baseLocus b)\(support baseLocus b))\(support baseLocus b); - (P1,P2,P3) := toSequence decompose((baseLocus b)\\P0); - P := {P0,P1,P2,P3}; - assert(⋃ {3*P_0,P_1,P_2,P_3} == baseLocus b); - assert(dim(P0*P1)==1 and dim(P0*P2)==1 and dim(P0*P3)==1); - assert(all(P,L -> degree L == 1 and dim L == 2)); - (P,b) -)); - -curvesOnSurface = method(); -- some curves of degree d and genus g on a rational surface -curvesOnSurface (EmbeddedProjectiveVariety,ZZ,ZZ) := (S,d,g) -> ( - L := S.cache#"linear system on PP^2"; - if #L != 4 then error "not implemented yet: the linear system on PP^2 must be of the form {a,i,j,k}"; - U := {}; - if g == 0 then ( - for a from 1 to 2 do for i to L_1 do for j to L_2 do for k to L_3 do - if a*L_0-i-2*j-3*k == d and i+j+k <= (if a == 1 then 2 else 5) then U = append(U,(a,{i,j,k})); - ) else if g == 1 then ( - for i to L_1 do for j to L_2 do for k to L_3 do - if 3*L_0-i-2*j-3*k == d and i+j+k <= 9 then U = append(U,(3,{i,j,k})); - ) else error "not implemented yet: the genus must be 0 or 1"; - apply(U,u -> S.cache#"takeCurve" u) -); - -takeGMsfromSurfaceInP6 = method(Options => {InputCheck => 1, Verbose => true, "Gluing" => "cubic scroll", Degrees => (options makeGMfromCurveOnSurfaceInP6).Degrees, "OnlyNewOnes" => false, "Output" => true}); -takeGMsfromSurfaceInP6 EmbeddedProjectiveVariety := o -> S -> ( - if dim S != 2 or dim ambient S != 6 then error "expected a surface in PP^6"; - if not(S.cache#?"linear system on PP^2" and S.cache#?"takeCurve") then error "expected a surface constructed with the function \"surface\""; - a := apply(toSequence S.cache#"linear system on PP^2",toString); - if #a != 4 then error "not implemented yet: the surface must be of the form \"surface {a,i,j,k}\""; - S.cache#"nice description" = "S("|a_0|";"|a_1|","|a_2|","|a_3|",NumNodes=>"|(if S.cache#?"FiniteNumberOfNodes" then toString(S.cache#"FiniteNumberOfNodes") else "?")|") ⊂ PP^"|toString(dim linearSpan S)|(if codim linearSpan S > 0 then " ⊂ PP^"|toString(dim ambient S) else "")|" of degree: "|toString(degree S)|", genus: "|toString(sectionalGenus S)|", and degrees: "|(toStringDegreesVar S); - if o.Verbose then <<"*******"<= 3 and g == 0 then "rational" else if d >= 3 and g == 1 then "elliptic" else "")<<" curve of degree "<"|(if S.cache#?"FiniteNumberOfNodes" then toString(S.cache#"FiniteNumberOfNodes") else "?")|")"<o.InputCheck,Verbose=>o.Verbose,"Gluing"=>o#"Gluing",Degrees=>o.Degrees) else continue; - if (not o#"OnlyNewOnes" or recognize X === "NotRecognized") and (not member(describe X,apply(U,describe))) then ( - U = append(U,X); - if o.Verbose then <<"using "< 0 then (","|toString(S.cache#"FiniteNumberOfNodes")|")") else ")")<= 3 and sectionalGenus C == 0 then "a rational" else if degree C >= 3 and sectionalGenus C == 1 then "an elliptic" else "")<<" curve of degree "< (a,b,s,nK) -> ( - if #a =!= #b then error "expected two arrays of the same length"; - if s =!= "cubic scroll" and s =!= "quartic scroll" then error "expected string to be \"cubic scroll\" or \"quartic scroll\""; - (n,K) := if instance(nK,ZZ) then (nK,ZZ/65521) else if instance(nK,Ring) then (0,nK) else if instance(nK,VisibleList) and #nK==2 then (nK_0,nK_1) else error "not valid input"; - C := (surface(a,K,NumNodes=>n,ambient=>6)).cache#"takeCurve" (first b,toList take(b,-(#b-1))); - makeGMfromCurveOnSurfaceInP6(C,InputCheck=>o.InputCheck,Verbose=>o.Verbose,"Gluing"=>s) -); -specialGushelMukaiFourfold (Array,Array,String) := o -> (a,b,s) -> specialGushelMukaiFourfold(a,b,s,(0,ZZ/65521),InputCheck=>o.InputCheck,Verbose=>o.Verbose); -specialGushelMukaiFourfold (Array,Array,Thing) := o -> (a,b,nK) -> specialGushelMukaiFourfold(a,b,"cubic scroll",nK,InputCheck=>o.InputCheck,Verbose=>o.Verbose); -specialGushelMukaiFourfold (Array,Array) := o -> (a,b) -> specialGushelMukaiFourfold(a,b,"cubic scroll",(0,ZZ/65521),InputCheck=>o.InputCheck,Verbose=>o.Verbose); - ------------------------------------------------------------------------- ----------------------------- Documentation ----------------------------- ------------------------------------------------------------------------- - -beginDocumentation() - -document {Key => SpecialFanoFourfolds, -Headline => "A package for working with Hodge-special fourfolds", -PARA {"This package contains several tools related to the rationality problem for cubic fourfolds, Gushel-Mukai fourfolds, and some other special Fano fourfolds. See ",HREF{"https://arxiv.org/abs/2204.11518","arXiv:2204.11518"}," for some applications."}, -PARA {"The following are some papers that have benefited from this package."}, -References => UL{ -{"M. Hoff and G. Staglianò, ",EM"Explicit constructions of K3 surfaces and unirational Noether-Lefschetz divisors",", available at ",HREF{"https://arxiv.org/abs/2110.15819","arXiv:2110.15819"}," (2021)."}, -{"G. Staglianò, ",EM"Some new rational Gushel fourfolds",", available at ",HREF{"https://arxiv.org/abs/2003.07809","arXiv:2003.07809"}," (2020)."}, -{"G. Staglianò, ",EM"On some families of Gushel-Mukai fourfolds",", available at ",HREF{"https://arxiv.org/abs/2002.07026","arXiv:2002.07026"}," (2020)."}, -{"M. Hoff and G. Staglianò, ",EM"New examples of rational Gushel-Mukai fourfolds",", available at ",HREF{"https://arxiv.org/abs/1910.12838","arXiv:1910.12838"}," (2020)."}, -{"F. Russo and G. Staglianò, ",EM"Trisecant Flops, their associated K3 surfaces and the rationality of some Fano fourfolds",", available at ",HREF{"https://arxiv.org/abs/1909.01263","arXiv:1909.01263"}," (2020)."}, -{"F. Russo and G. Staglianò, ",EM"Explicit rationality of some cubic fourfolds",", available at ",HREF{"https://arxiv.org/abs/1811.03502","arXiv:1811.03502"}," (2019)."}, -{"F. Russo and G. Staglianò, ",EM"Congruences of 5-secant conics and the rationality of some admissible cubic fourfolds",", available at ",HREF{"https://arxiv.org/abs/1707.00999","arXiv:1707.00999"}," (2018)."}}} - -document {Key => {SpecialGushelMukaiFourfold}, -Headline => "the class of all special Gushel-Mukai fourfolds", -PARA{"The general type of Gushel-Mukai fourfold (called ",EM "ordinary",") can be realized as the intersection of a smooth del Pezzo fivefold ", TEX///$\mathbb{G}(1,4)\cap\mathbb{P}^8\subset \mathbb{P}^8$///, " with a quadric hypersurface in ", TEX///$\mathbb{P}^8$///, ". A Gushel-Mukai fourfold is said to be ", EM"special", " if it contains a surface whose cohomology class ", EM "does not come", " from the Grassmannian ", TEX///$\mathbb{G}(1,4)$///, ". The special Gushel-Mukai fourfolds are parametrized by a countable union of (not necessarily irreducible) hypersurfaces in the corresponding moduli space, labelled by the integers ", TEX///$d \geq 10$///, " with ", TEX///$d = 0, 2, 4\ ({mod}\ 8)$///, "; the number ",TEX///$d$///," is called the discriminant of the fourfold. For precise definition and results, we refer mainly to the paper ", HREF{"https://arxiv.org/abs/1302.1398", "Special prime Fano fourfolds of degree 10 and index 2"}, ", by O. Debarre, A. Iliev, and L. Manivel."}, -PARA{"An object of the class ", TO SpecialGushelMukaiFourfold, " is basically represented by a couple ", TEX///(S,X)///, ", where ", TEX///$X$///, " is a Gushel-Mukai fourfold and ", TEX///$S$///, " is a surface contained in ", TEX///$X$///, ". The main constructor for the objects of the class is the function ", TO specialGushelMukaiFourfold,"."}, -SeeAlso => {(discriminant,SpecialGushelMukaiFourfold)}} - -document {Key => {(discriminant, SpecialCubicFourfold), (discriminant, HodgeSpecialFourfold)}, -Headline => "discriminant of a special cubic fourfold", -Usage => "discriminant X", -Inputs => {"X" => SpecialCubicFourfold}, -Outputs => {ZZ => {"the discriminant of ", TEX///$X$///}}, -PARA{"This calculation passes through the determination of the topological Euler characteristic of the surface contained in the fourfold, which is obtained thanks to the functions ", TO EulerCharacteristic, " and ", TO Euler, " (the option ", TT "Algorithm", " allows you to select the method)."}, -EXAMPLE {"X = specialCubicFourfold \"quintic del Pezzo surface\";", "time discriminant X"}, -SeeAlso => {(discriminant, SpecialGushelMukaiFourfold)}} - -document {Key => {(discriminant, SpecialGushelMukaiFourfold)}, -Headline => "discriminant of a special Gushel-Mukai fourfold", -Usage => "discriminant X", -Inputs => {"X" => SpecialGushelMukaiFourfold}, -Outputs => {ZZ => {"the discriminant of ", TEX///$X$///}}, -PARA{"This function applies a formula given in Section 7 of the paper ", HREF{"https://arxiv.org/abs/1302.1398", "Special prime Fano fourfolds of degree 10 and index 2"}, ", obtaining the data required through the functions ", TO cycleClass, ", ", TO EulerCharacteristic, " and ", TO Euler, " (the option ", TT "Algorithm", " allows you to select the method)."}, -EXAMPLE {"X = specialGushelMukaiFourfold \"tau-quadric\";", "time discriminant X"}, -SeeAlso => {(discriminant, SpecialCubicFourfold)}} - -undocumented{(expression, SpecialGushelMukaiFourfold), (describe, SpecialGushelMukaiFourfold)} - -document {Key => {Verbose, [specialCubicFourfold, Verbose], [specialGushelMukaiFourfold, Verbose], [mirrorFourfold, Verbose], [specialFourfold, Verbose], [parameterCount, Verbose], [associatedK3surface, Verbose], [associatedCastelnuovoSurface, Verbose], [detectCongruence, Verbose], [trisecantFlop, Verbose]}, -Headline => "request verbose feedback"} - -document {Key => {specialGushelMukaiFourfold, (specialGushelMukaiFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (specialGushelMukaiFourfold, Ideal, Ideal), [specialGushelMukaiFourfold, InputCheck]}, -Headline => "make a special Gushel-Mukai fourfold", -Usage => "specialGushelMukaiFourfold(S,X)", -Inputs => {"S" => EmbeddedProjectiveVariety => {"a smooth irreducible surface ", TEX///$S\subset\mathbb{P}^8$///}, "X" => EmbeddedProjectiveVariety => {"a smooth prime Fano fourfold ", TEX///$X\subset \mathbb{P}^8$///, " of degree 10 and sectional genus 6, which contains the surface ", TEX///$S$///}}, -Outputs => {SpecialGushelMukaiFourfold => {"the special Gushel-Mukai fourfold corresponding to the pair ", TEX///$(S,X)$///}}, -PARA{"In the following example, we define a Gushel-Mukai fourfold containing a so-called ", TEX///$\tau$///, "-quadric."}, -EXAMPLE {"K = ZZ/33331; x = gens ring PP_K^8;", "S = projectiveVariety ideal(x_6-x_7, x_5, x_3-x_4, x_1, x_0-x_4, x_2*x_7-x_4*x_8);", "X = projectiveVariety ideal(x_4*x_6-x_3*x_7+x_1*x_8, x_4*x_5-x_2*x_7+x_0*x_8, x_3*x_5-x_2*x_6+x_0*x_8+x_1*x_8-x_5*x_8, x_1*x_5-x_0*x_6+x_0*x_7+x_1*x_7-x_5*x_7, x_1*x_2-x_0*x_3+x_0*x_4+x_1*x_4-x_2*x_7+x_0*x_8, x_0^2+x_0*x_1+x_1^2+x_0*x_2+2*x_0*x_3+x_1*x_3+x_2*x_3+x_3^2-x_0*x_4-x_1*x_4-2*x_2*x_4-x_3*x_4-2*x_4^2+x_0*x_5+x_2*x_5+x_5^2+2*x_0*x_6+x_1*x_6+2*x_2*x_6+x_3*x_6+x_5*x_6+x_6^2-3*x_4*x_7+2*x_5*x_7-x_7^2+x_1*x_8+x_3*x_8-3*x_4*x_8+2*x_5*x_8+x_6*x_8-x_7*x_8);", "time F = specialGushelMukaiFourfold(S,X);", "time describe F", "assert(F == X)"}, -SeeAlso => {(specialGushelMukaiFourfold, EmbeddedProjectiveVariety), (specialGushelMukaiFourfold, String, Ring), specialFourfold}} - -document {Key => {(specialGushelMukaiFourfold, EmbeddedProjectiveVariety),(specialGushelMukaiFourfold, Ideal)}, -Headline => "random special Gushel-Mukai fourfold", -Usage => "specialGushelMukaiFourfold S -specialGushelMukaiFourfold (S%Y)", -Inputs => {"S" => EmbeddedProjectiveVariety => {"a smooth irreducible surface ",TEX///$S$///," which is a ",TO2{(symbol %,MultiprojectiveVariety,MultiprojectiveVariety),"subvariety"}," of a del Pezzo fivefold/sixfold ",TEX///$Y$///,"; alternatively, you can pass the ideal of ",TEX///$S$///," in ",TEX///$Y$///," (e.g., an ideal in the ring ", TO Grass, TEX///$(1,4)$///, ")"}}, -Outputs => {SpecialGushelMukaiFourfold => {"a random special Gushel-Mukai fourfold containing the given surface"}}, -EXAMPLE {"Y = GG(ZZ/33331,1,4);", "-- cubic scroll in G(1,4)"|newline|"S = schubertCycle({2,0},Y) * schubertCycle({1,0},Y) * schubertCycle({1,0},Y);", "X = specialGushelMukaiFourfold S;", "discriminant X"}, -SeeAlso => {(specialGushelMukaiFourfold, String, Ring),(symbol %,MultiprojectiveVariety,MultiprojectiveVariety)}} - -document {Key => {(specialGushelMukaiFourfold, String, Ring), (specialGushelMukaiFourfold, String)}, -Headline => "random special Gushel-Mukai fourfold of a given type", -Usage => "specialGushelMukaiFourfold(n,K) -specialGushelMukaiFourfold n", -Inputs => {"n" => String => {"the name of some known type of Gushel-Mukai fourfolds"}, "K" => {"the coefficient ring"}}, -Outputs => {SpecialGushelMukaiFourfold => {"a random special Gushel-Mukai fourfold of the indicated type over ",TT"K"}}, -EXAMPLE {"X = specialGushelMukaiFourfold(\"cubic scroll\",ZZ/65521);", "describe X"}, -References => UL{ -{"O. Debarre, A. Iliev, and L. Manivel, ",EM"Special prime Fano fourfolds of degree 10 and index 2",", available at ",HREF{"https://arxiv.org/abs/1302.1398","arXiv:1302.1398"}," (2014)."}, -{"G. Staglianò, ",EM"On some families of Gushel-Mukai fourfolds",", available at ",HREF{"https://arxiv.org/abs/2002.07026","arXiv:2002.07026"}," (2020)."}}, -SeeAlso => {(specialGushelMukaiFourfold, EmbeddedProjectiveVariety), GMtables}} - -document {Key => {toGrass, (toGrass, SpecialGushelMukaiFourfold)}, -Headline => "Gushel morphism from a GM fourfold to GG(1,4)", -Usage => "toGrass X", -Inputs => {"X" => SpecialGushelMukaiFourfold}, -Outputs => {MultirationalMap => {"a linear morphism from ", TEX///$X$///, " into the ",TO2{GrassmannianVariety,"Grassmannian"}," ", TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///, ", Plücker embedded, which is an embedding when ",TEX///$X$///," is of ordinary type"}}, -EXAMPLE {"x = gens ring PP_(ZZ/33331)^8;", "X = specialGushelMukaiFourfold(ideal(x_6-x_7, x_5, x_3-x_4, x_1, x_0-x_4, x_2*x_7-x_4*x_8), ideal(x_4*x_6-x_3*x_7+x_1*x_8, x_4*x_5-x_2*x_7+x_0*x_8, x_3*x_5-x_2*x_6+x_0*x_8+x_1*x_8-x_5*x_8, x_1*x_5-x_0*x_6+x_0*x_7+x_1*x_7-x_5*x_7, x_1*x_2-x_0*x_3+x_0*x_4+x_1*x_4-x_2*x_7+x_0*x_8, x_0^2+x_0*x_1+x_1^2+x_0*x_2+2*x_0*x_3+x_1*x_3+x_2*x_3+x_3^2-x_0*x_4-x_1*x_4-2*x_2*x_4-x_3*x_4-2*x_4^2+x_0*x_5+x_2*x_5+x_5^2+2*x_0*x_6+x_1*x_6+2*x_2*x_6+x_3*x_6+x_5*x_6+x_6^2-3*x_4*x_7+2*x_5*x_7-x_7^2+x_1*x_8+x_3*x_8-3*x_4*x_8+2*x_5*x_8+x_6*x_8-x_7*x_8));", "time toGrass X", "show oo"}, -SeeAlso => {(toGrass, EmbeddedProjectiveVariety), (symbol ===>, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}} - -document {Key => {(toGrass, EmbeddedProjectiveVariety)}, -Headline => "embedding of an ordinary Gushel-Mukai fourfold or a del Pezzo variety into GG(1,4)", -Usage => "toGrass X", -Inputs => {"X" => EmbeddedProjectiveVariety => {"an ordinary Gushel-Mukai fourfold, or a del Pezzo variety of dimension at least 4 (e.g., a sixfold projectively equivalent to ", TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///,")"}}, -Outputs => {MultirationalMap => {"an embedding of ", TEX///$X$///, " into the ",TO2{GrassmannianVariety,"Grassmannian"}," ", TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///, ", Plücker embedded"}}, -EXAMPLE {"x = gens ring PP_(ZZ/33331)^8;", "X = projectiveVariety ideal(x_4*x_6-x_3*x_7+x_1*x_8, x_4*x_5-x_2*x_7+x_0*x_8, x_3*x_5-x_2*x_6+x_0*x_8+x_1*x_8-x_5*x_8, x_1*x_5-x_0*x_6+x_0*x_7+x_1*x_7-x_5*x_7, x_1*x_2-x_0*x_3+x_0*x_4+x_1*x_4-x_2*x_7+x_0*x_8);", "time toGrass X", "show oo"}, -SeeAlso => {(toGrass,SpecialGushelMukaiFourfold), (symbol ===>, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}} - -undocumented{(cycleClass, SpecialGushelMukaiFourfold)} - -document {Key => {GMtables, (GMtables, ZZ, Ring), (GMtables, ZZ), [GMtables, Verify]}, -Headline => "make examples of reducible subschemes of P^5", -Usage => "GMtables(i,K)", -Inputs => {"i" => ZZ => {"an integer between 1 and 21"}, "K" => Ring => {"the coefficient ring"}}, -Outputs => {{"a triple of ",TO2{EmbeddedProjectiveVariety,"varieties"},", ",TEX///$(B,V,C)$///, ", which represents a reducible subscheme of ", TEX///$\mathbb{P}^5$///, ", in accordance with the 21 examples listed in Table 2 of the paper ", HREF{"https://arxiv.org/abs/2002.07026", "On some families of Gushel-Mukai fourfolds"}, "."}}, -EXAMPLE {"(B,V,C) := GMtables(1,ZZ/33331)", "B * V == C"}, -PARA{"The corresponding example of fourfold reported in Table 1 of the aforementioned paper can be obtained as follows."}, -EXAMPLE {"psi = rationalMap(ideal B,Dominant=>2);", "X = specialGushelMukaiFourfold psi ideal V;"}, -PARA{"This is basically the same as doing this:"}, -EXAMPLE {"specialGushelMukaiFourfold(\"1\",ZZ/33331);"}, -SeeAlso => {(specialGushelMukaiFourfold,String,Ring),(specialGushelMukaiFourfold,Array,Array)}} - -undocumented {(GMtables, Ring, String), (GMtables,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety)}; - -document {Key => {parameterCount, (parameterCount, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (parameterCount, HodgeSpecialFourfold)}, -Headline => "count of parameters", -Usage => "parameterCount(S,X)", -Inputs => {"S" => EmbeddedProjectiveVariety, "X" => EmbeddedProjectiveVariety => {"such that ", TEX///$S\subseteq X$///}}, -Outputs => {{"a count of parameters to estimate the dimensions of the corresponding Hilbert schemes"}}, -PARA{"See ",TO (parameterCount, SpecialCubicFourfold)," and ", TO (parameterCount, SpecialGushelMukaiFourfold)," for more precise applications of this function."}, -PARA{"The following calculation shows that the family of complete intersections of 3 quadrics in ",TEX///$\mathbb{P}^5$///," containing a rational normal quintic curve has codimension 1 in the space of all such complete intersections."}, -EXAMPLE {"K = ZZ/33331; S = PP_K^(1,5);", "X = random({{2},{2},{2}},S);", "time parameterCount(S,X,Verbose=>true)"}, -SeeAlso => {(parameterCount, SpecialCubicFourfold), (parameterCount, SpecialGushelMukaiFourfold), normalSheaf}} - -document {Key => {(parameterCount, SpecialCubicFourfold)}, -Headline => "count of parameters in the moduli space of GM fourfolds", -Usage => "parameterCount X", -Inputs => {"X" => SpecialCubicFourfold => {"a special cubic fourfold containing a surface ", TEX///$S$///}}, -Outputs => {ZZ => {"an upper bound for the codimension in the moduli space of cubic fourfolds of the locus of cubic fourfolds that contain a surface belonging to the same irreducible component of the Hilbert scheme containing ", TEX///$[S]$///}, Sequence => {"the triple of integers: ", TEX///$(h^0(I_{S/P^5}(3)), h^0(N_{S/P^5}), h^0(N_{S/X}))$///}}, -PARA{"This function implements a parameter count explained in the paper ", HREF{"https://arxiv.org/abs/1503.05256", "Unirationality of moduli spaces of special cubic fourfolds and K3 surfaces"}, ", by H. Nuer."}, -PARA{"Below, we show that the closure of the locus of cubic fourfolds containing a Veronese surface has codimension at most one (hence exactly one) in the moduli space of cubic fourfolds. Then, by the computation of the discriminant, we deduce that the cubic fourfolds containing a Veronese surface describe the Hassett's divisor ", TEX///$\mathcal{C}_{20}$///}, -EXAMPLE {"K = ZZ/33331; V = PP_K^(2,2);", "X = specialCubicFourfold V;", "time parameterCount(X,Verbose=>true)", "discriminant X"}, -SeeAlso => {(parameterCount, SpecialGushelMukaiFourfold), normalSheaf}} - -document {Key => {(parameterCount, SpecialGushelMukaiFourfold)}, -Headline => "count of parameters in the moduli space of GM fourfolds", -Usage => "parameterCount X", -Inputs => {"X" => SpecialGushelMukaiFourfold => {"a special GM fourfold containing a surface ", TEX///$S$///, " and contained in a del Pezzo fivefold ", TEX///$Y$///}}, -Outputs => {ZZ => {"an upper bound for the codimension in the moduli space of GM fourfolds of the locus of GM fourfolds that contain a surface belonging to the same irreducible component of the Hilbert scheme of ", TEX///$Y$///, " that contains ", TEX///$[S]$///}, Sequence => {"the triple of integers: ", TEX///$(h^0(I_{S/Y}(2)), h^0(N_{S/Y}), h^0(N_{S/X}))$///}}, -PARA{"This function implements a parameter count explained in the paper ", HREF{"https://arxiv.org/abs/2002.07026", "On some families of Gushel-Mukai fourfolds"}, "."}, -PARA{"Below, we show that the closure of the locus of GM fourfolds containing a cubic scroll has codimension at most one (hence exactly one) in the moduli space of GM fourfolds."}, -EXAMPLE {"G = GG(ZZ/33331,1,4);", "S = (schubertCycle({2,0},G) * random({{1},{1}},0_G))%G", "X = specialGushelMukaiFourfold S;", "time parameterCount(X,Verbose=>true)", "discriminant X"}, -SeeAlso => {(parameterCount, SpecialCubicFourfold), normalSheaf}} - -document {Key => {normalSheaf, (normalSheaf, EmbeddedProjectiveVariety), (normalSheaf, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}, -Headline => "normal sheaf", -Usage => "normalSheaf X"|newline|"normalSheaf(X % Y)"|newline|"normalSheaf(X,Y)", -Inputs => {"X" => EmbeddedProjectiveVariety, "Y" => EmbeddedProjectiveVariety => {" such that ",TEX///$X\subset Y$///," (if not given, it is taken to be the ",TO2{ambientVariety,"ambient variety"}," of ",TEX///$X$///,")"}}, -Outputs => {CoherentSheaf => {"the normal sheaf ", TEX///$\mathcal{N}_{X, Y}$///, " of ", TEX///$X$///, " in ", TEX///$Y$///}}, -EXAMPLE {"X = PP_(ZZ/65521)^(2,2);", "Y = random(2,X);", "N = normalSheaf X;", "N' = normalSheaf(X,Y);", "rank HH^0 N", "rank HH^0 N'"}} - -undocumented {(normalSheaf, MultiprojectiveVariety), (normalSheaf, MultiprojectiveVariety, MultiprojectiveVariety)} - -document {Key => {isAdmissible, (isAdmissible, ZZ), (isAdmissible, SpecialCubicFourfold)}, -Headline => "whether an integer is admissible (in the sense of the theory of cubic fourfolds)", -Usage => "isAdmissible d", -Inputs => {"d" => ZZ}, -Outputs => {Boolean => {"whether ", TT"d", " is admissible, i.e., it is an even integer ", TT"d>6", " which is not divisible by 4, 9 or any odd prime congruent to 2 modulo 3"}}, -EXAMPLE{"select(150,isAdmissible)"}, -SeeAlso => {isAdmissibleGM}} - -document {Key => {isAdmissibleGM, (isAdmissibleGM, ZZ), (isAdmissibleGM, SpecialGushelMukaiFourfold)}, -Headline => "whether an integer is admissible (in the sense of the theory of GM fourfolds)", -Usage => "isAdmissibleGM d", -Inputs => {"d" => ZZ}, -Outputs => {Boolean => {"whether ",TEX///$d$///," is an integer ",TEX///$>$///," 8 and ",TEX///$\equiv$///," 2 or 4 (mod 8) such that the only odd primes that divide ",TEX///$d$///," are ",TEX///$\equiv$///," 1 (mod 4). In other words, whether a GM fourfold of discriminant ", TT"d", " has an associated K3 surface."}}, -EXAMPLE{"select(140,isAdmissibleGM)"}, -SeeAlso => {isAdmissible}} - -document {Key => {CongruenceOfCurves}, -Headline => "the class of all congruences of secant curves to surfaces", -PARA{"Objects of this type are created by ",TO detectCongruence,"."}} - -document {Key => {(symbol SPACE, CongruenceOfCurves, EmbeddedProjectiveVariety), (symbol SPACE, CongruenceOfCurves, Ideal)}, -Headline => "get the curve of a congruence passing through a point", -Usage => "f(p)", -Inputs => {"f" => CongruenceOfCurves => {"a congruence of curves to a surface inside a variety ", TEX///$Y$///}, "p" => EmbeddedProjectiveVariety => {"a general point on ",TEX///$Y$///," (that is, a point on ",TEX///$Y$///," outside a certain proper Zariski closed subset)"}}, -Outputs => {EmbeddedProjectiveVariety => {"the unique curve of the congruence ", TEX///$f$///, " that passes through ", TEX///$p$///}}, -EXAMPLE {"X = specialCubicFourfold surface {3,4};", "f = detectCongruence(X,1);","C = f point ambient X;","member(C,f)","assert oo"}, -SeeAlso => {detectCongruence, (member, EmbeddedProjectiveVariety, CongruenceOfCurves)}} - -document {Key => {(map, CongruenceOfCurves)}, -Headline => "compute the parameter space of a congruence", -Usage => "map f", -Inputs => {"f" => CongruenceOfCurves => {"a congruence of curves to a surface inside a variety ", TEX///$Y$///}}, -Outputs => {MultirationalMap => {"a dominant map from ",TEX///$Y$///," to the parameter space of ",TEX///$f$///," whose general fibers are the curves of the congruence"}}, -EXAMPLE {"S = PP_(ZZ/65521)[2,2];","Y = ambient S;","X = specialCubicFourfold S;","f = detectCongruence(X,1);","F = map f;","Q = target F","f;","p = point Y;","assert(f p == F^* F p)"}, -SeeAlso => {detectCongruence}} - -document {Key => {(member, EmbeddedProjectiveVariety, CongruenceOfCurves)}, -Headline => "test membership in a congruence of curves", -Usage => "member(C,f)", -Inputs => {"C" => EmbeddedProjectiveVariety => {"a curve"}, "f" => CongruenceOfCurves}, -Outputs => {Boolean => {"whether the curve ",TEX///$C$///," belongs to the congruence ", TEX///$f$///}}, -SeeAlso => {(symbol SPACE, CongruenceOfCurves, EmbeddedProjectiveVariety)}} - -undocumented{(toString, CongruenceOfCurves), (net, CongruenceOfCurves), (texMath, CongruenceOfCurves)} - -document {Key => {detectCongruence, (detectCongruence, HodgeSpecialFourfold, ZZ), (detectCongruence, HodgeSpecialFourfold)}, -Headline => "detect and return a congruence of secant curves to a surface", -PARA{"See ",TO (detectCongruence, SpecialCubicFourfold)," and ",TO (detectCongruence, SpecialGushelMukaiFourfold),"."}} - -document {Key => {(detectCongruence, SpecialCubicFourfold, ZZ), (detectCongruence, SpecialCubicFourfold)}, -Headline => "detect and return a congruence of (3e-1)-secant curves of degree e", -Usage => "detectCongruence X"|newline|"detectCongruence(X,e)", -Inputs => {"X" => SpecialCubicFourfold => {"containing a surface ", TEX///$S\subset\mathbb{P}^5$///}, "e" => ZZ => {"a positive integer (optional but recommended)"}}, -Outputs => {CongruenceOfCurves => {"that is a function which takes a general point ", TEX///$p\in\mathbb{P}^5$///, " (that is, outside a certain proper Zariski closed subset of ",TEX///$\mathbb{P}^5$///,") and returns the unique rational curve of degree ", TEX///$e$///, ", ", TEX///$(3e-1)$///, "-secant to ", TEX///$S$///, ", and passing through ", TEX///$p$///, " (an error is thrown if such a curve does not exist or is not unique)"}}, -EXAMPLE {"-- A general cubic fourfold of discriminant 26"|newline|"X = specialCubicFourfold(\"3-nodal septic scroll\",ZZ/33331);", "describe X", "time f = detectCongruence(X,Verbose=>true);", "p := point ambient X -- random point on P^5", "time C = f p; -- 5-secant conic to the surface", "assert(dim C == 1 and degree C == 2 and dim(C * surface X) == 0 and degree(C * surface X) == 5 and isSubset(p, C))"}, -SeeAlso => {(detectCongruence, SpecialGushelMukaiFourfold, ZZ), coneOfLines}} - -document {Key => {(detectCongruence, SpecialGushelMukaiFourfold, ZZ), (detectCongruence, SpecialGushelMukaiFourfold)}, -Headline => "detect and return a congruence of (2e-1)-secant curves of degree e inside a del Pezzo fivefold", -Usage => "detectCongruence X"|newline|"detectCongruence(X,e)", -Inputs => {"X" => SpecialGushelMukaiFourfold => {"containing a surface ", TEX///$S\subset Y$///,", where ",TEX///$Y$///," denotes the unique del Pezzo fivefold containing the fourfold ",TEX///$X$///}, "e" => ZZ => {"a positive integer (optional but recommended)"}}, -Outputs => {CongruenceOfCurves => {"that is a function which takes a general point ", TEX///$p\in Y$///, "(that is, a point on ",TEX///$Y$///," outside a certain proper Zariski closed subset) and returns the unique rational curve of degree ", TEX///$e$///, ", ", TEX///$(2e-1)$///, "-secant to ", TEX///$S$///, ", contained in ",TEX///$Y$///," and passing through ", TEX///$p$///, " (an error is thrown if such a curve does not exist or is not unique)"}}, -EXAMPLE{"-- A GM fourfold of discriminant 20"|newline|"X = specialGushelMukaiFourfold(\"17\",ZZ/33331);", "describe X", "time f = detectCongruence(X,Verbose=>true);", "Y = ambientFivefold X; -- del Pezzo fivefold containing X", "p := point Y -- random point on Y", "time C = f p; -- 3-secant conic to the surface", "S = surface X;", "assert(dim C == 1 and degree C == 2 and dim(C*S) == 0 and degree(C*S) == 3 and isSubset(p,C) and isSubset(C,Y))"}, -SeeAlso => {(detectCongruence, SpecialCubicFourfold, ZZ), coneOfLines}} - -document {Key => {SpecialCubicFourfold}, -Headline => "the class of all special cubic fourfolds", -PARA{"A cubic fourfold is a smooth cubic hypersurface in ", TEX///$\mathbb{P}^5$///, ". A cubic fourfold ", TEX///$X\subset \mathbb{P}^5$///, " is ", EM "special", " of discriminant ", TEX///$d>6$///, " if it contains an algebraic surface ", TEX///$S$///, ", and the discriminant of the saturated lattice spanned by ", TEX///$h^2$///, " and ", TEX///$[S]$///, " in ", TEX///$H^{2,2}(X,\mathbb{Z}):=H^4(X,\mathbb{Z})\cap H^2(\Omega_X^2)$///, " is ", TEX///$d$///, ", where ", TEX///$h$///, " denotes the class of a hyperplane section of ", TEX///$X$///, ". The set ", TEX///$\mathcal{C}_d$///, " of special cubic fourfolds of discriminant ", TEX///$d$///, " is either empty or an irreducible divisor inside the moduli space of cubic fourfolds ", TEX///$\mathcal{C}$///, ". Moreover, ", TEX///$\mathcal{C}_d\neq \emptyset$///, " if and only if ", TEX///$d>6$///, " and ", TEX///$d=$///, "0 or 2 (mod 6). For the general theory, see the papers ", HREF{"https://link.springer.com/article/10.1023/A:1001706324425", "Special cubic fourfolds"}, " and ", HREF{"http://imperium.lenin.ru/~kaledin/math/hasset.pdf", "Some rational cubic fourfolds"}, ", by B. Hassett."}, -PARA{"An object of the class ", TO SpecialCubicFourfold, " is basically represented by a couple ", TEX///(S,X)///, ", where ", TEX///$X$///, " is a cubic fourfold and ", TEX///$S$///, " is a surface contained in ", TEX///$X$///, ". The surface ", TEX///$S$///, " is required to be smooth or with at most a finite number ", TEX///$n$///, " of non-normal nodes. This number ", TEX///$n$///, " (if known) can be specified manually using the option ", TT "NumNodes", ". The main constructor for the objects of the class is the function ", TO specialCubicFourfold,"."}, -SeeAlso => {(discriminant,SpecialCubicFourfold)}} - -undocumented{(expression, SpecialCubicFourfold), (describe, SpecialCubicFourfold)} - -undocumented{InputCheck, NumNodes} - -document {Key => {specialCubicFourfold, (specialCubicFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (specialCubicFourfold, Ideal, Ideal), (specialCubicFourfold, Ideal, RingElement), [specialCubicFourfold, NumNodes], [specialCubicFourfold, InputCheck]}, -Headline => "make a special cubic fourfold", -Usage => "specialCubicFourfold(S,X)"|newline|"specialCubicFourfold(S,X,NumNodes=>n)", -Inputs => {"S" => EmbeddedProjectiveVariety => {"an irreducible surface ", TEX///$S\subset\mathbb{P}^5$///, ", which has as singularities only a finite number ",TEX///$n\geq 0$///," of non-normal nodes (this number ",TEX///$n$///," should be passed with the option ", TT "NumNodes",", otherwise it is obtained using a probabilistic method)"}, "X" => EmbeddedProjectiveVariety => {"a smooth cubic fourfold ", TEX///$X\subset \mathbb{P}^5$///, " containing the surface ", TEX///$S$///}}, -Outputs => {SpecialCubicFourfold => {"the special cubic fourfold corresponding to the pair ", TEX///$(S,X)$///}}, -PARA{"In the example below, we define a cubic fourfold containing a rational scroll of degree 7 with 3 nodes."}, -EXAMPLE {"K = ZZ/33331; x = gens ring PP_K^5;", "S = projectiveVariety ideal(x_0*x_2*x_3-2*x_1*x_2*x_3-x_1*x_3^2-x_2*x_3^2-x_0*x_1*x_4+2*x_1^2*x_4-x_1*x_2*x_4+x_2^2*x_4+2*x_0*x_3*x_4-x_1*x_3*x_4-x_1*x_4^2+x_1*x_3*x_5, x_1^2*x_3-4*x_1*x_2*x_3-x_0*x_3^2-3*x_1*x_3^2-2*x_2*x_3^2+2*x_0^2*x_4-9*x_0*x_1*x_4+11*x_1^2*x_4-x_0*x_2*x_4-2*x_1*x_2*x_4+2*x_2^2*x_4+12*x_0*x_3*x_4-7*x_1*x_3*x_4-4*x_3^2*x_4+x_0*x_4^2-6*x_1*x_4^2+4*x_2*x_4^2-2*x_3*x_4^2-2*x_4^3-x_0*x_1*x_5+x_1^2*x_5+2*x_1*x_2*x_5+3*x_0*x_3*x_5+2*x_1*x_3*x_5-x_3^2*x_5-x_0*x_4*x_5-4*x_1*x_4*x_5+3*x_2*x_4*x_5+2*x_3*x_4*x_5-x_1*x_5^2, x_0*x_1*x_3-7*x_1*x_2*x_3-3*x_0*x_3^2-4*x_1*x_3^2-3*x_2*x_3^2+x_3^3+3*x_0^2*x_4-14*x_0*x_1*x_4+17*x_1^2*x_4-x_0*x_2*x_4-3*x_1*x_2*x_4+3*x_2^2*x_4+19*x_0*x_3*x_4-9*x_1*x_3*x_4-x_2*x_3*x_4-6*x_3^2*x_4+x_0*x_4^2-9*x_1*x_4^2+6*x_2*x_4^2-3*x_3*x_4^2-3*x_4^3-2*x_0*x_1*x_5+2*x_1^2*x_5+4*x_1*x_2*x_5+5*x_0*x_3*x_5+4*x_1*x_3*x_5-2*x_3^2*x_5-2*x_0*x_4*x_5-7*x_1*x_4*x_5+5*x_2*x_4*x_5+3*x_3*x_4*x_5-2*x_1*x_5^2, x_0^2*x_3-12*x_1*x_2*x_3-6*x_0*x_3^2-6*x_1*x_3^2-5*x_2*x_3^2+2*x_3^3+5*x_0^2*x_4-24*x_0*x_1*x_4+29*x_1^2*x_4-x_0*x_2*x_4-5*x_1*x_2*x_4+5*x_2^2*x_4+32*x_0*x_3*x_4-14*x_1*x_3*x_4-2*x_2*x_3*x_4-10*x_3^2*x_4+x_0*x_4^2-15*x_1*x_4^2+10*x_2*x_4^2-5*x_3*x_4^2-5*x_4^3-3*x_0*x_1*x_5+3*x_1^2*x_5+6*x_1*x_2*x_5+8*x_0*x_3*x_5+7*x_1*x_3*x_5-3*x_3^2*x_5-3*x_0*x_4*x_5-11*x_1*x_4*x_5+8*x_2*x_4*x_5+5*x_3*x_4*x_5-3*x_1*x_5^2, x_1*x_2^2+6*x_1*x_2*x_3+2*x_0*x_3^2+3*x_1*x_3^2+2*x_2*x_3^2-x_3^3-3*x_0^2*x_4+12*x_0*x_1*x_4-14*x_1^2*x_4-2*x_2^2*x_4-15*x_0*x_3*x_4+6*x_1*x_3*x_4+x_2*x_3*x_4+5*x_3^2*x_4+x_0*x_4^2+8*x_1*x_4^2-5*x_2*x_4^2+2*x_3*x_4^2+2*x_4^3+x_0*x_1*x_5-2*x_1^2*x_5-4*x_1*x_2*x_5-4*x_0*x_3*x_5-3*x_1*x_3*x_5+2*x_3^2*x_5+2*x_0*x_4*x_5+7*x_1*x_4*x_5-4*x_2*x_4*x_5-2*x_3*x_4*x_5+2*x_1*x_5^2, x_0*x_2^2+10*x_1*x_2*x_3+3*x_0*x_3^2+5*x_1*x_3^2+4*x_2*x_3^2-x_3^3-5*x_0^2*x_4+19*x_0*x_1*x_4-22*x_1^2*x_4-x_0*x_2*x_4+3*x_1*x_2*x_4-4*x_2^2*x_4-24*x_0*x_3*x_4+9*x_1*x_3*x_4+x_2*x_3*x_4+8*x_3^2*x_4+2*x_0*x_4^2+11*x_1*x_4^2-7*x_2*x_4^2+4*x_3*x_4^2+3*x_4^3+2*x_0*x_1*x_5-4*x_1^2*x_5-7*x_1*x_2*x_5-7*x_0*x_3*x_5-5*x_1*x_3*x_5-x_2*x_3*x_5+3*x_3^2*x_5+4*x_0*x_4*x_5+12*x_1*x_4*x_5-7*x_2*x_4*x_5-3*x_3*x_4*x_5+4*x_1*x_5^2, x_1^2*x_2+17*x_1*x_2*x_3+6*x_0*x_3^2+9*x_1*x_3^2+7*x_2*x_3^2-2*x_3^3-9*x_0^2*x_4+36*x_0*x_1*x_4-44*x_1^2*x_4+3*x_0*x_2*x_4+5*x_1*x_2*x_4-7*x_2^2*x_4-47*x_0*x_3*x_4+21*x_1*x_3*x_4+2*x_2*x_3*x_4+16*x_3^2*x_4+24*x_1*x_4^2-16*x_2*x_4^2+7*x_3*x_4^2+7*x_4^3+3*x_0*x_1*x_5-6*x_1^2*x_5-9*x_1*x_2*x_5-12*x_0*x_3*x_5-8*x_1*x_3*x_5+5*x_3^2*x_5+5*x_0*x_4*x_5+19*x_1*x_4*x_5-12*x_2*x_4*x_5-7*x_3*x_4*x_5+5*x_1*x_5^2, x_0*x_1*x_2+29*x_1*x_2*x_3+11*x_0*x_3^2+15*x_1*x_3^2+12*x_2*x_3^2-4*x_3^3-16*x_0^2*x_4+62*x_0*x_1*x_4-74*x_1^2*x_4+5*x_0*x_2*x_4+9*x_1*x_2*x_4-12*x_2^2*x_4-80*x_0*x_3*x_4+35*x_1*x_3*x_4+4*x_2*x_3*x_4+27*x_3^2*x_4+40*x_1*x_4^2-27*x_2*x_4^2+12*x_3*x_4^2+12*x_4^3+5*x_0*x_1*x_5-10*x_1^2*x_5-16*x_1*x_2*x_5-21*x_0*x_3*x_5-14*x_1*x_3*x_5+9*x_3^2*x_5+9*x_0*x_4*x_5+33*x_1*x_4*x_5-21*x_2*x_4*x_5-12*x_3*x_4*x_5+9*x_1*x_5^2, x_0^2*x_2+49*x_1*x_2*x_3+19*x_0*x_3^2+25*x_1*x_3^2+20*x_2*x_3^2-7*x_3^3-28*x_0^2*x_4+106*x_0*x_1*x_4-124*x_1^2*x_4+8*x_0*x_2*x_4+16*x_1*x_2*x_4-20*x_2^2*x_4-134*x_0*x_3*x_4+58*x_1*x_3*x_4+7*x_2*x_3*x_4+45*x_3^2*x_4+66*x_1*x_4^2-45*x_2*x_4^2+20*x_3*x_4^2+20*x_4^3+9*x_0*x_1*x_5-18*x_1^2*x_5-28*x_1*x_2*x_5-37*x_0*x_3*x_5-23*x_1*x_3*x_5+16*x_3^2*x_5+16*x_0*x_4*x_5+57*x_1*x_4*x_5-36*x_2*x_4*x_5-20*x_3*x_4*x_5+16*x_1*x_5^2, x_1^3+47*x_1*x_2*x_3+18*x_0*x_3^2+23*x_1*x_3^2+19*x_2*x_3^2-7*x_3^3-24*x_0^2*x_4+97*x_0*x_1*x_4-117*x_1^2*x_4+8*x_0*x_2*x_4+16*x_1*x_2*x_4-19*x_2^2*x_4-127*x_0*x_3*x_4+54*x_1*x_3*x_4+7*x_2*x_3*x_4+42*x_3^2*x_4-x_0*x_4^2+62*x_1*x_4^2-42*x_2*x_4^2+19*x_3*x_4^2+19*x_4^3+9*x_0*x_1*x_5-16*x_1^2*x_5-25*x_1*x_2*x_5-33*x_0*x_3*x_5-23*x_1*x_3*x_5+14*x_3^2*x_5+14*x_0*x_4*x_5+51*x_1*x_4*x_5-33*x_2*x_4*x_5-19*x_3*x_4*x_5+14*x_1*x_5^2, x_0*x_1^2+79*x_1*x_2*x_3+29*x_0*x_3^2+40*x_1*x_3^2+32*x_2*x_3^2-11*x_3^3-41*x_0^2*x_4+164*x_0*x_1*x_4-196*x_1^2*x_4+14*x_0*x_2*x_4+26*x_1*x_2*x_4-32*x_2^2*x_4-214*x_0*x_3*x_4+92*x_1*x_3*x_4+11*x_2*x_3*x_4+71*x_3^2*x_4-2*x_0*x_4^2+105*x_1*x_4^2-71*x_2*x_4^2+32*x_3*x_4^2+32*x_4^3+14*x_0*x_1*x_5-26*x_1^2*x_5-41*x_1*x_2*x_5-55*x_0*x_3*x_5-38*x_1*x_3*x_5+23*x_3^2*x_5+23*x_0*x_4*x_5+85*x_1*x_4*x_5-55*x_2*x_4*x_5-32*x_3*x_4*x_5+23*x_1*x_5^2, x_0^2*x_1+133*x_1*x_2*x_3+48*x_0*x_3^2+68*x_1*x_3^2+54*x_2*x_3^2-18*x_3^3-70*x_0^2*x_4+278*x_0*x_1*x_4-330*x_1^2*x_4+24*x_0*x_2*x_4+44*x_1*x_2*x_4-54*x_2^2*x_4-361*x_0*x_3*x_4+156*x_1*x_3*x_4+18*x_2*x_3*x_4+120*x_3^2*x_4-4*x_0*x_4^2+177*x_1*x_4^2-120*x_2*x_4^2+54*x_3*x_4^2+54*x_4^3+23*x_0*x_1*x_5-44*x_1^2*x_5-69*x_1*x_2*x_5-93*x_0*x_3*x_5-63*x_1*x_3*x_5+39*x_3^2*x_5+39*x_0*x_4*x_5+144*x_1*x_4*x_5-93*x_2*x_4*x_5-54*x_3*x_4*x_5+39*x_1*x_5^2, x_0^3+224*x_1*x_2*x_3+80*x_0*x_3^2+115*x_1*x_3^2+91*x_2*x_3^2-30*x_3^3-119*x_0^2*x_4+470*x_0*x_1*x_4-555*x_1^2*x_4+41*x_0*x_2*x_4+75*x_1*x_2*x_4-91*x_2^2*x_4-608*x_0*x_3*x_4+263*x_1*x_3*x_4+30*x_2*x_3*x_4+202*x_3^2*x_4-8*x_0*x_4^2+297*x_1*x_4^2-202*x_2*x_4^2+91*x_3*x_4^2+91*x_4^3+39*x_0*x_1*x_5-76*x_1^2*x_5-118*x_1*x_2*x_5-158*x_0*x_3*x_5-105*x_1*x_3*x_5+67*x_3^2*x_5+68*x_0*x_4*x_5+245*x_1*x_4*x_5-158*x_2*x_4*x_5-91*x_3*x_4*x_5+67*x_1*x_5^2);", "X = projectiveVariety ideal(x_1^2*x_3+x_0*x_2*x_3-6*x_1*x_2*x_3-x_0*x_3^2-4*x_1*x_3^2-3*x_2*x_3^2+2*x_0^2*x_4-10*x_0*x_1*x_4+13*x_1^2*x_4-x_0*x_2*x_4-3*x_1*x_2*x_4+3*x_2^2*x_4+14*x_0*x_3*x_4-8*x_1*x_3*x_4-4*x_3^2*x_4+x_0*x_4^2-7*x_1*x_4^2+4*x_2*x_4^2-2*x_3*x_4^2-2*x_4^3-x_0*x_1*x_5+x_1^2*x_5+2*x_1*x_2*x_5+3*x_0*x_3*x_5+3*x_1*x_3*x_5-x_3^2*x_5-x_0*x_4*x_5-4*x_1*x_4*x_5+3*x_2*x_4*x_5+2*x_3*x_4*x_5-x_1*x_5^2);", "time F = specialCubicFourfold(S,X,NumNodes=>3);", "time describe F", "assert(F == X)"}, -SeeAlso => {(specialCubicFourfold, EmbeddedProjectiveVariety), (specialCubicFourfold, String, Ring), specialFourfold}} - -document {Key => {(specialCubicFourfold, EmbeddedProjectiveVariety), (specialCubicFourfold, Ideal)}, -Headline => "random special cubic fourfold", -Usage => "specialCubicFourfold S"|newline|"specialCubicFourfold(S,NumNodes=>n)", -Inputs => {"S" => EmbeddedProjectiveVariety => {"an irreducible surface in ", TEX///$\mathbb{P}^5$///}}, -Outputs => {SpecialCubicFourfold => {"a random cubic fourfold containing the given surface"}}, -EXAMPLE {"-- quintic del Pezzo surface"|newline|"S = surface({3,4},ZZ/33331);", "X = specialCubicFourfold S;", "discriminant X"}, -SeeAlso => {(specialCubicFourfold, String, Ring)}} - -document {Key => {(specialCubicFourfold, String, Ring), (specialCubicFourfold, String)}, -Headline => "random special cubic fourfold of a given type", -Usage => "specialCubicFourfold(n,K) -specialCubicFourfold n", -Inputs => {"n" => String => {"the name of some known type of cubic fourfolds"}, "K" => {"the coefficient ring"}}, -Outputs => {SpecialCubicFourfold => {"a random special cubic fourfold of the indicated type over ",TT"K"}}, -EXAMPLE {"X = specialCubicFourfold(\"3-nodal septic scroll\",ZZ/65521);", "describe X"}, -SeeAlso => (specialCubicFourfold, EmbeddedProjectiveVariety)} - -document {Key => {ambientFivefold, (ambientFivefold, HodgeSpecialFourfold)}, -Headline => "get the ambient fivefold of the Hodge-special fourfold", -Usage => "ambientFivefold X", -Inputs => {"X" => HodgeSpecialFourfold}, -Outputs => {EmbeddedProjectiveVariety => {"the ambient fivefold of ",TT"X"}}, -EXAMPLE {"S = surface {4,5,1};", "V = random(3,S);", "X = V * random(2,S);", "F = specialFourfold(S,X,V);", "ambientFivefold F"}, -PARA {"When ",TEX///$X$///," is a ",TO2{SpecialGushelMukaiFourfold,"GM fourfold"},", the ambient fivefold of ",TEX///$X$///," is a fivefold ",TEX///$Y\subset\mathbb{P}^8$///," of degree 5 such that ",TEX///$X\subset Y$///," is a quadric hypersurface. We have that the fourfold ",TEX///$X$///," is of ordinary type if and only if ",TEX///$Y$///," is smooth."}, -EXAMPLE { -"X = specialFourfold(\"21\",ZZ/33331);", -"describe X", -"Y = ambientFivefold X;", -"isSubset(X,Y)", -"Y!"}} - -document {Key => {(map, SpecialCubicFourfold)}, -Headline => "associated cubic map", -Usage => "map X", -Inputs => {"X" => SpecialCubicFourfold => {"containing a surface ", TEX///$S\subset\mathbb{P}^5$///}}, -Outputs => {RationalMap => {"the rational map from ", TEX///$\mathbb{P}^5$///, " defined by the linear system of cubics through ", TEX///$S$///}}} - -document {Key => {(map, SpecialGushelMukaiFourfold)}, -Headline => "associated quadratic map", -Usage => "map X", -Inputs => {"X" => SpecialGushelMukaiFourfold => {"containing a surface ", TEX///$S\subset Y$///, ", where ", TEX///$Y\subset\mathbb{P}^8$///, " is the unique del Pezzo fivefold containing ", TEX///$X$///}}, -Outputs => {RationalMap => {"the rational map from ", TEX///$Y$///, " defined by the linear system of quadrics through ", TEX///$S$///}}} - -document {Key => {surface, (surface, HodgeSpecialFourfold)}, -Headline => "get the special surface contained in the fourfold", -Usage => "surface X", -Inputs => {"X" => HodgeSpecialFourfold}, -Outputs => {EmbeddedProjectiveVariety => {"the special surface contained in the fourfold ",TT"X"}}, -EXAMPLE {"X = specialFourfold \"quintic del Pezzo surface\";", "V = ambientFivefold X;", "S = surface X;", "assert isSubset(S,X)"}, -SeeAlso => {(surface,List)}} - -document { -Key => {(surface, List), (surface, VisibleList, Ring), (surface, VisibleList, Option), (surface, VisibleList, Option), (surface, VisibleList, Ring, Option), (surface, VisibleList, Option, Option), (surface, VisibleList, Ring, Option, Option)}, -Headline => "get a rational surface", -Usage => "surface {a,i,j,k,...} -surface({a,i,j,k,...),K) -surface({a,i,j,k,...},K,NumNodes=>n,ambient=>m)", -Inputs => {List => {"a list ",TEX///$\{a,i,j,k,\ldots\}$///," of nonnegative integers"}}, -Outputs => {EmbeddedProjectiveVariety => {"the image of the rational map defined by the linear system of curves of degree ",TEX///$a$///," in ",TEX///$\mathbb{P}_{K}^2$///," having ",TEX///$i$///," random base points of multiplicity 1, ",TEX///$j$///," random base points of multiplicity 2, ",TEX///$k$///," random base points of multiplicity 3, and so on until the last integer in the given list."}}, -PARA{"In the example below, we take the image of the rational map defined by the linear system of septic plane curves with 3 random simple base points and 9 random double points."}, -EXAMPLE { -"S = surface {7,3,9};", -"coefficientRing S", -"T = surface({7,3,9},ZZ/33331);", -"X = specialCubicFourfold T;", -"coefficientRing X", -"describe X"}, -SeeAlso => {(rationalMap,PolynomialRing,List),(specialGushelMukaiFourfold,Array,Array)}} - -typValSurf := typicalValues#surface; -typicalValues#surface = Nothing; -document {Key => {(surface, MultiprojectiveVariety, MultiprojectiveVariety)}, -Headline => "make a Hodge-special surface", -Usage => "surface(C,S)", -Inputs => {"C" => MultiprojectiveVariety => {"an irreducible curve"}, "S" => MultiprojectiveVariety => {"a smooth surface ", TEX///$S$///, " containing the curve ", TEX///$C$///}}, -Outputs => {{"the Hodge special surface corresponding to the pair ", TEX///$(C,S)$///}}, -PARA{"The curve ",TEX///$C$///," can be recovered using the function ",TT "curve","."}, -EXAMPLE lines ///K = ZZ/65521; -C = random PP_K^(1,3); -- random twisted cubic in P^3 -j = parametrize PP_K(1,1,1,4); -C = (rationalMap(ambient C,source j) * j) C; -describe C -S = random(8,C); -describe S -S = surface(C,S); -discriminant S -parameterCount(S,Verbose=>true) -f := map(S,1,0) -f = quadricFibration f -discriminant f///, -Caveat => {"This feature is currently under development."}} -typicalValues#surface = typValSurf; - -undocumented {"curve"} - -document {Key => {unirationalParametrization, (unirationalParametrization, SpecialCubicFourfold), (unirationalParametrization, SpecialCubicFourfold, EmbeddedProjectiveVariety), (unirationalParametrization, SpecialGushelMukaiFourfold), (unirationalParametrization, HodgeSpecialFourfold)}, -Headline => "unirational parametrization", -Usage => "unirationalParametrization X", -Inputs => {"X" => SpecialCubicFourfold => {"or ", ofClass SpecialGushelMukaiFourfold}}, -Outputs => {MultirationalMap => {"a rational map of degree 2 from ",TEX///$\mathbb{P}^4$///," to ",TEX///$X$///}}, -PARA{"The degree of the forms defining the returned map is 10 in the case of cubic fourfolds, and 26 in the case of GM fourfolds."}, -EXAMPLE {"K = ZZ/10000019; S = PP_K^(2,2); -- Veronese surface;", "X = specialCubicFourfold S;", "time f = unirationalParametrization X;", "degreeSequence f", "degree(f,Strategy=>\"random point\")"}, -SeeAlso => {(parametrize, HodgeSpecialFourfold), (parametrize, MultiprojectiveVariety)}} - -document {Key => {(parametrize, HodgeSpecialFourfold)}, -Headline => "rational parametrization", -Usage => "parametrize X", -Inputs => {"X" => HodgeSpecialFourfold}, -Outputs => {MultirationalMap => {"a birational map from a rational fourfold to ", TT "X"}}, -PARA{"Some Hodge-special fourfolds are known to be rational. In this case, the function tries to obtain a birational map from ", TEX///$\mathbb{P}^4$///, " (or, e.g., from a quadric hypersurface in ", TEX///$\mathbb{P}^5$///, ") to the fourfold."}, -EXAMPLE {"X = specialFourfold surface {3,4};", "phi = parametrize X;", "describe phi"}, -EXAMPLE {"Y = specialFourfold \"tau-quadric\";", "psi = parametrize Y;", "describe psi"}, -EXAMPLE {"Z = specialFourfold \"plane in PP^7\";", "eta = parametrize Z;", "describe eta"}, -SeeAlso => {unirationalParametrization, (parametrize,MultiprojectiveVariety)}} - -document {Key => {fromOrdinaryToGushel, (fromOrdinaryToGushel, SpecialGushelMukaiFourfold)}, -Headline => "try to deform to a fourfold of Gushel type", -Usage => "fromOrdinaryToGushel X", -Inputs => {"X" => SpecialGushelMukaiFourfold => {"a fourfold of ordinary type"}}, -Outputs => {SpecialGushelMukaiFourfold => {"a fourfold of Gushel type, a deformation of ",TT"X"}}, -EXAMPLE {"X = specialGushelMukaiFourfold \"quintic del Pezzo surface\";", "singularLocus ambientFivefold X", "X' = fromOrdinaryToGushel X;", "support singularLocus ambientFivefold X'"}} - -document {Key => {associatedK3surface, [associatedK3surface, Strategy], building}, -Headline => "K3 surface associated to a rational fourfold", -PARA{"See ",TO (associatedK3surface, SpecialCubicFourfold)," and ",TO (associatedK3surface, SpecialGushelMukaiFourfold),"."}} - -document {Key => {(associatedK3surface, SpecialCubicFourfold)}, -Headline => "K3 surface associated to a rational cubic fourfold", -Usage => "U' = associatedK3surface X", -Inputs => {"X" => SpecialCubicFourfold => {"containing a surface ", TEX///$S\subset\mathbb{P}^5$///," that admits a ",TO2{CongruenceOfCurves,"congruence"}," of ",TEX///$(3e-1)$///,"-secant curves of degree ",TEX///$e$///}}, -Outputs => {"U'" => {"the (minimal) K3 surface associated to ",TEX///$X$///}}, -Consequences => {{{TT"building U'"," will return the following four objects:"}, UL{{"the dominant ",TO2{MultirationalMap,"rational map"}," ",TEX///$\mu:\mathbb{P}^5 \dashrightarrow W$///," defined by the linear system of hypersurfaces of degree ",TEX///$3e-1$///," having points of multiplicity ",TEX///$e$///," along ",TEX///$S$///,";"}, {"the ",TO2{EmbeddedProjectiveVariety,"surface"}," ",TEX///$U\subset W$///," determining the inverse map of the restriction of ",TEX///$\mu$///," to ",TEX///$X$///,";"}, {"the ",TO2{List,"list"}," of the exceptional curves on the surface ",TEX///$U$///,";"}, {"a ",TO2{MultirationalMap,"rational map"}," of degree 1 from the surface ",TEX///$U$///," to the minimal K3 surface ",TEX///$U'$///,"."}}}}, -PARA {"For more details and notation, see the papers ",HREF{"https://arxiv.org/abs/1909.01263","Trisecant Flops, their associated K3 surfaces and the rationality of some Fano fourfolds"}," and ",HREF{"https://arxiv.org/abs/2204.11518","Explicit computations with cubic fourfolds, Gushel-Mukai fourfolds, and their associated K3 surfaces"},"."}, -EXAMPLE {"X = specialCubicFourfold \"quartic scroll\";", "describe X", "time U' = associatedK3surface X;", "(mu,U,C,f) = building U';", "? mu", "? U", "last C", "assert(image f == U')"}, -SeeAlso => {(associatedK3surface, SpecialGushelMukaiFourfold), detectCongruence, mirrorFourfold}} - -document {Key => {(associatedK3surface, SpecialGushelMukaiFourfold)}, -Headline => "K3 surface associated to a rational Gushel-Mukai fourfold", -Usage => "U' = associatedK3surface X", -Inputs => {"X" => SpecialGushelMukaiFourfold => {"containing a surface ", TEX///$S\subset Y$///," that admits a ",TO2{CongruenceOfCurves,"congruence"}," of ",TEX///$(2e-1)$///,"-secant curves of degree ",TEX///$e$///," inside the ",TO2{ambientFivefold,"ambient fivefold"}," ",TEX///$Y$///," of the fourfold ",TEX///$X$///}}, -Outputs => {"U'" => {"the (minimal) K3 surface associated to ",TEX///$X$///}}, -Consequences => {{{TT"building U'"," will return the following four objects:"}, UL{{"the dominant ",TO2{MultirationalMap,"rational map"}," ",TEX///$\mu:Y\dashrightarrow W$///," defined by the linear system of hypersurfaces of degree ",TEX///$2e-1$///," having points of multiplicity ",TEX///$e$///," along ",TEX///$S$///,";"}, {"the ",TO2{EmbeddedProjectiveVariety,"surface"}," ",TEX///$U\subset W$///," determining the inverse map of the restriction of ",TEX///$\mu$///," to ",TEX///$X$///,";"}, {"the ",TO2{List,"list"}," of the exceptional curves on the surface ",TEX///$U$///,";"}, {"a ",TO2{MultirationalMap,"rational map"}," of degree 1 from the surface ",TEX///$U$///," to the minimal K3 surface ",TEX///$U'$///,"."}}}}, -PARA {"For more details and notation, see the paper ",HREF{"https://arxiv.org/abs/2204.11518","Explicit computations with cubic fourfolds, Gushel-Mukai fourfolds, and their associated K3 surfaces"},"."}, -EXAMPLE {"X = specialGushelMukaiFourfold \"tau-quadric\";", "describe X", "time U' = associatedK3surface X;", "(mu,U,C,f) = building U';", "? mu", "? U", "first C -- two disjoint lines", "assert(image f == U')"}, -SeeAlso => {(associatedK3surface, SpecialCubicFourfold), detectCongruence, mirrorFourfold}} - -document {Key => {parametrizeFanoFourfold, (parametrizeFanoFourfold, EmbeddedProjectiveVariety), [parametrizeFanoFourfold,Strategy]}, -Headline => "rational parametrization of a prime Fano fourfold of coindex at most 3", -Usage => "parametrize X -parametrizeFanoFourfold(X,Strategy=>...)", -Inputs => {"X" => EmbeddedProjectiveVariety => {"a prime Fano fourfold ",TEX///$X$///," of coindex at most 3 having degree ",TEX///$d$///," and genus ",TEX///$g$///," with ",TEX///$(d,g)\in\{(2,0),(4,1),(5,1),(12,7),(14,8),(16,9),(18,10)\}$///}}, -Outputs => {MultirationalMap => {"a birational map from ",TEX///$\mathbb{P}^4$///," to ", TEX///$X$///}}, -PARA{"This function is mainly based on results contained in the classical paper ",HREF{"https://link.springer.com/article/10.1007/BF02413916","Algebraic varieties with canonical curve sections"},", by L. Roth. In some examples, more strategies are available. For instance, if ",TEX///$X\subset\mathbb{P}^7$///," is a 4-dimensional linear section of ",TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///,", then by passing ",TT"Strategy=>1"," (which is the default choice) we get the inverse of the projection from the plane spanned by a conic contained in ",TEX///$X$///,"; while with ",TT"Strategy=>2"," we get the projection from the unique ",TEX///$\sigma_{2,2}$///,"-plane contained in ",TEX///$X$///," (Todd's result)."}, -EXAMPLE {"K = ZZ/65521; X = GG_K(1,4) * random({{1},{1}},0_(GG_K(1,4)));","? X", "time parametrizeFanoFourfold X"}, -SeeAlso => {fanoFourfold,(parametrize,HodgeSpecialFourfold),unirationalParametrization,(parametrize,MultiprojectiveVariety)}} - -document {Key => {fanoFourfold, (fanoFourfold,ZZ,ZZ), [fanoFourfold,CoefficientRing]}, -Headline => "random prime Fano fourfold of coindex at most 3", -Usage => "fanoFourfold(d,g) -fanoFourfold(d,g,CoefficientRing=>K)", -Inputs => {{TT"(d,g)"," a pair of integers belonging to the set ",TEX///$\{(2,0),(3,1),(4,1),(5,1),(4,3),(6,4),(8,5),(10,6),(12,7),(14,8),(16,9),(18,10)\}$///}}, -Outputs => {EmbeddedProjectiveVariety => {"a random prime Fano fourfold of coindex at most 3 having degree ",TEX///$d$///," and genus ",TEX///$g$///}}, -EXAMPLE {"X = fanoFourfold(4,1);", "describe X", "parametrize X"}, -SeeAlso => {parametrizeFanoFourfold}} - -document { -Key => {(clean,HodgeSpecialFourfold)}, -Headline => "clean the internal information of a fourfold", -Usage => "clean X", -Inputs => {"X" => HodgeSpecialFourfold}, -Outputs => {HodgeSpecialFourfold => {"which is mathematically identical to ",TT"X",", but new to the system"}}, -PARA{"This function is only useful for testing."}, -EXAMPLE {"X = specialFourfold \"quartic scroll\"", "X' = clean X", "X === X'"}} - -document {Key => {trisecantFlop}, -Headline => "examples of trisecant flops", -Usage => "trisecantFlop i", -Inputs => {"i" => ZZ => {"an integer between 0 and 17"}}, -Outputs => {{"the i-th example of birational map ",TEX///$X\dashrightarrow W$///," in accordance to the Table 1 in the paper ",HREF{"https://arxiv.org/abs/1909.01263","Trisecant Flops, their associated K3 surfaces and the rationality of some Fano fourfolds"},"."}}, -PARA{"This function requires the package ",HREF{"https://github.com/giovannistagliano/TrisecantFlops","TrisecantFlops"},". If not present the user will be asked to automatically install the package."}, -SeeAlso => {(specialFourfold, String, ZZ)}} -undocumented {(trisecantFlop,ZZ)} - -document {Key => {(specialFourfold, String, ZZ)}, -Headline => "load a prebuilt example of fourfold", -Usage => "specialFourfold(str,i)", -Inputs => {"str" => String => {"such as \"",TT"prebuilt-example-in-P5","\" or \"",TT"prebuilt-example-in-P7","\"."}, "i" => ZZ}, -Outputs => {HodgeSpecialFourfold => {"the i-th example of fourfold in accordance with some classification (e.g., ",TT"specialFourfold(\"prebuilt-example-in-P5\",i)"," is the same as ",TO2{(source,MultirationalMap),"source"}," ",TO trisecantFlop,TT"(i)","."}}, -PARA{"This function requires the package ",HREF{"https://github.com/giovannistagliano/TrisecantFlops","TrisecantFlops"},". If not present the user will be asked to automatically install the package."}, -SeeAlso => trisecantFlop} - -undocumented {(random, HodgeSpecialFourfold), (symbol **, HodgeSpecialFourfold,Ring), (map, HodgeSpecialFourfold), (describe, HodgeSpecialFourfold)} - -document {Key => {(specialGushelMukaiFourfold, Array, Array, String, Thing), (specialGushelMukaiFourfold, Array, Array), (specialGushelMukaiFourfold, Array, Array, String), (specialGushelMukaiFourfold, Array, Array, Thing)}, -Headline => "construct GM fourfolds by gluing cubic or quartic scrolls to surfaces in PP^6", -Usage => "specialGushelMukaiFourfold(surface,curve) -specialGushelMukaiFourfold(surface,curve,scroll) -specialGushelMukaiFourfold(surface,curve,K) -specialGushelMukaiFourfold(surface,curve,scroll,K)", -Inputs => {"surface" => Array => {"an array of integers ",TT"[a,i,j,k,...]"," to indicate the rational surface ",TEX///$S\subset\mathbb{P}^6$///," constructed by ",TO2{(surface,List),"surface"},TT"({a,i,j,k,...},K,ambient=>6)"}, - "curve" => Array => {"an array of integers ",TT"[d,l,m,n,...]"," to indicate the plane representation of a curve ",TEX///$C$///," on the surface ",TEX///$S$///," (the command that constructs ",TEX///$C$///," is ",TT///S.cache#"takeCurve"(d,{l,m,n,...})///,")"}, - "scroll" => String => {"which can be either \"cubic scroll\" (the default value) or \"quartic scroll\", to indicate the type of scroll ",TEX///$B\subset\mathbb{P}^6$///," to be used; in the former case ",TEX///$B\simeq\mathbb{P}^1\times\mathbb{P}^2\subset\mathbb{P}^5\subset\mathbb{P}^6$///," while in the latter case ",TEX///$B\subset\mathbb{P}^6$///," is a generic projection of a rational normal quartic scroll of dimension 4 in ",TEX///$\mathbb{P}^7$///}, - "K" => {{"the coefficient ring (",TT"ZZ/65521"," is used by default)"}}}, -Outputs => {SpecialGushelMukaiFourfold => {"a GM fourfold ",TEX///$X$///," containing the surface ",TEX///$\overline{\psi_{B}(S)}\subset\mathbb{G}(1,4)\subset\mathbb{P}^9$///,", where ",TEX///$B$///," is a scroll of the indicated type such that ",TEX///$C\subseteq S\cap B$///," and ",TEX///$\psi_{B}:\mathbb{P}^6\dashrightarrow\mathbb{G}(1,4)$///," is the birational map defined by ",TEX///$B$///}}, -PARA {"From the returned fourfold ",TEX///$X$///,", with the following commands we obtain the surface ",TEX///$S$///,", the curve ",TEX///$C$///,", and the scroll ",TEX///$B$///," used in the construction: "},PARA{TT///(B,C) = X.cache#"Construction"; S = ambientVariety C;///},PARA{"Then the surface ",TEX///$\overline{\psi_{B}(S)}\subset\mathbb{G}(1,4)$///," can be constructed with "},PARA{TT///psi = rationalMap B; (psi S)%(image psi);///}, -PARA {"In the following example we construct a GM fourfold containing the image via ",TEX///$\psi_B:\mathbb{P}^6\dashrightarrow\mathbb{G}(1,4)$///," of a quintic del Pezzo surface ",TEX///$S\subset\mathbb{P}^5\subset\mathbb{P}^6$///,", obtained as the image of the plane via the linear system of quartic curves with three general simple base points and two general double points, which cuts ",TEX///$B\simeq\mathbb{P}^1\times\mathbb{P}^2\subset\mathbb{P}^5\subset\mathbb{P}^6$///," along a rational normal quartic curve obtained as the image of a general conic passing through the two double points."}, -EXAMPLE lines ///X = specialGushelMukaiFourfold([4, 3, 2],[2, 0, 2]); -describe X -(B,C) = X.cache#"Construction"; -S = ambientVariety C; -C; -B; -assert(C == S * B)///, -References => UL{{"G. Staglianò, ",EM"Explicit computations with cubic fourfolds, Gushel-Mukai fourfolds, and their associated K3 surfaces",", available at ",HREF{"https://arxiv.org/abs/2204.11518","arXiv:2204.11518"}," (2022)."}}, -SeeAlso => {(surface,VisibleList,Ring), GMtables}} - -document {Key => {specialFourfold, (specialFourfold, EmbeddedProjectiveVariety), (specialFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (specialFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (specialFourfold, Ideal), (specialFourfold, Ideal, Ideal), (specialFourfold, Ideal, RingElement), (specialFourfold, String), (specialFourfold, String, Ring), [specialFourfold, NumNodes], [specialFourfold, InputCheck]}, -Headline => "make a Hodge-special fourfold", -Usage => "specialFourfold(S,X,V)"|newline|"specialFourfold(S,X)"|newline|"specialFourfold S", -Inputs => {"S" => EmbeddedProjectiveVariety => {"an irreducible ",TO2{(surface,HodgeSpecialFourfold),"surface"}}, "X" => EmbeddedProjectiveVariety => {"a smooth fourfold ", TEX///$X$///, " containing the surface ", TEX///$S$///}, "V" => EmbeddedProjectiveVariety => {"optional, a ",TO2{ambientFivefold,"fivefold"}," where ",TT"X"," is a hypersurface"}}, -Outputs => {HodgeSpecialFourfold => {"which corresponds to the pair ", TEX///$(S,X)$///}}, -PARA{"This can also be used as a shortcut for both ",TO specialCubicFourfold," and ",TO specialGushelMukaiFourfold,"."}, -EXAMPLE {"S = surface {3,4};", "X = specialFourfold S -- a random cubic fourfold through S", "describe X", "Y = specialFourfold \"tau-quadric\" -- a random GM fourfold through a tau-quadric", "describe Y", "T = surface {3,2};", "Z = specialFourfold T -- a random c. i. of 3 quadrics through T", "describe Z"}, -SeeAlso => {specialCubicFourfold, specialGushelMukaiFourfold}} - -document { -Key => {Singular, [associatedK3surface, Singular], [associatedCastelnuovoSurface, Singular], [mirrorFourfold, Singular]}, -Headline => "whether to transfer computation to Singular", -Usage => "Singular => true or false", -PARA{"This option allows you to transfer part of the computation to Singular."}, -SeeAlso => {associatedK3surface}} - -document {Key => {mirrorFourfold, (mirrorFourfold, SpecialCubicFourfold), (mirrorFourfold, SpecialGushelMukaiFourfold), (mirrorFourfold, IntersectionOfThreeQuadricsInP7), (mirrorFourfold, HodgeSpecialFourfold), [mirrorFourfold, Strategy]}, -Headline => "associated fourfold to a rational cubic or GM fourfold", -Usage => "mirrorFourfold X", -Inputs => {"X" => SpecialCubicFourfold => {"or ", ofClass SpecialGushelMukaiFourfold}}, -Outputs => {HodgeSpecialFourfold => {"the fourfold ",TT"W"," containing the surface ",TT"U",", with the same notation as in the function ",TO associatedK3surface,"."}}, -EXAMPLE {"X = specialFourfold(PP_(ZZ/65521)[2,2]);", "W = mirrorFourfold X;", "U = surface W;", "mirrorFourfold W", "(building associatedK3surface X)_1", "assert(oo === U)"}, -EXAMPLE {"X' = specialFourfold \"tau-quadric\";", "W' = mirrorFourfold X';", "U' = surface W';", "mirrorFourfold W'", "(building associatedK3surface X')_1", "assert(oo === U')"}, -SeeAlso => {associatedK3surface}} - -document { -Key => {(toExternalString, HodgeSpecialFourfold)}, -Headline => "convert to a readable string", -Usage => "toExternalString X", -Inputs => {"X" => HodgeSpecialFourfold}, -Outputs => {String => {"a string representation of ",TT "X",", which can be used, in conjunction with ",TO "value",", to read the object back into the program later"}}, -PARA{"Some of the internal data of the input ",TT"X"," are included in the returned string."}, -EXAMPLE {"describe (X = specialFourfold \"tau-quadric\")", "str = toExternalString X;", "describe (value str)"}} - -undocumented {(expression, HodgeSpecialFourfold)} - -document {Key => {HodgeSpecialFourfold}, -Headline => "the class of all Hodge-special fourfolds", -PARA{"An object of the class ", TO HodgeSpecialFourfold, " is basically represented by a couple ", TEX///(S,X)///, ", where ", TEX///$X$///, " is a fourfold and ", TEX///$S$///, " is a surface contained in ", TEX///$X$///,". Such objects are created by the function ",TO specialFourfold,"."}, -SeeAlso => {specialFourfold, SpecialCubicFourfold, SpecialGushelMukaiFourfold, IntersectionOfThreeQuadricsInP7}} - -document {Key => {(check, ZZ, CongruenceOfCurves), (check, CongruenceOfCurves)}, -Headline => "check that a congruence of curves is well-defined", -Usage => "check f"|newline|"check(n,f)", -Inputs => {"n" => ZZ => {"(optional with default value 5)"}, "f" => CongruenceOfCurves}, -Outputs => {CongruenceOfCurves => {"the same object passed as input, but an error is thrown if the congruence fails when ",TO2{(symbol SPACE, CongruenceOfCurves, EmbeddedProjectiveVariety),"evaluated"}," on ",TT"n"," random points."}}} - -undocumented {(symbol ?, HodgeSpecialFourfold, HodgeSpecialFourfold)} - -document {Key => {IntersectionOfThreeQuadricsInP7}, -Headline => "the class of all special intersection of three quadrics in P^7", -PARA {"This is a subtype of the ",TO HodgeSpecialFourfold," type."}, -SeeAlso => {specialFourfold}} - -undocumented {(expression, IntersectionOfThreeQuadricsInP7)} - -document {Key => {associatedCastelnuovoSurface, (associatedCastelnuovoSurface, IntersectionOfThreeQuadricsInP7), [associatedCastelnuovoSurface, Strategy]}, -Headline => "Castelnuovo surface associated to a rational complete intersection of three quadrics in P^7", -Usage => "U' = associatedCastelnuovoSurface X", -Inputs => {"X" => IntersectionOfThreeQuadricsInP7 => {"containing a surface ", TEX///$S\subset Y$///," that admits a ",TO2{CongruenceOfCurves,"congruence"}," of ",TEX///$(2e-1)$///,"-secant curves of degree ",TEX///$e$///," inside the ",TO2{ambientFivefold,"ambient fivefold"}," ",TEX///$Y$///," of the fourfold ",TEX///$X$///}}, -Outputs => {"U'" => {"the (minimal) Castelnuovo surface associated to ",TEX///$X$///}}, -Consequences => {{{TT"building U'"," will return the following four objects:"}, UL{{"the dominant ",TO2{MultirationalMap,"rational map"}," ",TEX///$\mu:Y\dashrightarrow W$///," defined by the linear system of hypersurfaces of degree ",TEX///$2e-1$///," having points of multiplicity ",TEX///$e$///," along ",TEX///$S$///,";"}, {"the ",TO2{EmbeddedProjectiveVariety,"surface"}," ",TEX///$U\subset W$///," determining the inverse map of the restriction of ",TEX///$\mu$///," to ",TEX///$X$///,";"}, {"the ",TO2{List,"list"}," of the exceptional curves on the surface ",TEX///$U$///,";"}, {"a ",TO2{MultirationalMap,"rational map"}," of degree 1 from the surface ",TEX///$U$///," to the minimal Castelnuovo surface ",TEX///$U'$///,"."}}}}, -PARA {"More details will be provided in a forthcoming paper by F. Russo and G. Staglianò."}, -EXAMPLE {"X = specialFourfold random({5:{1}},0_(PP_(ZZ/33331)^7));", "describe X", "time U' = associatedCastelnuovoSurface X;", "(mu,U,C,f) = building U';", "? mu"}, -SeeAlso => {associatedK3surface, detectCongruence}} - -document {Key => {beauvilleMap, (beauvilleMap, IntersectionOfThreeQuadricsInP7)}, -Headline => "construction of Beauville for complete intersections of three quadrics in P^7", -Usage => "f = beauvilleMap X", -Inputs => {"X" => IntersectionOfThreeQuadricsInP7}, -Outputs => {"f" => MultirationalMap => {"a birational map from ",TEX///$X$///," to a fourfold ",TEX///$Y\subset\mathbb{P}^2\times\mathbb{P}^5$///," whose first projection ",TEX///$Y\to\mathbb{P}^2$///," is a quadric surface bundle"}}, -PARA{"Smooth intersections of three quadrics in ",TEX///$\mathbb{P}^7$///," are birational to quadric surface bundles over ",TEX///$\mathbb{P}^2$///," with discriminant curve of degree 8. This is a construction of Beauville; see e.g. Proposition 6 in the paper ",HREF{"https://www.intlpress.com/site/pub/files/_fulltext/journals/sdg/2017/0022/0001/SDG-2017-0022-0001-a009.pdf", "Intersections of three quadrics in P7"}, ", by B. Hassett, A. Pirutka, and Y. Tschinkel."}, -EXAMPLE {"X = specialFourfold random({5:{1}},0_(PP_(ZZ/33331)^7));", "f = beauvilleMap X;", "Y = target f;", "inverse f;", "first projectionMaps target f;"}} - ------------------------------------------------------------------------- -------------------------------- Tests ---------------------------------- ------------------------------------------------------------------------- - -TEST /// -- Test 0 -- cubic fourfolds from strings: describe, discriminant, parameterCount -strIn := {"quintic del Pezzo surface", "quartic scroll", "3-nodal septic scroll", "one-nodal septic del Pezzo surface", "general cubic 4-fold of discriminant 38", "general cubic 4-fold of discriminant 42", "cubic 4-fold of discriminant 48"}; -strOut := "Special cubic fourfold of discriminant 14 -containing a (smooth) surface of degree 5 and sectional genus 1 -cut out by 5 hypersurfaces of degree 2 -Special cubic fourfold of discriminant 14 -containing a (smooth) surface of degree 4 and sectional genus 0 -cut out by 6 hypersurfaces of degree 2 -Special cubic fourfold of discriminant 26 -containing a 3-nodal surface of degree 7 and sectional genus 0 -cut out by 13 hypersurfaces of degree 3 -Special cubic fourfold of discriminant 26 -containing a 1-nodal surface of degree 7 and sectional genus 1 -cut out by 14 hypersurfaces of degree 3 -Special cubic fourfold of discriminant 38 -containing a (smooth) surface of degree 10 and sectional genus 6 -cut out by 10 hypersurfaces of degree 3 -Special cubic fourfold of discriminant 42 -containing a 5-nodal surface of degree 9 and sectional genus 2 -cut out by 9 hypersurfaces of degree 3 -Special cubic fourfold of discriminant 48 -containing a 6-nodal surface of degree 9 and sectional genus 2 -cut out by 5 hypersurfaces of degrees (2,3,3,3,3) -"; -X = apply(strIn,specialCubicFourfold); --- X = apply(strIn,x -> specialCubicFourfold(x,InputCheck=>10,Verbose=>true)); -assert all(X,x -> x.cache#?(surface x,"label")); -assert(concatenate apply(X,x -> toString describe x | newline) == strOut); -Y = apply(X,x -> specialCubicFourfold surface x); -assert all(Y,y -> not y.cache#?(surface y,"label")); -assert(apply(Y,discriminant) == {14,14,26,26,38,42,48}); -assert(concatenate apply(Y,y -> toString describe y | newline) == strOut); -assert(parameterCount(Y_0,Verbose=>true) == (1, (25, 35, 5)) and parameterCount(Y_1,Verbose=>true) == (1, (28, 29, 2))); -/// - -TEST /// -- Test 1 (1/2) -- GM fourfolds from strings: describe, discriminant, parameterCount, toGrass -strIn := {"sigma-plane", "rho-plane", "tau-quadric"}; -strOut := "Special Gushel-Mukai fourfold of discriminant 10('') -containing a surface in PP^8 of degree 1 and sectional genus 0 -cut out by 6 hypersurfaces of degree 1 -and with class in G(1,4) given by s_(3,1) -Type: ordinary -(case 6 of Table 1 in arXiv:2002.07026) -Special Gushel-Mukai fourfold of discriminant 12 -containing a surface in PP^8 of degree 1 and sectional genus 0 -cut out by 6 hypersurfaces of degree 1 -and with class in G(1,4) given by s_(2,2) -Type: ordinary -(case 9 of Table 1 in arXiv:2002.07026) -Special Gushel-Mukai fourfold of discriminant 10(') -containing a surface in PP^8 of degree 2 and sectional genus 0 -cut out by 6 hypersurfaces of degrees (1,1,1,1,1,2) -and with class in G(1,4) given by s_(3,1)+s_(2,2) -Type: ordinary -(case 1 of Table 1 in arXiv:2002.07026) -"; -X = apply(strIn,specialGushelMukaiFourfold); -assert(apply(X,x -> x.cache#(surface x,"label")) == {6, 9, 1}); -assert(concatenate apply(X,x -> toString describe x | newline) == strOut); -Y = apply(X,x -> specialGushelMukaiFourfold(sub(ideal (toGrass x) surface x,ring target toGrass x),InputCheck=>0)) -assert all(Y,y -> not y.cache#?(surface y,"label")); -assert(apply(Y,discriminant) == {10, 12, 10}); -assert(concatenate apply(Y,y -> toString describe y | newline) == strOut); -assert(parameterCount(Y_0,Verbose=>true) == (2, (34, 4, 0)) and parameterCount(Y_1,Verbose=>true) == (3, (34, 3, 0))); -/// - -TEST /// -- Test 2 (2/2) -- GM fourfolds from strings: describe, discriminant, parameterCount, toGrass -strIn := {"cubic scroll", "quintic del Pezzo surface", "general GM 4-fold of discriminant 20"}; -strOut := "Special Gushel-Mukai fourfold of discriminant 12 -containing a surface in PP^8 of degree 3 and sectional genus 0 -cut out by 7 hypersurfaces of degrees (1,1,1,1,2,2,2) -and with class in G(1,4) given by 2*s_(3,1)+s_(2,2) -Type: ordinary -(case 7 of Table 1 in arXiv:2002.07026) -Special Gushel-Mukai fourfold of discriminant 10('') -containing a surface in PP^8 of degree 5 and sectional genus 1 -cut out by 8 hypersurfaces of degrees (1,1,1,2,2,2,2,2) -and with class in G(1,4) given by 3*s_(3,1)+2*s_(2,2) -Type: ordinary -(case 4 of Table 1 in arXiv:2002.07026) -Special Gushel-Mukai fourfold of discriminant 20 -containing a surface in PP^8 of degree 9 and sectional genus 2 -cut out by 19 hypersurfaces of degree 2 -and with class in G(1,4) given by 6*s_(3,1)+3*s_(2,2) -Type: ordinary -(case 17 of Table 1 in arXiv:2002.07026) -"; -X = apply(strIn,x -> clean specialGushelMukaiFourfold x); -debug SpecialFanoFourfolds; -assert(apply(X,recognize) == {7, 4, 17}); -assert(concatenate apply(X,x -> toString describe x | newline) == strOut); -Y = apply(X,x -> specialGushelMukaiFourfold(sub(ideal (toGrass x) surface x,ring target toGrass x),InputCheck=>0)) -assert all(Y,y -> not y.cache#?(surface y,"label")); -assert(apply(Y,discriminant) == {12, 10, 20}); -assert(concatenate apply(Y,y -> toString describe y | newline) == strOut); -assert(parameterCount(Y_1,Verbose=>true) == (1, (24, 18, 3))); -/// - -TEST /// -- Test 3 -- 21 examples from GMtables -X = for i from 1 to 21 list ( - A = GMtables(i,ZZ/65521); - time specialGushelMukaiFourfold((rationalMap(ideal A_0,Dominant=>2)) ideal A_1,InputCheck=>0) -); -S = apply(X,x -> surface x); -assert(apply(X,x -> degree surface x) === {2, 4, 14, 5, 9, 1, 3, 7, 1, 10, 10, 14, 12, 8, 9, 11, 9, 7, 10, 4, 12}); -assert(apply(X,x-> sectionalGenus surface x) == {0, 0, 8, 1, 3, 0, 0, 2, 0, 4, 3, 8, 5, 2, 3, 5, 2, 0, 3, 0, 5}); -assert(last cycleClass X_18 == (6,4) and discriminant X_18 == 24); -assert(last cycleClass X_7 == (4,3) and discriminant X_7 == 12); -/// - -TEST /// -- Test 4 -- parametrizations of Fano fourfolds -setRandomSeed 0; -for dg in {(2,0),(3,1),(4,1),(5,1),(4,3),(6,4),(8,5),(10,6),(12,7),(14,8),(16,9),(18,10)} do ( - <<"(d,g) = "<"random point") == 1 and target h === X and ambient source h == source h and h#"inverse" =!= null); -time f = unirationalParametrization X; -assert(# factor f == 1 and target f === X and unique degrees ideal matrix first factor f == {{10}}); -assert isSubset(f point source f,X); -S = schubertCycle({3,1},GG(ZZ/33331,1,4),Standard=>true); -Y = specialGushelMukaiFourfold S; -time g = parametrize Y; -assert(degree(g,Strategy=>"random point") == 1 and target g === Y and dim ambient source g == 5 and dim source g == 4 and g#"inverse" =!= null); --- time g = unirationalParametrization Y; --- assert(# factor g == 1 and target g === Y and unique degrees ideal matrix first factor g == {{26}}) --- assert isSubset(g point source g,Y) -/// - -TEST /// -- Test 6 (1/3) -- associated K3 surfaces -f = last building associatedK3surface(specialCubicFourfold "quartic scroll",Verbose=>false); -assert(f#"image" =!= null and dim image f == 2 and degree image f == 14 and dim target f == 8) -/// - -TEST /// -- Test 7 (2/3) -- associated K3 surfaces -g = last building associatedK3surface(specialCubicFourfold "quintic del Pezzo surface",Verbose=>true,Singular=>false); -assert(g#"image" =!= null and dim image g == 2 and degree image g == 14 and dim target g == 8) -/// - -TEST /// -- Test 8 (3/3) -- associated K3 surfaces -building associatedK3surface(specialGushelMukaiFourfold "tau-quadric",Verbose=>false); -/// - -TEST /// -- Test 9 -- simple tests on schubertCycle -debug MultiprojectiveVarieties; -S = schubertCycle({2,2},GG(ZZ/33331,1,4),Standard=>true) -assert(idealOfSubvariety S == idealOfSubvariety tangentialChowForm(projectiveVariety ideal((Grass(0,4,ZZ/33331,Variable=>"x"))_0,(Grass(0,4,ZZ/33331,Variable=>"x"))_1),1,1)) -S = schubertCycle({3,2,1},GG(ZZ/33331,2,5),Standard=>true) -use ring ambientVariety S; -assert(idealOfSubvariety S == ideal(x_(0,4,5),x_(0,3,5),x_(1,2,5),x_(0,2,5),x_(0,1,5),x_(2,3,4),x_(1,3,4),x_(0,3,4),x_(1,2,4),x_(0,2,4),x_(0,1,4),x_(1,2,3),x_(0,2,3),x_(0,1,3),x_(0,1,2))) -/// - -TEST /// -- Test 10 (1/2) -- detectCongruence -X = specialCubicFourfold("quintic del Pezzo surface",ZZ/33331); -detectCongruence(X,Verbose=>true); -/// - -TEST /// -- Test 11 (2/2) -- detectCongruence -use Grass(1,4,ZZ/33331); -S31 = ideal(p_(3,4),p_(2,4),p_(1,4),p_(0,4),p_(2,3),p_(1,3),p_(1,2)); -Y = specialGushelMukaiFourfold(S31,InputCheck=>0); -assert(not Y.cache#?(surface Y,"label")); Y.cache#(surface Y,"label") = 6; -detectCongruence(Y,Verbose=>true); --- Y = specialGushelMukaiFourfold("18",ZZ/3331); --- detectCongruence Y; -/// - -TEST /// -- Test 12 (1/2) -- GM fourfolds containing nodal surfaces -debug SpecialFanoFourfolds; -K = ZZ/65521; -X = makeGMfromCurveOnSurfaceInP6((surface({2,0,0,0},K,ambient=>6)).cache#"takeCurve" (1,(0,0,0)),InputCheck=>0); -assert(discriminant X == 36); -assert(numberNodes surface X == 1); -X' = random X; -assert(surface X === surface X' and ambientFivefold X === ambientFivefold X' and isSubset(surface X',X') and dim(X*X') == 3); -assert(discriminant X' == 44 and discriminant X == 44); -/// - -TEST /// -- Test 13 (2/2) -- GM fourfolds containing nodal surfaces -X = specialGushelMukaiFourfold("nodal surface of degree 11 and genus 3 with class (7,4)",ZZ/33331,InputCheck=>0); -assert(discriminant X == 26 and last cycleClass X == (7,4) and degree surface X == 11 and sectionalGenus surface X == 3); -Y = specialGushelMukaiFourfold("nodal D44",ZZ/33331,InputCheck=>0); -assert(discriminant Y == 44 and last cycleClass Y == (6,3) and degree surface Y == 9 and sectionalGenus surface Y == 1); -/// - -TEST /// -- Test 14 -- gluing scrolls along curves -debug SpecialFanoFourfolds -S = surface({3,4,0,0},ambient=>6); -for a in {(1,0),(2,0),(3,0),(4,0),(5,0),(5,1)} do ( - (d,g) := a; - E := curvesOnSurface(S,d,g); - assert(#E>0); - for C in E do ( - <<"(d,g) = "<<(d,g)<<", curve: "<6),InputCheck=>0,"Gluing"=>"cubic scroll",Degrees=>hashTable{1=>(1,1),2=>(19,infinity),3=>(0,0)}); -X = first L; -assert(#L == 1 and discriminant X == 18 and last cycleClass X == (5,3)) --- L = takeGMsfromSurfaceInP6(surface({3,1,1,0},ambient=>6),InputCheck=>0,"Gluing"=>"quartic scroll",Degrees=>hashTable{1=>(1,1),2=>(19,infinity),3=>(0,0)}); --- X = first L; --- assert(#L == 1 and discriminant X == 20 and last cycleClass X == (4,3)) -/// +load "./SpecialFanoFourfolds/utils.m2"; -TEST /// -- Test 16 -debug SpecialFanoFourfolds; -S = surface({3,1},NumNodes=>2); -assert(dim S == 2 and degree S == 8 and dim ambient S == 6 and degrees S == {({2},5),({3},4)}); -T = image experimentalNormalizationInv S; -assert(dim T == 2 and degree T == 8 and dim ambient T == 8 and degrees T == {({2},20)}) -/// +load "./SpecialFanoFourfolds/HodgeSpecialSurfaces.m2"; -TEST /// -- Test 17 -X = specialFourfold "plane in PP^7"; -assert(discriminant X == 31); -S = associatedCastelnuovoSurface X; -assert((dim S,dim ambient S,degree S,sectionalGenus S,degrees S) == (2, 4, 9, 9, {({3}, 1), ({4}, 3)})) -/// +load "./SpecialFanoFourfolds/docs.m2"; -TEST /// -- Test 18 -debug MultiprojectiveVarieties; -K := ZZ/65521; -P := PP_K(1,1,1,4); -C = random({{2},{3}},0_P) -assert(discriminant(surface(C,random(8,C)),Algorithm=>2) == -7) -D = internalProjection_2 (surface({3, 1, 1, 0},K)).cache#"takeCurve"(3,{0, 0, 0}); -x := gens ring P; -f := (rationalMap {{x_0^4,x_0^3*x_1,x_0^3*x_2,x_3}}) << (ambient D); -E = f^* D; -assert(discriminant(surface(E,random(8,E)),Algorithm=>1) == -43) -E' = f^* random D; -assert(discriminant(surface(E',random(8,E')),Algorithm=>2) == -43) -/// +load "./SpecialFanoFourfolds/tests.m2"; diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/AssociatedSurfaces.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/AssociatedSurfaces.m2 new file mode 100644 index 00000000000..a6fd267c3cd --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/AssociatedSurfaces.m2 @@ -0,0 +1,188 @@ + +------------------------------------------------------------------------ +--------------------- Associated K3 and Castelnuovo surfaces ----------- +------------------------------------------------------------------------ + +SurfaceAssociatedToRationalFourfold = new Type of EmbeddedProjectiveVariety; +WeightedSurfaceAssociatedToRationalFourfold = new Type of WeightedProjectiveVariety; + +globalAssignment SurfaceAssociatedToRationalFourfold; +globalAssignment WeightedSurfaceAssociatedToRationalFourfold; + +WeightedSurfaceAssociatedToRationalFourfold.synonym = SurfaceAssociatedToRationalFourfold.synonym = "surface associated to rational fourfold"; + +expression WeightedSurfaceAssociatedToRationalFourfold := expression SurfaceAssociatedToRationalFourfold := U -> ( + X := recoverFourfold U; + (S,F) := if instance(X,CubicFourfold) + then ("K3 surface","cubic fourfold") + else if instance(X,GushelMukaiFourfold) + then ("K3 surface","GM fourfold") + else if instance(X,IntersectionOfThreeQuadricsInP7) + then ("Castelnuovo surface","complete intersection of 3 quadrics in PP^7") + else ("surface","fourfold"); + if dim U == -1 then S = "not-fully-calculated "|S; + A := if hasAttribute(X,ReverseDictionary) then toString getAttribute(X,ReverseDictionary) else (F|" of discriminant "|toString(discriminant X)); + expression(S|" associated to "|A) +); + +net WeightedSurfaceAssociatedToRationalFourfold := net SurfaceAssociatedToRationalFourfold := U -> ( + if hasAttribute(U,ReverseDictionary) then return toString getAttribute(U,ReverseDictionary); + if dim U >= 0 then return ? U; + "-* some calculations are missing *-" +); +texMath WeightedSurfaceAssociatedToRationalFourfold := texMath SurfaceAssociatedToRationalFourfold := texMath @@ net; + +describe WeightedSurfaceAssociatedToRationalFourfold := describe SurfaceAssociatedToRationalFourfold := S -> ( + out := "Fourfold: "; + if hasAttribute(recoverFourfold S,ReverseDictionary) then out = out|(toString getAttribute(recoverFourfold S,ReverseDictionary))|", "; + (out|shortDescriptionFourfold(recoverFourfold S,false))||(describeMirrorFourfoldAndK3 recoverFourfold S) +); + +makeSurfaceAssociated = (X,mu,U,C,f) -> ( + assert(instance(X,HodgeSpecialFourfold) and instance(mu,MultirationalMap) and instance(U,EmbeddedProjectiveVariety) and instance(C,List) and (instance(f,Nothing) or instance(f,MultirationalMap))); + S := if f =!= null then image f else projectiveVariety((coefficientRing X)[],Saturate=>false); + S = if instance(S,WeightedProjectiveVariety) then new WeightedSurfaceAssociatedToRationalFourfold from S else new SurfaceAssociatedToRationalFourfold from S; + S.cache#"construction of SurfaceAssociatedToRationalFourfold" = (mu,U,C,f); + S.cache#"Hodge-special fourfold" = X; + if f =!= null then ( + if instance(X,DoublySpecialCubicFourfold) then mu.cache#("AssociatedUnderlyingK3",X) = S else X.cache#"AssociatedSurfaceCompleteData" = S; + ) else ( + if instance(X,DoublySpecialCubicFourfold) then mu.cache#("AssociatedK3PartialData",X) = S else X.cache#"AssociatedSurfacePartialData" = S; + ); + return S; +); + +building = method(); +building WeightedSurfaceAssociatedToRationalFourfold := building SurfaceAssociatedToRationalFourfold := S -> S.cache#"construction of SurfaceAssociatedToRationalFourfold"; + +recoverFourfold = method(); +recoverFourfold WeightedSurfaceAssociatedToRationalFourfold := recoverFourfold SurfaceAssociatedToRationalFourfold := S -> S.cache#"Hodge-special fourfold"; + +printFinalLogSurface = strSurf -> ( + Tstart := currentTime(); + TstartCPU := cpuTime(); + U -> ( + Tend := currentTime() - Tstart; + TendCPU := cpuTime() - TstartCPU; + U.cache#"computationTime" = (Tend, TendCPU); + X := recoverFourfold U; + isStandard := if instance(X,CubicFourfold) or instance(X,GushelMukaiFourfold) then isStandardK3surface U else true; + statusOut := if dim U == 2 and isStandard + then (" ✦ ", "successfully completed") + else if dim U == 2 + then (" ⟠", "completed") + else (" ✧ ", "completed (partial data)"); + << first statusOut << strSurf << last statusOut << " in " << humanReadableSeconds(Tend) << " (cpu: " << humanReadableSeconds(floor TendCPU) << ")" << endl; + ) +); + +associatedK3surface = method(Options => {Verbose => false, Strategy => null, Singular => null}); +associatedCastelnuovoSurface = method(Options => {Verbose => false, Strategy => null, Singular => null}); + +associatedK3surface CubicFourfold := associatedK3surface GushelMukaiFourfold := associatedCastelnuovoSurface IntersectionOfThreeQuadricsInP7 := o -> X -> ( + if X.cache#?"AssociatedSurfaceCompleteData" then return X.cache#"AssociatedSurfaceCompleteData"; + if X.cache#?"AssociatedSurfacePartialData" then ( + UtildeP := X.cache#"AssociatedSurfacePartialData"; + return buildAssociatedSurfaceFromPartialData(UtildeP,Verbose=>o.Verbose); + ); + strSurf := if instance(X,IntersectionOfThreeQuadricsInP7) then "Castelnuovo " else "K3 "; + if o.Verbose then ( + printFinalLog := printFinalLogSurface("associated "|strSurf); + << "-- starting associated " << strSurf << "computation" << endl; + << "-- input: fourfold containing a " << surfaceDescription surface X << endl; + printInfoOnPlannedSteps X; + ); + if (not X.cache#?(surface X,"label")) and o.Verbose then << "-- recognizing the fourfold" << endl; + recognize X; + if o.Verbose then ( + if X.cache#(surface X,"label") === "NotRecognized" then << "-- fourfold not recognized" << endl else << "-- the fourfold has been successfully recognized" << endl; + ); + fanoMap(X,Singular=>o.Singular,Verbose=>o.Verbose); + (L,C) := exceptionalCurves(X,Verbose=>o.Verbose,Strategy=>o.Strategy); + U := ambientVariety L; + mu := multirationalMap fanoMap X; + f := contractionMap(U,X,Verbose=>o.Verbose,Strategy=>o.Strategy,"ForceNormalization"=>false); + Utilde := makeSurfaceAssociated(X,mu,U,{L,C},f); + if o.Verbose then printFinalLog Utilde; + Utilde +); + +associatedUnderlyingK3Raw = method(TypicalValue => SurfaceAssociatedToRationalFourfold, Options => {Verbose => true, Strategy => null, FanoMapType => null}); +associatedUnderlyingK3Raw DoublySpecialCubicFourfold := o -> X -> ( + mu := getCachedFanoMapIfCompatible(X,o.FanoMapType); + if mu =!= null and mu.cache#?("AssociatedUnderlyingK3",X) then return mu.cache#("AssociatedUnderlyingK3",X); + if o.Verbose then ( + printFinalLog := printFinalLogSurface "underlying K3 "; + << "-- starting underlying K3 computation" << endl; + << "-- input: cubic fourfold containing two surfaces:" << endl; + << (" -- " | surfaceDescription first surfaces X) << endl; + << (" -- " | surfaceDescription last surfaces X) << endl; + << "-- settings: Verbose => " << o.Verbose << ", Strategy => " << (if instance(o.Strategy, String) then "\"" | o.Strategy | "\"" else toString(o.Strategy)) << ", FanoMapType => " << (if instance(o.FanoMapType, String) then "\"" | o.FanoMapType | "\"" else toString(o.FanoMapType)) << endl; + << "-- available strategies: \"Inverse\", \"Approximate\"" << endl; + printInfoOnPlannedSteps X; + if not any(surfaces X, isPlaneInP5) then << "-- warning: no plane detected; trying K3 computation anyway" << endl; + ); + if mu === null then ( + if o.FanoMapType === "Standard" then setFanoMapToBeStandard(X,Verbose=>o.Verbose); + if o.FanoMapType === "P2xP2" then setFanoMapToBeMapFromP5toP2xP2(X,Verbose=>o.Verbose); + mu = fanoMapDSCF(X,Verbose=>o.Verbose); + ); + if mu.cache#?("AssociatedUnderlyingK3",X) then return mu.cache#("AssociatedUnderlyingK3",X); + if (not isPlaneInP5 last surfaces X) and X.cache#?"fusedVersion" and (surface fuse X).cache#?("fanoMap",ambientFivefold fuse X) then ( + if ((fanoMap fuse X)#"map").cache#"multiplicityFanoMap" >= 2 then ( + if o.Verbose then ( + << "-------------------------------------------------------------------" << endl; + << "-- info: special configuration detected. " << endl; + << "-- redirecting computation to associatedK3surface(fuse X)... " << endl; + << "-------------------------------------------------------------------" << endl; + ); + UtildeFuse := associatedK3surface(fuse X,Verbose=>o.Verbose,Strategy=>"F4"); + if o.Verbose then printFinalLog UtildeFuse; + return UtildeFuse; + ); + ); + Str := setStrategyDSCFtoK3(X,o.Strategy); + (L,C) := exceptionalCurves(X,Verbose=>o.Verbose,Strategy=>Str); + U := ambientVariety L; + f := contractionMap(U,X,Verbose=>o.Verbose,Strategy=>Str,"ForceNormalization"=>isNormalizationKnownToTerminateQuickly(X)); + Utilde := makeSurfaceAssociated(X,mu,U,{L,C},f); + if o.Verbose then printFinalLog Utilde; + Utilde +); + +printInfoOnPlannedSteps = X -> ( + << "-- planned steps:" << endl; + << "-- 1. compute Fano map μ" << (if dim ambient X == 5 then " : ℙⵠ⇢ W" else "") << endl; + << "-- 2. extract surface U from the base locus of (μ|X)â»Â¹ : W ⇢ X" << endl; + << "-- 3. take a general "<< (if instance(X,CubicFourfold) then "cubic " else "fourfold ") << "X' containing the " << (if instance(X,DoublySpecialCubicFourfold) then "two surfaces " else "surface ") << "and extract U'" << endl; + << "-- 4. analyze the intersection U ∩ U' for exceptional curves" << endl; + << "-- 5. compute the contraction map f : U -> Ũ" << endl; + if instance(X,DoublySpecialCubicFourfold) then ( + << "-- 6. prepare data for lattice polarization" << endl; + << "-- info: re-run the function for lattice computation and use building() to access" << endl; + << "-- construction data (μ, U, exceptional curves, f)" << endl; + ) else ( + << "-- info: use building() to access construction data (μ, U, exceptional curves, f)" << endl; + ); + << endl; +); + +buildAssociatedSurfaceFromPartialData = method(Options => {Verbose => true}); +buildAssociatedSurfaceFromPartialData SurfaceAssociatedToRationalFourfold := o -> Utilde -> ( + (mu,U,exC,f) := building Utilde; + if f =!= null or Utilde.cache#?"attemptedBuildAssociatedSurfaceFromPartialData" then return Utilde; + X := recoverFourfold Utilde; + EulerExpVal := if instance(X,IntersectionOfThreeQuadricsInP7) then 4 else 2; + if euler hilbertPolynomial U == EulerExpVal then return Utilde; + Str := null; + if U.cache#?"strategy for surface U" then ( + Str = U.cache#"strategy for surface U"; + ) else ( + << "-- debug: missing 'strategy for surface U' in cache" << endl; + ); + f = contractionMap(U,X,Verbose=>o.Verbose,Strategy=>Str,"ForceNormalization"=>true); + Utilde.cache#"attemptedBuildAssociatedSurfaceFromPartialData" = true; + if f === null then return Utilde; + (L,C) := toSequence exC; + return makeSurfaceAssociated(X,mu,U,{L,C},f); +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/Congruences.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/Congruences.m2 new file mode 100644 index 00000000000..37a1f1ab41e --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/Congruences.m2 @@ -0,0 +1,147 @@ + +------------------------------------------------------------------------ +----------------------------- Congruences ------------------------------ +------------------------------------------------------------------------ + +CongruenceOfCurves = new Type of HashTable; + +globalAssignment CongruenceOfCurves; + +CongruenceOfCurves.synonym = "congruence of curves"; + +detectCongruence = method(TypicalValue => CongruenceOfCurves, Options => {Verbose => false}); + +detectCongruence (CubicFourfold,ZZ) := o -> (X,e) -> congruenceOfCurves(X,e); + +detectCongruence (GushelMukaiFourfold,ZZ) := o -> (X,e) -> congruenceOfCurves(X,e); + +detectCongruence (HodgeSpecialFourfold,ZZ) := o -> (X,e) -> congruenceOfCurves(X,e); + +detectCongruenceInt = method(Options => {Verbose => false}); +detectCongruenceInt (EmbeddedProjectiveVariety,HodgeSpecialFourfold) := o -> (p,X) -> ( + a := degreeHypersurface X; + if not(isPoint p and ring ambient p === ring ambient X and isSubset(p,ambientFivefold X)) then error "expected a point in the ambient fivefold"; + phi := map X; + imageOfAssociatedMap X; -- image of phi + S := surface X; + secants := new MutableList from {null,0,0,0,0,0,0}; + phip := phi p; + E := coneOfLines(Var image phi,phip); + if dim E == 0 then ( + if o.Verbose then <<"no ("<1); + if not(dim E' == 0 and degree E' == degree E) then error "something went wrong"; + local n; local C; + P := apply(decompose E',q -> (assert(dim q == 0); C = phi^* pr'^* q; assert(dim C == 1 and dim(C*S) == 0); (degree q,C))); + for nC in P do ( + (n,C) = nC; + for e from 1 to 6 do + if degree(C) == n*e and degree(C*S) == n*(a*e-1) and all(delete(e,toList(1..6)),e' -> not(degree(C) == n*e' and degree(C*S) == n*(a*e'-1))) + then secants#e = secants#e + n; + ); + secants = toList take(secants,-(#secants-1)); + if sum secants =!= degree E then <<"--warning: something went wrong: "< a*(e+1)-1),(s,t) -> "number "|toString(t)|"-secant "|s); + if o.Verbose then for e to #secants-1 do if secants_e != 0 then < X -> detectCongruenceInt(point ambient X,X,Verbose=>o.Verbose); +detectCongruence GushelMukaiFourfold := o -> X -> detectCongruenceInt(pointOnLinearSectionOfG14 ambientFivefold X,X,Verbose=>o.Verbose); +detectCongruence HodgeSpecialFourfold := o -> X -> detectCongruenceInt(point ambientFivefold X,X,Verbose=>o.Verbose); + +congruenceOfCurves = method(); + +congruenceOfCurves (HodgeSpecialFourfold,ZZ) := (X,e) -> ( + a := degreeHypersurface X; + Y := ambientFivefold X; + phi := map X; + imageOfAssociatedMap X; -- image of phi + S := surface X; + f := method(); + f EmbeddedProjectiveVariety := p -> ( + if not isPoint p then error "expected a point"; + if not(ring ambient p === ring ambient Y and isSubset(p,Y)) then error "expected a point on the ambient fivefold containing the surface"; + phip := phi p; + E := coneOfLines(Var image phi,phip); + if dim E == 0 then error "no congruences detected"; + if dim E != 1 then error "expected cone of lines to be one dimensional"; + if degree E == 1 then ( + D := phi^* E; + if not(dim D == 1 and degree D == e and dim(D*S) == 0 and degree(D*S) == a*e-1) then error "no congruences detected"; + if sectionalGenus D != 0 then D = top D; + return makeSubvariety(D,Y); + ); + pr := rationalMap phip; + v := (rationalMap {random(1,ring target pr),random(1,ring target pr)}); + pr' := toRationalMap((pr * v)|E); + E' := Var kernel(map pr',SubringLimit=>1); + if not(dim E' == 0 and degree E' == degree E) then error "something went wrong"; + decE' := select(decompose E',q -> (dim q == 0 and degree q == 1)); + P := select(apply(decE',q -> phi^* pr'^* q),C -> (dim C == 1 and degree C == e and dim(C*S) == 0 and degree(C*S) == a*e-1)); + if #P != 1 then (if #P > 1 + then error "got more than one curve of the congruence that passes through the point" + else error "got no curve of the congruence that passes through the point"); + C := first P; + if sectionalGenus C != 0 then C = top C; + makeSubvariety(C,Y) + ); + try f (if instance(X,GushelMukaiFourfold) then pointOnLinearSectionOfG14 Y else point Y) else error "no congruences detected"; + new CongruenceOfCurves from { + "function" => f, + "fourfold" => X, + "degree" => e, + "string" => "of "|toString(a*e-1)|"-secant "|(if e == 1 then "lines" else if e == 2 then "conics" else if e == 3 then "cubic curves" else if e == 4 then "quartic curves" else if e == 5 then "quintic curves" else "curves of degree "|toString(e)) + } +); + +toString CongruenceOfCurves := net CongruenceOfCurves := f -> if hasAttribute(f,ReverseDictionary) then toString getAttribute(f,ReverseDictionary) else "a congruence "|f#"string"; +texMath CongruenceOfCurves := texMath @@ net; + +CongruenceOfCurves#{WebApp,AfterPrint} = CongruenceOfCurves#{WebApp,AfterNoPrint} = +CongruenceOfCurves#{Standard,AfterPrint} = CongruenceOfCurves#{Standard,AfterNoPrint} = f -> ( + S := surface f#"fourfold"; + Y := ambientFivefold f#"fourfold"; + e := f#"degree"; + << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << "Congruence " << f#"string" << " to "; + << if hasAttribute(S,ReverseDictionary) then toString getAttribute(S,ReverseDictionary) else "surface"; + <<" in "; + << if hasAttribute(Y,ReverseDictionary) then toString getAttribute(Y,ReverseDictionary) else (if codim Y > 0 then "a fivefold in PP^"|toString(dim ambient Y) else "PP^5"); + if S.cache#?("fanoMap",Y) and ((S.cache#("fanoMap",Y))#"map").cache#"multiplicityFanoMap" == e + then << "; parameter space: " << Var target S.cache#("fanoMap",Y); + << endl; +); + +CongruenceOfCurves EmbeddedProjectiveVariety := (f,p) -> f#"function" p; +CongruenceOfCurves Ideal := (f,Ip) -> idealOfSubvariety f projectiveVariety gens Ip; + +member(EmbeddedProjectiveVariety,CongruenceOfCurves) := (C,f) -> ( + L := (map f#"fourfold") C; + dim L == 1 and unique degrees ideal L === {{1}} +); + +map CongruenceOfCurves := o -> f -> ( + X := f#"fourfold"; + e := f#"degree"; + mu := fanoMap(X,e); + e' := (mu#"map").cache#"multiplicityFanoMap"; + if e' =!= e then error("the congruence seems not valid; try the command: detectCongruence(...,"|toString(e')|")"); + multirationalMap mu +); + +check (ZZ,CongruenceOfCurves) := o -> (n,f) -> ( + X := f#"fourfold"; + try for i to n-1 do f(point ambientFivefold X) else error "the congruence of curves is not valid"; + f +); +check CongruenceOfCurves := o -> f -> check(5,f); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/CubicFourfolds.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/CubicFourfolds.m2 new file mode 100644 index 00000000000..17737e93a62 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/CubicFourfolds.m2 @@ -0,0 +1,385 @@ + +------------------------------------------------------------------------ +--------------------------- Cubic fourfolds ---------------------------- +------------------------------------------------------------------------ + +CubicFourfold = new Type of HodgeSpecialFourfold; + +globalAssignment CubicFourfold; + +CubicFourfold.synonym = "cubic fourfold"; + +cubicFourfold = method(TypicalValue => CubicFourfold, Options => {NumNodes => null, InputCheck => 1, Verbose => false}); + +cubicFourfold (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X) -> ( + if ring ideal S =!= ring ideal X then error "expected varieties in the same ambient space"; + if not (dim ambient X == 5 and degrees X == {({3},1)}) then error "expected a cubic fourfold"; + if dim S != 2 then error "expected a surface"; + i := o.InputCheck; + if not(instance(i,ZZ) and i >= -1) then error("option InputCheck expects a nonnegative integer:"|newline|"0: no check is done about the smoothness of the fourfold and of the (normalization of the) surface"|newline|"1: just the smoothness of the fourfold is checked"|newline|"2: the smoothness of the fourfold and of a general hyperplane section of the surface are checked"|newline|"3: as above and furthermore the smoothness of the normalization of the surface is checked"); + if i >= 0 then if not isSubset(S,X) then error "the given surface is not contained in the cubic fourfold"; + if i >= 1 then if not isSmooth X then error "expected a smooth cubic fourfold"; + n := o.NumNodes; + if n === null then n = numberNodes(S,Verbose=>o.Verbose); + if not(instance(n,ZZ) and n >= 0) then error "option NumNodes expects a nonnegative integer or null"; + if S.cache#?"FiniteNumberOfNodes" then if n =!= S.cache#"FiniteNumberOfNodes" then error "the number of nodes is wrong"; + if i == 2 or (i >= 3 and n > 0) then ( + if not isSmooth(S * random(1,0_S)) then error "expected a surface with at most a finite number of nodes"; + if o.Verbose then <<"-- smoothness in codimension 1 of the surface verified"<= 3 then ( + if n > 0 then ( + q = normalization(S,Verbose=>o.Verbose); + if not isSmooth(Var source q) then error "expected a surface with smooth normalization"; + if o.Verbose then <<"-- smoothness of the normalization of the surface verified (assuming equidimensionality)"<o.Verbose) then error "the number of nodes is wrong"; + if o.Verbose then <<"-- number of nodes (partially) verified"<0)"; + if o.Verbose then <<"-- smoothness of the surface verified (assuming equidimensionality)"<= 4 then ( + if S != top S then error "expected an irreducible reduced surface"; + if o.Verbose then <<"-- equidimensionality of the surface verified"< (idS,idX) -> cubicFourfold(projectiveVariety idS,projectiveVariety idX,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +cubicFourfold (Ideal,RingElement) := o -> (idS,C) -> cubicFourfold(idS,ideal C,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +cubicFourfold EmbeddedProjectiveVariety := o -> S -> ( + if dim ambient S == 5 and codim S == 1 and degrees S === {({3},1)} then return cubicFourfold(S * random({2:{1}},0_S),S,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if not(dim ambient S == 5 and dim S == 2) then error "expected a surface in P^5"; + cubicFourfold(S,random({3},S),NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose) +); + +cubicFourfold Ideal := o -> idS -> cubicFourfold(projectiveVariety idS,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +cubicFourfold (String,Ring) := o -> (str,K) -> ( + if o.NumNodes =!= null then error "the option NumNodes is ignored, the number of nodes is determined automatically"; + local X; + if str === "quintic del Pezzo surface" then ( + X = cubicFourfold(surface({3,4},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "quinticDelPezzoSurface"; + return X; + ); + if str === "quartic scroll" then ( + X = cubicFourfold(surface({3,1,1},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "quarticScrollSurface"; + return X; + ); + if str === "general cubic 4-fold of discriminant 38" or str === "C38" then ( + X = cubicFourfold(surface({10,0,0,10},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "C38Coble"; + return X; + ); + if str === "6-nodal octic scroll" then ( + X = cubicFourfold("C38",K,InputCheck=>0,Verbose=>o.Verbose); + X = cubicFourfold(((top baseLocus fanoMap X) * X)\surface X,X,NumNodes=>6,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "6NodalOcticSrollC38"; + return X; + ); + if str === "3-nodal septic scroll" or str === "Farkas-Verra C26" then ( + t := gens ring PP_K^2; + f := multirationalMap rationalMap(ring PP_K^2,ring PP_K^8,{t_0^5, t_0^4*t_1, t_0^3*t_1^2, t_0^2*t_1^3, t_0^4*t_2, t_0^3*t_1*t_2, t_0^2*t_1^2*t_2, t_0*t_1^3*t_2, t_1^4*t_2}); + f = f * rationalMap linearSpan apply(3,i -> point linearSpan {f point source f,f point source f}); + X = cubicFourfold(image f,NumNodes=>3,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "FarkasVerra"; + return X; + ); + if str === "one-nodal septic del Pezzo surface" then ( + g := multirationalMap rationalMap(ring PP_K^2,{3,2}); + g = g * rationalMap(ring target g,ring PP_K^5,gens ideal linearSpan {point target g,point linearSpan {g point source g,g point source g}}); + X = cubicFourfold(image g,NumNodes=>1,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "oneNodalSepticDelPezzoSurfaceC26"; + return X; + ); + if str === "general cubic 4-fold of discriminant 42" or str === "C42" then ( + X = cubicFourfold(last last randomS42data(K),NumNodes=>5,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "C42"; + return X; + ); + if str === "cubic 4-fold of discriminant 48" or str === "C48" then ( + X = cubicFourfold(randomS48 K,NumNodes=>6,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "C48"; + return X; + ); + if str === "general cubic 4-fold of discriminant 32" or str === "C32" then ( + X = cubicFourfold(surface({9,1,4,6},K),NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "C32"; + return X; + ); + if str === "general cubic 4-fold of discriminant 44" or str === "C44" then ( -- Enriques surface (see e.g. https://arxiv.org/pdf/1210.1903.pdf, p. 7) + J := Var ideal jacobian ideal discriminant first genericPolynomials({2,-1,-1,-1},K); + X = cubicFourfold((parametrize random({{1},{1},{1},{1}},0_J))^* J,NumNodes=>0,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "C44"; + return X; + ); + if str === "8-nodal nonic scroll" then ( + X = LaiFarkasVerraC42(K,NumNodes=>8,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "LaiFarkasVerraC42"; + (surface X).cache#"euler" = -44; + return X; + ); + error(///not valid string, permitted strings are: +"quintic del Pezzo surface", +"quartic scroll", +"3-nodal septic scroll", +"one-nodal septic del Pezzo surface", +"6-nodal octic scroll", +"general cubic 4-fold of discriminant 32", +"general cubic 4-fold of discriminant 38", +"general cubic 4-fold of discriminant 42", +"8-nodal nonic scroll", +"general cubic 4-fold of discriminant 44", +"cubic 4-fold of discriminant 48"///); +); + +cubicFourfold String := o -> str -> cubicFourfold(str,ZZ/65521,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +expression CubicFourfold := X -> expression("cubic fourfold containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X)); + +describe CubicFourfold := X -> ( + descr:="Special cubic fourfold of discriminant "|(toString discriminant X)|newline|"containing a "|surfaceDescription(0,surface X,true); + if recognize X === "LaiFarkasVerraC42" then descr = descr|newline|"(the surface is the 8-nodal nonic scroll studied by K.-W. Lai, G. Farkas and A. Verra,"|newline|"this implementation is due to M. Hoff)"; + if computationStatus X >= 0 then descr = descr|newline|(computationStatusLog X)|newline|toString(describeMirrorFourfoldAndK3 X); + net expression descr +); + +shortDescriptionFourfold (CubicFourfold,Boolean) := (X,UseAttribute) -> ( + if UseAttribute and hasAttribute(X,ReverseDictionary) then return toString getAttribute(X,ReverseDictionary); + "cubic fourfold in C_"|(toString discriminant X) +); + +-- discriminant CubicFourfold := o -> X -> ( +-- if X.cache#?(surface X,"discriminantFourfold") then return last X.cache#(surface X,"discriminantFourfold"); +-- S := surface X; +-- degS := degree S; g := sectionalGenus S; chiOS := euler hilbertPolynomial S; +-- chiS := eulerCharacteristic(S,Algorithm=>if o.Algorithm === "Poisson" then null else o.Algorithm); +-- KS2 := 12*chiOS-chiS; +-- n := numberNodes S; +-- S2 := 3*degS+6*g-12*chiOS+2*KS2+2*n-6; +-- d := 3*S2 - degS^2; +-- X.cache#(surface X,"discriminantFourfold") = (S2,d); +-- d +-- ); + +discriminant CubicFourfold := discriminant HodgeSpecialFourfold := o -> X -> ( + if X.cache#?(surface X,"discriminantFourfold") then return last X.cache#(surface X,"discriminantFourfold"); + r := codim X; + a := flatten degrees ideal X; + if #a != r then error "expected a special fourfold which is a complete intersection"; + S := surface X; + HS2 := degree S; + KSHS := 2*(sectionalGenus S)-2-HS2; + chiOS := euler hilbertPolynomial S; + c2TS := eulerCharacteristic(S,Algorithm=>if o.Algorithm === "Poisson" then null else o.Algorithm); + KS2 := 12*chiOS-c2TS; + n := if instance(X,CubicFourfold) or S.cache#?"FiniteNumberOfNodes" or S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" or (S.cache#?"fitVariety" and (S.cache#"fitVariety").cache#?"nonSaturatedSingularLocus") then numberNodes S else 0; + S2 := 2*n + (binomial(r+5,2) - (r+5)*(sum a) + (sum a)^2 - sum flatten for i to r-1 list for j from i+1 to r-1 list a_i*a_j) * HS2 + (r+5-sum a) * KSHS + KS2 - c2TS; + d := det(X.cache#(surface X,"LatticeIntersectionMatrix") = matrix {{degree X,HS2},{HS2,S2}}); + if S.cache#?"FiniteNumberOfNodes" then X.cache#(surface X,"discriminantFourfold") = (S2,d); + d +); + +map CubicFourfold := o -> X -> associatedMapFromFivefold X; + +recognizeCubicFourfold = X -> ( + S := surface X; + d := discriminant X; + e := eulerCharacteristic S; + n := numberNodes surface X; + invS := (degree S,sectionalGenus S,euler hilbertPolynomial S); + degs := flatten degrees ideal S; + if (d == 14 and e == 7 and n == 0 and invS === (5,1,1) and degs == toList(5:2)) then return "quinticDelPezzoSurface"; + if (d == 14 and e == 4 and n == 0 and invS === (4,0,1) and degs == toList(6:2)) then return "quarticScrollSurface"; + if (d == 32 and e == 14 and n == 0 and invS === (10,6,1) and degs == toList(10:3)) then return "C32"; + if (d == 38 and e == 13 and n == 0 and invS === (10,6,1) and degs == toList(10:3)) then return "C38Coble"; + if (d == 38 and e == -32 and n == 6 and invS === (8,0,-5) and degs == toList(10:3)) then return "6NodalOcticSrollC38"; + if (d == 44 and e == 12 and n == 0 and invS === (10,6,1) and degs == toList(10:3)) then return "C44"; + if (d == 26 and e == -14 and n == 3 and invS === (7,0,-2) and degs == toList(13:3)) then return "FarkasVerra"; + if (d == 26 and e == -1 and n == 1 and invS === (7,1,0) and degs == toList(14:3)) then return "oneNodalSepticDelPezzoSurfaceC26"; + if (d == 42 and e == -23 and n == 5 and invS === (9,2,-4) and degs == toList(9:3)) then return "C42"; + if (d == 48 and e == -29 and n == 6 and invS === (9,2,-5) and degs == {2,3,3,3,3}) then return "C48"; + if (d == 14 and e == 46 and n == 0 and invS === (13,12,4) and degs == toList(7:3)) then return "hyperplane section of a conic bundle over PP2"; + "NotRecognized" +); + +random CubicFourfold := o -> X -> ( + X' := cubicFourfold(surface X,InputCheck=>-1); + if X.cache#?(surface X,"label") and (not X'.cache#?(surface X',"label")) then X'.cache#(surface X',"label") = X.cache#(surface X,"label"); + X' +); + +parameterCount CubicFourfold := o -> X -> parameterCount(surface X,X,Verbose=>o.Verbose); + +isAdmissible = method(); + +isAdmissible ZZ := d -> ( + if d <= 6 then return false; + if d % 2 != 0 then return false; + if d % 4 == 0 then return false; + if d % 9 == 0 then return false; + for p from 3 to floor(d/2) do if (p % 3 == 2 and isPrime p and d % p == 0) then return false; + if d % 6 != 0 and d % 6 != 2 then error toString d; + return true; +); + +isAdmissible CubicFourfold := X -> isAdmissible discriminant X; + +unirationalParametrization (CubicFourfold,EmbeddedProjectiveVariety) := (X,L) -> ( + if not(isSubset(L,X) and dim L == 1 and degree L == 1) then error "expected a line in the cubic fourfold"; + ringP5 := ring ambient X; + K := coefficientRing X; + l := toRationalMap parametrize L; + K' := frac(source l); + ringP5' := K'[gens ringP5]; + p' := trim minors(2,(vars ringP5')||(matrix l)); + X' := sub(ideal X,ringP5'); + TpX' := trim ideal((vars ringP5') * sub(jacobian X',apply(gens ringP5',flatten entries coefficients parametrize p',(x,s) -> x => s))); + U := TpX' + ideal(last gens ringP5'); + u := parametrize U; + u = rationalMap(Grass(0,3,K',Variable=>"s"),source u) * u; + e := lcm apply(flatten entries sub(last coefficients matrix u,K'),denominator); + M := transpose((matrix l)||(e * matrix u)); + ringP1xP3 := (source l) ** K[gens source u]; + M = sub(M,ringP1xP3); + r := local r; + Kr := ringP1xP3[r]; + P := first first entries gens sub(ideal X,apply(gens ringP5,flatten entries(submatrix(sub(M,Kr),{0}) + r*submatrix(sub(M,Kr),{1})),(v,v') -> v => v')); + psi := rationalMap(ringP1xP3,ringP5,transpose(coefficient(r^3,P) * submatrix(M,{0}) - coefficient(r^2,P) * submatrix(M,{1}))); + Psi := multirationalMap({parametrize psi},X); + if not isSubset(Psi point source Psi,X) then error "internal error encountered"; + Psi +); + +unirationalParametrization CubicFourfold := (cacheValue "unirationalParametrization") (X -> unirationalParametrization(X,line X)); + +randomS42data = method(); +randomS42data Ring := K -> ( + p := apply(5,i -> point PP_K^2); + f := (rationalMap(p_0 + p_2 + p_3 + p_4,2)) | (rationalMap(p_0 + p_1,2)); + s := multirationalMap segre target f; + f = multirationalMap segre f; + L := linearSpan {f point source f,f point source f}; + j := parametrize random(1,L); + pr := rationalMap point (j^^ L); + B := pr(j^^ (image s)); + C := pr(j^^ (image f)); + phi := toRationalMap rationalMap(C,Dominant=>true); + forceInverseMap(phi,inverseMap phi); + Bs := baseLocus inverse phi; + Q := 0_Bs; + while select(degrees ideal Q,d -> d <= {2}) =!= {{1},{1},{1},{2}} do ( + u := parametrize random({{1},{1}},0_Bs); + v := (rationalMap {random(1,ring source u),random(1,ring source u)})|(u^^ Bs); + Q = Q + u support v^*((v (u^^ Bs))\support(v (u^^ Bs))); + ); + Q = Var trim ideal select(flatten entries gens ideal Q,d -> degree d <= {2}); + if not (dim Q == 2 and degree Q == 2 and dim singularLocus Q == -1) then error "internal error encountered"; + P1xP2 := phi^* Q; + w := Var trim lift(phi (rationalMap flatten entries syz gens ideal P1xP2)^-1 (ideal submatrix'(vars ring ambient P1xP2,{0})),ambient target phi); + e := super inverse(rationalMap(w%Q),Verify=>false); + w = e point source e; + (L1,L2) := toSequence decompose(Q * tangentSpace(w,Q)); + D := phi^* L1; + if not(dim D == 2 and degree D == 5 and sectionalGenus D == 1) then D = phi^* L2; + if not(dim D == 2 and degree D == 5 and sectionalGenus D == 1) then error "internal error encountered"; + psi := rationalMap(B,3); + T := psi D; + if not(dim T == 2 and codim T == 6 and degree T == 9 and sectionalGenus T == 2) then error "internal error encountered"; + eta := rationalMap((SchubertCycle22OnLinearSectionOfG14 image psi)%(image psi)); + S42 := eta T; + if not(dim S42 == 2 and degree S42 == 9 and sectionalGenus S42 == 2 and genera ideal S42 == {-5,2,8} and degrees S42 == {({3},9)}) then error "internal error encountered"; + S42.cache#"euler" = -23; S42.cache#"FiniteNumberOfNodes" = 5; + T.cache#"euler" = 7; T.cache#"FiniteNumberOfNodes" = 0; + ((psi,D),(super inverse3 eta,S42)) +); + +randomS48 = method(); +randomS48 Ring := K -> ( + (psi,D) := first randomS42data K; + p := psi point source psi; + V := coneOfLines(image psi,p); + j := parametrize linearSpan V; + V' := j^* V; + p' := j^* p; + h := rationalMap p'_V'; + e := point image h; + P := j h^* ((coneOfLines(image h,e))\e); + assert(dim P == 2 and degree P == 1 and isSubset(P,image psi)); + S := (psi * rationalMap P) D; + if not(dim S == 2 and degree S == 9 and sectionalGenus S == 2 and genera ideal S == {-6,2,8} and degrees S == {({2}, 1),({3}, 4)}) then error "internal error encountered"; + return S; +); + +LaiFarkasVerraC42 = method(Options => options cubicFourfold); +LaiFarkasVerraC42 Ring := o -> K -> ( +-- ** +-- The code of this function has been written by Michael Hoff +-- ** +-- 24.10.2019 +-- The construction and notation follow Farkas and Verra: +-- "THE UNIRATIONALITY OF THE MODULI SPACE OF K3 SURFACES OF DEGREE 42" + y := local y; + P2 := K[y_0..y_2]; + p := ideal(y_0,y_1); + z := local z; + P4 := K[z_0..z_4]; + blowup := map(P2,P4, gens p *matrix basis(2,p)); -- linear system |2h-p| + F1 := kernel blowup; -- Hirzebruch surface F1 + l := ideal(random(P4^1,P4^{3:-1})); -- line in P4 + t := apply(8, i-> (preimage_blowup(ideal(random(P2^1,P2^{2:-1}))))); -- 8 points on F1 + spantl := apply(#t,i->gens intersect(t_i,l)*matrix basis(1,intersect(t_i,l))); -- linear span of l and t_i + points16 := intersect (points2 := apply(#spantl,i->(saturate((ideal(spantl_i)+F1),t_i)))); -- 16 residual points on F1 + assert((dim points16, degree points16, genus points16) == (1, 16, 15)); + -- see FV end of page 4 + RF1 := P4/F1; + z = gens RF1; + E := ideal(z_0,z_1,z_3); + pointsOnF1 := sub(points16,RF1); + I := intersect(pointsOnF1,E); + C := ideal(gens I * random(source gens I, RF1^{1:-3})); + C = saturate(sub(C,P4)+F1,sub(E,P4)); + assert((dim C, degree C, genus C) == (2, 8, 4)); + projl := map(P4,P2,gens l); + octic := preimage_projl(C); -- plane octic curve with 8 + 9 singular points + singOctic := saturate ideal singularLocus octic; + assert((dim singOctic, degree singOctic, genus singOctic) == (1, 17, 16)); + points8 := saturate(preimage_projl(points16)); + points9 := saturate(singOctic, points8); + -- blow up of 9 points + x := local x; + P5 := K[x_0..x_5]; + blowup2 := map(P2,P5,gens points9*matrix basis(4,points9)); + T := kernel blowup2; + -- betti res T + gamma := preimage_blowup2(octic); -- hyperelliptic curve of degree 14 and arithmetic genus 12 + -- betti res gamma + singGamma := preimage_blowup2(points8); + -- betti (resSingGamma = res singGamma) + -- I take the canonical map to compute pairs of points which are involuted. + w := local w; + P3 := K[w_0..w_3]; + canMap := map(P2,P3,gens singOctic*matrix basis(5,singOctic)); + twistedCubic := preimage_canMap(octic); + betti (resTwistedC := res twistedCubic); + assert((dim twistedCubic, degree twistedCubic, genus twistedCubic) == (2, 3, 0)); + M := resTwistedC.dd_2; + -- I take many lines and compute the ideal where the generators stabilizes + pNip := apply(40, i-> saturate(canMap(ideal(random(K)*M_{0} + random(K)*M_{1})),singOctic)); + L := apply(#pNip,i->(ideal(gens(preimage_blowup2(pNip#i)))_{0,1,2,3})); + betti (R := intersect(L)); + R = ideal( (gens R)_{0..5,6..8}); -- the nonic scroll with 8 singularities + -- betti res R + assert((dim R, degree R, genus R) == (3, 9, -8) and gamma + R == gamma); + if o.NumNodes =!= 8 then error "the number of nodes is equal to 8"; + cubicFourfold(R,NumNodes=>8,Verbose=>o.Verbose,InputCheck=>o.InputCheck) +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/DSCF.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/DSCF.m2 new file mode 100644 index 00000000000..e7be4d2c448 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/DSCF.m2 @@ -0,0 +1,664 @@ + +------------------------------------------------------------------------ +--------------------- Doubly special cubic fourfolds ------------------- +------------------------------------------------------------------------ + +PairOfSurfaces = new Type of List; +globalAssignment PairOfSurfaces; +EmbeddedProjectiveVariety & EmbeddedProjectiveVariety := (S,T) -> ( + if dim S != 2 or dim T != 2 then error "expected two surfaces"; + if ring ideal S =!= ring ideal T then error "expected surfaces in the same ambient projective space"; + new PairOfSurfaces from {S,T} +); +ambient PairOfSurfaces := SandT -> ambient first SandT; +random (ZZ,PairOfSurfaces) := random (List,PairOfSurfaces) := o -> (l,SandT) -> random(l,sum SandT); + +DoublySpecialCubicFourfold = new Type of CubicFourfold; +globalAssignment DoublySpecialCubicFourfold; +DoublySpecialCubicFourfold.synonym = "doubly special cubic fourfold"; + +specialFourfold (PairOfSurfaces,EmbeddedProjectiveVariety) := cubicFourfold (PairOfSurfaces,EmbeddedProjectiveVariety) := o -> (SandT,X) -> ( + (S,T) := toSequence SandT; + (nS,nT) := if instance(o.NumNodes,VisibleList) and # o.NumNodes == 2 then toSequence o.NumNodes else (o.NumNodes,o.NumNodes); + Y := cubicFourfold(T,X,NumNodes=>nT,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + Z := new DoublySpecialCubicFourfold from cubicFourfold(S,Y,NumNodes=>nS,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + Z.cache#"parentCubicFourfold" = Y; + assert(surface Z === S and surface Y === T and take(Z#"SurfaceContainedInTheFourfold",2) === {S,T}); + if dim (S * T) == 2 then error "intersection of the two surfaces has dimension 2 (unsupported)"; + Z +); + +specialFourfold PairOfSurfaces := cubicFourfold PairOfSurfaces := o -> SandT -> ( + if dim ambient SandT != 5 then error "expected two surfaces in P^5"; + cubicFourfold(SandT,random(3,SandT),NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose) +); + +------------------------------------------------------------------------ +RationalSurfaceWithAttachedPlaneInCubicFourfold = new Type of EmbeddedProjectiveVariety; +globalAssignment RationalSurfaceWithAttachedPlaneInCubicFourfold; +rationalSurfaceWithAttachedPlaneInCubicFourfold = method(TypicalValue => RationalSurfaceWithAttachedPlaneInCubicFourfold, Options => {"EulerCharacteristic" => true, Verbose => true}); +rationalSurfaceWithAttachedPlaneInCubicFourfold (EmbeddedProjectiveVariety,VisibleList) := o -> (S,dj1j2j3) -> ( + -- Input: 1) a rational surface S = surface(a,i1,i2,...), or its defining parameters (a,i1,i2,...). + -- 2) the parameters for a curve C on S, defined as the image of a plane curve of degree d and multiplicities (j1,j2,...). + -- Output: if no errors occur, the projection of S into PP^5 from a center contained in the span . + ai1i2i3 := S.cache#"linear system on PP^2"; + expEul := 3 + sum toList drop(ai1i2i3,1); + if not o#"EulerCharacteristic" then ( + S.cache#"euler" = expEul; + if o.Verbose then << "-- set the Euler characteristic of the surface in PP^" << dim ambient S << " to the expected value " << S.cache#"euler" << endl; + ); + C := S.cache#"takeCurve" (first dj1j2j3, toList drop(dj1j2j3,1)); + if dim linearSpan S < dim ambient S then error "the surface is degenerate"; + if dim ambient S - 5 != dim linearSpan C - 2 then error("dim ambient S = "|toString(dim ambient S)|", dim linearSpan C = "|toString(dim linearSpan C)|", dim ambient S - 5 != dim linearSpan C - 2"); + if o.Verbose and dim linearSpan S > 5 then ( + << "-- computing the projection to PP^5 of" << endl; + << " -- " << ? S << endl; + << "-- from points on the linear span of" << endl; + << " -- " << ? C << endl; + ); + piLin := if dim linearSpan S > 5 then rationalMap linearSpan apply(dim linearSpan S - 5, i -> point linearSpan C) else rationalMap(ambient S,ambient S); + if dim target piLin != 5 then error "linear projection failed, target dimension is not 5"; + f := (super parametrize S) * piLin; + S' := new RationalSurfaceWithAttachedPlaneInCubicFourfold from image f; + if min apply(degrees S',i -> first first i) > 3 then error "surface in PP^5 is not contained in any cubic hypersurfaces"; + C' := piLin C; + planeC' := linearSpan C'; + if dim planeC' != 2 then error "dim linearSpan C' != 2"; + if not(dim C' == 1 and degree C' == degree C) then error "not: dim C' == 1 and degree C' == degree C"; + S'.cache#"rationalParametrization" = rationalMap(f,Dominant=>true); + if sectionalGenus C' < 0 or sectionalGenus C < 0 then error "reducible curve"; + S'.cache#"FiniteNumberOfNodes" = (sectionalGenus C') - (sectionalGenus C); + planeC'.cache#"FiniteNumberOfNodes" = 0; + cubicS' := random(3, S' + planeC'); + if not isSmooth cubicS' then error "obtained a singular cubic fourfold"; + if o#"EulerCharacteristic" then ( + if not S.cache#?"euler" then ( + if o.Verbose then << "-- computing Euler characteristic of the surface in PP^" << dim ambient S << ", expected value: " << expEul << endl; + eulerCharacteristic S; + assert(S.cache#?"euler"); + if o.Verbose then << "-- Euler characteristic computation done, obtained value: " << S.cache#"euler" << endl; + ) else ( + if o.Verbose then << "-- Euler characteristic of the surface in PP^" << dim ambient S << " found in cache, skipping computation" << endl; + ); + ); + S'.cache#"euler" = (eulerCharacteristic S) - 6*(numberNodes S'); + if o.Verbose then << "-- constructing a cubic fourfold containing the surface and the plane" << endl; + X := cubicFourfold(S' & planeC',cubicS',Verbose=>o.Verbose); + X.cache#"Construction" = "X = specialFourfold surface("|(toString toSequence ai1i2i3)|","|(toString toSequence dj1j2j3)|");"; + X.cache#"DataConstruction" = (S,C,piLin); + surfaceIntersectionNumber(X,Verbose=>o.Verbose,Verify=>true,"AttemptComputation"=>false); + if o.Verbose then < (ai1i2i3,dj1j2j3,K) -> ( + if # ai1i2i3 != # dj1j2j3 then error "expected two lists of the same length"; + rationalSurfaceWithAttachedPlaneInCubicFourfold(surface(toList ai1i2i3,K),dj1j2j3,"EulerCharacteristic"=>o#"EulerCharacteristic",Verbose=>o.Verbose) +); + +surface (VisibleList,VisibleList,Ring,Option) := (ai1i2i3,dj1j2j3,K,opt) -> ( + o := toList opt; + if not(#o == 2 and first o === Verbose) then error "Verbose is the only available option for surface(VisibleList,VisibleList)"; + rationalSurfaceWithAttachedPlaneInCubicFourfold(ai1i2i3,dj1j2j3,K,"EulerCharacteristic"=>true,Verbose=>last o) +); +surface (VisibleList,VisibleList,Option) := (ai1i2i3,dj1j2j3,opt) -> surface(ai1i2i3,dj1j2j3,ZZ/65521,opt); +surface (VisibleList,VisibleList,Ring) := (ai1i2i3,dj1j2j3,K) -> surface(ai1i2i3,dj1j2j3,K,Verbose=>false); +surface (VisibleList,VisibleList) := (ai1i2i3,dj1j2j3) -> surface(ai1i2i3,dj1j2j3,Verbose=>false); + +specialFourfold RationalSurfaceWithAttachedPlaneInCubicFourfold := cubicFourfold RationalSurfaceWithAttachedPlaneInCubicFourfold := o -> S -> S.cache#"pickedCubicFourfold"; +------------------------------------------------------------------------ + +surfaces = method(); +surfaces DoublySpecialCubicFourfold := X -> toSequence take(X#"SurfaceContainedInTheFourfold",2); + +expression DoublySpecialCubicFourfold := X -> ( + (S,T) := surfaces X; + if isPlaneInP5 S and isPlaneInP5 T then return expression("cubic fourfold in C_8 containing two planes"); + if isPlaneInP5 S then return expression("cubic fourfold in C_8 containing a plane and a surface of degree "|toString(degree T)|" and sectional genus "|toString(sectionalGenus T)); + if isPlaneInP5 T then return expression("cubic fourfold in C_8 containing a surface of degree "|toString(degree S)|" and sectional genus "|toString(sectionalGenus S)|" and a plane"); + expression("cubic fourfold containing two surfaces of degrees " | (toString degree S) | " and " | (toString degree T) | ", sectional genera " | (toString sectionalGenus S) | " and " | (toString sectionalGenus T)) +); + +describe DoublySpecialCubicFourfold := X -> ( + (S,T) := surfaces X; + Y := X.cache#"parentCubicFourfold"; + d1 := discriminant X; + d2 := discriminant Y; + A := latticeIntersectionMatrix3x3 X; + Cd1Cd2 := "C_"|(toString d1); + if d1 != d2 then Cd1Cd2 = Cd1Cd2|" ∩ C_"|(toString d2); + descr := "Cubic fourfold in "|Cd1Cd2|" over "|toString(coefficientRing X)|" of lattice discriminant"; + descr = descr||((net(newline|"det("))|(net A)|(net(newline|") = "|(toString det A)))); + descr = descr||net "containing two surfaces:"; + descr = descr||net(" - " | surfaceDescription(3,S,true)); + descr = descr||net(" - " | surfaceDescription(3,T,true)); + if dim(S * T) >= 0 and top(S * T) != S * T then ( + descr = descr||("Intersection of the surfaces: non-equidimensional scheme of dimension "|(toString dim (S * T))); + ) else ( + -- if dim(S * T) >= 2 then descr = descr||("Intersection of the surfaces: "|(? ideal (S * T))); + if dim(S * T) == 1 then descr = descr||("Intersection of the surfaces: curve of degree "|toString degree(S * T)|" and arithmetic genus "|toString sectionalGenus(S * T)); + if dim(S * T) <= 0 then descr = descr||("Intersection of the surfaces: "|(toString degree (S * T))|" points"); + if dim (S * T) >= 1 and degree(S * T) >= 2 then ( + if dim singularLocus(S * T) <= 0 then ( + m := degree support singularLocus(S * T); + descr = descr||(net "Singular locus of the intersection: "|(if m == 0 then "∅" else (if m == 1 then "a single point" else (toString m)|" points"))); + ) else ( + descr = descr||(net "Singular locus of the intersection: "|(? ideal support singularLocus(S * T))); + ); + ); + ); + if isPlaneInP5 T or isPlaneInP5 S then ( + idX := if substring(0,8,recognizeDSCF X) === "DSCF-V1-" then " {ID: "|substring(8,recognizeDSCF X)|"}" else ""; + h := quadricFibration X; + symb := if X.cache#"quadricFibrationCubicFourfoldInC8"_1 then "★ " else "☆ "; + descr = descr||(symb|X.cache#"quadricFibrationCubicFourfoldInC8"_2); + if X.cache#"quadricFibrationCubicFourfoldInC8"_1 then descr = descr||((computationStatusLog X)|idX); + if computationStatus X >= 1 then descr = descr || (describeMirrorFourfoldAndK3 X); + ); + net expression descr +); + +shortDescriptionFourfold (DoublySpecialCubicFourfold,Boolean) := (X,UseAttribute) -> ( + if UseAttribute and hasAttribute(X,ReverseDictionary) then return toString getAttribute(X,ReverseDictionary); + Y := X.cache#"parentCubicFourfold"; + d1 := discriminant X; + d2 := discriminant Y; + Cd1Cd2 := "C_"|(toString d1); + if d1 != d2 then Cd1Cd2 = Cd1Cd2|" ∩ C_"|(toString d2); + "cubic fourfold in "|Cd1Cd2 +); + +genRingIntMatr3x3 = memoize(() -> ( + â–¡ := local â–¡; + K := ZZ[â–¡]; + first gens K +)); + +latticeIntersectionMatrix3x3 = method(); +latticeIntersectionMatrix3x3 DoublySpecialCubicFourfold := X -> ( + (S,T) := surfaces X; + if X.cache#?(S,T,"LatticeIntersectionMatrix3x3") then return X.cache#(S,T,"LatticeIntersectionMatrix3x3"); + d1 := discriminant X; + MS := X.cache#(S,"LatticeIntersectionMatrix"); + Y := X.cache#"parentCubicFourfold"; + d2 := discriminant Y; + MT := Y.cache#(T,"LatticeIntersectionMatrix"); + h2sq := MS_(0,0); -- (H^2)^2 = deg X (= 3) + h2S := MS_(0,1); -- H^2 * S = deg S + S2 := MS_(1,1); -- S^2 + h2T := MT_(0,1); -- H^2 * T = deg T + T2 := MT_(1,1); -- T^2 + ST := if X.cache#?(S,T,"intersection of surface cycles in cubic fourfold") + then X.cache#(S,T,"intersection of surface cycles in cubic fourfold") + else if dim(S * T) <= 0 + then degree(S * T) + else genRingIntMatr3x3(); + A := matrix { + {h2sq, h2S, h2T}, + {h2S, S2, ST }, + {h2T, ST, T2 } + }; + if ring A === ZZ then X.cache#(S,T,"LatticeIntersectionMatrix3x3") = A; + A +); + +deformViaDoubleLiaison = method(Options => {Verbose => true, Verify => true}); +deformViaDoubleLiaison (ZZ,EmbeddedProjectiveVariety) := o -> (e,S) -> ( + if o.Verbose then << "-- initial ideal generators degrees: " << toStringDegreesVar S << endl; + c := codim S; + if number(flatten degrees ideal S, d -> d <= e) <= c then error("not enough freedom to deform via " | toString(c:e) | " liaison"); + S' := random({c:{e}}, S) \ S; + if o.Verbose then << "-- first " << toString(c:e) << " liaison step: " << toStringDegreesVar S' << endl; + if number(flatten degrees ideal S', d -> d <= e) <= c then error "secondary liaison is trivial: not enough freedom to deform"; + S'' := random({c:{e}}, S') \ S'; + if degrees S'' =!= degrees S then error "liaison failed to preserve degrees"; + if o.Verify then ( + if hilbertPolynomial S != hilbertPolynomial S'' then error "liaison failed to preserve Hilbert polynomial"; + if not isSmooth S'' then error "deformation not smooth"; + ); + S'' +); + +surfaceIntersectionNumber = method(Options => {Verbose => true, Verify => true, "AttemptComputation" => true}); +surfaceIntersectionNumber DoublySpecialCubicFourfold := o -> X -> ( + (S,T) := surfaces X; + if X.cache#?(S,T,"intersection of surface cycles in cubic fourfold") then return X.cache#(S,T,"intersection of surface cycles in cubic fourfold"); + if not o#"AttemptComputation" then return genRingIntMatr3x3(); + ST := S + T; + e := 0; + if number(flatten degrees ideal ST, d -> d <= 2) >= 4 then e = 2 + else if number(flatten degrees ideal ST, d -> d <= 3) >= 4 then e = 3; + if e == 0 then return genRingIntMatr3x3(); + if o.Verbose then << "-- trying to compute surface cycle intersection via " << (e,e,e) << " liaison" << endl; + try ST'' := deformViaDoubleLiaison(e,ST,Verbose=>o.Verbose,Verify=>o.Verify) then ( + if o.Verbose then ( + if o.Verify then << "-- smooth deformation of the union of the surfaces obtained" << endl + else << "-- deformation of the union of the surfaces obtained" << endl; + ); + Z := cubicFourfold(ST'',Verbose=>false); + discriminant Z; + selfIntST := first Z.cache#(ST'',"discriminantFourfold"); + if o.Verbose then << "-- discriminant of the cubic fourfold containing the deformed surface: " << discriminant Z << endl; + discriminant X; + selfIntS := first X.cache#(S,"discriminantFourfold"); + Y := X.cache#"parentCubicFourfold"; + discriminant Y; + selfIntT := first Y.cache#(T,"discriminantFourfold"); + a := lift((selfIntST - selfIntS - selfIntT)/2, ZZ); + if o.Verbose then << "-- surface cycles intersection value: " << a << endl; + return X.cache#(S,T,"intersection of surface cycles in cubic fourfold") = a; + ) else ( + if o.Verbose then << "-- liaison " << (e,e,e) << " did not yield a suitable deformation" << endl; + return genRingIntMatr3x3(); + ); +); + +random DoublySpecialCubicFourfold := o -> X -> ( + (S,T) := surfaces X; + Y := cubicFourfold(S & T,InputCheck=>-1); + if X.cache#?(S,T,"labelDSCF") and (not Y.cache#?(S,T,"labelDSCF")) then Y.cache#(S,T,"labelDSCF") = X.cache#(S,T,"labelDSCF"); + if isFanoMapStandard X then setFanoMapToBeStandard Y; + if isFanoMapToP2xP2 X then setFanoMapToBeMapFromP5toP2xP2 Y; + Y +); + +clean DoublySpecialCubicFourfold := X -> ( + (S,T) := surfaces X; + K := coefficientRing X; + x := local x; + R := K[x_0..x_5]; + S' := Var sub(sub(ideal S,vars R),vars ring ambient X); + if S.cache#?"euler" then S'.cache#"euler" = S.cache#"euler"; + T' := Var sub(sub(ideal T,vars R),vars ring ambient X); + if T.cache#?"euler" then T'.cache#"euler" = T.cache#"euler"; + X' := Var sub(sub(ideal X,vars R),vars ring ambient X); + nS := if S.cache#?"FiniteNumberOfNodes" then S.cache#"FiniteNumberOfNodes" else null; + nT := if T.cache#?"FiniteNumberOfNodes" then T.cache#"FiniteNumberOfNodes" else null; + cubicFourfold(S' & T',X',InputCheck=>0,NumNodes=>(nS,nT)) +); + +swap = method(); +swap DoublySpecialCubicFourfold := X -> ( + if X.cache#?"swappedSurfaces" then return X.cache#"swappedSurfaces"; + (S,T) := surfaces X; + K := coefficientRing X; + x := local x; + R := K[x_0..x_5]; + idX := sub(sub(ideal X,vars R),vars ring ambient X); + nS := if S.cache#?"FiniteNumberOfNodes" then S.cache#"FiniteNumberOfNodes" else null; + nT := if T.cache#?"FiniteNumberOfNodes" then T.cache#"FiniteNumberOfNodes" else null; + Y := cubicFourfold(T & S,Var idX,InputCheck=>0,NumNodes=>(nT,nS),Verbose=>false); + X.cache#"swappedSurfaces" = Y; + Y.cache#"swappedSurfaces" = X; + Y +); + +fuse = method(); +fuse DoublySpecialCubicFourfold := X -> ( + if X.cache#?"fusedVersion" then return X.cache#"fusedVersion"; + (S,T) := surfaces X; + K := coefficientRing X; + x := local x; + R := K[x_0..x_5]; + idX := sub(sub(ideal X,vars R),vars ring ambient X); + Y := cubicFourfold(S+T,Var idX,InputCheck=>0,NumNodes=>0,Verbose=>false); + X.cache#"fusedVersion" = Y; + Y.cache#"fusedOrigin" = X; + Y +); + +unfuse = method(); +unfuse CubicFourfold := X -> ( + if X.cache#?"fusedOrigin" then return X.cache#"fusedOrigin"; + ST := surface X; + decST := decompose ST; + if # decST != 2 then error("unfuse: expected a surface with exactly 2 components, but found " | toString(#decST)); + K := coefficientRing X; + x := local x; + R := K[x_0..x_5]; + idX := sub(sub(ideal X,vars R),vars ring ambient X); + Z := cubicFourfold(decST_0 & decST_1,Var idX,InputCheck=>1,Verbose=>false); + Z.cache#"fusedVersion" = X; + X.cache#"fusedOrigin" = Z; + Z +); + +quadricFibration DoublySpecialCubicFourfold := o -> X -> ( + if X.cache#?"quadricFibrationCubicFourfoldInC8" then return first X.cache#"quadricFibrationCubicFourfoldInC8"; + (T,P) := surfaces X; + if not isPlaneInP5 P then (P,T) = (T,P); + if not isPlaneInP5 P then error "one of the two surfaces is required to be a plane"; + h := quadricFibration(rationalMap(P_X),Verify=>false); + if not(codim target h == 0 and dim target h == 2) then error "something went wrong in the construction of the quadric fibration, target != PP^2"; + F := h^* point target h; + if not(dim F == 2 and degree F == 2) then error "something went wrong in the construction of the quadric fibration, the generic fiber is not a quadric surface"; + Z := (F * T)\\P; + assert(dim Z <= 1); + resFib := ""; + if dim Z == 1 then resFib = "The generic quadric fiber meets the other surface residually along a curve"; + if dim Z == 0 and degree Z != 1 then resFib = "The generic quadric fiber meets the other surface residually in "|(toString degree Z)|" points"; + if dim Z == 0 and degree Z == 1 then resFib = "The generic quadric fiber meets the other surface residually in a single point"; + if dim Z == -1 then resFib = "The generic quadric fiber meets the other surface residually in the empty set"; + X.cache#"quadricFibrationCubicFourfoldInC8" = (h, dim Z == 0 and degree Z == 1, resFib); + first X.cache#"quadricFibrationCubicFourfoldInC8" +); + +------------------------------------------------------------------------ +----------- Recognition and auxiliary utilities for D. S. C. F. -------- +------------------------------------------------------------------------ + +-* -- recognizeDSCF -- +debug SpecialFanoFourfolds; +getInv = i -> (X := exampleDSCFourfoldC8(i,ZZ/65521); (S,T) := surfaces X; C := S * T; invX := (degrees S,degree S,sectionalGenus S,euler hilbertPolynomial S,eulerCharacteristic S,numberNodes S, degrees T,degree T,sectionalGenus T,euler hilbertPolynomial T,eulerCharacteristic T,numberNodes T,degrees C,degree C,degrees(S + T)); "if invX === "|(toString invX)|" then return X.cache#(S,T,\"labelDSCF\") = \"DSCF-V1-"|toString(i)|"\""); +getInvTot = () -> (L := ""; for i from 1 to 40 do L = L|newline|getInv(i); L); +*- +recognizeDSCF = X -> ( + (S,T) := surfaces X; + if X.cache#?(S,T,"labelDSCF") then return X.cache#(S,T,"labelDSCF"); + C := S * T; + invX := (degrees S,degree S,sectionalGenus S,euler hilbertPolynomial S,eulerCharacteristic S,numberNodes S, + degrees T,degree T,sectionalGenus T,euler hilbertPolynomial T,eulerCharacteristic T,numberNodes T, + degrees C,degree C,degrees(S + T)); + if invX === ({({2},4)},6,2,1,10,0,{({1},3)},1,0,1,3,0,{({1},3), ({2},1)},2,{({2},3), ({3},1)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-1"; + if invX === ({({2},5)},5,1,1,7,0,{({1},3)},1,0,1,3,0,{({1},3), ({2},1)},2,{({2},4)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-2"; + if invX === ({({2},3), ({3},1)},7,3,1,12,0,{({1},3)},1,0,1,3,0,{({1},3), ({3},1)},3,{({2},3)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-3"; + if invX === ({({2},1), ({3},8)},8,3,0,6,1,{({1},3)},1,0,1,3,0,{({1},3), ({3},1)},3,{({2},1), ({3},7)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-4"; + if invX === ({({3},7), ({4},4)},10,4,-2,-4,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({3},7), ({4},3)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-5"; + if invX === ({({2},6)},4,0,1,3,0,{({1},3)},1,0,1,3,0,{({1},3), ({2},1)},2,{({2},5)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-6"; + if invX === ({({2},2), ({3},5)},7,2,0,3,1,{({1},3)},1,0,1,3,0,{({1},3), ({3},1)},3,{({2},2), ({3},4)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-7"; + if invX === ({({2},1), ({3},8)},8,3,0,5,1,{({1},3)},1,0,1,3,0,{({1},3), ({3},1)},3,{({2},1), ({3},7)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-8"; + if invX === ({({2},3), ({3},2)},6,1,0,0,1,{({1},3)},1,0,1,3,0,{({1},3), ({3},1)},3,{({2},3), ({3},1)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-9"; + if invX === ({({3},10), ({4},1)},9,3,-2,-7,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({3},10)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-10"; + if invX === ({({2},1), ({3},6), ({4},1)},9,4,-1,1,2,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({2},1), ({3},6)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-11"; + if invX === ({({2},2), ({3},3), ({4},1)},8,3,-1,-1,2,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({2},2), ({3},3)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-12"; + if invX === ({({3},7), ({4},2)},10,4,-2,-6,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({3},7), ({4},1)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-13"; + if invX === ({({3},4), ({4},9), ({5},1)},11,4,-5,-23,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},4), ({4},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-14"; + if invX === ({({3},10), ({4},1)},9,3,-2,-8,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({3},10)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-15"; + if invX === ({({3},1), ({4},21), ({5},1)},12,5,-5,-23,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},1), ({4},21)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-16"; + if invX === ({({2},1), ({3},7), ({4},1)},8,2,-2,-10,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({2},1), ({3},7)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-17"; + if invX === ({({3},7), ({4},2)},10,4,-2,-7,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({3},7), ({4},1)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-18"; + if invX === ({({3},6), ({4},2), ({5},1)},11,5,-4,-16,5,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},6), ({4},2)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-19"; + if invX === ({({3},1), ({4},21), ({5},1)},12,5,-5,-24,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},1), ({4},21)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-20"; + if invX === ({({2},2), ({3},4), ({4},1)},7,1,-2,-13,3,{({1},3)},1,0,1,3,0,{({1},3), ({4},1)},4,{({2},2), ({3},4)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-21"; + if invX === ({({3},7), ({4},1), ({5},1)},10,3,-5,-26,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},7), ({4},1)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-22"; + if invX === ({({3},9), ({5},1)},10,4,-4,-18,5,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-23"; + if invX === ({({3},4), ({4},9), ({5},1)},11,4,-5,-25,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},4), ({4},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-24"; + if invX === ({({3},6), ({4},2), ({5},1)},11,5,-4,-17,5,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},6), ({4},2)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-25"; + if invX === ({({2},1), ({3},6), ({5},1)},9,3,-4,-20,5,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({2},1), ({3},6)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-26"; + if invX === ({({3},1), ({4},21), ({5},1)},12,5,-5,-25,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},1), ({4},21)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-27"; + if invX === ({({3},7), ({4},1), ({5},1)},10,3,-5,-27,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},7), ({4},1)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-28"; + if invX === ({({3},4), ({4},9), ({5},1)},11,4,-5,-26,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},4), ({4},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-29"; + if invX === ({({3},10), ({5},1)},9,2,-5,-29,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({3},10)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-30"; + if invX === ({({3},3), ({4},12), ({6},1)},12,5,-8,-41,9,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},3), ({4},12)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-31"; + if invX === ({({3},3), ({4},12), ({6},1)},12,5,-8,-42,9,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},3), ({4},12)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-32"; + if invX === ({({2},1), ({3},7), ({5},1)},8,1,-5,-32,6,{({1},3)},1,0,1,3,0,{({1},3), ({5},1)},5,{({2},1), ({3},7)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-33"; + if invX === ({({3},4), ({4},9), ({6},1)},11,3,-9,-51,10,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},4), ({4},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-34"; + if invX === ({({3},6), ({4},2), ({6},1)},11,4,-8,-43,9,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},6), ({4},2)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-35"; + if invX === ({({3},9), ({6},1)},10,3,-8,-45,9,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-36"; + if invX === ({({3},4), ({4},9), ({6},1)},11,3,-9,-52,10,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},4), ({4},9)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-37"; + if invX === ({({3},7), ({4},2), ({6},1)},10,2,-9,-54,10,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},7), ({4},2)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-38"; + if invX === ({({3},10), ({6},1)},9,1,-9,-57,10,{({1},3)},1,0,1,3,0,{({1},3), ({6},1)},6,{({3},10)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-39"; + if invX === ({({3},6), ({4},3), ({7},1)},11,3,-13,-76,14,{({1},3)},1,0,1,3,0,{({1},3), ({7},1)},7,{({3},6), ({4},3)}) then return X.cache#(S,T,"labelDSCF") = "DSCF-V1-40"; + if invX === ({({2}, 6)}, 4, 0, 1, 3, 0, {({1}, 3)}, 1, 0, 1, 3, 0, {({1}, 3), ({2}, 3)}, 3, {({2}, 3), ({3}, 3)}) then return X.cache#(S,T,"labelDSCF") = "Tregub1"; + X.cache#(S,T,"labelDSCF") = "NotRecognized" +); + +isSurfaceUknownToBeAlreadyEquidimensional = (X,mu) -> ( + if mu.cache#?"surface U is already equidimensional" then return mu.cache#"surface U is already equidimensional"; + if isFanoMapStandard(X) and member(recognizeDSCF X,{"DSCF-V1-13", "DSCF-V1-15", "DSCF-V1-18", "DSCF-V1-20", "DSCF-V1-25", "DSCF-V1-27", "DSCF-V1-36"}) then ( + return mu.cache#"surface U is already equidimensional" = false; + ); + if isFanoMapStandard(X) and member(recognizeDSCF X, {"DSCF-V1-1", "DSCF-V1-2", "DSCF-V1-3", "DSCF-V1-4", "DSCF-V1-5", "DSCF-V1-6", "DSCF-V1-7", "DSCF-V1-8", "DSCF-V1-9", "DSCF-V1-10", "DSCF-V1-11", "DSCF-V1-12", "DSCF-V1-14", "DSCF-V1-16", "DSCF-V1-17", "DSCF-V1-19", "DSCF-V1-21", "DSCF-V1-22", "DSCF-V1-23", "DSCF-V1-24", "DSCF-V1-26", "DSCF-V1-28", "DSCF-V1-29", "DSCF-V1-30", "DSCF-V1-31", "DSCF-V1-32", "DSCF-V1-33", "DSCF-V1-34", "DSCF-V1-35", "DSCF-V1-37", "DSCF-V1-38", "DSCF-V1-39", "DSCF-V1-40"}) then ( + return mu.cache#"surface U is already equidimensional" = true; + ); + return false; +); + +isNormalizationKnownToTerminateQuickly = X -> isFanoMapStandard(X) and member(recognizeDSCF X,{"DSCF-V1-17", "DSCF-V1-27", "DSCF-V1-30"}); + +isHigherDegreeCurveInExceptionalSetKnownToBeSpecial = X -> isFanoMapStandard(X) and member(recognizeDSCF X,{"DSCF-V1-34","DSCF-V1-40"}); + +isSelfIntersectionVerificationKnownToBeSuperfluous = X -> isFanoMapStandard(X) and member(recognizeDSCF X,{"DSCF-V1-40"}); + +someExceptionalCurvesKnownToAppearWithMultiplicity = X -> isFanoMapStandard(X) and member(recognizeDSCF X,{"DSCF-V1-5", "DSCF-V1-14"}); + +setStrategyDSCFtoK3 = (X,Str) -> ( + if Str =!= null then return Str; + if isFanoMapStandard X then ( + if member(recognizeDSCF X,{"DSCF-V1-13","DSCF-V1-18","DSCF-V1-24","DSCF-V1-25","DSCF-V1-27","DSCF-V1-31","DSCF-V1-40"}) then return "Approximate"; + return "Inverse"; + ); + if isFanoMapToP2xP2 X then return "Approximate"; + return "Inverse"; +); + +setStrategyDSCFtoPolarize = (Utilde,Str) -> ( + if Str =!= null then return Str; + X := recoverFourfold Utilde; + (mu,U,LC,f) := building Utilde; + if U.cache#?"special curves on U" or isHigherDegreeCurveInExceptionalSetKnownToBeSpecial(X) then return "SpecialCurve"; + if isFanoMapStandard X then ( + if member(recognizeDSCF X,{"DSCF-V1-6","DSCF-V1-21","DSCF-V1-26","DSCF-V1-30","DSCF-V1-33","DSCF-V1-36","DSCF-V1-39"}) then return "MapFromU"; + if f =!= null then return "MapFromW" else return "MapFromW-Virtual"; + ); + if isFanoMapToP2xP2 X then ( + if f =!= null then return "MapFromU" else return "MapFromU-Virtual"; + ); + return "MapFromU"; +); + +------------------------------------------------------------------------ +---------------------- Construction of DSCF examples ------------------ +------------------------------------------------------------------------ + +exampleDSCFourfoldC8 = method(TypicalValue => DoublySpecialCubicFourfold, Options => {Verbose => true}); +exampleDSCFourfoldC8 (ZZ,Ring) := o -> (i,K) -> ( + if not (1 <= i and i <= 40) then error "DSCF example out of range: expected an integer between 1 and 40"; + L := {null, +((4,6,1,0,0),(1,2,0,0,0)), +((3,4,0,0,0),(1,1,0,0,0)), +((4,9,0,0,0),(3,9,0,0,0)), +((5,8,0,1,0),(1,2,0,0,0)), +((6,10,0,0,1),(1,2,0,0,0)), +((2,0,0,0,0),(1,0,0,0,0)), +((4,5,1,0,0),(1,1,0,0,0)), +((4,8,0,0,0),(2,5,0,0,0)), +((3,3,0,0,0),(1,0,0,0,0)), +((5,7,0,1,0),(1,1,0,0,0)), +((5,8,2,0,0),(3,7,2,0,0)), +((4,8,0,0,0),(3,8,0,0,0)), +((5,7,2,0,0),(2,4,1,0,0)), +((6,9,0,0,1),(1,1,0,0,0)), +((4,7,0,0,0),(2,4,0,0,0)), +((6,7,2,1,0),(2,4,0,1,0)), +((4,4,1,0,0),(1,0,0,0,0)), +((6,2,6,0,0),(1,2,0,0,0)), +((5,10,1,0,0),(3,8,1,0,0)), +((6,4,5,0,0),(2,3,2,0,0)), +((3,2,0,0,0),(2,2,0,0,0)), +((5,6,0,1,0),(1,0,0,0,0)), +((5,7,2,0,0),(3,6,2,0,0)), +((5,6,2,0,0),(2,3,1,0,0)), +((6,5,5,0,0),(3,5,4,0,0)), +((4,7,0,0,0),(3,7,0,0,0)), +((8,0,4,4,0),(2,0,4,1,0)), +((4,6,0,0,0),(2,3,0,0,0)), +((6,1,6,0,0),(1,1,0,0,0)), +((4,3,1,0,0),(2,1,1,0,0)), +((5,9,1,0,0),(3,7,1,0,0)), +((6,4,5,0,0),(3,4,4,0,0)), +((3,1,0,0,0),(2,1,0,0,0)), +((5,5,0,1,0),(2,1,0,1,0)), +((5,6,2,0,0),(3,5,2,0,0)), +((4,6,0,0,0),(3,6,0,0,0)), +((4,5,0,0,0),(2,2,0,0,0)), +((4,2,1,0,0),(2,0,1,0,0)), +((3,0,0,0,0),(2,0,0,0,0)), +((4,5,0,0,0),(3,5,0,0,0))}; + if o.Verbose then << "-- constructing example n. " << i << ", specialFourfold surface" << toString(L_i) << endl; + cubicFourfold rationalSurfaceWithAttachedPlaneInCubicFourfold(splice(L_i,K),Verbose=>o.Verbose) +); + +check DoublySpecialCubicFourfold := o -> X -> ( + if not (X.cache#?"DataConstruction" and X.cache#?"Construction" and instance(X.cache#"Construction",String) and substring(0,29,X.cache#"Construction") == "X = specialFourfold surface((") then error "expected a cubic fourfold constructed via specialFourfold surface((...),(...))"; + (S,P) := surfaces X; + (S',C',pr) := X.cache#"DataConstruction"; + C := pr C'; + if not isSubset(C, S * P) then error "projection of curve is not contained in surface-plane intersection"; + D := (S * P) \\ C; + if dim D != -1 then error("surface-plane intersection contains extra components: " | (? ideal D)); + SingC := support singularLocus C; + n := S.cache#"FiniteNumberOfNodes"; + if degree SingC == n then ( + if o.Verbose then << "-- verified: surface-plane intersection has correct number of nodes (" << n << ")" << endl; + ) else ( + error("incorrect number of nodes in surface-plane intersection: expected " | (toString n) | ", obtained " | (toString degree SingC)); + ); + SingS := support singularLocus S; + if not isSubset(SingC, SingS) then error "singular locus of intersection curve is not contained in the singular locus of the surface"; + resSing := SingS \\ SingC; + if dim resSing == -1 then ( + if o.Verbose then << "-- verified: surface is smooth outside the curve" << endl; + ) else ( + errLog := if dim resSing == 0 then (toString degree resSing) | " points" else "singular locus is not 0-dimensional"; + error("surface has singularities outside the curve: " | errLog); + ); + return X; +); + +discoverCubicFourfoldsInC8 = (e,dmin,dmax,Nmin,Nmax,summaryFileName,rationalSectionOnly,maxTime,NewCollectionFourfolds) -> ( + -- usage: discoverCubicFourfoldsInC8({10,10,10,10,10},1,4,5,13,"examples_v1",true,300,{}); + startTime := currentTime(); + runTime := () -> humanReadableSeconds(currentTime() - startTime); + for Z in NewCollectionFourfolds do (Z.cache#"instanceCount" = 0; Z.cache#"calculationTime" = humanReadableSeconds(0)); + instanceCount := 0; + K := ZZ/65521; + local X; local S; local T; local P; local C; local SummaryFile; local DataFile; + for a from 1 to e_0 do + for i1 to e_1 do + for i2 to e_2 do + for i3 to e_3 do + for i4 to e_4 do ( + if binomial(a+2,2) - i1 - 3*i2 - 6*i3 - 10*i4 - 1 > Nmax or binomial(a+2,2) - i1 - 3*i2 - 6*i3 - 10*i4 - 1 < Nmin then continue; + try ( + alarm maxTime; + <<"-- constructing surface S"<<(a,i1,i2,i3,i4)<<"..."< Nmax or dim ambient S < Nmin then continue; + if dim ambient S < Nmin then continue; + for d from dmin to dmax do + for u1 to i1 do + for u2 to i2 do + for u3 to i3 do + for u4 to i4 do ( + <<"-- surface: "< surface is a section but formula not satisfied: 1 != (a-d)^2 - (i1-u1+u2) - 4*(i2-u2+u3) - 9*(i3-u3+u4) - 16*(i4-u4) = "<<((a-d)^2 - (i1-u1+u2) - 4*(i2-u2+u3) - 9*(i3-u3+u4) - 16*(i4-u4))< (degrees surface Y,betti res ideal surface Y,degree surface Y,sectionalGenus surface Y,euler hilbertPolynomial surface Y,eulerCharacteristic surface Y,numberNodes surface Y,dim((surface Y)*(surface Y.cache#"parentCubicFourfold")),degree((surface Y)*(surface Y.cache#"parentCubicFourfold")),degrees((surface Y)+(surface Y.cache#"parentCubicFourfold"))))) then ( + instanceCount = instanceCount + 1; + X.cache#"instanceCount" = instanceCount; + X.cache#"calculationTime" = runTime(); + NewCollectionFourfolds = sort append(NewCollectionFourfolds,X); + <i===Y)+1<<") obtained after "< ( + X := specialFourfold("DSCF-"|(toString i),Verbose=>false); + (S,P) := surfaces X; + E := polarizedK3surface polarizedK3surface X; + (mu,U,LC,f) := building E; + W := target mu; + (i, nextPrime random(33331,65521), degree S, sectionalGenus S, discriminant X, degree U, sectionalGenus U, euler hilbertPolynomial U, dim ambient W, degree W, sectionalGenus W, degree first LC, degree last LC, dim target f, latticeMatrix latticePolarization E) +); + +runExampleTest = (i,charK,degS,gS,dX,degU,gU,chiOU,ambW,degW,gW,degL,degC,gK3,M) -> ( + X := specialFourfold("DSCF-"|(toString i),ZZ/charK,Verbose=>true); + assert(charK === char coefficientRing X); + (S,P) := surfaces X; + describe X; + assert(X.cache#(S,P,"labelDSCF") === "DSCF-V1-"|toString(i)); + assert(degree P == 1 and degree S == degS and sectionalGenus S == gS); + assert(discriminant X == dX and discriminant X.cache#"parentCubicFourfold" == 8); + assert(computationStatus X == -1); + E := polarizedK3surface(X,Verbose=>true); + assert instance(E, K3SurfaceFromDoublySpecialCubicFourfold); + assert(computationStatus X == 3); + (mu,U,LC,f) := building E; (L,C) := toSequence LC; + assert(U === surface mirrorFourfold X); + assert(degree U == degU and sectionalGenus U == gU and euler hilbertPolynomial U == chiOU); + W := target mu; + assert(W == mirrorFourfold X); + assert(dim ambient W == ambW and degree W == degW and sectionalGenus W == gW); + assert(degree L == degL and degree C == degC); + assert(dim target f == gK3 and f#"image" =!= null and sectionalGenus image f == gK3); + assert(ideal projectiveVariety E === ideal image f); + assert instance(projectiveVariety E, SurfaceAssociatedToRationalFourfold); + assert(isFanoMapStandard X and fanoMapDSCF X === mu); + assert(E#"LatticePolarization" === null); + polarizedK3surface(X,Verbose=>true); + assert(E#"LatticePolarization" =!= null); + assert(computationStatus X == 4); + assert instance(latticePolarization E, LatticePolarizationOnK3Surface); + assert(latticeMatrix latticePolarization E == M); + assert(recoverFourfold E === X and recoverFourfold projectiveVariety E === X); + assert(setStrategyDSCFtoK3(X,null) === U.cache#"strategy for surface U"); + if setStrategyDSCFtoK3(X,null) === "Approximate" then return; + (mu',eta) := U.cache#"birational maps from X to W and from W to X"; + assert(eta === getInverseFanoMap X and eta === getInverseFanoMap E); + assert(source eta === target mu and target eta == X and eta#"inverse" === mu'); +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/FanoMaps.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/FanoMaps.m2 new file mode 100644 index 00000000000..c103569be9b --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/FanoMaps.m2 @@ -0,0 +1,450 @@ + +------------------------------------------------------------------------ +------------------------------ Fano maps ------------------------------- +------------------------------------------------------------------------ + +fanoMap = method(Options => {Singular => null, RaiseError => true, Verbose => false}); + +fanoMap HodgeSpecialFourfold := o -> X -> ( + if (surface X).cache#?("fanoMap",ambientFivefold X) then return (surface X).cache#("fanoMap",ambientFivefold X); + if (surface X).cache#?("fanoMap",ambient X) and dim ambient X >= 5 then ( + Mu := (surface X).cache#("fanoMap",ambient X); + mu := toRationalMap(Mu|(ambientFivefold X)); + (mu#"map").cache#"multiplicityFanoMap" = (Mu#"map").cache#"multiplicityFanoMap"; + if mu#"idealImage" === null then forceImage(mu,ideal(0_(target mu))); -- + (surface X).cache#("fanoMap",ambientFivefold X) = mu; + return mu + ); + local e; sat := 1; + if member(recognize X,{"quinticDelPezzoSurface","quarticScrollSurface",1,6,"planeInPP7"}) + then e = 1 + else if member(recognize X,{"C38Coble","FarkasVerra","oneNodalSepticDelPezzoSurfaceC26",17,18,"october2021-26''"}) + then e = 2 + else if member(recognize X,{"C42","6NodalOcticSrollC38",3,"october2021-34'","mukai26''","october2021-20","surf-5-7-0-1"}) + then e = 3 + else if member(recognize X,{"surf-5-10-1","surf-7-1-9"}) + then e = 4 + else if member(recognize X,{"gushel26''","internal-projection-K3-genus-8", "hyperplane section of a conic bundle over PP2", "surf-4-3-1-external"}) + then e = 5 + else if member(recognize X,{"surf-5-6-2-nodal"}) + then e = 6 + else e = (detectCongruence(X,Verbose=>o.Verbose))#"degree"; + if o.Verbose then <<"-- detected degree of the curves of the congruence: "<o.Singular,RaiseError=>o.RaiseError,Verbose=>o.Verbose) +); + +fanoMap (HodgeSpecialFourfold,ZZ) := o -> (X,e) -> ( + sat := 1; + if member(recognize X,{"october2021-34'","gushel26''","surf-7-1-9"}) then sat = 2; + fanoMap(X,e,sat,Singular=>o.Singular,RaiseError=>o.RaiseError,Verbose=>o.Verbose) +); + +imageOfFanoMap = method(Options => {RaiseError => true, Verbose => false}); +imageOfFanoMap (HodgeSpecialFourfold,RationalMap) := o -> (X,mu) -> ( + M := mu#"dimAmbientSource"; + if M =!= mu#"dimSource" then error "expected target of map to be a projective space"; + if M <= 3 then error "Fano map not valid"; + if mu#"idealImage" =!= null then return image mu; + if o.Verbose then <<"-- computing/forcing image of map to PP^"<o.Verbose) + else if member(recognize X,{3, "internal-projection-K3-genus-8", "october2021-34'"}) + then interpolateImage(mu,{3},3,Verbose=>o.Verbose) + else if recognize X === "hyperplane section of a conic bundle over PP2" + then interpolateImage(mu,{2,2,2},2,Verbose=>o.Verbose) + else if M == 4 + then forceImage(mu,ideal(0_(target mu))) + else if M == 5 + then (try interpolateImage(mu,{2},2,Verbose=>o.Verbose) else try interpolateImage(mu,{3},3,Verbose=>o.Verbose)) + else if M == 6 + then (try interpolateImage(mu,{2,2},2,Verbose=>o.Verbose)) + else if M == 7 + then (try interpolateImage(mu,{2,2,2,2,2},2,Verbose=>o.Verbose) else try interpolateImage(mu,{2,2,2},2,Verbose=>o.Verbose)) + else if member(M,{8,9,10,11,12}) + then (try interpolateImage(mu,toList(binomial(M-4,2) : 2),2,Verbose=>o.Verbose)); + if mu#"idealImage" =!= null then ( + if dim image mu != 5 then error "something went wrong; the image of the Fano map is not of dimension 4"; + return image mu; + ); + if o.RaiseError then error "not implemented yet: image of Fano map for unrecognized fourfold"; +); + +fanoMap (HodgeSpecialFourfold,ZZ,ZZ) := o -> (X,e,sat) -> ( + if (surface X).cache#?("fanoMap",ambientFivefold X) then return (surface X).cache#("fanoMap",ambientFivefold X); + P := o.Singular; + if P === null then ( + P = findProgram("Singular","Singular --help",RaiseError=>false) =!= null; + if o.Verbose then ( + displayVar := getenv "DISPLAY"; + hasX11 := displayVar =!= null and displayVar =!= ""; + P = P and hasX11 and findProgram("xterm","xterm -version",RaiseError=>false) =!= null; + ); + ); + if not instance(P,Boolean) then error "option Singular expects true or false"; + if e == 1 then sat = 0; + local mu; + if P then ( + mu = fanoMapUsingSingular(X,e,sat,Verbose=>o.Verbose); + ) else ( + S := idealOfSubvariety((surface X)%(ambientFivefold X)); + a := degreeHypersurface X; + if o.Verbose then <<"-- computing power of ideal"< degree i <= {a*e-1}); + ); + W := imageOfFanoMap(X,mu,RaiseError=>o.RaiseError,Verbose=>o.Verbose); + if W =!= null then ( + mu = rationalMap(mu,Dominant=>true); + (mu#"map").cache#"multiplicityFanoMap" = e; + (surface X).cache#("fanoMap",ambientFivefold X) = mu; + ) else ( + <<"--error: not implemented yet: image of Fano map for unrecognized fourfold"< {Verbose => false}); +fanoMapUsingSingular (HodgeSpecialFourfold,ZZ,ZZ) := o -> (X,e,sat) -> ( + Singular := findProgram("Singular", "Singular --help"); + if o.Verbose then XTermSingular := findProgram("xterm", "xterm -version"); -- using xterm we see Singular messages instantly and not at the end of the calculation + V := ambientFivefold X; + S := surface X; + x := local x; + K := coefficientRing X; + if not (if char K == 0 then K === QQ else K === ZZ/(char K)) then error "expected coefficient ring of the form ZZ/p or QQ"; + R := Grass(0,dim ambient X,K,Variable=>x); + idV := sub(ideal V,vars R); + R = R/idV; + I := sub(idealOfSubvariety(S%V),vars R); + dir := temporaryFileName() | "/"; + mkdir dir; + if o.Verbose then <<"-- writing Singular code on file: "<<(dir|"input.singular")<=1; i=i-1) {if (deg(Q[i]) <= "<dir,RaiseError=>true,Verbose=>o.Verbose,KeepFiles=>false) + else runProgram(Singular,"-qc 'execute(read(\"input.singular\"));'",RunDirectory=>dir,RaiseError=>true,Verbose=>o.Verbose,KeepFiles=>false); + Out := "x := local x; x = gens Grass(0,"|toString(dim ambient X)|","|(toExternalString K)|",Variable=>x);" | newline | get(dir|"output.singular") | newline | "OUTPUTVALUE" | newline; + D := value Out; + D = if ring D === ZZ then sub(D,ring V) else sub(D,vars ring V); + rationalMap gens trim D +); + +------------------------------------------------------------------------ +------------------- Fano maps for D. S. C. F. -------------------------- +------------------------------------------------------------------------ + +fanoMapDSCF = method(Options => {Verify => true, Verbose => true}); +fanoMapDSCF DoublySpecialCubicFourfold := o -> X -> ( + (S,T) := surfaces X; + if S.cache#?("FanoMapDSCF",T) then return S.cache#("FanoMapDSCF",T); + if isFanoMapStandard X then return S.cache#("FanoMapDSCF",T) = fanoMapDSCFstandard(X,Verify=>o.Verify,Verbose=>o.Verbose); + if isFanoMapToP2xP2 X then return S.cache#("FanoMapDSCF",T) = fanoMapDSCFtoP2xP2(X,Verify=>o.Verify,Verbose=>o.Verbose); + -- error "no Fano map type detected"; + setFanoMapToBeStandard(X,Verbose=>o.Verbose); + fanoMapDSCF(X,Verify=>o.Verify,Verbose=>o.Verbose) +); + +fanoMapDSCFstandard = method(Options => {Verify => true, Verbose => true}); +fanoMapDSCFstandard (DoublySpecialCubicFourfold,ZZ,ZZ,ZZ) := o -> (X,d,a,b) -> ( + (S,T) := surfaces X; + if S.cache#?("FanoMapDSCFstandard",T,d,a,b) then return S.cache#("FanoMapDSCFstandard",T,d,a,b); + mu := rationalMap(a*S + b*T, d); + if o.Verbose then ( + << "-- computed map μ: PP^5 --> PP^" << (dim ambient target mu) << " defined by hypersurfaces of degree " << d << endl; + << "-- with multiplicities " << a << " and " << b << " along the two surfaces" << endl; + ); + if o.Verify then ( + if dim target mu <= 3 then ( + S.cache#("generic fiber verification fano map",T,d,a,b) = false; + error "generic fiber verification failed: target dimension is too small for the expected map"; + ); + C := mu^* mu point source mu; + e := degree C; + CS := C * S; + CT := C * T; + if not(dim C == 1 and dim CS == 0 and dim CT == 0 and degree CS + degree CT == 3*e-1) then ( + S.cache#("generic fiber verification fano map",T,d,a,b) = false; + error "generic fiber verification failed: expected a curve of degree e which is (3e-1)-secant the union of the two surfaces"; + ); + if o.Verbose then ( + if e == 1 and flatten degrees ideal C === toList(4:1) and isPoint CS and isPoint CT then ( + << "-- verified: generic fiber is a line connecting a point on each of the two surfaces" << endl; + ) else ( + assert(e >= 1); + << "-- verified: generic fiber is a curve of degree " << e << " intersecting the surfaces in " << degree CS << " + " << degree CT << " = " << (degree CS + degree CT) << " points" << endl; + ); + ); + S.cache#("generic fiber verification fano map",T,d,a,b) = true; + ); + S.cache#("FanoMapDSCFstandard",T,d,a,b) = mu +); + +fanoMapDSCFstandard DoublySpecialCubicFourfold := o -> X -> ( + (S,T) := surfaces X; + if S.cache#?("FanoMapDSCFstandard",T) then return S.cache#("FanoMapDSCFstandard",T); + local mu; + found := false; + linSys := null; + if isPlaneInP5 T then ( + quadricFibration X; + assert X.cache#?"quadricFibrationCubicFourfoldInC8"; + if o.Verbose and (not X.cache#"quadricFibrationCubicFourfoldInC8"_1) then ( + <<"-- warning: fanoMap: possible infinite loop. " << X.cache#"quadricFibrationCubicFourfoldInC8"_2 << endl; + ); + if X.cache#"quadricFibrationCubicFourfoldInC8"_1 then ( + for d from 2 do ( + if o.Verbose then << "-- fanoMap: attempting map μ with linear system of degree " << d << "..." << endl << flush; + try mu = fanoMapDSCFstandard(X,d,1,d-1,Verify=>true,Verbose=>o.Verbose); + if S.cache#("generic fiber verification fano map",T,d,1,d-1) then ( + found = true; + linSys = (d,1,d-1); + if o.Verbose then ( + << "-- success: valid map found at degree " << d << " with multiplicities (" << 1 << ", " << d-1 << ")" << endl << endl << flush; + ); + break; + ); + ); + ); + ); + if not found then ( + if o.Verbose and (isPlaneInP5 S) and (not isPlaneInP5 T) then ( + << "-- note: the first surface in the fourfold is a plane. fanoMap() performs better" << endl + << " if the plane is the second surface. Consider interchanging them" << endl + << " when constructing the fourfold." << endl; + ); + if o.Verbose then ( + << "-----------------------------------------" << endl; + << "-- fanoMap: brute-force search starting " << endl; + << "-----------------------------------------" << endl; + ); + for d from 2 do ( + if o.Verbose then << "-- fanoMap: attempting map μ with linear system of degree " << d << "..." << endl << flush; + for a from 1 to d-1 do ( + for b from 1 to d-1 do ( + try mu = fanoMapDSCFstandard(X,d,a,b,Verify=>true,Verbose=>o.Verbose); + if S.cache#("generic fiber verification fano map",T,d,a,b) then ( + found = true; + linSys = (d,a,b); + if o.Verbose then ( + << "-- success: valid map found at degree " << d << " with multiplicities (" << a << ", " << b << ")" << endl << endl << flush; + ); + break; + ); + ); + if found then break; + ); + if found then break; + ); + ); + targetDim := dim ambient target mu; + if targetDim == 4 then forceImage(mu,target mu); + local pDegs; + if targetDim == 5 and mu#"image" === null then ( + pDegs = reverse projectiveDegrees mu; + assert(pDegs_0 == 0 and pDegs_1 != 0); + if o.Verbose then ( + << "-- projective degrees of μ: " << reverse pDegs << endl; + << " -- computing image of μ : PP^5 --> PP^" << targetDim << " using 'image(μ, " << pDegs_1 << ")'..." << endl; + ); + forceImage(mu,image(mu,pDegs_1)); + if o.Verbose then << "-- image of μ: " << ? image mu << endl << endl << flush; + ); + F4StrategyLimit := 7; + if targetDim > 5 and mu#"image" === null then ( + if targetDim <= F4StrategyLimit then ( + if o.Verbose then << "-- computing image of μ : PP^5 --> PP^" << targetDim << " using 'F4' algorithm..." << endl; + image(mu,"F4"); + ) else ( + if o.Verbose then ( + << "-- computing image of μ : PP^5 --> PP^" << targetDim << " using 'W = image(μ, 2)'..." << endl; + << " -- determining degree of the image via projective degrees..." << endl; + ); + pDegs = reverse projectiveDegrees mu; + assert(pDegs_0 == 0 and pDegs_1 != 0); + if o.Verbose then << " -- projective degrees: " << reverse pDegs << ", expected degree: " << pDegs_1 << endl; + W := image(mu,2); + if not (dim W == 4 and degree W == pDegs_1) then ( + if o.Verbose then << "-- 'image(μ, 2)' failed, recomputing with 'F4' algorithm..." << endl; + image(mu,"F4"); + ) else ( + if o.Verbose then << " -- degree confirmed: proceeding to force the image to W" << endl; + forceImage(mu,W); + ); + ); + if o.Verbose then << "-- image of μ: " << ? image mu << endl << endl << flush; + ); + Mu := rationalMap(mu,Dominant=>true); + Mu.cache#"FanoMapType" = "Standard"; + S.cache#("FanoMapDSCFstandard",T) = Mu; + if linSys_1 == linSys_2 and linSys_0 == 3 * linSys_1 - 1 and (not (surface fuse X).cache#?("fanoMap",ambientFivefold fuse X)) then ( + mu2 := toRationalMap Mu; + (mu2#"map").cache#"multiplicityFanoMap" = linSys_1; + (surface fuse X).cache#("fanoMap",ambientFivefold fuse X) = mu2; + ); + Mu +); + +fanoMapDSCFtoP2xP2 = method(Options => {Verify => true, Verbose => true}); +fanoMapDSCFtoP2xP2 DoublySpecialCubicFourfold := o -> X -> ( + (S,T) := surfaces X; + if S.cache#?("FanoMapDSCFtoP2xP2",T) then return S.cache#("FanoMapDSCFtoP2xP2",T); + if o.Verbose then << "-- constructing the map PP^5 --> PP^2 x PP^2 via abstract join" << endl << flush; + (f,g) := abstractJoinOfRationalSurfaces(S,T,Verbose=>o.Verbose); + if o.Verbose then << "-- computing the inverse of the second projection..." << endl; + g' := inverse(g, Verify=>false); + if o.Verify and g' * g != 1 then error "inverse verification failed"; + if o.Verbose then << "-- inverse of second projection computed" << endl; + h := g' * f; + if o.Verify then ( + if o.Verbose then << "-- verifying properties on composition PP^5 --> .. --> PP^2 x PP^2" << endl << flush; + (a1,a2) := toSequence apply(projectionMaps h, degreeOfDefiningForms); + (M1,M2) := toSequence apply(factor h,matrix); + Z1 := projectiveVariety ideal det(M1 * matrix apply(numColumns M1, i -> {random coefficientRing h})); + Z2 := projectiveVariety ideal det(M2 * matrix apply(numColumns M2, i -> {random coefficientRing h})); + assert(degree Z1 == a1 and degree Z2 == a2); + multHyp := (p,Z) -> (if not isSubset(p,Z) then return 0; degree tangentCone(p,Z)); + (z1,z2) := ((a1, multHyp(point S,Z1), multHyp(point T,Z1)), (a2, multHyp(point S,Z2), multHyp(point T,Z2))); + p := point target h; + Fibh := h^* p; + if degree(h|X,Strategy=>"random point") == 1 and dim Fibh == 1 and degree Fibh == 1 and isPoint(Fibh * S) and isPoint(Fibh * T) then ( + if o.Verbose then ( + << " -- info on the map: (1) deg " << z1_0 << ", mult. (" << z1_1 << "," << z1_2 << "); (2) deg " << z2_0 << ", mult. (" << z2_1 << "," << z2_2 << ")" << endl; + << " -- generic fiber is a line connecting a point on each of the two surfaces" << endl; + << " -- restriction to the cubic fourfold is birational" << endl << flush; + ); + h#"isDominant" = true; + ) else error "some verifications did not match the expected results"; + ); + s := rationalMap(segreEmbedding target h,Dominant=>true); + mu := h * s; + if o.Verify then assert(mu#"isDominant"); + mu.cache#"FactorsViaSegreEmbedding" = (h,s); + mu.cache#"FanoMapType" = "FromP5toP2xP2"; + S.cache#("FanoMapDSCFtoP2xP2",T) = mu +); + +abstractJoinOfRationalSurfaces = method(Options => {Verbose => true}); +abstractJoinOfRationalSurfaces (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,P) -> ( + -- Input: two rational surfaces in PP^n + -- Output: the two projections from the abstract join: J --> PP^2xPP^2, J --> PP^n + if S.cache#?("abstract join of rational surfaces",P) then return S.cache#("abstract join of rational surfaces",P); + if o.Verbose then << " -- computing abstract join of two rational surfaces..." << endl; + if ring ideal P =!= ring ideal S then error "expected varieties in the same ambient projective space"; + if dim P != 2 or dim S != 2 then error "expected two surfaces"; + try ( + f := super parametrize P; + assert(dim source f == 2 and codim source f == 0); + ) else error("abstractJoin: failed to parametrize the surface: " | (? P)); + try ( + g := super parametrize S; + assert(dim source g == 2 and codim source g == 0); + ) else error("abstractJoin: failed to parametrize the surface: " | (? S)); + K := coefficientRing S; + n := dim ambient S; + (t,x,y,z) := (local t,local x,local y,local z); + R := K[t_0,t_1, x_0..x_2, y_0..y_2, z_0..z_n, MonomialOrder=>Eliminate 2]; + F := (map(R,ring source f,{x_0..x_2})) matrix f; + G := (map(R,ring source g,{y_0..y_2})) matrix g; + J := ideal(matrix{{z_0..z_n}} - t_0*F - t_1*G); + R' := K[x_0..x_2, y_0..y_2, z_0..z_n, Degrees=>{3:{1,0,0},3:{0,1,0},(n+1):{0,0,1}}]; + J' := sub(ideal selectInSubring(1, gens gb J), R'); + if o.Verbose then << " -- abstract join computation almost complete" << endl; + JJ := projectiveVariety J'; + if o.Verbose then << " -- constructing the two projections: p1:..-->PP^2xPP^2 and p2:..-->PP^" << n << endl; + -- psi := rationalMap(last projectionMaps JJ,target f); -- might cause bugs, forgot details + psi := rationalMap(last projectionMaps JJ); psi = psi * rationalMap(target psi,target f); + eta := rationalMap((projectionMaps JJ)_0 | (projectionMaps JJ)_1, PP_K^{2,2}); + S.cache#("abstract join of rational surfaces",P) = (eta,psi) +); + +setFanoMapToBeMapFromP5toP2xP2 = method(Options => {Verbose => true}); +setFanoMapToBeMapFromP5toP2xP2 DoublySpecialCubicFourfold := o -> X -> ( + if isFanoMapToP2xP2 X then return; + (S,T) := surfaces X; + if S.cache#?("FanoMapDSCF",T) then ( + if isFanoMapToP2xP2 S.cache#("FanoMapDSCF",T) then ( + X.cache#"FanoMapType" = "FromP5toP2xP2"; + return; + ); + if o.Verbose then << "-- existing Fano map in cache: overwriting with map to PP^2 x PP^2..." << endl; + ); + mu := fanoMapDSCFtoP2xP2(X,Verbose=>o.Verbose,Verify=>true); + X.cache#"FanoMapType" = "FromP5toP2xP2"; + S.cache#("FanoMapDSCF",T) = mu; +); + +setFanoMapToBeStandard = method(Options => {Verbose => true}); +setFanoMapToBeStandard DoublySpecialCubicFourfold := o -> X -> ( + if isFanoMapStandard X then return; + (S,T) := surfaces X; + if S.cache#?("FanoMapDSCF",T) then ( + if isFanoMapStandard S.cache#("FanoMapDSCF",T) then ( + X.cache#"FanoMapType" = "Standard"; + return; + ); + if o.Verbose then << "-- existing Fano map in cache: overwriting with standard map..." << endl; + ); + mu := fanoMapDSCFstandard(X,Verbose=>o.Verbose,Verify=>true); + X.cache#"FanoMapType" = "Standard"; + S.cache#("FanoMapDSCF",T) = mu; +); + +isFanoMapStandard = E -> E.cache#?"FanoMapType" and E.cache#"FanoMapType" === "Standard"; +isFanoMapToP2xP2 = E -> E.cache#?"FanoMapType" and E.cache#"FanoMapType" === "FromP5toP2xP2"; + +getCachedFanoMapIfCompatible = method(); +getCachedFanoMapIfCompatible (DoublySpecialCubicFourfold,Thing) := (X,FanoMapTypeStr) -> ( + assert member(FanoMapTypeStr,{null,"Standard","P2xP2"}); + (S,T) := surfaces X; + if not S.cache#?("FanoMapDSCF",T) then return; + mu := S.cache#("FanoMapDSCF",T); + if FanoMapTypeStr === null then ( + assert(X.cache#?"FanoMapType" and mu.cache#?"FanoMapType"); + assert(X.cache#"FanoMapType" === mu.cache#"FanoMapType"); + return mu; + ); + if FanoMapTypeStr === "Standard" and isFanoMapStandard X then ( + assert isFanoMapStandard mu; + return mu; + ); + if FanoMapTypeStr === "P2xP2" and isFanoMapToP2xP2 X then ( + assert isFanoMapToP2xP2 mu; + return mu; + ); +); + +fanoMap DoublySpecialCubicFourfold := o -> X -> toRationalMap fanoMapDSCF(X,Verify=>o.RaiseError,Verbose=>o.Verbose); -- for compatibility diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/GushelMukai.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/GushelMukai.m2 new file mode 100644 index 00000000000..ba8167df504 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/GushelMukai.m2 @@ -0,0 +1,996 @@ + +------------------------------------------------------------------------ +--------------------------- GM fourfolds ------------------------------- +------------------------------------------------------------------------ + +GushelMukaiFourfold = new Type of HodgeSpecialFourfold; + +globalAssignment GushelMukaiFourfold; + +GushelMukaiFourfold.synonym = "Gushel-Mukai fourfold"; + +gushelMukaiFourfold = method(TypicalValue => GushelMukaiFourfold, Options => {InputCheck => 1, Verbose => false}); + +gushelMukaiFourfold (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X) -> ( + if ring ideal S =!= ring ideal X then error "expected varieties in the same ambient space"; + if not (dim ambient X == 8 and degrees X == {({2},6)} and codim X == 4 and degree X == 10 and sectionalGenus X == 6) then error "expected a 4-dimensional subvariety of PP^8 of degree 10 and sectional genus 6 cut out by 6 quadrics"; + if dim S != 2 then error "expected a surface"; + i := o.InputCheck; + if not(instance(i,ZZ) and i >= -1) then error("option InputCheck expects a nonnegative integer:"|newline|"0: no check is done about the smoothness of the fourfold and of the surface"|newline|"1: just the smoothness of the fourfold is checked"|newline|"2: both the smoothness of the fourfold and the smoothness of the surface are checked"); + if i >= 0 then if not isSubset(S,X) then error "the given surface is not contained in the fourfold"; + if i >= 1 then if not isSmooth X then error "expected a smooth GM fourfold"; + if i >= 2 then ( + if not isSmooth S then error "expected a smooth surface"; + if o.Verbose then <<"-- smoothness of the surface verified (assuming equidimensionality)"<= 4 then ( + if S != top S then error "expected an irreducible reduced surface"; + if o.Verbose then <<"-- equidimensionality of the surface verified"< (idS,idX) -> gushelMukaiFourfold(projectiveVariety idS,projectiveVariety idX,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +gushelMukaiFourfold EmbeddedProjectiveVariety := o -> S -> ( + if dim ambient S == 8 and codim S == 4 and degrees S === {({2},6)} and degree S == 10 then return gushelMukaiFourfold(S * random({2:{1}},0_S),S,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + Y := ambientVariety S; + er := "expected a surface in a GM fourfold or del Pezzo fivefold or del Pezzo sixfold"; + if dim S != 2 then error er; + if not((dim ambient Y == 9 and dim Y == 6 and degrees Y === {({2},5)}) or + (dim ambient Y == 8 and dim Y == 5 and degrees Y === {({2},5)}) or + (dim ambient Y == 8 and dim Y == 4 and degrees Y === {({2},6)}) + ) then error er; + if dim Y == 4 then return gushelMukaiFourfold(S,Y,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + local X; + if dim Y == 5 then ( + X = gushelMukaiFourfold(S,Y * random(2,S),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#"AmbientFivefold" = Y; + return X; + ); + if dim Y == 6 then ( + if dim linearSpan S > 8 then error "expected linear span of the surface to be of dimension at most 8"; + j := parametrize random(1,linearSpan S); + T := makeSubvariety(j^^ S,j^^ ambientVariety S); + if S.cache#?"FiniteNumberOfNodes" and (not T.cache#?"FiniteNumberOfNodes") then T.cache#"FiniteNumberOfNodes" = S.cache#"FiniteNumberOfNodes"; + if S.cache#?"top" and (not T.cache#?"top") then (T.cache#"top" = j^^(S.cache#"top"); if top T == T then T.cache#"top" = T); + if S.cache#?"singularLocus" and (not T.cache#?"singularLocus") then T.cache#"singularLocus" = j^^(S.cache#"singularLocus"); + if S.cache#?"euler" and (not T.cache#?"euler") then T.cache#"euler" = S.cache#"euler"; + X = gushelMukaiFourfold(T,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if instance(Y,GrassmannianVariety) then ( + j = j||Y; + (source j).cache#"toGrass" = j; + try assert(ideal ambientFivefold X == ideal source j) else error "internal error encountered"; + X.cache#"AmbientFivefold" = source j; + ); + return X; + ); +); + +gushelMukaiFourfold Ideal := o -> I -> gushelMukaiFourfold(makeSubvariety I,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +gushelMukaiFourfold (String,Ring) := o -> (str,K) -> ( + G := GG(K,1,4); + local X; local S; + if str === "sigma-plane" then ( + X = gushelMukaiFourfold(schubertCycle({3,1},G),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 6; + return X; + ); + if str === "rho-plane" then ( + X = gushelMukaiFourfold(schubertCycle({2,2},G),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 9; + return X; + ); + if str === "tau-quadric" then ( + X = gushelMukaiFourfold((schubertCycle({1,1},G) * random({{1},{1}},0_G))%G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 1; + return X; + ); + if str === "cubic scroll" then ( + X = gushelMukaiFourfold((schubertCycle({2,0},G) * random({{1},{1}},0_G))%G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 7; + return X; + ); + if str === "quintic del Pezzo surface" then ( + X = gushelMukaiFourfold((G * random({4:{1}},0_G))%G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 4; + return X; + ); + if str === "K3 surface of genus 8 with class (9,5)" then ( + G15 := Grass replace(1,5,Grass ring G); + pr := rationalMap(G15,ring G,select(gens ambient G15,g -> last last baseName g != 5)); + X = gushelMukaiFourfold(pr sub(ideal for i to 5 list random(1,ambient G15),G15),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 3; + return X; + ); + if str === "general GM 4-fold of discriminant 20" then ( + (g,T) := first randomS42data(K); + X = gushelMukaiFourfold((g T)%image(g),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = 17; + return X; + ); + if #str >= 1 and #str <= 2 then ( + Vstr := value str; + if instance(Vstr,ZZ) and Vstr >= 1 and Vstr <= 21 then return fourfoldFromTriple(Vstr,GMtables(Vstr,K),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + ); + if str === "nodal surface of degree 11 and genus 3 with class (7,4)" then ( + X = gushelMukaiFourfold([4,5,1,0],[3,5,1,0],"cubic scroll",(1,K),InputCheck=>o.InputCheck,Verbose=>false); + (surface X).cache#"FiniteNumberOfNodes" = 1; + X.cache#(surface X,"label") = "mukai26''"; + return X; + ); + if str === "nodal D44" then ( + X = gushelMukaiFourfold([2,0,0,0],[1,0,0,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); + (surface X).cache#"FiniteNumberOfNodes" = 1; + X.cache#(surface X,"label") = "nodal D44"; + return X; + ); + if str === "GM 4-fold of discriminant 26('')" then ( + X = gushelMukaiFourfold([4,5,1,0],[2,3,0,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); + (surface X).cache#"FiniteNumberOfNodes" = 0; + X.cache#(surface X,"label") = "october2021-26''"; + return X; + ); + if str === "GM 4-fold of discriminant 28" then ( + X = gushelMukaiFourfold([6,4,6,0],[3,3,5,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); + (surface X).cache#"FiniteNumberOfNodes" = 0; + X.cache#(surface X,"label") = "october2021-28"; + return X; + ); + if str === "GM 4-fold of discriminant 34(')" then ( + X = gushelMukaiFourfold([6,4,6,0],[3,1,6,0],"cubic scroll",K,InputCheck=>o.InputCheck,Verbose=>false); + (surface X).cache#"FiniteNumberOfNodes" = 0; + X.cache#(surface X,"label") = "october2021-34'"; + return X; + ); + if str === "triple projection of K3 surface of degree 26" then ( + X = gushelMukaiFourfold([4,5,1],[2,3,0],"cubic scroll",K,InputCheck=>0,Verbose=>false); + B := projectiveVariety matrix fanoMap X; + interpolateTop(B,{2},Verbose=>false,cache=>true,"Deep"=>2); + P := (B\top B)\top B; + f := rationalMap(P % ambientFivefold X); + S = (X * f^* f surface X)\(surface X); + if not(dim S == 2 and degree S == 17 and sectionalGenus S == 11) then error "something went wrong"; + X = gushelMukaiFourfold(S,X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "gushel26''"; + return X; + ); + if str === "surface of degree 11 and genus 3 with class (7,4)" then ( + X = gushelMukaiFourfold([4,5,1],[2,3,0],"cubic scroll",K,InputCheck=>0,Verbose=>false); + S = (X * ((fanoMap X)^* first decompose first exceptionalCurves X))\surface X; + if not(dim S == 2 and degree S == 11 and sectionalGenus S == 3 and degrees S == {({2},16)}) then error "something went wrong"; + S.cache#"FiniteNumberOfNodes" = 1; + X = gushelMukaiFourfold(S,X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = "mukai26''"; + return X; + ); + error(///not valid string, permitted strings are: +"sigma-plane", +"rho-plane", +"tau-quadric", +"cubic scroll", +"quintic del Pezzo surface", +"K3 surface of genus 8 with class (9,5)", +"general GM 4-fold of discriminant 20", +"1","2",...,"21", +"nodal surface of degree 11 and genus 3 with class (7,4)", +"surface of degree 11 and genus 3 with class (7,4)", +"GM 4-fold of discriminant 26('')", +"GM 4-fold of discriminant 28", +"GM 4-fold of discriminant 34(')", +"triple projection of K3 surface of degree 26"///); +); + +gushelMukaiFourfold String := o -> str -> gushelMukaiFourfold(str,ZZ/65521,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +expression GushelMukaiFourfold := X -> expression("GM fourfold containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X)|(if X.cache#?(surface X,"classSurfaceInG14") then (" with class "|toString(first cycleClass X)) else "")); + +describe GushelMukaiFourfold := X -> ( + (cS,ab) := cycleClass X; + (a,b) := ab; + recognize X; + discrX := discriminant X; + descr:="Special Gushel-Mukai fourfold of discriminant "|toString(discrX); + if discrX % 8 == 2 then ( + if even(a+b) and odd(b) then + descr = descr|"(')" + else + if odd(a+b) and even(b) then + descr = descr|"('')" + else error "internal error encountered" + ); + descr = descr|newline|"containing a "|surfaceDescription(0,surface X); + descr = descr|newline|"and with class in G(1,4) given by "|toString(cS); + if dim singLocus ambientFivefold X >= 0 then descr = descr|newline|"Type: Gushel (not ordinary)" else descr = descr|newline|"Type: ordinary"; + if instance(recognize X,ZZ) then descr = descr|newline|"(case "|toString(recognize X)|" of Table 1 in arXiv:2002.07026)"; + if recognize X === "gushel26''" then ( + if dim singLocus ambientFivefold X >= 0 + then descr = descr|newline|"(case considered in Section 1 of arXiv:2003.07809)" + else descr = descr|newline|"(case discovered in November 2021; see also Section 1 of arXiv:2003.07809)"; + ); + if recognize X === "mukai26''" and dim singLocus ambientFivefold X >= 0 then descr = descr|newline|"(case considered in Section 2 of arXiv:2003.07809)"; + if recognize X === "mukai26''" and dim singLocus ambientFivefold X == -1 then descr = descr|newline|"(case considered in Section 3 of arXiv:2003.07809)"; + if recognize X === "october2021-26''" or recognize X === "october2021-28" or recognize X === "october2021-28-2" or recognize X === "october2021-34'" then descr = descr|newline|"(case discovered in October 2021)"; + if recognize X === "october2021-20" then descr = descr|newline|"(case discovered in October 2021; see also Table 1 of arXiv:2003.07809)"; + if recognize X === "april2022-GM42''" then descr = descr|newline|"(strange example discovered in October 2021)"; + if computationStatus X >= 0 then descr = descr|newline|(computationStatusLog X)|newline|toString(describeMirrorFourfoldAndK3 X); + net expression descr +); + +shortDescriptionFourfold (GushelMukaiFourfold,Boolean) := (X,UseAttribute) -> ( + if UseAttribute and hasAttribute(X,ReverseDictionary) then return toString getAttribute(X,ReverseDictionary); + "Gushel-Mukai fourfold of discriminant "|(discriminant X) +); + +discriminant GushelMukaiFourfold := o -> X -> ( + if X.cache#?(surface X,"discriminantFourfold") then return last X.cache#(surface X,"discriminantFourfold"); + S := surface X; + degS := degree S; g := sectionalGenus S; chiOS := euler hilbertPolynomial S; + chiS := eulerCharacteristic(S,Algorithm=>if o.Algorithm === "Poisson" then null else o.Algorithm); + KS2 := 12*chiOS-chiS; + KSHS := 2*g-2-degS; + (a,b) := last cycleClass X; + n := if S.cache#?"FiniteNumberOfNodes" or S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" or (S.cache#?"fitVariety" and (S.cache#"fitVariety").cache#?"nonSaturatedSingularLocus") then numberNodes S else 0; + S2 := 3*a + 4*b + 2*KSHS + 2*KS2 - 12*chiOS + 2*n; + d := 4*S2 - 2*(b^2+(a-b)^2); + if S.cache#?"FiniteNumberOfNodes" then X.cache#(surface X,"discriminantFourfold") = (S2,d); + d +); + +cycleClass GushelMukaiFourfold := X -> ( + if X.cache#?(surface X,"classSurfaceInG14") then return X.cache#(surface X,"classSurfaceInG14"); + S := surface X; + j := toGrass X; + cS := cycleClass makeSubvariety(j S,target j); + ab := toSequence flatten entries lift(transpose last coefficients(cS,Monomials=>vars ring cS),ZZ); + X.cache#(surface X,"classSurfaceInG14") = (cS,ab) +); + +map GushelMukaiFourfold := o -> X -> associatedMapFromFivefold X; + +recognizeGMFourfold = X -> ( + S := surface X; + d := discriminant X; + e := eulerCharacteristic S; + (a,b) := last cycleClass X; + invS := (degree S,sectionalGenus S,euler hilbertPolynomial S); + degs := flatten degrees ideal S; + if (d == 10 and e == 4 and a == 1 and b == 1 and invS == (2,0,1) and degs == {1, 1, 1, 1, 1, 2}) then return 1; + if (d == 10 and e == 4 and a == 3 and b == 1 and invS == (4,0,1) and degs == {1, 1, 1, 2, 2, 2, 2, 2, 2}) then return 2; + if (d == 10 and e == 24 and a == 9 and b == 5 and invS == (14,8,2) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 3; + if (d == 10 and e == 7 and a == 3 and b == 2 and invS == (5,1,1) and degs == {1, 1, 1, 2, 2, 2, 2, 2}) then return 4; + if (d == 10 and e == 11 and a == 5 and b == 4 and invS == (9,3,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 5; + if (d == 10 and e == 3 and a == 1 and b == 0 and invS == (1,0,1) and degs == {1, 1, 1, 1, 1, 1}) then return 6; + if (d == 12 and e == 4 and a == 2 and b == 1 and invS == (3,0,1) and degs == {1, 1, 1, 1, 2, 2, 2}) then return 7; + if (d == 12 and e == 9 and a == 4 and b == 3 and invS == (7,2,1) and degs == {1, 1, 2, 2, 2, 2, 2, 2, 2, 2}) then return 8; + if (d == 12 and e == 3 and a == 0 and b == 1 and invS == (1,0,1) and degs == {1, 1, 1, 1, 1, 1}) then return 9; + if (d == 16 and e == 12 and a == 6 and b == 4 and invS == (10,4,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 10; + if (d == 16 and e == 10 and a == 6 and b == 4 and invS == (10,3,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 11; + if (d == 16 and e == 24 and a == 8 and b == 6 and invS == (14,8,2) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 12; + if (d == 18 and e == 13 and a == 7 and b == 5 and invS == (12,5,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 13; + if (d == 18 and e == 8 and a == 5 and b == 3 and invS == (8,2,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 14; + if (d == 18 and e == 10 and a == 5 and b == 4 and invS == (9,3,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 15; + if (d == 18 and e == 13 and a == 7 and b == 4 and invS == (11,5,1) and degs == {1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 16; + if (d == 20 and e == 7 and a == 6 and b == 3 and invS == (9,2,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 17; + if (d == 20 and e == 4 and a == 4 and b == 3 and invS == (7,0,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 18; + if (d == 24 and e == 9 and a == 6 and b == 4 and invS == (10,3,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 19; + if (d == 24 and e == 3 and a == 2 and b == 2 and invS == (4,0,1) and degs == {1, 1, 1, 2, 2, 2, 2, 2, 2}) then return 20; + if (d == 26 and e == 12 and a == 7 and b == 5 and invS == (12,5,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return 21; + -- + if (d == 26 and e == 25 and a == 11 and b == 6 and invS == (17,11,2) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "gushel26''"; + if ((d == 18 or d == 26) and e == 3 and a == 7 and b == 4 and invS == (11,3,0) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then if numberNodes(S,Verbose=>false) == 1 and discriminant X == 26 then return "mukai26''"; + -- + if (d == 20 and e == 14 and a == 8 and b == 5 and invS == (13,6,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-20"; + if (d == 26 and e == 7 and a == 5 and b == 4 and invS == (9,2,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-26''"; + if (d == 28 and e == 13 and a == 8 and b == 5 and invS == (13,6,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-28"; + if (d == 28 and e == 10 and a == 6 and b == 5 and invS == (11,4,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) then return "october2021-28-2"; + if (d == 34 and e == 13 and a == 9 and b == 5 and invS == (14,7,1) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3}) then return "october2021-34'"; + if (d == 34 and e == 7 and a == 9 and b == 6 and invS == (15,7,0) and degs == {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3}) then if numberNodes(S,Verbose=>false) == 1 and discriminant X == 42 then return "april2022-GM42''"; + "NotRecognized" +); + +random GushelMukaiFourfold := o -> X -> ( + X' := gushelMukaiFourfold(surface X,random(2,surface X) * ambientFivefold X,InputCheck=>-1); + X'.cache#"AmbientFivefold" = ambientFivefold X; + if X.cache#?(surface X,"label") and (not X'.cache#?(surface X',"label")) then X'.cache#(surface X',"label") = X.cache#(surface X,"label"); + X' +); + +toGrass = method(TypicalValue => MultirationalMap) + +toGrass EmbeddedProjectiveVariety := (cacheValue "toGrass") (X -> ( + K := coefficientRing X; + Y6 := GG(K,1,4); + if dim X == 6 and dim ambient X == 9 and degrees X == {({2},5)} and degree X == 5 + then return (findIsomorphism(X,Y6,Verify=>true))||Y6; + if dim X == 5 and dim ambient X == 8 and degrees X == {({2},5)} and degree X == 5 + then ( + if dim singLocus X == -1 then return ((findIsomorphism(X,Y5 K,Verify=>true))||(Y5 K)) * (mapY5 K); + p := sum decompose singLocus X; + if not isPoint p then error "expected a point to be the vertex of a cone"; + p' := Var ideal submatrix'(vars ring ambient X,{0}); + f := inverse findIsomorphism(p,p',Verify=>true); + X' := f^^ X; + j := toGrass Var sub(ideal X',K[flatten entries submatrix'(vars ring ambient X,{0})]); + y0 := local y0; + coneG := quotient sub(ideal target j,K[y0,gens ring ambient target j]); + J := multirationalMap( + rationalMap(ring X,ring X',matrix first factor inverse f) * + rationalMap(ring X',coneG,matrix{{first gens ring ambient X}}|sub(lift(matrix first factor j,ring ambient source j),ring ambient X)) + ); + if not(source J == X and degree(J,Strategy=>"random point") == 1) then error "internal error encountered"; + return J; + ); + if dim X == 4 and dim ambient X == 7 and degrees X == {({2},5)} and degree X == 5 + then return ((findIsomorphism(X,Y4 K,Verify=>true))||(Y4 K)) * (mapY4 K); + if dim X == 4 and dim ambient X == 8 and degrees X == {({2},6)} and degree X == 10 + then ( + Y := varietyDefinedBylinearSyzygies X; + psi := toGrass Y; + if dim singLocus Y == -1 then return psi|X; + return psi * rationalMap(ring target psi,ring Y6,submatrix'(vars ring ambient target psi,{0})); + ); + error "expected a Gushel-Mukai fourfold, or a del Pezzo fourfold/fivefold/sixfold"; +)); + +toGrass GushelMukaiFourfold := (cacheValue "toGrass") (X -> ( -- for better documentation + Y := ambientFivefold X; + psi := toGrass Y; + if dim singLocus Y == -1 then return psi|X; + K := coefficientRing X; + check multirationalMap((psi|X) * rationalMap(ring target psi,ring GG(K,1,4),submatrix'(vars ring ambient target psi,{0})),GG(K,1,4)) +)); + +isAdmissibleGM = method(); + +isAdmissibleGM ZZ := d -> ( + if d <= 8 then return false; + if d % 8 == 0 then return false; + if d % 8 != 2 and d % 8 != 4 then return false; + for p from 3 to floor(d/2) do if ((p % 4 == 0 or p % 4 == 2 or p % 4 == 3) and isPrime p and d % p == 0) then return false; + return true; +); + +isAdmissibleGM GushelMukaiFourfold := X -> isAdmissibleGM discriminant X; + +parameterCount GushelMukaiFourfold := o -> X -> ( + S := surface X; + Y := ambientFivefold X; + if o.Verbose then <<"S: "|toString(? ideal S)<= "|toString(h0N + m-1 - h0NX)< ( + f := toRationalMap toGrass X; + H := image(f,1); + Q := ideal random(2,makeSubvariety image f); + S := trim(Q + tangentialChowForm(chowEquations(H_0),0,1)); + return trim lift(f^* S,ambient source f); +); + +unirationalParametrization GushelMukaiFourfold := (cacheValue "unirationalParametrization") (X -> ( + K := coefficientRing X; + S := sigmaQuadric X; + s := parametrize S; + s = rationalMap(Grass(0,2,K,Variable=>"u"),source s) * s; + K' := frac(K[gens source s]); + ringP8' := K'[gens ring ambient X]; + p' := trim minors(2,vars ringP8' || sub(matrix s,K')); + Y := ideal ambientFivefold X; + V := ideal coneOfLines(Var sub(Y,ringP8'),Var p'); + j := parametrize((ideal select(V_*,v -> degree v == {1})) + (ideal first gens ring V)); + W := trim (map j) V; + P := plucker(W,2); while dim P <= 0 do P = plucker(W,2); P = trim sub(plucker P,vars ring W); + Q := trim quotient(W,P); + q := trim minors(2,vars ring W || transpose submatrix(coefficients parametrize(P+Q),,{0})); + f := (inverse(rationalMap trim sub(q,quotient Q),Certify=>true)) * j; + ringP2xP2 := (source s) ** K[gens source f]; + K'' := frac(ringP2xP2); + ringP8'' := K''[gens ring ambient X]; + X'' := sub(ideal X,ringP8''); + p'' := sub(p',ringP8''); + ringP1'' := Grass(0,1,K'',Variable=>"v"); + l := rationalMap(ringP1'',ringP8'', (vars ringP1'') * (sub(matrix s,K'') || sub(matrix f,K''))); + e := parametrize trim quotient(trim (map l) X'',trim (map l) p''); + el := rationalMap((map e) * (map l)); + el = rationalMap(source el,target el,(lcm apply(flatten entries sub(last coefficients matrix el,K''),denominator)) * (matrix el)); + psi := rationalMap(ringP2xP2,ring ambient X,sub(transpose coefficients el,ringP2xP2)); + Psi := multirationalMap({parametrize psi},X); + if not isSubset(Psi point source Psi,X) then error "internal error encountered"; + Psi +)); + +toGushel = method(); +toGushel GushelMukaiFourfold := X -> ( + if dim singLocus ambientFivefold X >= 0 then return X; + j := toRationalMap toGrass X; + Y := local Y; + i := rationalMap(target j,(coefficientRing X)[Y,gens ambient target j],0|vars target j); + i = rationalMap(i,Dominant=>sub(ideal target j,target i)); + S := trim lift((j*i) ideal surface X,ambient target i); + Sv := intersect(S,ideal submatrix'(vars ambient target i,{0})); + try H := ideal random({{1},{1}},Var Sv) else error "not able to specialize to Gushel type"; + h := (parametrize H)||(target i); + gushelMukaiFourfold h^* S +); + +fromOrdinaryToGushel = method(); +fromOrdinaryToGushel GushelMukaiFourfold := X -> try toGushel X else error "not able to deform to Gushel type"; + +------------------------------------------------------------------------ +---------------- arXiv:2002.07026 -------------------------------------- +------------------------------------------------------------------------ + +GMtables = method(Options => {Verify => true}); +GMtables (ZZ,Ring) := o -> (i,K) -> ( + if i < 1 or i > 21 then error "expected an integer between 1 and 21"; + t := gens ring PP_K^2; + s := multirationalMap rationalMap(ring PP_K^2,ring PP_K^5,{t_0^2,t_0*t_1,t_1^2,t_0*t_2,t_1*t_2,0}); + S := image s; + p0 := Var ideal(t_0,t_1); -- base point of s + assert(p0 == baseLocus s); + x := gens ring PP_K^5; + L0 := Var ideal(x_0,x_1,x_2,x_5); -- directrix line of S + u := gens ring PP_K^3; + b := multirationalMap rationalMap(ring PP_K^3,ring PP_K^5,{u_0^3*u_1-u_0*u_1^3, u_0^2*u_1^2-u_0*u_1^3+u_0^3*u_2-u_1^3*u_3, u_0^2*u_1*u_2-u_1^3*u_3, -u_0^2*u_1^2+u_0*u_1^3+u_0*u_1^2*u_2-u_1^3*u_3, u_0^2*u_1*u_3-u_1^3*u_3, u_0^2*u_1^2-u_0*u_1^3+u_0*u_1^2*u_3-u_1^3*u_3}); + B := image b; + E := (Var ideal(u_0,u_1), Var ideal(u_3,u_0), Var ideal(u_2,u_1), Var ideal(u_2-u_3,u_0-u_1)); -- base locus of b: E_0 triple line, E_1,E_2,E_3 simple lines + assert(⋃ {3*E_0,E_1,E_2,E_3} == baseLocus b); + pE := apply(E,L -> parametrize L); + curveOnS := e -> ( + assert(instance(e,ZZ) and e >= 1 and e <= 4); + local j; + if e == 1 then j = parametrize random({1},p0); + if e == 2 then j = parametrize random({1},0_(PP_K^2)); + if e == 3 then j = inverse rationalMap(p0_(random({2},p0))); + if e == 4 then ( + p1 := point PP_K^2; + j = inverse rationalMap(p1_(random({2},p1))); + ); + g := rationalMap(j * s,Dominant=>true); + (target g).cache#"rationalParametrization" = g; + return target g; + ); + curveOnB := e -> ( + assert(instance(e,ZZ) and e >= 1 and e <= 5); + local C; local p; local p'; local p''; local j; + if e == 1 then ( + p = point source pE_0; + C = random({{1},{1}},pE_0 p); + j = parametrize C; + ); + if e == 2 then ( + p = point source pE_0; + p' = point source pE_0; + C = random({{2},{1}},pE_0(p) + pE_0(p')); + j = inverse rationalMap((pE_0 p)_C); + ); + if e == 3 then ( + p = point source pE_0; + p' = point source pE_1; + p'' = point source pE_2; + C = random({{2},{1}},pE_0(p) + pE_1(p') + pE_2(p'')); + j = inverse rationalMap((pE_0 p)_C); + ); + if e == 4 then ( + p = point source pE_0; + p' = point source pE_1; + C = random({{2},{1}},pE_0(p) + pE_1(p')); + j = inverse rationalMap((pE_0 p)_C); + ); + if e == 5 then ( + p = point source pE_0; + C = random({{2},{1}},pE_0 p); + j = inverse rationalMap((pE_0 p)_C); + ); + g := rationalMap(j * b,Dominant=>true); + (target g).cache#"rationalParametrization" = g; + return target g; + ); + SB := {,S,S,S,S,B,S,S,B,S,B,B,S,S,S,S,S,S,B,B,S,S}; + degsV := {,2,2,8,4,3,1,3,2,1,4,4,8,7,4,6,6,5,4,4,4,7}; + sGenV := {,0,0,5,1,0,0,0,0,0,1,0,5,3,1,2,3,1,0,0,0,3}; + degsC := {,2,1,4,3,2,1,3,1,1,4,4,4,4,2,4,3,3,5,4,4,4}; + inters := {,0,0,0,0,7,0,0,5,0,4,6,0,0,0,0,0,0,3,6,0,0}; + verify := (i0,S',V',C') -> ( + if not o.Verify then return (S',V',C'); + try assert( + S' == SB_i0 and + dim V' == 2 and + degree V' == degsV_i0 and + sectionalGenus V' == sGenV_i0 and + dim C' == 1 and + degree C' == degsC_i0 and + sectionalGenus C' == 0 and + isSubset(C',S') and isSubset(C',V') and + degree((S' * V') \ C') == inters_i0 + ) else error ("something went wrong while constructing a triple for case "|toString(i)); + return (S',V',C'); + ); + local C; local p; local j; local D; local V; local v; local phi; + if i == 1 then ( + C = curveOnS 2; + V = random({{1},{1},{2}},C); + return verify(i,S,V,C); + ); + if i == 2 then ( + C = curveOnS 1; + V = random({{1},{1},{2}},C); + return verify(i,S,V,C); + ); + if i == 3 then ( + C = ⋃ {L0,curveOnS 1,curveOnS 1,curveOnS 1}; + V = random({{2},{2},{2}},C); + return verify(i,S,V,C); + ); + if i == 4 then ( + C = curveOnS 3; + V = random({{1},{2},{2}},C); + return verify(i,S,V,C); + ); + if i == 5 then ( + C = curveOnB 2; + D = curveOnS 2; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi S; + return verify(i,B,V,C); + ); + if i == 6 then ( + C = curveOnS 1; + V = random({{1},{1},{1}},C); + return verify(i,S,V,C); + ); + if i == 7 then ( + C = curveOnS 3; + D = curveOnS 3; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi S; + return verify(i,S,V,C); + ); + if i == 8 then ( + C = curveOnB 1; + D = random({{1},{1},{1},{1}},0_(PP_K^5)); + V = random({{1},{1},{2}},D); + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,B,V,C); + ); + if i == 9 then return verify(i,S,random({{1},{1},{1}},L0),L0); + if i == 10 then ( + C = curveOnB 4; + j = rationalMap((parametrize PP_K^(1,4)) << PP_K^5,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + V = random({{1},{2},{2}},D); + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,B,V,C); + ); + if i == 11 then ( + C = curveOnB 4; + p = for i to 1 list point PP_K^2; + v = rationalMap(p_0 + 2*p_1,3); + V = image v; + j = rationalMap((inverse rationalMap((p_1)_(random({2},p_1)))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,B,V,C); + ); + if i == 12 then ( + C = curveOnS 4; + V = random({{2},{2},{2}},C); + return verify(i,S,V,C); + ); + if i == 13 then ( + C = curveOnS 4; + p = for i to 9 list point PP_K^2; + v = rationalMap((⋃ take(p,9)) + 3*p_9,5); + V = image v; + j = rationalMap((inverse rationalMap((p_0)_(random({2},⋃{p_0,p_1,p_2,p_9})))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,S,V,C); + ); + if i == 14 then ( + C = curveOnS 2; + V = random({{1},{2},{2}},C); + return verify(i,S,V,C); + ); + if i == 15 then ( + C = curveOnS 4; + p = for i to 6 list point PP_K^2; + v = rationalMap((⋃ take(p,6)) + 2*p_6,4); + V = image v; + j = rationalMap((inverse rationalMap((p_0)_(random({2},⋃{p_0,p_1,p_6})))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,S,V,C); + ); + if i == 16 then ( + C = curveOnS 3; + p = for i to 9 list point PP_K^2; + v = rationalMap(⋃ p,4) << PP_K^5; + V = image v; + j = (inverse rationalMap((p_0)_(random({2},⋃ take(p,5))))) * v; + j = rationalMap(j,image j); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,S,V,C); + ); + if i == 17 then ( + C = curveOnS 3; + p = for i to 3 list point PP_K^2; + v = rationalMap(⋃ p,3); + V = image v; + j = rationalMap((parametrize random({1},0_(PP_K^2))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,S,V,C); + ); + if i == 18 then ( + C = curveOnB 5; + p = for i to 1 list point PP_K^2; + v = rationalMap(p_0 + 2*p_1,3); + V = image v; + j = rationalMap((inverse rationalMap((p_0)_(random({2},p_0)))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,B,V,C); + ); + if i == 19 then ( + C = curveOnB 4; + V = PP_K^(2,2); + v = parametrize V; + p = point source v; + j = rationalMap((inverse rationalMap(p_(random({2},p)))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,B,V,C); + ); + if i == 20 then ( + C = curveOnS 4; + V = PP_K^(2,2); + v = parametrize V; + p = point source v; + j = rationalMap((inverse rationalMap(p_(random({2},p)))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,S,V,C); + ); + if i == 21 then ( + C = curveOnS 4; + p = for i to 8 list point PP_K^2; + v = rationalMap(⋃ p,4); + V = image v; + j = rationalMap((inverse rationalMap((p_0)_(random({2},⋃ take(p,4))))) * v,Dominant=>true); + (target j).cache#"rationalParametrization" = j; + D = image j; + phi = findIsomorphism(D,C,Verify=>o.Verify); + V = phi V; + return verify(i,S,V,C); + ); +); + +GMtables (Ring,String) := o -> (K,name) -> ( -- store all data in a file +F := openOut (name|".dat"); +F <<"-- file created automatically using: GMtables("|toExternalString K|",\""|name|"\",Verify=>"<<(o.Verify)<<"); date: "|(toString get "!date")< j -> ("<o.Verify); + F = openOutAppend (name|".dat"); + F << " if j == "<false,MinimalGenerators=>false),"<false,MinimalGenerators=>false),"<false,MinimalGenerators=>false));"< i -> ( + if i < 1 or i > 21 then error "expected integer between 1 and 21"; + try value get "data_examples.dat" else error("file \"data_examples.dat\" not found. You can make it using GMtables(K,\"data_examples\")"); + GMtables i +); + +GMtables (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (B,V,C) -> ( + if not ((dim B == 2 or dim B == 3) and degree B == dim B + 1 and dim V == 2 and dim C == 1 and ring ambient C === ring ambient V and ring ambient C === ring ambient B) then error "invalid input for GMtables"; + psi := rationalMap(ideal B,Dominant=>2); + if o.Verify then <<"-- constructing random GM fourfold from the triple..."<(if o.Verify then 10 else 0),Verbose=>true); + if o.Verify then <<"-- GM fourfold correctly constructed."<= 1 and i <= 21) then error "something went wrong"; + <<"-- done (got case "<true); + <<"-- parameterCount successfully terminated (got: "<<(c,h)<<")"< {InputCheck => 1, Verbose => true}); +fourfoldFromTriple (ZZ,VisibleList) := o -> (i,E) -> ( + psi := rationalMap(ideal E_0,Dominant=>2); + X := gushelMukaiFourfold(psi ideal E_1,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + X.cache#(surface X,"label") = i; + return X; +); + +------------------------------------------------------------------------ +----------- GM fourfolds from curves on surfaces in PP^6 --------------- +------------------------------------------------------------------------ + +makeGMfromCurveOnSurfaceInP6 = method(Options => {InputCheck => 1, Verbose => true, "Gluing" => "cubic scroll", Degrees => hashTable{1=>(1,infinity),2=>(0,infinity),3=>(0,1)}}); +makeGMfromCurveOnSurfaceInP6 EmbeddedProjectiveVariety := o -> C -> ( + S := ambientVariety C; + if not (dim C == 1 and dim S == 2 and dim ambient S == 6) then error "expected a curve on a surface in PP^6"; + B := if o#"Gluing" === "cubic scroll" + then glueScroll C + else if o#"Gluing" === "quartic scroll" + then glueScroll' C + else error "the option \"Gluing\" expects \"cubic scroll\" or \"quartic scroll\""; + psi := rationalMap B; + psi' := if S.cache#?"rationalParametrization" then (parametrize S) * psi else psi|S; + H := image(1,psi'); + if codim H == 0 then (if o.Verbose then (<<"got surface in GG(1,4) ⊂ PP^9 with linear span of dimension 9"< o.Degrees#1_1 then error "request on the degrees is not satisfied"; + -- V := if char coefficientRing C <= 65521 then image(psi',"F4") else image psi'; + V := image psi'; + if o.Verbose then <<"got surface in GG(1,4) ⊂ PP^9 with ideal generated in degrees "< d == 2) < 6 then error "the surface in G(1,4) is not contained in any GM fourfold"; + if # select(degs,d -> d > 3) > 0 or + # select(degs,d -> d == 2) < o.Degrees#2_0 or # select(degs,d -> d == 2) > o.Degrees#2_1 or + # select(degs,d -> d == 3) < o.Degrees#3_0 or # select(degs,d -> d == 3) > o.Degrees#3_1 + then error "request on the degrees is not satisfied"; + if o.InputCheck >= 2 then ( + if isIsomorphism rationalMap(psi|S,V) then ( + if S.cache#?"FiniteNumberOfNodes" and S.cache#"FiniteNumberOfNodes" == 0 then (V.cache#"top" = V; V.cache#"singularLocus" = 0_V); + if S.cache#?"FiniteNumberOfNodes" and S.cache#"FiniteNumberOfNodes" > 0 then V.cache#"FiniteNumberOfNodes" = S.cache#"FiniteNumberOfNodes"; + V.cache#"euler" = eulerCharacteristic S; + if o.Verbose then <<"isomorphism between surface in PP^6 and surface in GG(1,4): YES"<o.InputCheck); + X.cache#"Construction" = (B,C); + return X; +); + +glueScroll = method(); -- glue P^1xP^2 along a curve +glueScroll EmbeddedProjectiveVariety := C -> ( + if not (dim C == 1 and dim ambientVariety C == 2 and dim ambient C == 6) then error "expected a curve on a surface in PP^6"; + if not((degree C <= 5 and sectionalGenus C == 0) or (degree C == 5 and sectionalGenus C == 1)) then error "not implemented yet: expected a rational curve of degree at most 5 or an elliptic curve of degree 5"; + K := coefficientRing C; + (p,L,s) := CubicScroll K; + E := image s; + local D; + if degree C == 5 and sectionalGenus C == 0 then ( + D = image(PP_K^(1,3) << source s); + D = ((point D) ===> (point L)) D; + E = (s(D) ===> C) E; + ); + if degree C == 5 and sectionalGenus C == 1 then ( + f := super parametrize ambientVariety C; + g := f|(f^* C); + H := random(1,linearSpan C); + pr := inverse parametrize H; + g = g * pr; + j := inverse rationalMap((linearSpan {g point source g,g point source g})_(image g),Dominant=>true); + B := image((rationalMap lift(matrix j,ring ambient source j)) * inverse pr); + if not(dim B == 2 and degree B == 3 and sectionalGenus B == 0 and isSubset(C,B)) then error "something went wrong"; + E = (random(1,0_E) * E ===> B) E; + ); + if degree C == 4 and sectionalGenus C == 0 then ( + D = random({{2},{1}},0_(source s)); + assert(dim(D*(baseLocus s)) == -1); + E = (s(D) ===> C) E; + ); + if degree C == 3 and sectionalGenus C == 0 then ( + E = (E * random({{1},{1}},0_E) ===> C) E; + ); + if degree C == 2 and sectionalGenus C == 0 then ( + D = random({{1},{1}},0_(source s)); + assert(dim(D*(baseLocus s)) == -1); + E = (s(D) ===> C) E; + ); + if degree C == 1 and sectionalGenus C == 0 then ( + D = random({{1},{1}},point L); + assert(dim(D*(baseLocus s)) == 0); + E = (s(D) ===> C) E; + ); + if not(dim E == 3 and degree E == 3 and isSubset(C,E)) then error "something went wrong"; + E +); + +glueScroll' = method(); -- glue quartic scroll fourfold along a curve +glueScroll' EmbeddedProjectiveVariety := C -> ( + if not (dim C == 1 and dim ambientVariety C == 2 and dim ambient C == 6) then error "expected a curve on a surface in PP^6"; + if not(degree C <= 6 and sectionalGenus C == 0) then error "not implemented yet: expected rational curve of degree at most 6"; + K := coefficientRing C; + (P,s) := QuarticScroll K; + E := image s; + local D; + if degree C == 6 and sectionalGenus C == 0 then ( + D = image(PP_K^(1,3) << source s); + D = (sum(4,i -> point D) ===> sum(P,point)) D; + E = (s(D) ===> C) E; + ); + if degree C == 5 and sectionalGenus C == 0 then ( + D = image(PP_K^(1,3) << source s); + D = (sum(3,i -> point D) ===> sum {point P_0,point P_0,point P_1}) D; + E = (s(D) ===> C) E; + ); + if degree C == 4 and sectionalGenus C == 0 then ( + D = image(PP_K^(1,3) << source s); + D = (sum(4,i -> point D) ===> sum {point P_0,point P_0,point P_1,point P_2}) D; + E = (s(D) ===> C) E; + ); + if degree C == 3 and sectionalGenus C == 0 then ( + D = random({2:{1},{2}},0_(source s)); + D = (sum(3,i -> point D) ===> sum {point P_0,point P_1,point P_2}) D; + E = (s(D) ===> C) E; + ); + if degree C == 2 and sectionalGenus C == 0 then ( + D = random({2:{1},{2}},0_(source s)); + D = (sum(2,i -> point D) ===> sum {point P_0,point P_0}) D; + E = (s(D) ===> C) E; + ); + if degree C == 1 and sectionalGenus C == 0 then ( + D = random({3:{1}},point P_0); + E = (s(D) ===> C) E; + ); + if not(dim E == 4 and degree E == 4 and isSubset(C,E)) then error "something went wrong"; + E +); + +CubicScroll = memoize(K -> ( + s := (multirationalMap segre parametrize(PP_K^1 ** PP_K^2)) << PP_K^6; + p := (baseLocus s)\top baseLocus s; + L := top baseLocus s; + assert(baseLocus s == L + p and dim(L*p) == -1 and dim L == 1 and degree L == 1 and dim p == 0 and degree p == 1); + (p,L,s) +)); + +QuarticScroll = memoize(K -> ( + b := super parametrize(PP_K[1,1,1,1]); + b = b * rationalMap point target b; + P0 := ((baseLocus b)\(support baseLocus b))\(support baseLocus b); + (P1,P2,P3) := toSequence decompose((baseLocus b)\\P0); + P := {P0,P1,P2,P3}; + assert(⋃ {3*P_0,P_1,P_2,P_3} == baseLocus b); + assert(dim(P0*P1)==1 and dim(P0*P2)==1 and dim(P0*P3)==1); + assert(all(P,L -> degree L == 1 and dim L == 2)); + (P,b) +)); + +curvesOnSurface = method(); -- some curves of degree d and genus g on a rational surface +curvesOnSurface (EmbeddedProjectiveVariety,ZZ,ZZ) := (S,d,g) -> ( + L := S.cache#"linear system on PP^2"; + if #L != 4 then error "not implemented yet: the linear system on PP^2 must be of the form {a,i,j,k}"; + U := {}; + if g == 0 then ( + for a from 1 to 2 do for i to L_1 do for j to L_2 do for k to L_3 do + if a*L_0-i-2*j-3*k == d and i+j+k <= (if a == 1 then 2 else 5) then U = append(U,(a,{i,j,k})); + ) else if g == 1 then ( + for i to L_1 do for j to L_2 do for k to L_3 do + if 3*L_0-i-2*j-3*k == d and i+j+k <= 9 then U = append(U,(3,{i,j,k})); + ) else error "not implemented yet: the genus must be 0 or 1"; + apply(U,u -> S.cache#"takeCurve" u) +); + +takeGMsfromSurfaceInP6 = method(Options => {InputCheck => 1, Verbose => true, "Gluing" => "cubic scroll", Degrees => (options makeGMfromCurveOnSurfaceInP6).Degrees, "OnlyNewOnes" => false, "Output" => true}); +takeGMsfromSurfaceInP6 EmbeddedProjectiveVariety := o -> S -> ( + if dim S != 2 or dim ambient S != 6 then error "expected a surface in PP^6"; + if not(S.cache#?"linear system on PP^2" and S.cache#?"takeCurve") then error "expected a surface constructed with the function \"surface\""; + a := apply(toSequence S.cache#"linear system on PP^2",toString); + if #a != 4 then error "not implemented yet: the surface must be of the form \"surface {a,i,j,k}\""; + S.cache#"nice description" = "S("|a_0|";"|a_1|","|a_2|","|a_3|",NumNodes=>"|(if S.cache#?"FiniteNumberOfNodes" then toString(S.cache#"FiniteNumberOfNodes") else "?")|") ⊂ PP^"|toString(dim linearSpan S)|(if codim linearSpan S > 0 then " ⊂ PP^"|toString(dim ambient S) else "")|" of degree: "|toString(degree S)|", genus: "|toString(sectionalGenus S)|", and degrees: "|(toStringDegreesVar S); + if o.Verbose then <<"*******"<= 3 and g == 0 then "rational" else if d >= 3 and g == 1 then "elliptic" else "")<<" curve of degree "<"|(if S.cache#?"FiniteNumberOfNodes" then toString(S.cache#"FiniteNumberOfNodes") else "?")|")"<o.InputCheck,Verbose=>o.Verbose,"Gluing"=>o#"Gluing",Degrees=>o.Degrees) else continue; + if (not o#"OnlyNewOnes" or recognize X === "NotRecognized") and (not member(describe X,apply(U,describe))) then ( + U = append(U,X); + if o.Verbose then <<"using "< 0 then (","|toString(S.cache#"FiniteNumberOfNodes")|")") else ")")<= 3 and sectionalGenus C == 0 then "a rational" else if degree C >= 3 and sectionalGenus C == 1 then "an elliptic" else "")<<" curve of degree "< (a,b,s,nK) -> ( + if #a =!= #b then error "expected two arrays of the same length"; + if s =!= "cubic scroll" and s =!= "quartic scroll" then error "expected string to be \"cubic scroll\" or \"quartic scroll\""; + (n,K) := if instance(nK,ZZ) then (nK,ZZ/65521) else if instance(nK,Ring) then (0,nK) else if instance(nK,VisibleList) and #nK==2 then (nK_0,nK_1) else error "not valid input"; + C := (surface(a,K,NumNodes=>n,ambient=>6)).cache#"takeCurve" (first b,toList take(b,-(#b-1))); + makeGMfromCurveOnSurfaceInP6(C,InputCheck=>o.InputCheck,Verbose=>o.Verbose,"Gluing"=>s) +); +gushelMukaiFourfold (Array,Array,String) := o -> (a,b,s) -> gushelMukaiFourfold(a,b,s,(0,ZZ/65521),InputCheck=>o.InputCheck,Verbose=>o.Verbose); +gushelMukaiFourfold (Array,Array,Thing) := o -> (a,b,nK) -> gushelMukaiFourfold(a,b,"cubic scroll",nK,InputCheck=>o.InputCheck,Verbose=>o.Verbose); +gushelMukaiFourfold (Array,Array) := o -> (a,b) -> gushelMukaiFourfold(a,b,"cubic scroll",(0,ZZ/65521),InputCheck=>o.InputCheck,Verbose=>o.Verbose); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/HodgeSpecialFourfolds.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/HodgeSpecialFourfolds.m2 new file mode 100644 index 00000000000..4581a953fa1 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/HodgeSpecialFourfolds.m2 @@ -0,0 +1,540 @@ + +------------------------------------------------------------------------ +----------------------- Hodge-special fourfolds ------------------------ +------------------------------------------------------------------------ + +HodgeSpecialFourfold = new Type of EmbeddedProjectiveVariety; + +globalAssignment HodgeSpecialFourfold; + +HodgeSpecialFourfold.synonym = "Hodge-special fourfold"; + +specialFourfold = method(TypicalValue => HodgeSpecialFourfold, Options => {NumNodes => null, InputCheck => 1, Verbose => false}); + +specialFourfold (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X,V) -> ( + if not (ring ideal S === ring ideal X and ring ideal X === ring ideal V) then error "expected varieties in the same ambient space"; + if dim X != 4 then error "expected a fourfold"; + if not (dim V == 5 and isSubset(X,V)) then error "expected a fivefold containing the fourfold"; + if dim S != 2 then error "expected a surface"; + i := o.InputCheck; + if not(instance(i,ZZ) and i >= -1) then error("option InputCheck expects a nonnegative integer:"|newline|"0: no check is done about the smoothness of the fourfold and of the surface"|newline|"1: just the smoothness of the fourfold is checked"|newline|"2: both the smoothness of the fourfold and the smoothness of the surface are checked"); + if i >= 0 then ( + if not isSubset(S,X) then error "the given surface is not contained in the fourfold"; + if not isSmooth V then error "the ambient fivefold is not smooth"; + ); + if i >= 1 then if not isSmooth X then error "expected a smooth fourfold"; + if i >= 2 then ( + if not isSmooth S then error "expected a smooth surface"; + if o.Verbose then <<"-- smoothness of the surface verified (assuming equidimensionality)"<= 4 then ( + if S != top S then error "expected an irreducible reduced surface"; + if o.Verbose then <<"-- equidimensionality of the surface verified"< (S,X) -> ( + if ring ideal S =!= ring ideal X then error "expected varieties in the same ambient space"; + if dim S != 2 then error "expected a surface"; + if dim X == 4 and dim ambient X == 5 and degrees X == {({3},1)} and degree X == 3 then return cubicFourfold(S,X,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim X == 4 and dim ambient X == 8 and degrees X == {({2},6)} and degree X == 10 then return gushelMukaiFourfold(S,X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim X == 5 and (degrees X == {({2},2)} or degrees X == {({3},1)}) then return specialFourfold(S,X * random(2,S),X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim X != 4 then error "expected a fourfold"; + if dim ambient X == 7 and degrees X == {({2},3)} and degree X == 8 then return specialFourfold(S,X,random({{2},{2}},X),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim ambient X == 6 and degrees X == {({2},1),({3},1)} and degree X == 6 then return specialFourfold(S,X,random(3,X),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim ambient X == 5 and degrees X == {({4},1)} and degree X == 4 then return specialFourfold(S,X,ambient X,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if o.InputCheck >= 0 then if not isSubset(S,X) then error "the given surface is not contained in fourfold"; + if o.InputCheck >= 1 then if not isSmooth X then error "expected a smooth fourfold"; + Fourfold := new HodgeSpecialFourfold from X; + if Fourfold#?"SurfaceContainedInTheFourfold" then Fourfold#"SurfaceContainedInTheFourfold" = prepend(S,Fourfold#"SurfaceContainedInTheFourfold") else Fourfold#"SurfaceContainedInTheFourfold" = {S}; + Fourfold +); + +specialFourfold (Ideal,Ideal) := o -> (idS,idX) -> specialFourfold(projectiveVariety idS,projectiveVariety idX,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +specialFourfold (Ideal,RingElement) := o -> (idS,C) -> specialFourfold(idS,ideal C,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +specialFourfold EmbeddedProjectiveVariety := o -> S -> ( + if dim S == 4 then ( + if S#?"SurfaceContainedInTheFourfold" then + return specialFourfold(first S#"SurfaceContainedInTheFourfold",S,InputCheck=>o.InputCheck,Verbose=>o.Verbose) + else + return specialFourfold(S * random({2:{1}},0_S),S,InputCheck=>o.InputCheck,Verbose=>o.Verbose) + ); + if dim S != 2 then error "expected a surface"; + if dim ambient S == 5 then return cubicFourfold(S,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim ambient S == 6 then return specialFourfold(S,random({{2},{3}},S),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim ambient S == 7 then return specialFourfold(S,random({{2},{2},{2}},S),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + if dim ambient S == 8 or dim ambientVariety S == 5 or dim ambientVariety S == 6 then return gushelMukaiFourfold(S,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + error "no valid input provided"; +); + +specialFourfold Ideal := o -> idS -> specialFourfold(makeSubvariety idS,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +specialFourfold (String,Ring) := o -> (str,K) -> ( + try return gushelMukaiFourfold(str,K,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + try return cubicFourfold(str,K,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + local X; local S; + if str === "plane in PP^7" then ( + X = specialFourfold(surface({1},K,ambient=>7),InputCheck=>o.InputCheck,Verbose=>o.Verbose); + assert(recognize X === "planeInPP7"); + return X; + ); + if str === "internal projection of K3 surface of genus 8" then ( + G := GG(K,1,5); + G = (parametrize random({6:{1}},0_G))^^ G; + X = specialFourfold((rationalMap point G) G,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + assert(recognize X === "internal-projection-K3-genus-8"); + return X; + ); + if substring(0,5,str) === "DSCF-" then ( + i := value substring(5,str); + if instance(i,ZZ) then return exampleDSCFourfoldC8(i,K,Verbose=>o.Verbose); + ); + error ///string may not be valid, permitted strings are: +*** Cubic fourfolds *** +"quartic scroll", +"3-nodal septic scroll", +"one-nodal septic del Pezzo surface", +"6-nodal octic scroll", +"general cubic 4-fold of discriminant 32", +"general cubic 4-fold of discriminant 38", +"general cubic 4-fold of discriminant 42", +"8-nodal nonic scroll", +"general cubic 4-fold of discriminant 44", +"cubic 4-fold of discriminant 48" +*** GM fourfolds *** +"sigma-plane", +"rho-plane", +"tau-quadric", +"cubic scroll", +"quintic del Pezzo surface", +"K3 surface of genus 8 with class (9,5)", +"general GM 4-fold of discriminant 20", +"1","2",...,"21", +"nodal surface of degree 11 and genus 3 with class (7,4)", +"surface of degree 11 and genus 3 with class (7,4)", +"GM 4-fold of discriminant 26('')", +"GM 4-fold of discriminant 28", +"GM 4-fold of discriminant 34(')", +"triple projection of K3 surface of degree 26" +*** Complete intersections of 3 quadrics in PP^7 *** +"plane in PP^7", +"internal projection of K3 surface of genus 8" +*** Doubly special cubic fourfolds *** +"DSCF-1","DSCF-2",...,"DSCF-40"///; +); + +specialFourfold String := o -> str -> specialFourfold(str,ZZ/65521,NumNodes=>o.NumNodes,InputCheck=>o.InputCheck,Verbose=>o.Verbose); + +specialFourfold (String,ZZ) := o -> (str,i) -> (prebuiltExamplesOfRationalFourfolds()) (str,i); + +expression HodgeSpecialFourfold := X -> ( + E := if dim ambient X == 4 + then "a " + else if degrees X == {({2},1),({3},1)} + then "complete intersection of type (2,3) in " + else if degrees X == {({4},1)} + then "quartic hypersurface in " + else "fourfold in "; + E = E|"PP^"|toString(dim ambient X)|" containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X); + expression E +); + +describe HodgeSpecialFourfold := X -> ( + descr := if dim ambient X == 4 + then "Four-dimensional projective space" + else if degrees X == {({2},3)} + then "Complete intersection of 3 quadrics in PP^7" + else if degrees X == {({2},1),({3},1)} + then "Complete intersection of type (2,3) in PP^6" + else if degrees X == {({4},1)} + then "Quartic hypersurface in PP^5" + else "Hodge-special fourfold in PP^"|toString(dim ambient X); + a := flatten degrees ideal X; r := codim X; + if r > 0 and #a == r then ( + disc := discriminant X; + A := X.cache#(surface X,"LatticeIntersectionMatrix"); + descr = descr|newline|toString("of discriminant "|(toString disc)|" = det"|(net A)); + ); + descr = descr|newline|"containing a "|surfaceDescription(0,surface X); + net expression descr +); + +shortDescriptionFourfold = method(); +shortDescriptionFourfold (HodgeSpecialFourfold,Boolean) := (X,UseAttribute) -> ( + if UseAttribute and hasAttribute(X,ReverseDictionary) then return toString getAttribute(X,ReverseDictionary); + "Hodge-special fourfold" +); +shortDescriptionFourfold HodgeSpecialFourfold := X -> shortDescriptionFourfold(X,true); + +ambientFivefold = method(TypicalValue => EmbeddedProjectiveVariety); + +ambientFivefold HodgeSpecialFourfold := X -> ( + if X.cache#?"AmbientFivefold" then return X.cache#"AmbientFivefold"; + if codim X == 1 then return X.cache#"AmbientFivefold" = ambient X; + if instance(X,GushelMukaiFourfold) then return X.cache#"AmbientFivefold" = varietyDefinedBylinearSyzygies X; + error "no ambient fivefold found"; +); + +associatedMapFromFivefold = X -> ( + if (surface X).cache#?("AssociatedMapFromFivefold",ambientFivefold X) then return (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X); + if dim ambient X == 5 then return (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap(ideal surface X,degreeHypersurface X); + if degreeHypersurface X == 2 then ( + S := ideal surface X; + J1 := select(S_*,s -> degree s == {1}); + if #J1 > 0 then J1 = (ideal J1) * (ideal vars ring S) else J1 = ideal ring S; + J2 := select(S_*,s -> degree s == {2}); + if #J2 > 0 then J2 = ideal J2 else J2 = ideal ring S; + return (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap trim sub(J1+J2,ring ambientFivefold X); + ); + (surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap(idealOfSubvariety((surface X)%(ambientFivefold X)),degreeHypersurface X) +); + +map HodgeSpecialFourfold := o -> X -> associatedMapFromFivefold X; + +imageOfAssociatedMap = method(); +imageOfAssociatedMap HodgeSpecialFourfold := X -> ( + f := map X; + if f#"idealImage" =!= null then return image f; + e := if X.cache#?(surface X,"label") then X.cache#(surface X,"label") else "not recognized yet"; + if e === "quinticDelPezzoSurface" or e === "quarticScrollSurface" or e === "FarkasVerra" or e === "planeInPP7" then forceImage(f,image(f,2)); + if e === "C42" then forceImage(f,image(f,3)); + if instance(e,ZZ) and e >= 1 and e <= 21 and e != 3 and e != 21 then forceImage(f,image(f,2)); + if instance(e,ZZ) and (e == 3 or e == 21) then forceImage(f,trim lift(kernel(map rationalMap(f,Dominant=>2),SubringLimit=>1),ambient target f)); + if member(e,{"gushel26''", "hyperplane section of a conic bundle over PP2", "surf-5-6-2-nodal"}) then forceImage(f,trim kernel(map f,SubringLimit=>1)); + ch := char coefficientRing X; + if (coefficientRing X === ZZ/ch and ch <= 65521) then image(f,"F4") else image f +); + +degreeHypersurface = method(); + +degreeHypersurface HodgeSpecialFourfold := X -> ( + if instance(X,CubicFourfold) then return 3; + if instance(X,GushelMukaiFourfold) or instance(X,IntersectionOfThreeQuadricsInP7) then return 2; + I := flatten degrees trim sub(ideal X,ring ambientFivefold X); + if #I != 1 then error "degree is not defined"; + first I +); + +recognize = method(); + +recognize HodgeSpecialFourfold := X -> ( + if instance(X,DoublySpecialCubicFourfold) then return recognizeDSCF X; + if X.cache#?(surface X,"label") then return X.cache#(surface X,"label"); + if instance(X,CubicFourfold) then return X.cache#(surface X,"label") = recognizeCubicFourfold X; + if instance(X,GushelMukaiFourfold) then return X.cache#(surface X,"label") = recognizeGMFourfold X; + if instance(X,IntersectionOfThreeQuadricsInP7) then return X.cache#(surface X,"label") = recognize3QuadricsP7 X; + "NotRecognized" +); + +HodgeSpecialFourfold ? HodgeSpecialFourfold := (X,Y) -> ( + if not uniform {X,Y} then return incomparable; + try (dX,dY) := (discriminant X,discriminant Y) else return incomparable; + if dX < dY then return symbol <; + if dX > dY then return symbol >; + if instance(X,GushelMukaiFourfold) and instance(Y,GushelMukaiFourfold) and dX == dY and dX % 8 == 2 then ( + (a,b) := last cycleClass X; (a',b') := last cycleClass Y; + if (even(a+b) and odd(b)) and (odd(a'+b') and even(b')) then return symbol <; + if (odd(a+b) and even(b)) and (even(a'+b') and odd(b')) then return symbol >; + ); + if X.cache#?(surface X,"parameterCount") and Y.cache#?(surface Y,"parameterCount") then ( + if first X.cache#(surface X,"parameterCount") < first Y.cache#(surface Y,"parameterCount") then return symbol <; + if first X.cache#(surface X,"parameterCount") > first Y.cache#(surface Y,"parameterCount") then return symbol >; + ); + if degree surface X < degree surface Y then return symbol <; + if degree surface X > degree surface Y then return symbol >; + if sectionalGenus surface X < sectionalGenus surface Y then return symbol <; + if sectionalGenus surface X > sectionalGenus surface Y then return symbol >; + if (surface X).cache#?"linear system on PP^2" and (surface Y).cache#?"linear system on PP^2" then return (((surface X).cache#"linear system on PP^2") ? ((surface Y).cache#"linear system on PP^2")); + if X == Y and surface X == surface Y then return symbol ==; + return incomparable; +); + +parameterCount = method(Options => {Verbose => false}) + +parameterCount (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := o -> (S,X) -> ( + isSing := true; + if S.cache#?"isSmooth" then isSing = not isSmooth S else ( + if S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" then isSing = dim singLocus S >= 0 else ( + if S.cache#?"FiniteNumberOfNodes" then isSing = numberNodes S >= 1; + ); + ); + if ring ambient S =!= ring ambient X then error "expected varieties in the same ambient space"; + d := first first degrees ideal X; + c := codim X; + if not ({{d}} === unique degrees ideal X and c == # degrees ideal X) then error "the second argument must be a complete intersection of hypersurfaces of the same degree"; + r := dim S; + if (r <= 0) then error "the first argument must be a positive dimensional scheme"; + if not isSubset(S,X) then error "expected the first scheme to be a subscheme of the second one"; + if o.Verbose then <<"S: "|toString(? ideal S)< 1 and o.Verbose then <<"dim GG("|toString(c-1)|","|toString(m-1)|") = "|toString(M)< 1 then "dim GG("|toString(c-1)|","|toString(m-1)|")" else toString(m-1))|" = "|toString(h0N + M)<= "|toString(h0N + M - h0NX)< 1 then "dim GG("|toString(c-1)|",P(H^0(O_(P^"|toString(n)|")("|toString(d)|")))) = " else "dim P(H^0(O_(P^"|toString(n)|")("|toString(d)|"))) = ")|toString(c * (binomial(n+d,d) - c))< X -> ( + Y := ambientFivefold X; + S := surface X; + a := degreeHypersurface X; + if o.Verbose then <<"S: "|toString(? ideal S)<= "|toString(h0N + m-1 - h0NX)< F -> F.variety.cache#"embedded projective variety"; +CoherentSheafOnEmbeddedProjectiveVariety#{WebApp,AfterPrint} = CoherentSheafOnEmbeddedProjectiveVariety#{WebApp,AfterNoPrint} = +CoherentSheafOnEmbeddedProjectiveVariety#{Standard,AfterPrint} = CoherentSheafOnEmbeddedProjectiveVariety#{Standard,AfterNoPrint} = F -> (<< endl << concatenate(interpreterDepth:"o") << lineNumber << " : Coherent sheaf on " << projectiveVariety F << endl); + +normalSheaf = method(TypicalValue => CoherentSheaf); +normalSheaf MultiprojectiveVariety := normalSheaf EmbeddedProjectiveVariety := X -> ( + if X.cache#?("normalSheaf",ambientVariety X) then return X.cache#("normalSheaf",ambientVariety X); + I := idealOfSubvariety X; + R := (ring I)/I; + N := sheaf Hom((module I) ** R,R); + if N.variety.ring =!= R then error "internal error encountered"; + N.variety.cache#"embedded projective variety" = X; + X.cache#("normalSheaf",ambientVariety X) = new CoherentSheafOnEmbeddedProjectiveVariety from N +); +normalSheaf (MultiprojectiveVariety,MultiprojectiveVariety) := normalSheaf (EmbeddedProjectiveVariety,EmbeddedProjectiveVariety) := (X,Y) -> ( + Z := ambientVariety X; + N := normalSheaf makeSubvariety(X,Y); + makeSubvariety(X,Z); + N +); + +rankHH = method(); +rankHH (ZZ,CoherentSheafOnEmbeddedProjectiveVariety) := (i,F) -> ( + X := projectiveVariety F; + if X.cache#?("rank HH",i,F) then return X.cache#("rank HH",i,F); + X.cache#("rank HH",i,F) = rank HH^i F +); + +unirationalParametrization = method(); + +unirationalParametrization HodgeSpecialFourfold := (cacheValue "unirationalParametrization") (X -> ( + error "not implemented yet"; +)); + +parametrize HodgeSpecialFourfold := X -> ( + if X.cache#?"rationalParametrization" then return X.cache#"rationalParametrization"; + Psi := fanoMap X; + X.cache#"rationalParametrization" = inverse3(Psi|X) +); + +surface = method(TypicalValue => EmbeddedProjectiveVariety); + +surface HodgeSpecialFourfold := X -> first X#"SurfaceContainedInTheFourfold"; + +surface (VisibleList,Ring,Option,Option) := (L,K,oN,oA) -> ( + oN = toList oN; oA = toList oA; + if first oN === ambient and first oA === NumNodes then (oN,oA) = (oA,oN); + if first oN =!= NumNodes and first oA =!= ambient then error "NumNodes and ambient are the only available options for surface(VisibleList)"; + if not (#L>0 and all(L,i->instance(i,ZZ) and i>=0)) then error "expected a list of nonnegative integers"; + P := for i from 1 to #L-1 list for j from 1 to L_i list (i,point PP_K^2); + B := if #L==1 or all(take(L,-(#L-1)),i->i==0) then 0_(PP_K^2) else ⋃ apply(flatten P,p -> p_0 * p_1); + f := rationalMap(B,L_0,Dominant=>true); + if last oN > 0 then f = f * rationalMap((linearSpan apply(last oN,i -> point linearSpan {f point source f,f point source f}))_(target f),Dominant=>true); + if last oA =!= null then f = rationalMap(f << PP_K^(last oA),Dominant=>true); + S := target f; + S.cache#"rationalParametrization" = f; + S.cache#"linear system on PP^2" = L; + S.cache#"points on PP^2" = P; + if dim linearSpan S >= 5 and last oN >= 0 then S.cache#"FiniteNumberOfNodes" = last oN; + S.cache#"takeCurve" = ((D,d) -> ( + if not(instance(D,ZZ) and D>0) then error "expected a positive integer for the degree of a plane curve"; + if not (instance(d,VisibleList) and #d==#L-1 and all(#d,i->instance(d_i,ZZ) and d_i>=0 and d_i<=L_(i+1))) + then error("expected a list of "|toString(#L-1)|" nonnegative integers representing a curve on the surface"|toString(L)); + local C; local pt; + if sum toList d == 0 then ( + pt = point PP_K^2; + C = random(D,pt); + ) else ( + pts := flatten for a to #d-1 list take(apply(P_a,last),d_a); + C = random(D,⋃ pts); + pt = first pts; + ); + if D == 2 then C.cache#"rationalParametrization" = inverse(rationalMap(pt%C,1),Verify=>true); + fC := f C; + makeSubvariety(fC,S,Verify=>true); + if not (dim fC == 1 and degree fC == D*L_0 - sum(#d,i->(i+1)*d_i)) then error "something went wrong when taking the curve"; + fC.cache#"plane representation" = (D,d); + if D <= 2 then fC.cache#"rationalParametrization" = check rationalMap((parametrize C)*f,fC); + return fC; + )); + return S; +); +surface (VisibleList,Ring,Option) := (L,K,opt) -> ( + o := first toList opt; + if o === ambient + then return surface(L,K,NumNodes=>0,opt) + else if o === NumNodes + then return surface(L,K,opt,ambient=>null); + error "NumNodes and ambient are the only available options for surface(VisibleList)"; +); +surface(VisibleList,Option,Option) := (L,opt1,opt2) -> surface(L,ZZ/65521,opt1,opt2); +surface(VisibleList,Option) := (L,opt) -> surface(L,ZZ/65521,opt); +surface (VisibleList,Ring) := (L,K) -> surface(L,K,NumNodes=>0,ambient=>null); +surface List := L -> surface(L,ZZ/65521,NumNodes=>0,ambient=>null); + +clean HodgeSpecialFourfold := X -> ( + K := coefficientRing X; + x := local x; + n := dim ambient X; + R := K[x_0..x_n]; + S := surface X; + S' := Var sub(sub(ideal S,vars R),vars ring ambient X); + X' := Var sub(sub(ideal X,vars R),vars ring ambient X); + V' := Var sub(sub(ideal ambientFivefold X,vars R),vars ring ambient X); + if S.cache#?"euler" then S'.cache#"euler" = S.cache#"euler"; + if S.cache#?"FiniteNumberOfNodes" then S'.cache#"FiniteNumberOfNodes" = S.cache#"FiniteNumberOfNodes"; + specialFourfold(S',X',V',InputCheck=>0) +); + +HodgeSpecialFourfold ** Ring := (X,K) -> ( + if not isField K then error "expected a field"; + S := (surface X) ** K; + if (surface X).cache#?"FiniteNumberOfNodes" and (not S.cache#?"FiniteNumberOfNodes") then S.cache#"FiniteNumberOfNodes" = (surface X).cache#"FiniteNumberOfNodes"; + if (surface X).cache#?"euler" and (not S.cache#?"euler") then S.cache#"euler" = (surface X).cache#"euler"; + specialFourfold(S,(projectiveVariety ring X) ** K,(ambientFivefold X) ** K,InputCheck=>0) +); + +random HodgeSpecialFourfold := o -> X -> ( + X' := specialFourfold(surface X,random(degreeHypersurface X,surface X) * ambientFivefold X,ambientFivefold X,InputCheck=>-1); + if X.cache#?(surface X,"label") and (not X'.cache#?(surface X',"label")) then X'.cache#(surface X',"label") = X.cache#(surface X,"label"); + X' +); + +toExternalString HodgeSpecialFourfold := X -> ( + x := local x; + K := coefficientRing X; + n := dim ambient X; + R := K[x_0..x_n]; + s := ///needsPackage "SpecialFanoFourfolds";///|newline; + s = s|"(i -> (x := local x;"|newline; + s = s|"R := "|toExternalString(K)|"[x_0..x_"|toString(n)|"];"|newline; + s = s|"S := projectiveVariety "|toString sub(ideal surface X,vars R)|";"|newline; + if codim ambientFivefold X == 0 then s = s|"V := ambient S;"|newline else s = s|"V := projectiveVariety "|toString sub(ideal ambientFivefold X,vars R)|";"|newline; + s = s|"X := projectiveVariety "|toString sub(ideal X,vars R)|";"|newline; + if instance(X,CubicFourfold) + then s = s|"X = specialFourfold(S,X,NumNodes=>"|toString(numberNodes surface X)|",InputCheck=>0);"|newline else + if instance(X,GushelMukaiFourfold) + then s = s|"X = specialFourfold(S,X,InputCheck=>0);"|newline|///X.cache#"AmbientFivefold" = V;///|newline else + s = s|"X = specialFourfold(S,X,V,InputCheck=>0);"|newline; + if (surface X).cache#?"euler" then s = s|///(surface X).cache#"euler" = ///|toString(euler surface X)|";"|newline; + if (surface X).cache#?"FiniteNumberOfNodes" then s = s|///(surface X).cache#"FiniteNumberOfNodes" = ///|toString(numberNodes surface X)|";"|newline; + N := numgens target map X -1; + if N >= 6 and (map X)#"idealImage" =!= null then ( + z := local z; Z := K[z_0..z_N]; + phi := sub(map X,R,Z); + s = s|"z := local z; Z := "|toExternalString(K)|"[z_0..z_"|toString(N)|"];"|newline; + s = s|///(surface X).cache#("AssociatedMapFromFivefold",ambientFivefold X) = rationalMap(ring V,Z,///|(toString entries phi)|");"|newline; + s = s|"forceImage(map X,"|(toString image phi)|");"|newline; + ); + if (surface X).cache#?("fanoMap",ambientFivefold X) then ( + mu := fanoMap X; + m := numgens ambient target mu -1; + y := local y; + T := K[y_0..y_m]; + mu = sub(mu,R,T); + s = s|"y := local y; T := "|toExternalString(K)|"[y_0..y_"|toString(m)|"];"|newline; + if m > 4 then s = s|"T = T/"|toString ideal target mu|";"|newline; + s = s|"mu := rationalMap map(ring V,T,"|toString entries mu|");"|newline; + s = s|"forceImage(mu,ideal(0_(target mu)));"|newline; + s = s|///(mu#"map").cache#"multiplicityFanoMap" = ///|toString(((fanoMap X)#"map").cache#"multiplicityFanoMap")|";"|newline; + s = s|///(surface X).cache#("fanoMap",ambientFivefold X) = mu;///|newline; + if (surface X).cache#?("surfaceDeterminingInverseOfFanoMap",ideal X) then ( + U := surfaceDeterminingInverseOfFanoMap X; + s = s|"U := projectiveVariety("|toString sub(ideal U,vars T)|",Saturate=>false);"|newline; + s = s|///(surface X).cache#("surfaceDeterminingInverseOfFanoMap",ideal X) = U;///|newline; + if U.cache#?"exceptionalCurves" then ( + (L,C) := exceptionalCurves X; + s = s|"L := "|(if dim L >= 0 then "projectiveVariety("|toString sub(ideal L,vars T)|",Saturate=>false)" else "0_U")|";"|newline; + s = s|"C := "|(if dim C >= 0 then "projectiveVariety("|toString sub(ideal C,vars T)|",Saturate=>false)" else "0_U")|";"|newline; + s = s|///U.cache#"exceptionalCurves" = (L%U,C%U);///|newline; + ); + ); + ); + s = s|"X))()"; + return s; +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/HodgeSpecialSurfaces.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/HodgeSpecialSurfaces.m2 new file mode 100644 index 00000000000..1303d69cbf2 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/HodgeSpecialSurfaces.m2 @@ -0,0 +1,119 @@ + +--------------------------------------------------------- +-- Experimental: stability and utility under evaluation. +-- These functions may be deprecated in future versions. + +HodgeSpecialSurface = new Type of WeightedProjectiveVariety; +globalAssignment HodgeSpecialSurface; +HodgeSpecialSurface.synonym = "Hodge-special surface"; +surface (MultiprojectiveVariety,MultiprojectiveVariety) := (C,S) -> ( + if ring ideal C =!= ring ideal S then error "expected varieties in the same ambient space"; + if dim S != 2 then error "expected a surface"; + if abs dim C != 1 then error "expected a curve"; + if not isSubset(C,S) then error "the given curve is not contained in the surface"; + newS := new HodgeSpecialSurface of (class S) from S; + if newS#?"CurveContainedInTheSurface" then newS#"CurveContainedInTheSurface" = prepend(C,newS#"CurveContainedInTheSurface") else newS#"CurveContainedInTheSurface" = {C}; + if not newS.cache#?"hyperplane" then newS.cache#"hyperplane" = random(1,0_S); + newS +); +map (HodgeSpecialSurface,ZZ,ZZ) := o -> (S,a,b) -> ( + if S.cache#?("map",a,b) then return S.cache#("map",a,b); + H := S.cache#"hyperplane"; + C := first S#"CurveContainedInTheSurface"; + if a < 0 then error "expected a nonnegative integer"; + S.cache#("map",a,b) = if b >= 0 then mapDefinedByDivisor(S,{(H,a),(C,b)}) else rationalMap(C_S,a,-b) +); +HodgeSpecialSurface Sequence := (S,ab) -> ( + if not(#ab == 2 and instance(first ab,ZZ) and instance(last ab,ZZ)) then error "expected a sequence of two integers"; + (a,b) := ab; + image map(S,a,b) +); +map HodgeSpecialSurface := o -> S -> ( + if S.cache#?"doubleCover" then return S.cache#"doubleCover"; + S.cache#"doubleCover" = quadricFibration(map(S,1,0),Verify=>true) +); +curve = method(); +curve HodgeSpecialSurface := U -> first U#"CurveContainedInTheSurface"; +discriminant HodgeSpecialSurface := ZZ => o -> S -> ( + if S.cache#?(curve S,"discriminantSurface") then return last S.cache#(curve S,"discriminantSurface"); + if not member(o.Algorithm, {null, 1, 2}) then error "the Algorithm option accepts the values 1 and 2"; + C := curve S; + if dim C != 1 then error "expected a Hodge-special surface"; + if o.Algorithm === 2 then return discriminant2 S; + f := map(S,0,1); + if dim target f <= 0 then (if o.Algorithm === 1 then error "the option Algorithm=>1 is not available for this case" else return discriminant2 S); + C' := f^* random(1,0_(target f)); + if dim(C * C') != 0 then error "something went wrong :("; + C2 := degree(C * C'); + H := S.cache#"hyperplane"; + H2 := S * H * random(1,0_S); + if dim H2 != 0 then error "something went wrong :("; + H2 = degree H2; + HC := S * H * C; + if dim HC != 0 then error "something went wrong :("; + HC = degree HC; + disc := det(S.cache#(curve S,"LatticeIntersectionMatrix") = matrix {{H2,HC},{HC,C2}}); + S.cache#(curve S,"discriminantSurface") = (C2,disc); + disc +); +discriminant2 = method(); +discriminant2 HodgeSpecialSurface := S -> ( + if not (codim S == 1 and numgens ideal S == 1) then error "the option Algorithm=>2 is not available for this case"; + if S.cache#?(curve S,"discriminantSurface") then return last S.cache#(curve S,"discriminantSurface"); + C := curve S; + H := S.cache#"hyperplane"; + HC := H * C; + if dim HC != 0 then error "something went wrong :("; + HC = degree HC; + a := degree S; + g := sectionalGenus image segreEmbedding C; + e := sum degrees ring ambient S; + if #e != 1 then error "something went wrong :("; + C2 := (e_0 - a)*HC + 2*g -2; + H2 := S * H * random(1,0_S); + if dim H2 != 0 then error "something went wrong :("; + H2 = degree H2; + disc := det(S.cache#(curve S,"LatticeIntersectionMatrix") = matrix {{H2,HC},{HC,C2}}); + S.cache#(curve S,"discriminantSurface") = (C2,disc); + disc +); +HodgeSpecialSurface#{WebApp,AfterPrint} = HodgeSpecialSurface#{WebApp,AfterNoPrint} = +HodgeSpecialSurface#{Standard,AfterPrint} = HodgeSpecialSurface#{Standard,AfterNoPrint} = S -> ( + d := degree S; + str := "ProjectiveVariety, "|(if d <= 9 then ({"linear", "quadratic", "cubic", "quartic", "quintic", "sextic", "septic", "octic", "nonic"}_(d-1))|" surface" else "surface of degree "|toString(d)); + str = str|" in "|(toString expression ambient S); + try discriminant S; + if S.cache#?(curve S,"LatticeIntersectionMatrix") then ( + M := S.cache#(curve S,"LatticeIntersectionMatrix"); + str = (str|" with rank 2 lattice")||("defined by the intersection matrix "|net(M)|" (det: "|(toString discriminant S)|")"); + ); + << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << str << endl; +); +parameterCount HodgeSpecialSurface := o -> S -> ( + if not(codim S == 1 and numgens ideal S == 1) then error "expected a surface in a three-dimensional weighted projective space"; + a := first degree (ideal S)_0; + C := curve S; + if dim C != 1 then error "expected a Hodge-special surface"; + if o.Verbose then <<"C: "|toString(? C)<= "|toString(h0N + m-1 - h0NS)< expression("complete intersection of three quadrics in PP^7 containing a surface of degree "|toString(degree surface X)|" and sectional genus "|toString(sectionalGenus surface X)); + +describe IntersectionOfThreeQuadricsInP7 := X -> ( + descr := "Complete intersection of 3 quadrics in PP^7"; + disc := discriminant X; + A := X.cache#(surface X,"LatticeIntersectionMatrix"); + descr = descr|newline|toString("of discriminant "|(toString disc)|" = det"|(net A)); + descr = descr|newline|"containing a "|surfaceDescription(0,surface X); + if member(recognize X,{"surf-5-7-0-1","surf-5-10-1","internal-projection-K3-genus-8","surf-4-3-1-external","surf-5-6-2-nodal","surf-7-1-9"}) then descr = descr|newline|"(This is a rational fourfold discovered in August 2022)"; + if recognize X === "planeInPP7" then descr = descr|newline|"(This is a classical example of rational fourfold)"; + if computationStatus X >= 0 then descr = descr|newline|(computationStatusLog X)|newline|toString(describeMirrorFourfoldAndK3 X); + net expression descr +); + +shortDescriptionFourfold (IntersectionOfThreeQuadricsInP7,Boolean) := (X,UseAttribute) -> ( + if UseAttribute and hasAttribute(X,ReverseDictionary) then return toString getAttribute(X,ReverseDictionary); + "complete intersection of three quadrics in PP^7" +); + +recognize3QuadricsP7 = X -> ( + S := surface X; + d := discriminant X; + e := eulerCharacteristic S; + invS := (degree S,sectionalGenus S,euler hilbertPolynomial S); + degs := flatten degrees ideal S; + if (d == 31 and e == 3 and invS === (1,0,1) and degs == toList(5:1)) then return "planeInPP7"; + if (d == 47 and e == 11 and invS === (9,3,1) and degs == toList(12:2)) then return "surf-5-7-0-1"; + if (d == 55 and e == 14 and invS === (11,5,1) and degs == toList(10:2)) then return "surf-5-10-1"; + if (d == 55 and e == 25 and invS === (13,8,2) and degs == toList(9:2)) then return "internal-projection-K3-genus-8"; + if (d == 79 and e == 7 and invS === (9,2,1) and degs == {2,2,2,2,2,2,2,2,2,2,3}) then return "surf-4-3-1-external"; + if ((d == 71 or d == 87) and e == 5 and invS === (11,4,0) and degs == toList(9:2)) then if numberNodes(S,Verbose=>false) == 1 and discriminant X == 87 then return "surf-5-6-2-nodal"; + if (d == 96 and e == 13 and invS === (12,6,1) and degs == {2,2,2,2,2,2,2,2,2,3}) then return "surf-7-1-9"; + "NotRecognized" +); + +infoAboutParameterCountInAmbientP7 = (x,y) -> ( + -------------------------------------------------- + -- Conversion between parameter counts + -- X : c. i. of 3 quadrics in PP^7; Y = ambientFivefold X; S = surface X; + -- Suppose we have computed: + -- h^1(N_{S,Y}) = 0, h^0(N_{S,Y}) = a + -- h^1(O_S(2)) = 0, and h^0(I_{S,Y}(2)) = b = h^0(O_Y(2)) - \chi(O_S(2)); + -- h^0(N_{S,X}) = c, dim P(H^0(O_Y(2))) = 33 + -- codim{[X] : S ⊂ X ⊂ Y} <= 33 - (a + (b-1) - c) + -- parameterCount(X,Y) ----> (34-a-b+c, (b, a, c)) + -- Now from the exact sequence 0 -> N_(S,Y) -> N_(S,PP^7) -> N_(Y,PP^7)|_S -> 0 + -- that is 0 -> N_(S,Y) -> N_(S,PP^7) -> O_S(2)++O_S(2) -> 0 + -- we deduce h^0(N_(S,PP^7)) = h^0(N_{S,Y}) + h^0(O_S(2)++O_S(2)) = a + 2*(34 - b) = 68 + a - 2b + -- Further, from the exact sequence 0 -> I_(Y,PP^7) -> I_(S,PP^7) -> I_(S,Y) -> 0 + -- we deduce h^0(I_{S,PP^7}(2)) = h^0(I_{S,Y}(2)) + 2 = b + 2 + -- from which we have dim GG(2, PP(h^0(I_{S,PP^7}(2)))) = 3 * (b-1) + -- Since dim GG(2,P(H^0(O_(P^7)(2)))) = 99 we obtain + -- 99 - ( (68 + a - 2b) + 3*(b-1) - c ) = 31 - a + 2b - 3b + 3 + c = 34 - a - b + c + -- Therefore parameterCount(X) ----> (34-a-b+c, (b+2, 68 + a - 2b , c)) + -------------------------------------------------- + (b,a,c) := y; + if 34-a-b+c != x then <<"--warning: something went wrong"< ( + R := ring ambient X; + l := projectiveVariety ideal submatrix'(vars R,{0,1}); + j := rationalMap(((line X) ===> l)|X,Dominant=>true); + X' := target j; + if not isSubset(l,X') then error "something went wrong"; + K := coefficientRing R; + x := local x; P7 := projectiveVariety(K[x_0..x_7]); + t := local t; P2 := projectiveVariety(K[t_0..t_2]); + u := local u; P5 := projectiveVariety(K[u_0..u_5]); + Q := flatten entries sub(gens ideal X',vars ring P7); + S := (K[x_2..x_7])[x_0,x_1]; + L := apply(3,i -> coefficient((x_0)_S,sub(Q_i,S))); + M := apply(3,i -> coefficient((x_1)_S,sub(Q_i,S))); + F := apply(3,i -> coefficient(1_S,sub(Q_i,S))); + if F != apply(3,i -> sub(Q_i,S) - (x_0)_S * L_i - (x_1)_S * M_i) then error "something went wrong"; + P2xP5 := P2 ** P5; + A := sub(matrix {L,M,F},for i to 5 list (gens coefficientRing S)_i => u_i); + Z := projectiveVariety ideal det A; + Y := projectiveVariety ideal(((map last projections P2xP5) A) * transpose matrix {first P2xP5#"multigens"}); + fromXtoZ := j * rationalMap(ring X',ring P5,submatrix'(vars R,{0,1})); + if image fromXtoZ != Z then error "something went wrong"; + fromXtoZ = rationalMap(fromXtoZ,Z); + fromYtoZ := (last projections P2xP5)|Y; + if image fromYtoZ != Z then error "something went wrong"; + fromYtoZ = rationalMap(fromYtoZ,Z); + Y.cache#"projection maps" = {quadricFibration first projectionMaps Y, fromYtoZ}; + f := fromXtoZ * inverse fromYtoZ; + g := fromYtoZ * inverse fromXtoZ; + if not(f * g == 1 and g * f == 1) then error "something went wrong"; + forceInverseMap(f,g); + f +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/LatticePolarizedK3.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/LatticePolarizedK3.m2 new file mode 100644 index 00000000000..97e8c7e7ea9 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/LatticePolarizedK3.m2 @@ -0,0 +1,603 @@ + +------------------------------------------------------------------------ +--- Lattice-polarized K3 surfaces from doubly special cubic fourfolds -- +------------------------------------------------------------------------ + +LatticePolarizationOnK3Surface = new Type of HashTable; +globalAssignment LatticePolarizationOnK3Surface; +LatticePolarizationOnK3Surface.synonym = "lattice-polarization"; + +net LatticePolarizationOnK3Surface := S -> ( + M := latticeMatrix S; + w := if S#"isVirtual" then "Virtual lattice" else "Lattice"; + w = (w | " rank-2 polarization defined by the intersection matrix: ") | (net M); + if even M_(0,0) then w = w || (scanPolarizations S) || "[ more lines with: polarize(i,...) ]"; + w +); +texMath LatticePolarizationOnK3Surface := texMath @@ net; + +LatticePolarizationOnK3Surface#{WebApp,AfterPrint} = +LatticePolarizationOnK3Surface#{WebApp,AfterNoPrint} = +LatticePolarizationOnK3Surface#{Standard,AfterPrint} = +LatticePolarizationOnK3Surface#{Standard,AfterNoPrint} = S -> ( + virtualK3 := if S#"isVirtual" then "Virtual lattice" else "Lattice"; + << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << virtualK3 << "-polarization on K3 surface associated to " << (shortDescriptionFourfold recoverFourfold S) << endl; +); + +latticeMatrix = method(); +latticeMatrix LatticePolarizationOnK3Surface := S -> S#"latticeMatrix"; + +recoverFourfold LatticePolarizationOnK3Surface := S -> recoverFourfold S#"SurfaceAssociatedToRationalFourfold"; + +building LatticePolarizationOnK3Surface := S -> building S#"SurfaceAssociatedToRationalFourfold"; + +latticePolarizationOnK3Surface = method(TypicalValue => LatticePolarizationOnK3Surface, Options => {Verbose => true, Verify => true}); +latticePolarizationOnK3Surface (SurfaceAssociatedToRationalFourfold,EmbeddedProjectiveVariety) := o -> (S,C) -> ( + if not isStandardK3surface S then error "expected a standard K3 surface"; + if dim C != 1 then error("while polarizing the K3 surface: expected a curve; found: "|(? C)); + if not isSubset(C,S) then error "expected a curve contained in the K3 surface"; + gC := sectionalGenus C; + if gC < 0 then error "found: sectional genus of the curve < 0"; + n := 2*gC - 2; + if gC > 1 and o.Verify then ( + if o.Verbose then << "-- verifying self-intersection of the curve..." << endl; + f := rationalMap(S, tally {C}); + if dim target f != gC then error "self-intersection calculation failed: unexpected target dimension for the linear system"; + E1 := f^* random(1,0_(target f)); + E2 := f^* random(1,0_(target f)); + if dim E1 != 1 or dim E2 != 1 or dim(E1 * E2) != 0 then error "self-intersection calculation failed: divisors are not in general position"; + assert(degree E1 == degree C and sectionalGenus E1 == gC); + assert(degree E2 == degree C and sectionalGenus E2 == gC); + n' := degree(E1 * E2); + if n != n' then error("self-intersection calculation failed: expected "|(toString n)|", obtained "|(toString n')); + ); + d := degree C; + gS := sectionalGenus S; + if o.Verbose then << "-- constructing lattice polarized K3 with (g, d, C^2) = (" << gS << ", " << d << ", " << n << ")" << endl; + M := matrix{{2*gS-2,d},{d,n}}; + if det M == 0 then error "lattice polarization failed: intersection matrix has determinant 0"; + new LatticePolarizationOnK3Surface from { + symbol cache => new CacheTable, + "SurfaceAssociatedToRationalFourfold" => S, + "specialCurve" => C, + "latticeMatrix" => M, + "isVirtual" => false + } +); + +latticePolarizationOnK3Surface (SurfaceAssociatedToRationalFourfold,ZZ,ZZ,ZZ) := o -> (S,degS,degC,C2) -> ( + M := matrix{{degS,degC},{degC,C2}}; + if det M == 0 then error "virtual lattice polarization failed: intersection matrix has determinant 0"; + new LatticePolarizationOnK3Surface from { + symbol cache => new CacheTable, + "SurfaceAssociatedToRationalFourfold" => S, + "specialCurve" => null, + "latticeMatrix" => M, + "isVirtual" => true + } +); + +LatticePolarizationOnK3Surface Sequence := (S,ab) -> ( + if not(#ab == 2 and instance(first ab,ZZ) and instance(last ab,ZZ)) then error "expected a sequence of two integers"; + (a,b) := ab; + if not S#"isVirtual" then ( + f := map(S,a,b); + if char coefficientRing f <= 65521 then return image(f,"F4"); + return image f; + ); + if gcd(a,b) != 1 then error "expected a and b to be coprime for a primitive polarization"; + M := latticeMatrix S; + g := lift((M_(0,0) + 2)/2, ZZ); + d := M_(0,1); + n := M_(1,1); + g' := (a^2*(2*g-2) + 2*a*b*d + b^2*n + 2)/2; + d' := a*d + b*n; + if not (floor g' == g' and g' >= 3 and d' >= 1) then error "failed to construct virtual lattice polarized K3 surface"; + latticePolarizationOnK3Surface(S#"SurfaceAssociatedToRationalFourfold", 2*(lift(g',ZZ))-2, d', n) +); + +map (LatticePolarizationOnK3Surface,ZZ,ZZ) := o -> (S,a,b) -> ( + if S.cache#?("map",a,b) then return S.cache#("map",a,b); + T := S#"SurfaceAssociatedToRationalFourfold"; + C := S#"specialCurve"; + if not(dim T =!= -1 and C =!= null and (not S#"isVirtual")) then error "invalid or virtual lattice polarization"; + H := random(1,0_T); + M := latticeMatrix S; + d := M_(0,1); + n := M_(1,1); + g := lift((M_(0,0) + 2)/2, ZZ); + g' := lift((a^2*(2*g-2) + 2*a*b*d + b^2*n + 2)/2,ZZ); + if g' <= 0 then error "invalid pair of integers: target of map would be empty or a point"; + phi := if a > 0 and b < 0 then rationalMap((-b)*(C % T), a) else mapDefinedByDivisor(T,{(H,a),(C,b)}); + if dim target phi =!= g' then error("expected map to PP^"|(toString g')|", but got map to PP^"|toString(dim target phi)); + S.cache#("map",a,b) = phi +); + +polarize (ZZ,LatticePolarizationOnK3Surface) := o -> (i,S) -> scanPolarizations(i,S); + +scanPolarizations = method(); +scanPolarizations (ZZ,Matrix) := (i,M) -> ( + if numColumns M != 2 or numRows M != 2 or ring M =!= ZZ then error "expected a 2x2 matrix over ZZ"; + g := lift((M_(0,0) + 2)/2, ZZ); + d := M_(0,1); + assert(d == M_(1,0)); + n := M_(1,1); + L := {}; + local g'; local d'; + for a from -i to i do ( + for b from -i to i do ( + g' = (a^2*(2*g-2) + 2*a*b*d + b^2*n + 2)/2; + d' = a*d + b*n; + if floor g' == g' and g' >= 3 and d' >= 1 and gcd(a,b) == 1 and (2*g'-2)*n - d'^2 != 0 then ( + g' = lift(g',ZZ); + L = prepend((g', d', n, a, b, (2*g'-2)*n - d'^2), L); + ); + ); + ); + sort L +); + +integerSols = p -> ( + if instance(p,ZZ) then ( + if p == 0 then return {null} else return {}; + ); + R := ring p; + if not (isPolynomialRing R and coefficientRing R === ZZ and numgens R == 1) then error "expected a univariate polynomial ring over ZZ"; + x := first gens R; + L := toList factor p; + solList := {}; + local f; local c0; local c1; + for i in L do ( + f = first i; + if first degree f == 1 and abs leadCoefficient f == 1 then ( + c0 = lift(sub(f, x => 0),ZZ); + c1 = leadCoefficient f; + solList = append(solList, -c0 // c1); + ); + ); + solList +); + +scanPolarizations (ZZ,LatticePolarizationOnK3Surface) := (i,T) -> ( + M := latticeMatrix T; + L := scanPolarizations(i,M); + c1 := {}; d1 := {}; c2 := {}; d2 := {}; c3 := {}; c4 := {}; c5 := {}; + for e in L do ( + c1 = append(c1, "(a,b) = " | toString(e_3,e_4)); + d1 = append(d1, " -> "); + c2 = append(c2, "g = "| toString e_0); + d2 = append(d2, " "); + c3 = append(c3, "H.C = " | toString e_1); + c4 = append(c4, "det = " | (toString(2*e_0-2) | "*" | (if e_2 < 0 then "(" | toString e_2 | ")" else toString e_2) | "-" | toString e_1 | "^2")); + c5 = append(c5, (" = " | toString e_5)); + ); + F := stack c1 | stack d1 | stack c2 | stack d2 | stack c3 | stack d2 | stack c4 | stack c5; + X := recoverFourfold T; + A := latticeIntersectionMatrix3x3 X; + D := det A; + local s; + ST := {}; + for e in L do ( + s = integerSols(e_5 + D); + if #s == 0 then ST = append(ST,"") else ( + if ring A === ZZ + then ST = append(ST, " [S.T = " | (toString A_(1,2)) | "]") + else ST = append(ST, " [S.T = " | (toString unsequence toSequence s) |"]"); + ); + ); + F | stack ST +); + +scanPolarizations LatticePolarizationOnK3Surface := T -> scanPolarizations(3,T); + +------------------------------------------------------------------------ +------------------------------------------------------------------------ + +K3SurfaceFromDoublySpecialCubicFourfold = new Type of MutableHashTable; +globalAssignment K3SurfaceFromDoublySpecialCubicFourfold; +K3SurfaceFromDoublySpecialCubicFourfold.synonym = "K3 surface"; + +net K3SurfaceFromDoublySpecialCubicFourfold := describe K3SurfaceFromDoublySpecialCubicFourfold := S -> ( + out := describe underlyingK3 S; + if S#"LatticePolarization" === null then return (out|| "Lattice polarization: not yet computed; use 'polarize' or 'polarizedK3surface'"); + M := latticeMatrix S; + if isVirtualLatticeK3 S and computationStatus S <= 3 then out = out||("Lattice intersection matrix (virtual, computed from U): "|(net M)); + out +); +texMath K3SurfaceFromDoublySpecialCubicFourfold := texMath @@ net; + +K3SurfaceFromDoublySpecialCubicFourfold#{WebApp,AfterPrint} = +K3SurfaceFromDoublySpecialCubicFourfold#{WebApp,AfterNoPrint} = +K3SurfaceFromDoublySpecialCubicFourfold#{Standard,AfterPrint} = +K3SurfaceFromDoublySpecialCubicFourfold#{Standard,AfterNoPrint} = S -> ( + << endl << concatenate(interpreterDepth:"o") << lineNumber << " : " << "Lattice-polarized K3 surface associated to " << (shortDescriptionFourfold recoverFourfold S) << " — " << computationStatusLog(S) << endl; +); + +underlyingK3 = method(); +underlyingK3 K3SurfaceFromDoublySpecialCubicFourfold := S -> S#"UnderlyingK3"; + +projectiveVariety K3SurfaceFromDoublySpecialCubicFourfold := o -> S -> ( + if dim underlyingK3 S == -1 then << "-- warning: underlying K3 surface not fully computed (incomplete data)" << endl; + underlyingK3 S +); + +building K3SurfaceFromDoublySpecialCubicFourfold := S -> building underlyingK3 S; +recoverFourfold K3SurfaceFromDoublySpecialCubicFourfold := S -> recoverFourfold underlyingK3 S; +getInverseFanoMap K3SurfaceFromDoublySpecialCubicFourfold := Utilde -> getInverseFanoMap underlyingK3 Utilde; + +latticePolarization = method(); +latticePolarization K3SurfaceFromDoublySpecialCubicFourfold := S -> ( + if S#"LatticePolarization" === null then error "lattice polarization not yet computed; use 'polarize' or 'polarizedK3surface'"; + S#"LatticePolarization" +); + +latticeMatrix K3SurfaceFromDoublySpecialCubicFourfold := S -> latticeMatrix latticePolarization S; +K3SurfaceFromDoublySpecialCubicFourfold Sequence := (S,ab) -> (latticePolarization S) ab; +map(K3SurfaceFromDoublySpecialCubicFourfold,ZZ,ZZ) := o -> (S,a,b) -> map(latticePolarization S,a,b); + +isVirtualLatticeK3 = method(); +isVirtualLatticeK3 K3SurfaceFromDoublySpecialCubicFourfold := S -> S#"LatticePolarization" =!= null and (S#"LatticePolarization")#"isVirtual"; + +polarizedK3surface = method(TypicalValue => K3SurfaceFromDoublySpecialCubicFourfold, Options => {Verbose => false, Strategy => null, FanoMapType => null}); +polarizedK3surface DoublySpecialCubicFourfold := o -> X -> ( + if not instance(o.Verbose,Boolean) then error "expected a Boolean value for option 'Verbose'"; + local StrK3; local StrPol; local S; + StrK3Set := {"Inverse","Approximate"}; + StrPolSet := {null, "SpecialCurve", "MapFromW", "MapFromU", "MapFromW-Virtual", "MapFromU-Virtual"}; + if member(o.Strategy,StrPolSet) + then (StrK3,StrPol) = (null,o.Strategy) + else if member(o.Strategy,StrK3Set) + then (StrK3,StrPol) = (o.Strategy,null) + else if instance(o.Strategy,VisibleList) and # o.Strategy == 2 and member(first o.Strategy, StrK3Set) and member(last o.Strategy, StrPolSet) + then (StrK3,StrPol) = toSequence o.Strategy + else error("polarizedK3surface: invalid Strategy; expected one of {\"SpecialCurve\", \"MapFromW\", \"MapFromU\", \"MapFromW-Virtual\", \"MapFromU-Virtual\"}, or {\"Inverse\", \"Approximate\"}, or a pair of these"); + if not member(o.FanoMapType,{null,"Standard","P2xP2"}) then error("polarizedK3surface: invalid FanoMapType '" | toString(o.FanoMapType) | "'; expected one of {\"Standard\", \"P2xP2\"}"); + mu := getCachedFanoMapIfCompatible(X,o.FanoMapType); + if mu === null or (not mu.cache#?("K3SurfaceFromDoublySpecialCubicFourfold",X)) then ( + U := associatedUnderlyingK3Raw(X, Verbose=>o.Verbose, Strategy=>StrK3, FanoMapType=>o.FanoMapType); + assert(mu === null or mu === first building U); + mu = first building U; + if mu.cache#?("K3SurfaceFromDoublySpecialCubicFourfold",X) then ( + S = mu.cache#("K3SurfaceFromDoublySpecialCubicFourfold",X); + ) else ( + S = new K3SurfaceFromDoublySpecialCubicFourfold from { + symbol cache => new CacheTable, + "UnderlyingK3" => U, + "LatticePolarization" => null + }; + mu.cache#("K3SurfaceFromDoublySpecialCubicFourfold",X) = S; + ); + S.cache#"userStrategyPolarization" = StrPol; + return S; + ); + S = mu.cache#("K3SurfaceFromDoublySpecialCubicFourfold",X); + if StrPol === null then ( + if S#"LatticePolarization" =!= null then return S; + if S.cache#?"userStrategyPolarization" then StrPol = S.cache#"userStrategyPolarization"; + ); + StrPol = setStrategyDSCFtoPolarize(S,StrPol); + if not S.cache#?("polarization",StrPol) then S.cache#("polarization",StrPol) = associatedLatticePolarizationRaw(underlyingK3 S,Verbose=>o.Verbose,Strategy=>StrPol); + T := S.cache#("polarization",StrPol); + S#"UnderlyingK3" = T#"SurfaceAssociatedToRationalFourfold"; + S#"LatticePolarization" = T; + return S; +); + +polarizedK3surface K3SurfaceFromDoublySpecialCubicFourfold := o -> S -> polarizedK3surface(recoverFourfold S,Verbose=>o.Verbose,Strategy=>o.Strategy,FanoMapType=>o.FanoMapType); +polarizedK3surface SurfaceAssociatedToRationalFourfold := o -> S -> ( + X := recoverFourfold S; + if not instance(X,DoublySpecialCubicFourfold) then error "K3 surface is expected to be associated to a doubly special cubic fourfold"; + polarizedK3surface(recoverFourfold S,Verbose=>o.Verbose,Strategy=>o.Strategy,FanoMapType=>o.FanoMapType) +); + +polarize K3SurfaceFromDoublySpecialCubicFourfold := o -> S -> polarizedK3surface S; + +associatedK3surface DoublySpecialCubicFourfold := o -> X -> polarizedK3surface(X,Verbose=>o.Verbose,Strategy=>o.Strategy); + +------------------------------------------------------------------------ +------------------ Associated polarized K3 (raw data) ------------------ +------------------------------------------------------------------------ + +associatedLatticePolarizationRaw = method(TypicalValue => LatticePolarizationOnK3Surface, Options => {Verbose => true, Strategy => null}); +associatedLatticePolarizationRaw SurfaceAssociatedToRationalFourfold := o -> Utilde -> ( + if o.Verbose then ( + tPolStart := currentTime(); + tPolStartCPU := cpuTime(); + printFinalLog := () -> ( + tPolEnd := currentTime() - tPolStart; + tPolEndCPU := cpuTime() - tPolStartCPU; + << " ✦ polarization successfully completed in " << humanReadableSeconds(tPolEnd) << " (cpu: " << humanReadableSeconds(floor tPolEndCPU) << ")" << endl; + if Utilde.cache#?"computationTime" then ( + << "-- total time (K3 surface + polarization): " << humanReadableSeconds(first Utilde.cache#"computationTime" + tPolEnd) << " (cpu: " << humanReadableSeconds floor(last Utilde.cache#"computationTime" + tPolEndCPU) << ")" << endl; + ); + ); + << "-- starting polarization computation" << endl; + << "-- settings: Verbose => " << o.Verbose << ", Strategy => " << (if instance(o.Strategy,String) then "\"" | o.Strategy | "\"" else toString(o.Strategy)) << endl; + << "-- available strategies: \"SpecialCurve\", \"MapFromW\", \"MapFromU\", \"MapFromW-Virtual\", \"MapFromU-Virtual\"" << endl; + ); + if member(o.Strategy,{"MapFromW-Virtual","MapFromU-Virtual"}) then return virtualAssociatedLatticePolarizationRaw(Utilde,Verbose=>o.Verbose,Strategy=>o.Strategy); + Utilde = buildAssociatedSurfaceFromPartialData(Utilde,Verbose=>o.Verbose); + (mu,U,exC,f) := building Utilde; + X := recoverFourfold Utilde; + if f === null then error "incomplete K3 data (failed to obtain contraction map)"; + if not isStandardK3surface Utilde then error "expected a standard K3 surface (invariant mismatch)"; + StrPol := setStrategyDSCFtoPolarize(Utilde,o.Strategy); + specialCurveK3 := null; + if StrPol === "SpecialCurve" then specialCurveK3 = specialCurveOnK3FromCurvesOnU(Utilde,o.Verbose,isHigherDegreeCurveInExceptionalSetKnownToBeSpecial X); + if specialCurveK3 === null then ( + if isFanoMapStandard(X) and (not U.cache#?"birational maps from X to W and from W to X") then ( + if not U.cache#?"strategy for surface U" then error "surface U does not appear as computed using the standard polarization methods"; + if U.cache#"strategy for surface U" === "Approximate" then error("the K3 surface was computed with Strategy => \"Approximate\", which is incompatible with this specific lattice polarization case. Please clear the cache and recompute using Strategy => \"Inverse\". Example: X' = clean X; polarizedK3surface(X', Strategy => \"Inverse\")"); + ); + if StrPol === "MapFromU" or StrPol === "SpecialCurve" then ( + specialCurveK3 = specialCurveOnK3viaMapFromU(Utilde,o.Verbose) + ) else ( + if StrPol === "MapFromW" then ( + specialCurveK3 = specialCurveOnK3viaMapFromW(Utilde,o.Verbose); + ) else ( + error "internal error: unhandled polarization strategy"; + ); + ); + ); + if o.Verbose then << "-- constructing lattice polarization..." << endl; + T := latticePolarizationOnK3Surface(Utilde,specialCurveK3,Verbose=>o.Verbose,Verify=>not(isSelfIntersectionVerificationKnownToBeSuperfluous X)); + if o.Verbose then printFinalLog(); + T +); + +specialCurveOnK3FromCurvesOnU = (Utilde,PolarizeVerbosity,includeCurveC) -> ( + (mu,U,exC,f) := building Utilde; + (L,C) := toSequence exC; + if PolarizeVerbosity then << "-- special curves already detected on U" << endl << flush; + spC := if U.cache#?"special curves on U" + then select(U.cache#"special curves on U", spC0 -> not (isSubset(spC0,L) or isSubset(spC0,C))) + else {}; + if includeCurveC then spC = prepend(C,spC); + if # spC == 0 then ( + -- error "curves detected on U are exceptional"; + if PolarizeVerbosity then << "-- curves detected on U are exceptional; reverting to standard strategy" << endl; + return null; + ); + D := null; i := 0; + for E in spC when D === null do ( + i = i + 1; + if PolarizeVerbosity then << " -- pushing forward curve to K3 (" << i << "/" << #spC << ")..." << endl; + D = f E; + if dim D != 1 then ( + D = null; + if PolarizeVerbosity then << " -- image is not a curve, skipping..." << endl; + ); + ); + if D === null then ( + -- error "surface polarization calculation failed: no image on K3 is a curve"; + if PolarizeVerbosity then << "-- no image on K3 is a curve; reverting to standard strategy" << endl; + ) else ( + if PolarizeVerbosity then << " -- image curve: " << ? D << endl << flush; + ); + D +); + +specialCurveOnK3viaMapFromU = (Utilde,PolarizeVerbosity) -> ( + f := last building Utilde; + psi := mapFromUtoP2xP2(Utilde,Verbose=>PolarizeVerbosity); + (psi1,psi2) := toSequence projectionMaps psi; + if PolarizeVerbosity then << "-- obtained the two maps p1, p2: U --> PP^2" << endl; + if PolarizeVerbosity then << "-- computing p1^*(H_PP^2)" << endl << flush; + E1 := psi1^* random(1,0_(target psi1)); + if dim E1 != 1 then error "surface polarization calculation failed, expected dimension 1 for p1^*(H_PP^2)"; + sE1 := interpolateTop(E1,Verbose=>verbosityInterpolateTop(PolarizeVerbosity)); + if PolarizeVerbosity and sE1 != E1 then << " -- unexpected non-pure dimensional components were found" << endl; + if PolarizeVerbosity then << " -- obtained the first curve on U: " << ? sE1 << endl << " -- computing image on K3 surface..." << endl; + sE1onK3 := f sE1; + if dim sE1onK3 != 1 then error "surface polarization calculation failed: image on K3 is not a curve"; + if PolarizeVerbosity and degree sE1onK3 != degree Utilde then << " -- unexpected degree for f(p1^*(H_PP^2)): " << degree sE1onK3 << " (expected " << degree Utilde << ")" << endl; + if PolarizeVerbosity then << " -- image curve: " << ? sE1onK3 << endl; + if PolarizeVerbosity then << "-- computing p2^*(H_PP^2)" << endl << flush; + E2 := psi2^* random(1,0_(target psi2)); + if dim E2 != 1 then error "surface polarization calculation failed, expected dimension 1 for p2^*(H_PP^2)"; + sE2 := interpolateTop(E2,Verbose=>verbosityInterpolateTop(PolarizeVerbosity)); + if PolarizeVerbosity and sE2 != E2 then << " -- unexpected non-pure dimensional components were found" << endl; + if PolarizeVerbosity then << " -- obtained the second curve on U: " << ? sE2 << endl << " -- computing image on K3 surface..." << endl; + sE2onK3 := f sE2; + if dim sE2onK3 != 1 then error "surface polarization calculation failed: image on K3 is not a curve"; + if PolarizeVerbosity then << " -- image curve: " << ? sE2onK3 << endl << flush; + sE2onK3 +); + +specialCurveOnK3viaMapFromW = (Utilde,PolarizeVerbosity) -> ( + (mu,U,exC,f) := building Utilde; + psi := mapFromWtoP2xP2(Utilde,Verbose=>PolarizeVerbosity); + (psi1,psi2) := toSequence projectionMaps psi; + W := target mu; + -- -- assert(source psi === W); -- bug? + -- assert(source psi == W); + if PolarizeVerbosity then << "-- obtained the two maps p1, p2: W --> PP^2" << endl; + if PolarizeVerbosity then << "-- computing p1^*(H_PP^2)" << endl << flush; + E1 := psi1^* random(1,0_(target psi1)); + if dim E1 != 3 then error "surface polarization calculation failed, expected dimension 3 for p1^*(H_PP^2)"; + if PolarizeVerbosity and degree E1 != degree W then << " -- unexpected degree for p1^*(H_PP^2): " << degree E1 << " (expected " << degree W << ")" << endl; + if PolarizeVerbosity then << "-- computing p2^*(H_PP^2)" << endl << flush; + E2 := psi2^* random(1,0_(target psi2)); + if dim E2 != 3 then error "surface polarization calculation failed, expected dimension 3 for p2^*(H_PP^2)"; + E2U := E2 * U; + if dim E2U != 1 then error "surface polarization calculation failed, expected dimension 1 for p2^*(H_PP^2) * U"; + sE2 := interpolateTop(E2U,Verbose=>verbosityInterpolateTop(PolarizeVerbosity)); + if PolarizeVerbosity and sE2 != E2U then << " -- unexpected non-pure dimensional components were found" << endl; + if PolarizeVerbosity then << " -- obtained the curve on U: " << ? sE2 << endl << " -- computing image on K3 surface..." << endl; + sE2onK3 := f sE2; + if dim sE2onK3 != 1 then error "surface polarization calculation failed: image on K3 is not a curve"; + if PolarizeVerbosity then << " -- image curve: " << ? sE2onK3 << endl << flush; + sE2onK3 +); + +mapFromWtoP2xP2 = method(Options => {Verbose => true}); +mapFromWtoP2xP2 DoublySpecialCubicFourfold := o -> X -> ( + local mu; + if isFanoMapToP2xP2 X then ( + mu = fanoMapDSCF(X,Verbose=>o.Verbose); + s := last mu.cache#"FactorsViaSegreEmbedding"; + if s#"inverse" =!= null then return inverse s; + if o.Verbose then << "-- computing inverse of Segre embedding PP^2 x PP^2 -> W ⊂ PP^8" << endl; + return inverse s; + ); + eta := getInverseFanoMap X; + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>o.Verbose); + if U.cache#?"map from W to P2xP2" then return U.cache#"map from W to P2xP2"; + mu = fanoMapDSCFtoP2xP2(X,Verbose=>o.Verbose); + psi := first mu.cache#"FactorsViaSegreEmbedding"; + if o.Verbose then << "-- composing maps W --> X --> PP^2 x PP^2" << endl; + U.cache#"map from W to P2xP2" = eta * psi +); +mapFromWtoP2xP2 SurfaceAssociatedToRationalFourfold := o -> Utilde -> ( + X := recoverFourfold Utilde; + mapFromWtoP2xP2(X,Verbose=>o.Verbose) +); +mapFromWtoP2xP2 K3SurfaceFromDoublySpecialCubicFourfold := o -> S -> mapFromWtoP2xP2(underlyingK3 S,Verbose=>o.Verbose); + +mapFromUtoP2xP2 = method(Options => {Verbose => true}); +mapFromUtoP2xP2 DoublySpecialCubicFourfold := o -> X -> ( + fromWtoP2P2 := mapFromWtoP2xP2(X,Verbose=>o.Verbose); + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>o.Verbose); + if U.cache#?"map from U to P2xP2" then return U.cache#"map from U to P2xP2"; + (F,G) := toSequence projectionMaps fromWtoP2P2; + if o.Verbose then << "-- obtained the two maps W --> PP^2, restricting to U via suitable representatives" << endl; + local M; local B; + if o.Verbose then << "-- working on the representatives of the first map" << endl << flush; + f := toRationalMap F; + F' := null; + if not isSubset(U,projectiveVariety matrix f) then ( + F' = F; + ) else ( + degsf := degreeSequence f; -- this computes f#"maps" + assert(f#"maps" =!= null); + if o.Verbose then << " -- degree sequence: " << degsf << endl; + for h in f#"maps" when F' === null do ( + M = matrix rationalMap h; + B = projectiveVariety M; + if not isSubset(U,B) then F' = (Hom(source F, target F)) {M}; + ); + if F' === null then error "not possible to restrict first map W --> PP^2 to surface U"; + -- provisional test -- + assert(F == F'); + ); + F'' := F'|U; + if o.Verbose then << "-- working on the representatives of the second map" << endl << flush; + g := toRationalMap G; + G' := null; + if not isSubset(U,projectiveVariety matrix g) then ( + G' = G; + ) else ( + degsg := degreeSequence g; -- this computes g#"maps" + assert(g#"maps" =!= null); + if o.Verbose then << " -- degree sequence: " << degsg << endl; + for h in g#"maps" when G' === null do ( + M = matrix rationalMap h; + B = projectiveVariety M; + if not isSubset(U,B) then G' = (Hom(source G, target G)) {M}; + ); + if G' === null then error "not possible to restrict second map W --> PP^2 to surface U"; + -- provisional test -- + assert(G == G'); + ); + G'' := G'|U; + phi := (Hom(U, target fromWtoP2P2)) ((entries F'')|(entries G'')); + if o.Verbose then ( + << "-- map U --> PP^2 x PP^2 stored in cache: retrieve it with importFrom(SpecialFanoFourfolds, \"mapFromUtoP2xP2\")" << endl << flush; + -- provisional log -- + << " -- map degree: " << degree(phi,Strategy=>"random point") << endl << flush; + ); + return U.cache#"map from U to P2xP2" = phi; +); +mapFromUtoP2xP2 SurfaceAssociatedToRationalFourfold := o -> Utilde -> ( + X := recoverFourfold Utilde; + mapFromUtoP2xP2(X,Verbose=>o.Verbose) +); +mapFromUtoP2xP2 K3SurfaceFromDoublySpecialCubicFourfold := o -> S -> mapFromUtoP2xP2(underlyingK3 S,Verbose=>o.Verbose); + +virtualAssociatedLatticePolarizationRaw = method(Options => {Verbose => true, Strategy => "MapFromU-Virtual"}); +virtualAssociatedLatticePolarizationRaw SurfaceAssociatedToRationalFourfold := o -> Utilde -> ( + if o.Verbose then ( + tPolStart := currentTime(); + tPolStartCPU := cpuTime(); + printFinalLog := withoutWarning -> ( + tPolEnd := currentTime() - tPolStart; + tPolEndCPU := cpuTime() - tPolStartCPU; + if withoutWarning then ( + << " ✧ virtual polarization completed in " << humanReadableSeconds(tPolEnd) << " (cpu: " << humanReadableSeconds(floor tPolEndCPU) << ")" << endl; + ) else ( + << " ⟠virtual polarization completed with warnings in " << humanReadableSeconds(tPolEnd) << " (cpu: " << humanReadableSeconds(floor tPolEndCPU) << ")" << endl; + ); + if Utilde.cache#?"computationTime" then ( + << "-- total time (K3 surface + virtual polarization): " << humanReadableSeconds(first Utilde.cache#"computationTime" + tPolEnd) << " (cpu: " << humanReadableSeconds floor(last Utilde.cache#"computationTime" + tPolEndCPU) << ")" << endl; + ); + ); + ); + U := (building Utilde)_1; + local psi; + if o.Strategy === "MapFromU-Virtual" then ( + psi = mapFromUtoP2xP2(Utilde,Verbose=>o.Verbose); + if o.Verbose then << "-- obtained map p1xp2: U --> PP^2xPP^2" << endl; + ) else if o.Strategy === "MapFromW-Virtual" then ( + psi = mapFromWtoP2xP2(Utilde,Verbose=>o.Verbose); + if o.Verbose then << "-- obtained map p1xp2: W --> PP^2xPP^2" << endl; + ) else error("strategy \"" | (toString o.Strategy) | "\" not available; expected: \"MapFromW-Virtual\" or \"MapFromU-Virtual\""); + (psi1,psi2) := toSequence projectionMaps psi; + if o.Verbose then << "-- computing p1^*(H_PP^2)" << endl << flush; + E1 := psi1^* random(1,0_(target psi1)); + if o.Strategy === "MapFromW-Virtual" then E1 = E1 * U; + if dim E1 != 1 then error "surface polarization calculation failed, expected dimension 1 for p1^*(H_PP^2)"; + sE1 := interpolateTop(E1,Verbose=>verbosityInterpolateTop(o.Verbose)); + if o.Verbose and sE1 != E1 then << " -- unexpected non-pure dimensional components were found" << endl; + if o.Verbose then << " -- obtained p1^*(H_PP^2): " << ? sE1 << endl; + if o.Verbose then << "-- computing another p1^*(H_PP^2)" << endl << flush; + E2:= psi1^* random(1,0_(target psi1)); + if o.Strategy === "MapFromW-Virtual" then E2 = E2 * U; + if dim E2 != 1 then error "surface polarization calculation failed, expected dimension 1 for p1^*(H_PP^2)"; + sE2 := interpolateTop(E2,Verbose=>verbosityInterpolateTop(o.Verbose)); + if o.Verbose and sE2 != E2 then << " -- unexpected non-pure dimensional components were found" << endl; + if o.Verbose then << " -- second p1^*(H_PP^2) obtained: " << ? sE2 << endl; + sE1sE2 := sE1*sE2; + if dim sE1sE2 > 0 then ( + if o.Verbose then <<" -- dim((p1^*(H)) * p1^*(H')) > 0, removing fixed components" << endl; + sE1sE2 = sE1sE2\\interpolateTop(sE1sE2,Verbose=>verbosityInterpolateTop(o.Verbose)); + ); + if dim sE1sE2 != 0 then error "(residual) intersection (p1^*(H)) * p1^*(H')) has dimension != 0"; + sE1sE2 = degree sE1sE2; + withoutWarning := true; + if 2*(sectionalGenus U) - 2 != sE1sE2 then ( + << "-- WARNING: [virtual polarization] degree mismatch! Expected " << (2*(sectionalGenus U) - 2) << " but got " << sE1sE2 << endl; + withoutWarning = false; + ); + if o.Verbose then << "-- computing p2^*(H_PP^2)" << endl << flush; + C1 := psi2^* random(1,0_(target psi2)); + if o.Strategy === "MapFromW-Virtual" then C1 = C1 * U; + if dim C1 != 1 then error "surface polarization calculation failed, expected dimension 1 for p2^*(H_PP^2)"; + sC1 := interpolateTop(C1,Verbose=>verbosityInterpolateTop(o.Verbose)); + if o.Verbose and sC1 != C1 then << " -- unexpected non-pure dimensional components were found" << endl; + if o.Verbose then << " -- obtained p2^*(H_PP^2): " << ? sC1 << endl; + sE1sC1 := sE1*sC1; + if dim sE1sC1 > 0 then ( + if o.Verbose then <<" -- dim((p1^*(H)) * p2^*(H)) > 0, removing fixed components" << endl; + sE1sC1 = sE1sC1\\interpolateTop(sE1sC1,Verbose=>verbosityInterpolateTop(o.Verbose)); + ); + if dim sE1sC1 != 0 then error "(residual) intersection (p1^*(H)) * p2^*(H)) has dimension != 0"; + sE1sC1 = degree sE1sC1; + if o.Verbose then << "-- computing another p2^*(H_PP^2)" << endl << flush; + C2:= psi2^* random(1,0_(target psi2)); + if o.Strategy === "MapFromW-Virtual" then C2 = C2 * U; + if dim C2 != 1 then error "surface polarization calculation failed, expected dimension 1 for p2^*(H_PP^2)"; + sC2 := interpolateTop(C2,Verbose=>verbosityInterpolateTop(o.Verbose)); + if o.Verbose and sC2 != C2 then << " -- unexpected non-pure dimensional components were found" << endl; + if o.Verbose then << " -- second p2^*(H_PP^2) obtained: " << ? sC2 << endl; + sC1sC2 := sC1*sC2; + if dim sC1sC2 > 0 then ( + if o.Verbose then <<" -- dim((p2^*(H)) * p2^*(H')) > 0, removing fixed components" << endl; + sC1sC2 = sC1sC2\\interpolateTop(sC1sC2,Verbose=>verbosityInterpolateTop(o.Verbose)); + ); + if dim sC1sC2 != 0 then error "(residual) intersection (p2^*(H)) * p2^*(H')) has dimension != 0"; + sC1sC2 = degree sC1sC2; + if o.Verbose then << flush; + virtK3 := latticePolarizationOnK3Surface(Utilde,sE1sE2,sE1sC1,sC1sC2); + if o.Verbose then printFinalLog withoutWarning; + virtK3 +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/contractionMaps.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/contractionMaps.m2 new file mode 100644 index 00000000000..cc2e244d41e --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/contractionMaps.m2 @@ -0,0 +1,379 @@ + +------------------------------------------------------------------------ +-------------- Exceptional curves on U and contraction maps ------------ +------------------------------------------------------------------------ + +exceptionalCurves = method(Options => {Verbose => false, Strategy => null, "NumberLines" => infinity}); + +exceptionalCurves HodgeSpecialFourfold := o -> X -> ( + NumLines := o#"NumberLines"; + if NumLines =!= infinity then if not(instance(NumLines,ZZ) and NumLines >= 0) then error "option NumberLines expects a nonnegative integer"; + if NumLines === infinity then ( + if member(recognize X,{"planeInPP7", "quarticScrollSurface", "oneNodalSepticDelPezzoSurfaceC26", "FarkasVerra", 3, "6NodalOcticSrollC38", 18, "gushel26''", "surf-5-6-2-nodal", "surf-4-3-1-external", "surf-7-1-9"}) then NumLines = 0; + if member(recognize X,{"surf-5-7-0-1", 17, 6, "mukai26''", "hyperplane section of a conic bundle over PP2"}) then NumLines = 1; + if recognize X === 1 then NumLines = 2; + if recognize X === "october2021-34'" then NumLines = 3; + if recognize X === "october2021-26''" then NumLines = 4; + if member(recognize X,{"quinticDelPezzoSurface", "C42", "october2021-20"}) then NumLines = 5; + if recognize X === "C38Coble" then NumLines = 10; + ); + a := degreeHypersurface X; + if o.Verbose then << "-- computing the Fano map μ from " << (if codim ambientFivefold X > 0 then "the fivefold in PP^"|toString(dim ambient X) else "PP^5") << endl; + mu := fanoMap X; + e := (map mu).cache#"multiplicityFanoMap"; + if o.Verbose then << "-- computed the map μ from " << (if codim ambientFivefold X > 0 then "the fivefold in PP^"|toString(dim ambient X) else "PP^5") << " to PP^" <<(numgens ambient target mu -1) << " defined by the hypersurfaces" << endl; + if o.Verbose then << "-- of degree " << a*e-1 << " with points of multiplicity " << e << " along the surface S of degree " << degree surface X << " and genus " << sectionalGenus surface X << endl << flush; + if o.Verbose then << endl << "-- computing the surface U corresponding to the fourfold X" << endl; + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>o.Verbose,Strategy=>o.Strategy); + if U.cache#?"exceptionalCurves" then return U.cache#"exceptionalCurves"; + if o.Verbose then << endl << "-- computing the surface U' corresponding to another fourfold X'" << endl; + X' := random X; + U' := surfaceDeterminingInverseOfFanoMap(X',Verbose=>o.Verbose,Strategy=>o.Strategy); + if dim(U*U') <= 0 then ( + if o.Verbose then ( + << "-- U ∩ U' contains no (exceptional) curves" << endl; + ); + return U.cache#"exceptionalCurves" = ((0_U)%U,(0_U)%U); + ); + if dim(U*U') > 1 then ( + if o.Verbose then ( + if not isStandardK3surface U then ( + << "-- warning: dim(U ∩ U') = 2: 'associatedK3surface' may not complete as expected." << endl; + ) else ( + << "-- obtained dim(U ∩ U') = 2" << endl; + ); + ); + return U.cache#"exceptionalCurves" = ((0_U)%U,(0_U)%U); + ); + if o.Verbose then << endl; + local LL; local L; + if instance(NumLines,ZZ) and NumLines > 0 and member(recognize X,{"quinticDelPezzoSurface", 1}) then ( + if o.Verbose then << "-- computing the " << NumLines << " exceptional lines as the top components of U ∩ U'" << endl; + L = interpolateTop(U*U',if recognize X === 1 then {2} else {3},Verbose=>o.Verbose,"Deep"=>2); + if degree(U*U') =!= degree(L) then error "something went wrong"; + ) else if instance(NumLines,ZZ) and NumLines == 1 and recognize X === "mukai26''" then ( + if o.Verbose then << "-- computing the " << NumLines << " exceptional line" << " as one of the top components of U ∩ U'" << endl; + L = first select(decompose interpolateTop((U*U'),{2},Verbose=>o.Verbose,"Deep"=>2),Cu -> dim Cu == 1 and degree Cu == 1); + ) else if instance(NumLines,ZZ) and NumLines > 0 then ( + if o.Verbose then << "-- computing the " << NumLines << " exceptional line(s) via the Fano scheme of lines" << endl; + LL = Fano(1,U*U'); + while not(dim LL == 0 and degree LL == NumLines) do (if o.Verbose then << "-- recomputing Fano scheme of lines" << endl; LL = Fano(1,U*U')); + L = Fano LL; + while not(dim L == 1 and degree L == NumLines) do (if o.Verbose then << "-- recomputing variety swept out by lines" << endl; L = Fano LL); + ) else if NumLines === infinity then ( + if o.Verbose then << "-- computing the exceptional lines via the Fano scheme of lines" << endl; + LL = Fano(1,U*U'); + L = if dim LL >= 0 then Fano LL else 0_U; + ) else L = 0_U; + if not (isSubset(L,U) and isSubset(L,U')) then error "containment of exceptional lines in U and U' failed"; + if degree(U*U') == degree(L) then ( + if o.Verbose then << "-- exceptional curves computed: obtained " << NumLines << " line(s)" << endl; + return U.cache#"exceptionalCurves" = (L%U,(0_U)%U); + ); + if degree((U*U')\L) != degree (U*U') - degree L then error "decomposition failed: multiplicity detected in exceptional lines (not currently supported)"; + if o.Verbose then << "-- computing the top components of (U ∩ U')\\{exceptional lines} via interpolation" << endl; + local C; + if member(recognize X,{"quarticScrollSurface", "oneNodalSepticDelPezzoSurfaceC26", "FarkasVerra", "C38Coble", "C42", 17, "october2021-26''", "october2021-34'", "6NodalOcticSrollC38", 18, " mukai26''"}) + then C = interpolateTop((U*U')\L,{2},Verbose=>o.Verbose,"Deep"=>2) + else C = interpolateTop(2,(U*U')\L,Verbose=>o.Verbose,"Deep"=>3); + U.cache#"exceptionalCurves" = (L%U,C%U) +); + +exceptionalCurves DoublySpecialCubicFourfold := o -> X -> ( + fanoMapDSCF(X,Verbose=>o.Verbose); + if o.Verbose then << "-- obtaining surface U corresponding to fourfold X..." << endl; + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>o.Verbose,Strategy=>o.Strategy); + if U.cache#?"exceptionalCurves" then return U.cache#"exceptionalCurves"; + if o.Verbose then << endl << "-- obtaining surface U' corresponding to another fourfold X'..." << endl; + X' := random X; + if ideal X' == ideal X then ( + if o.Verbose then << "-- obtained X == X'" << endl; + if U.cache#?"special curves on U" then ( + -- Used in case "DSCF-V1-27"; consider generalizing this approach for other cases. + SpecialLines := select(U.cache#"special curves on U", D -> degree D == 1); + SpecialLines = if #SpecialLines == 0 then (0_U)%U else (sum SpecialLines)%U; + OtherCurves := select(U.cache#"special curves on U", D -> degree D > 1); + OtherCurves = if #OtherCurves == 0 then (0_U)%U else (sum OtherCurves)%U; + return U.cache#"exceptionalCurves" = (SpecialLines,OtherCurves); + ); + return U.cache#"exceptionalCurves" = ((0_U)%U,(0_U)%U); + ); + U' := surfaceDeterminingInverseOfFanoMap(X',Verbose=>o.Verbose,Strategy=>o.Strategy); + if dim(U*U') <= 0 then ( + if o.Verbose then ( + << "-- U ∩ U' contains no (exceptional) curves" << endl; + ); + return U.cache#"exceptionalCurves" = ((0_U)%U,(0_U)%U); + ); + if dim(U*U') > 1 then ( + if o.Verbose then ( + if not isStandardK3surface U then ( + << "-- warning: dim(U ∩ U') = 2: 'polarizedK3surface' may not complete as expected." << endl; + ) else ( + << "-- obtained dim(U ∩ U') = 2" << endl; + ); + ); + return U.cache#"exceptionalCurves" = ((0_U)%U,(0_U)%U); + ); + if o.Verbose then << endl << "-- extracting the 1-dimensional part of the intersection U ∩ U'..." << endl; + E := interpolateTop(U * U',Verbose=>verbosityInterpolateTop(o.Verbose),cache=>true); + if o.Verbose then << "-- the result has dimension 1 and degree " << degree E << endl; + assert(degree E >= 1); + if o.Verbose then << "-- decomposing U ∩ U' via projection to PP^2..." << endl; + prToP2 := (rationalMap apply(3, i -> random(1, ring ambient E))) | E; + Z := projectiveVariety kernel(map toRationalMap prToP2,SubringLimit=>1); + if o.Verbose and degree E != degree Z then << "-- degree mismatch in projection to PP^2 (expected " << degree E << ", obtained " << degree Z << ")" << endl; + Z1 := {}; Z2 := {}; + for D in decompose Z do if dim D == 1 and degree D == 1 then Z1 = append(Z1,D) else Z2 = append(Z2,D); + if o.Verbose and degree E == degree Z then ( + if #Z2 == 0 and #Z1 == degree Z then ( + << "-- decomposition: only K-rational lines" << endl; + ); + if degree Z > sum(Z1,degree) + sum(Z2,degree) then << " -- multiplicity > 1 detected" << endl; + ); + if #Z1 == 0 then ( + if o.Verbose then << "-- no K-rational lines found in U ∩ U'" << endl; + if degree E == degree Z and degree Z > sum(Z2,degree) and #Z2 == 1 then E = support E; + return U.cache#"exceptionalCurves" = ((0_U)%U,E%U); + ); + if o.Verbose then << "-- computing inverse images of plane lines..." << endl; + LinesInE := select(apply(Z1, l -> prToP2^* l), D -> dim D >= 1); + LinesInE = apply(LinesInE, D -> interpolateTop(D,Verbose=>verbosityInterpolateTop(o.Verbose),cache=>true)); + if someExceptionalCurvesKnownToAppearWithMultiplicity X then LinesInE = apply(LinesInE,support); + assert all(LinesInE, l -> dim l == 1 and degree l == 1); + if o.Verbose then ( + << "-- number of lines found: " << #LinesInE << endl; + if degree E == degree Z and #LinesInE < #Z1 then << " -- some plane lines have inverse images of dimension < 1 (ignored)" << endl; + ); + L := sum LinesInE; + L.cache#"Decomposition" = LinesInE; + if degree E == degree L then ( + return U.cache#"exceptionalCurves" = (L%U,(0_U)%U); + ); + C := E \ L; + if degree C != degree E - degree L then error "decomposition failed: multiplicity detected in exceptional lines (not currently supported)"; + if someExceptionalCurvesKnownToAppearWithMultiplicity X then C = C \\ L; + U.cache#"exceptionalCurves" = (L%U,C%U) +); + +mapFromExceptionalCurves = method(Options => {Verbose => true, Strategy => null, "Normalization" => false, "ForceExperimentalNormalization" => false, "TargetExpDim" => null, "IsK3Type" => true}); + +mapFromExceptionalCurves EmbeddedProjectiveVariety := o -> U -> ( + if not U.cache#?"exceptionalCurves" then error "exceptional curves not found in cache: method exceptionalCurves() must be called first"; + (L,C) := U.cache#"exceptionalCurves"; + b := null; + if dim C == -1 then b = 0; + if dim C == 1 and isPresumedRationalNormalCurve C then b = degree C; + if b === null then ( + if o.Verbose then << "-- warning: multiple exceptional curves of degree > 1 detected; not yet supported" << endl; + return; + ); + mapFromExceptionalCurves(U,(1,1,b),Verbose=>o.Verbose,Strategy=>o.Strategy,"Normalization"=>o#"Normalization","ForceExperimentalNormalization"=>o#"ForceExperimentalNormalization","TargetExpDim"=>o#"TargetExpDim","IsK3Type"=>o#"IsK3Type") +); + +mapFromExceptionalCurves (EmbeddedProjectiveVariety,Sequence) := o -> (U,dab) -> ( + if not U.cache#?"exceptionalCurves" then error "exceptional curves not found in cache: method exceptionalCurves() must be called first"; + (L,C) := U.cache#"exceptionalCurves"; + (d,a,b) := dab; + withNorm := o#"Normalization"; + (eulerExpVal,strSurf) := if o#"IsK3Type" then (2,"K3") else (4,"Castelnuovo"); + if (not withNorm) and d == 1 and a == 0 and b == 0 then ( + if o.Verbose then << "-- U is already in the target space; defining f as the identity map" << endl; + return multirationalMap super toRationalMap(1_U); + ); + toNormU := null; + if withNorm then ( + if o#"ForceExperimentalNormalization" then ( + try toNormU = inverseNormalizationMapExperimentalRaw(U,"U",eulerExpVal,o.Verbose) else return; + ) else ( + try toNormU = inverseNormalizationMapRaw(U,"U",eulerExpVal,o.Verbose) else return; + ); + ); + if withNorm and d == 1 and a == 0 and b == 0 then return toNormU; + local f; + if toNormU === null then ( + if o.Verbose then << "-- computing the map f from U to the minimal " << strSurf << " surface" << endl << flush; + H := random(1,0_U); + D := {(H,d)}; + if a > 0 and dim L != -1 then D = append(D,(L,a)); + if b > 0 and dim C != -1 then D = append(D,(C,b)); + f = mapDefinedByDivisor(U,D); + ) else ( + if o.Verbose then << "-- computing the map from the normalization of U to the minimal " << strSurf << " surface" << endl << flush; + H' := random(1,0_(target toNormU)); + D' := {(H',d)}; + if a > 0 and dim L != -1 then D' = append(D',(toNormU L,a)); + if b > 0 and dim C != -1 then D' = append(D',(toNormU C,b)); + f = toNormU * mapDefinedByDivisor(image toNormU,D'); + ); + if o#"TargetExpDim" =!= null and o#"TargetExpDim" != dim target f then ( + if o.Verbose then << "-- failed to compute map to minimal " << strSurf << "; result is a map to PP^" << dim target f << " instead of PP^" << o#"TargetExpDim" << endl; + return null; + ); + makeImageMapWithStrategy(f,o.Strategy,o#"TargetExpDim",o.Verbose); + if o.Verbose and o#"IsK3Type" and (not isStandardK3surface image f) then << "-- note: invariant mismatch for standard K3 surface" << endl; + f +); + +makeImageMapWithStrategy = (f,Str,g,Verb) -> ( + if f#"image" =!= null then return image f; + if char coefficientRing f <= 65521 then ( + if Verb then << "-- computing the image of f via 'F4' algorithm..." << endl << flush; + image(f,"F4"); + assert(f#"image" =!= null); + return; + ); + if Str =!= "Inverse" and g =!= null then ( + if Verb then << "-- computing the image of f via interpolation..." << endl << flush; + interpolateImage(f,toList(binomial(g-2,2) : 2),2,Verbose=>Verb); + assert(f#"image" =!= null); + return; + ); + if Verb then << "-- computing the image of f..." << endl << flush; + image f; +); + +saveAndReturnMap = (f,U) -> ( + if f =!= null then U.cache#"MapToMinimalK3Surface" = f; + return f; +); + +contractionMap = method(Options => {Verbose => true, Strategy => null, "ForceNormalization" => false}); + +contractionMap (EmbeddedProjectiveVariety,HodgeSpecialFourfold) := o -> (U,X) -> ( + if U.cache#?"MapToMinimalK3Surface" then return U.cache#"MapToMinimalK3Surface"; + withNorm := o#"ForceNormalization"; + if not U.cache#?"exceptionalCurves" then error "exceptional curves not found in cache: method exceptionalCurves() must be called first"; + (L,C) := U.cache#"exceptionalCurves"; + genK3 := null; + if instance(X,DoublySpecialCubicFourfold) and dim C == -1 then ( + genK3 = sectionalGenus U; + ) else if instance(X,CubicFourfold) or instance(X,GushelMukaiFourfold) then ( + genK3 = lift((discriminant(X)+2)/2,ZZ); + ); + f := null; + ------------------------------------- + --- CubicFourfold and GMFourfold ---- + if recognize X === 17 then ( + if o.Verbose then << "-- computing desingularization of U" << endl; + f0 := rationalMap((support singularLocus U)_U,2,Dominant=>true); + if o.Verbose then << "-- computing the map f from U to the minimal K3 surface of degree " << discriminant X << endl; + H := random(1,0_U); + f1 := mapDefinedByDivisor(target f0,{(f0 (H*U),1),(f0 (3*C),1),(f0 L,1)}); + f = f0 * f1; + if dim target f != genK3 then ( + if o.Verbose then << "-- failed to compute map to minimal K3 surface; result is a map to PP^" << dim target f << " instead of PP^" << genK3 << endl; + return null; + ); + makeImageMapWithStrategy(f,o.Strategy,genK3,o.Verbose); + if o.Verbose and (not isStandardK3surface image f) then << "-- note: invariant mismatch for standard K3 surface" << endl; + return saveAndReturnMap(f,U); + ); + if recognize X === 1 then ( + f = mapFromExceptionalCurves(U,Verbose=>o.Verbose,"Normalization"=>false); + f = rationalMap(f,Dominant=>true); + if o.Verbose then << "-- computing normalization of the surface image" << endl; + f = multirationalMap super toRationalMap(f * inverse3 normalization(target f,Verbose=>false)); + assert(dim target f == genK3 and f#"image" =!= null); + if o.Verbose and (not isStandardK3surface image f) then << "-- note: invariant mismatch for standard K3 surface" << endl; + return saveAndReturnMap(f,U); + ); + if recognize X === "C42" then ( + f = mapFromExceptionalCurves(U,(1,1,2),Verbose=>o.Verbose,"Normalization"=>false,"TargetExpDim"=>genK3); + return saveAndReturnMap(f,U); + ); + if recognize X === "mukai26''" then ( + f = mapFromExceptionalCurves(U,(1,1,3),Verbose=>o.Verbose,"Normalization"=>false,"TargetExpDim"=>genK3); + return saveAndReturnMap(f,U); + ); + if recognize X === 3 then ( + if withNorm then ( + f = mapFromExceptionalCurves(U,(1,0,0),Verbose=>o.Verbose,"Normalization"=>true,"TargetExpDim"=>genK3); + return saveAndReturnMap(f,U); + ) else ( + if o.Verbose then << "-- skipping computation of the map f from U to the minimal K3 surface of degree " << discriminant X << endl << "-- re-run associatedK3surface to finalize computation" << endl; + return; + ); + ); + if member(recognize X,{"oneNodalSepticDelPezzoSurfaceC26", 18}) then ( + if withNorm then ( + -- use "ForceExperimentalNormalization"=>true to restore previous working behavior + f = mapFromExceptionalCurves(U,Verbose=>o.Verbose,"Normalization"=>true,"ForceExperimentalNormalization"=>false,"TargetExpDim"=>genK3); + return saveAndReturnMap(f,U); + ) else ( + if o.Verbose then << "-- skipping computation of the map f from U to the minimal K3 surface of degree " << discriminant X << endl << "-- re-run associatedK3surface to finalize computation" << endl; + return; + ); + ); + if recognize X === "FarkasVerra" then ( + if withNorm then ( + f = mapFromExceptionalCurves(U,Verbose=>o.Verbose,"Normalization"=>false); + f = rationalMap(f,Dominant=>true); + -- use inverseNormalizationMapExperimentalRaw to restore previous working behavior + n := inverseNormalizationMapRaw(image f,"f(U)",2,o.Verbose); + f = super(f * n); + assert(f#"image" =!= null); + if dim target f != genK3 then ( + if o.Verbose then << "-- failed to compute map to minimal K3 surface; result is a map to PP^" << dim target f << " instead of PP^" << genK3 << endl; + return null; + ); + if o.Verbose and (not isStandardK3surface image f) then << "-- note: invariant mismatch for standard K3 surface" << endl; + return saveAndReturnMap(f,U); + ) else ( + if o.Verbose then << "-- skipping computation of the map f from U to the minimal K3 surface of degree " << discriminant X << endl << "-- re-run associatedK3surface to finalize computation" << endl; + return; + ); + ); + ------------------------------------- + -- IntersectionOfThreeQuadricsInP7 -- + if member(recognize X, {"planeInPP7", "internal-projection-K3-genus-8", "surf-5-6-2-nodal"}) then ( + f = mapFromExceptionalCurves(U,(1,0,0),Verbose=>o.Verbose,"Normalization"=>false,"IsK3Type"=>false); + return saveAndReturnMap(f,U); + ); + if recognize X === "surf-5-7-0-1" then ( + f = mapFromExceptionalCurves(U,(1,1,0),Verbose=>o.Verbose,"Normalization"=>false,"IsK3Type"=>false); + return saveAndReturnMap(f,U); + ); + if member(recognize X, {"surf-4-3-1-external", "surf-7-1-9"}) then ( + if withNorm then ( + f = mapFromExceptionalCurves(U,(1,0,0),Verbose=>o.Verbose,"Normalization"=>true,"IsK3Type"=>false); + return saveAndReturnMap(f,U); + ) else ( + if o.Verbose then << "-- skipping computation of the map f from U to the minimal Castelnuovo surface" << endl << "-- re-run associatedCastelnuovoSurface to finalize computation" << endl; + return; + ); + ); + if recognize X === "surf-5-10-1" then ( + if withNorm then ( + n1 := multirationalMap normalization(U,Verbose=>o.Verbose); + if o.Verbose then << "-- computing the map f from U to the minimal Castelnuovo surface" << endl; + h1 := rationalMap(source n1,tally {n1^* (random(1,0_U)),n1^* L},Dominant=>3); + f = multirationalMap inverse rationalMap(ring target h1,ring target n1,take(gens ring target h1,5)); + if not((f * (inverse f) == 1 and (inverse f) * f == 1)) then error "something went wrong"; + return saveAndReturnMap(f,U); + ) else ( + if o.Verbose then << "-- skipping computation of the map f from U to the minimal Castelnuovo surface" << endl << "-- re-run associatedCastelnuovoSurface to finalize computation" << endl; + return; + ); + ); + ------------------------------------- + ---- DoublySpecialCubicFourfold ----- + if instance(X,DoublySpecialCubicFourfold) and isStandardK3surface U then ( + f = mapFromExceptionalCurves(U,(1,0,0),Verbose=>o.Verbose,"Normalization"=>false); + return saveAndReturnMap(f,U); + ); + if instance(X,DoublySpecialCubicFourfold) and isHigherDegreeCurveInExceptionalSetKnownToBeSpecial X then ( + f = mapFromExceptionalCurves(U,(1,1,0),Verbose=>o.Verbose,Strategy=>o.Strategy,"Normalization"=>false); + return saveAndReturnMap(f,U); + ); + ------------------------------------- + (EulerExpVal,IsK3Type) := if instance(X,IntersectionOfThreeQuadricsInP7) then (4,false) else (2,true); + if withNorm or euler hilbertPolynomial U == EulerExpVal then ( + f = mapFromExceptionalCurves(U,Verbose=>o.Verbose,Strategy=>o.Strategy,"Normalization"=>(euler hilbertPolynomial U != EulerExpVal),"TargetExpDim"=>genK3,"IsK3Type"=>IsK3Type); + ); + if f === null and o.Verbose then ( + << "-- skipping computation of the map f : U -> Ũ" << endl; + << "-- re-run the function to try finalizing computation" << endl; + ); + saveAndReturnMap(f,U) +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/docs.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/docs.m2 new file mode 100644 index 00000000000..50ceca31de9 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/docs.m2 @@ -0,0 +1,573 @@ + +------------------------------------------------------------------------ +---------------------------- Documentation ----------------------------- +------------------------------------------------------------------------ + +beginDocumentation() + +document {Key => SpecialFanoFourfolds, +Headline => "A package for working with Hodge-special fourfolds", +PARA {"This package contains several tools related to the rationality problem for cubic fourfolds, Gushel-Mukai fourfolds, and some other special Fano fourfolds. See ",HREF{"https://arxiv.org/abs/2204.11518","arXiv:2204.11518"}," for some applications."}, + +SUBSECTION "Version History", UL { +{"v. 1.0 (Oct 2019) - Initial version."}, +{"v. 2.7.1 (May 2023) - Consolidated stable version."}, +{"v. 2.8 (Apr 2026) - Updates and new features."}}, +PARA {"The following papers have made use of this package."}, +References => UL{ +{"F. Russo and G. Staglianò, ",EM"On complete intersections of three quadrics in P^7",", available at ",HREF{"https://arxiv.org/abs/2312.01773","arXiv:2312.01773"}," (2023)."}, +{"M. Hoff and G. Staglianò, ",EM"Explicit constructions of K3 surfaces and unirational Noether-Lefschetz divisors",", available at ",HREF{"https://arxiv.org/abs/2110.15819","arXiv:2110.15819"}," (2021)."}, +{"G. Staglianò, ",EM"Some new rational Gushel fourfolds",", available at ",HREF{"https://arxiv.org/abs/2003.07809","arXiv:2003.07809"}," (2020)."}, +{"G. Staglianò, ",EM"On some families of Gushel-Mukai fourfolds",", available at ",HREF{"https://arxiv.org/abs/2002.07026","arXiv:2002.07026"}," (2020)."}, +{"M. Hoff and G. Staglianò, ",EM"New examples of rational Gushel-Mukai fourfolds",", available at ",HREF{"https://arxiv.org/abs/1910.12838","arXiv:1910.12838"}," (2020)."}, +{"F. Russo and G. Staglianò, ",EM"Trisecant Flops, their associated K3 surfaces and the rationality of some Fano fourfolds",", available at ",HREF{"https://arxiv.org/abs/1909.01263","arXiv:1909.01263"}," (2020)."}, +{"F. Russo and G. Staglianò, ",EM"Explicit rationality of some cubic fourfolds",", available at ",HREF{"https://arxiv.org/abs/1811.03502","arXiv:1811.03502"}," (2019)."}, +{"F. Russo and G. Staglianò, ",EM"Congruences of 5-secant conics and the rationality of some admissible cubic fourfolds",", available at ",HREF{"https://arxiv.org/abs/1707.00999","arXiv:1707.00999"}," (2018)."}}} + +document {Key => {GushelMukaiFourfold}, +Headline => "the class of all special Gushel-Mukai fourfolds", +PARA{"The general type of Gushel-Mukai fourfold (called ",EM "ordinary",") can be realized as the intersection of a smooth del Pezzo fivefold ", TEX///$\mathbb{G}(1,4)\cap\mathbb{P}^8\subset \mathbb{P}^8$///, " with a quadric hypersurface in ", TEX///$\mathbb{P}^8$///, ". A Gushel-Mukai fourfold is said to be ", EM"special", " if it contains a surface whose cohomology class ", EM "does not come", " from the Grassmannian ", TEX///$\mathbb{G}(1,4)$///, ". The special Gushel-Mukai fourfolds are parametrized by a countable union of (not necessarily irreducible) hypersurfaces in the corresponding moduli space, labelled by the integers ", TEX///$d \geq 10$///, " with ", TEX///$d = 0, 2, 4\ ({mod}\ 8)$///, "; the number ",TEX///$d$///," is called the discriminant of the fourfold. For precise definition and results, we refer mainly to the paper ", HREF{"https://arxiv.org/abs/1302.1398", "Special prime Fano fourfolds of degree 10 and index 2"}, ", by O. Debarre, A. Iliev, and L. Manivel."}, +PARA{"An object of the class ", TO GushelMukaiFourfold, " is basically represented by a pair ", TEX///(S,X)///, ", where ", TEX///$X$///, " is a Gushel-Mukai fourfold and ", TEX///$S$///, " is a surface contained in ", TEX///$X$///, ". The main constructor for the objects of the class is the function ", TO gushelMukaiFourfold,"."}, +SeeAlso => {(discriminant,GushelMukaiFourfold)}} + +document {Key => {(discriminant, CubicFourfold), (discriminant, HodgeSpecialFourfold)}, +Headline => "discriminant of a special cubic fourfold", +Usage => "discriminant X", +Inputs => {"X" => CubicFourfold}, +Outputs => {ZZ => {"the discriminant of ", TEX///$X$///}}, +PARA{"This calculation passes through the determination of the topological Euler characteristic of the surface contained in the fourfold, which is obtained thanks to the functions ", TO EulerCharacteristic, " and ", TO Euler, " (the option ", TT "Algorithm", " allows you to select the method)."}, +EXAMPLE {"X = cubicFourfold \"quintic del Pezzo surface\";", "discriminant X"}, +SeeAlso => {(discriminant, GushelMukaiFourfold)}} + +document {Key => {(discriminant, GushelMukaiFourfold)}, +Headline => "discriminant of a special Gushel-Mukai fourfold", +Usage => "discriminant X", +Inputs => {"X" => GushelMukaiFourfold}, +Outputs => {ZZ => {"the discriminant of ", TEX///$X$///}}, +PARA{"This function applies a formula given in Section 7 of the paper ", HREF{"https://arxiv.org/abs/1302.1398", "Special prime Fano fourfolds of degree 10 and index 2"}, ", obtaining the data required through the functions ", TO cycleClass, ", ", TO EulerCharacteristic, " and ", TO Euler, " (the option ", TT "Algorithm", " allows you to select the method)."}, +EXAMPLE {"X = gushelMukaiFourfold \"tau-quadric\";", "discriminant X"}, +SeeAlso => {(discriminant, CubicFourfold)}} + +undocumented{(expression, GushelMukaiFourfold), (describe, GushelMukaiFourfold)} + +document {Key => {Verbose, [cubicFourfold, Verbose], [gushelMukaiFourfold, Verbose], [mirrorFourfold, Verbose], [specialFourfold, Verbose], [parameterCount, Verbose], [associatedK3surface, Verbose], [associatedCastelnuovoSurface, Verbose], [polarizedK3surface, Verbose], [detectCongruence, Verbose], [trisecantFlop, Verbose]}, +Headline => "request verbose feedback"} + +document {Key => {gushelMukaiFourfold, (gushelMukaiFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (gushelMukaiFourfold, Ideal, Ideal), [gushelMukaiFourfold, InputCheck]}, +Headline => "make a special Gushel-Mukai fourfold", +Usage => "gushelMukaiFourfold(S,X)", +Inputs => {"S" => EmbeddedProjectiveVariety => {"a smooth irreducible surface ", TEX///$S\subset\mathbb{P}^8$///}, "X" => EmbeddedProjectiveVariety => {"a smooth prime Fano fourfold ", TEX///$X\subset \mathbb{P}^8$///, " of degree 10 and sectional genus 6, which contains the surface ", TEX///$S$///}}, +Outputs => {GushelMukaiFourfold => {"the special Gushel-Mukai fourfold corresponding to the pair ", TEX///$(S,X)$///}}, +PARA{"In the following example, we define a Gushel-Mukai fourfold containing a so-called ", TEX///$\tau$///, "-quadric."}, +EXAMPLE {"K = ZZ/33331; x := gens ring PP_K^8;", "S = projectiveVariety ideal(x_6-x_7, x_5, x_3-x_4, x_1, x_0-x_4, x_2*x_7-x_4*x_8);", "X = projectiveVariety ideal(x_4*x_6-x_3*x_7+x_1*x_8, x_4*x_5-x_2*x_7+x_0*x_8, x_3*x_5-x_2*x_6+x_0*x_8+x_1*x_8-x_5*x_8, x_1*x_5-x_0*x_6+x_0*x_7+x_1*x_7-x_5*x_7, x_1*x_2-x_0*x_3+x_0*x_4+x_1*x_4-x_2*x_7+x_0*x_8, x_0^2+x_0*x_1+x_1^2+x_0*x_2+2*x_0*x_3+x_1*x_3+x_2*x_3+x_3^2-x_0*x_4-x_1*x_4-2*x_2*x_4-x_3*x_4-2*x_4^2+x_0*x_5+x_2*x_5+x_5^2+2*x_0*x_6+x_1*x_6+2*x_2*x_6+x_3*x_6+x_5*x_6+x_6^2-3*x_4*x_7+2*x_5*x_7-x_7^2+x_1*x_8+x_3*x_8-3*x_4*x_8+2*x_5*x_8+x_6*x_8-x_7*x_8);", "F = gushelMukaiFourfold(S,X);", "describe F", "assert(F == X)"}, +SeeAlso => {(gushelMukaiFourfold, EmbeddedProjectiveVariety), (gushelMukaiFourfold, String, Ring), specialFourfold}} + +document {Key => {(gushelMukaiFourfold, EmbeddedProjectiveVariety),(gushelMukaiFourfold, Ideal)}, +Headline => "random special Gushel-Mukai fourfold", +Usage => "gushelMukaiFourfold S +gushelMukaiFourfold (S%Y)", +Inputs => {"S" => EmbeddedProjectiveVariety => {"a smooth irreducible surface ",TEX///$S$///," which is a ",TO2{(symbol %,MultiprojectiveVariety,MultiprojectiveVariety),"subvariety"}," of a del Pezzo fivefold/sixfold ",TEX///$Y$///,"; alternatively, you can pass the ideal of ",TEX///$S$///," in ",TEX///$Y$///," (e.g., an ideal in the ring ", TO Grass, TEX///$(1,4)$///, ")"}}, +Outputs => {GushelMukaiFourfold => {"a random special Gushel-Mukai fourfold containing the given surface"}}, +EXAMPLE {"Y = GG(ZZ/33331,1,4);", "-- cubic scroll in G(1,4)"|newline|"S = schubertCycle({2,0},Y) * schubertCycle({1,0},Y) * schubertCycle({1,0},Y);", "X = gushelMukaiFourfold S;", "discriminant X"}, +SeeAlso => {(gushelMukaiFourfold, String, Ring),(symbol %,MultiprojectiveVariety,MultiprojectiveVariety)}} + +document {Key => {(gushelMukaiFourfold, String, Ring), (gushelMukaiFourfold, String)}, +Headline => "random special Gushel-Mukai fourfold of a given type", +Usage => "gushelMukaiFourfold(n,K) +gushelMukaiFourfold n", +Inputs => {"n" => String => {"the name of some known type of Gushel-Mukai fourfolds"}, "K" => {"the coefficient ring"}}, +Outputs => {GushelMukaiFourfold => {"a random special Gushel-Mukai fourfold of the indicated type over ",TT"K"}}, +EXAMPLE {"X = gushelMukaiFourfold(\"cubic scroll\",ZZ/65521);", "describe X"}, +References => UL{ +{"O. Debarre, A. Iliev, and L. Manivel, ",EM"Special prime Fano fourfolds of degree 10 and index 2",", available at ",HREF{"https://arxiv.org/abs/1302.1398","arXiv:1302.1398"}," (2014)."}, +{"G. Staglianò, ",EM"On some families of Gushel-Mukai fourfolds",", available at ",HREF{"https://arxiv.org/abs/2002.07026","arXiv:2002.07026"}," (2020)."}}, +SeeAlso => {(gushelMukaiFourfold, EmbeddedProjectiveVariety), GMtables}} + +document {Key => {toGrass, (toGrass, GushelMukaiFourfold)}, +Headline => "Gushel morphism from a GM fourfold to GG(1,4)", +Usage => "toGrass X", +Inputs => {"X" => GushelMukaiFourfold}, +Outputs => {MultirationalMap => {"a linear morphism from ", TEX///$X$///, " into the ",TO2{GrassmannianVariety,"Grassmannian"}," ", TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///, ", Plücker embedded, which is an embedding when ",TEX///$X$///," is of ordinary type"}}, +EXAMPLE {"x := gens ring PP_(ZZ/33331)^8;", "X = gushelMukaiFourfold(ideal(x_6-x_7, x_5, x_3-x_4, x_1, x_0-x_4, x_2*x_7-x_4*x_8), ideal(x_4*x_6-x_3*x_7+x_1*x_8, x_4*x_5-x_2*x_7+x_0*x_8, x_3*x_5-x_2*x_6+x_0*x_8+x_1*x_8-x_5*x_8, x_1*x_5-x_0*x_6+x_0*x_7+x_1*x_7-x_5*x_7, x_1*x_2-x_0*x_3+x_0*x_4+x_1*x_4-x_2*x_7+x_0*x_8, x_0^2+x_0*x_1+x_1^2+x_0*x_2+2*x_0*x_3+x_1*x_3+x_2*x_3+x_3^2-x_0*x_4-x_1*x_4-2*x_2*x_4-x_3*x_4-2*x_4^2+x_0*x_5+x_2*x_5+x_5^2+2*x_0*x_6+x_1*x_6+2*x_2*x_6+x_3*x_6+x_5*x_6+x_6^2-3*x_4*x_7+2*x_5*x_7-x_7^2+x_1*x_8+x_3*x_8-3*x_4*x_8+2*x_5*x_8+x_6*x_8-x_7*x_8));", "time toGrass X", "show oo"}, +SeeAlso => {(toGrass, EmbeddedProjectiveVariety), (symbol ===>, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}} + +document {Key => {(toGrass, EmbeddedProjectiveVariety)}, +Headline => "embedding of an ordinary Gushel-Mukai fourfold or a del Pezzo variety into GG(1,4)", +Usage => "toGrass X", +Inputs => {"X" => EmbeddedProjectiveVariety => {"an ordinary Gushel-Mukai fourfold, or a del Pezzo variety of dimension at least 4 (e.g., a sixfold projectively equivalent to ", TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///,")"}}, +Outputs => {MultirationalMap => {"an embedding of ", TEX///$X$///, " into the ",TO2{GrassmannianVariety,"Grassmannian"}," ", TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///, ", Plücker embedded"}}, +EXAMPLE {"x = gens ring PP_(ZZ/33331)^8;", "X = projectiveVariety ideal(x_4*x_6-x_3*x_7+x_1*x_8, x_4*x_5-x_2*x_7+x_0*x_8, x_3*x_5-x_2*x_6+x_0*x_8+x_1*x_8-x_5*x_8, x_1*x_5-x_0*x_6+x_0*x_7+x_1*x_7-x_5*x_7, x_1*x_2-x_0*x_3+x_0*x_4+x_1*x_4-x_2*x_7+x_0*x_8);", "time toGrass X", "show oo"}, +SeeAlso => {(toGrass,GushelMukaiFourfold), (symbol ===>, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}} + +undocumented{(cycleClass, GushelMukaiFourfold)} + +document {Key => {GMtables, (GMtables, ZZ, Ring), (GMtables, ZZ), [GMtables, Verify]}, +Headline => "make examples of reducible subschemes of P^5", +Usage => "GMtables(i,K)", +Inputs => {"i" => ZZ => {"an integer between 1 and 21"}, "K" => Ring => {"the coefficient ring"}}, +Outputs => {{"a triple of ",TO2{EmbeddedProjectiveVariety,"varieties"},", ",TEX///$(B,V,C)$///, ", which represents a reducible subscheme of ", TEX///$\mathbb{P}^5$///, ", in accordance with the 21 examples listed in Table 2 of the paper ", HREF{"https://arxiv.org/abs/2002.07026", "On some families of Gushel-Mukai fourfolds"}, "."}}, +EXAMPLE {"(B,V,C) := GMtables(1,ZZ/33331)", "B * V == C"}, +PARA{"The corresponding example of fourfold reported in Table 1 of the aforementioned paper can be obtained as follows."}, +EXAMPLE {"psi = rationalMap(ideal B,Dominant=>2);", "X = gushelMukaiFourfold psi ideal V;"}, +PARA{"This is basically the same as doing this:"}, +EXAMPLE {"gushelMukaiFourfold(\"1\",ZZ/33331);"}, +SeeAlso => {(gushelMukaiFourfold,String,Ring),(gushelMukaiFourfold,Array,Array)}} + +undocumented {(GMtables, Ring, String), (GMtables,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety,EmbeddedProjectiveVariety)}; + +document {Key => {parameterCount, (parameterCount, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (parameterCount, HodgeSpecialFourfold)}, +Headline => "count of parameters", +Usage => "parameterCount(S,X)", +Inputs => {"S" => EmbeddedProjectiveVariety, "X" => EmbeddedProjectiveVariety => {"such that ", TEX///$S\subseteq X$///}}, +Outputs => {{"a count of parameters to estimate the dimensions of the corresponding Hilbert schemes"}}, +PARA{"See ",TO (parameterCount, CubicFourfold)," and ", TO (parameterCount, GushelMukaiFourfold)," for more precise applications of this function."}, +PARA{"The following calculation shows that the family of complete intersections of 3 quadrics in ",TEX///$\mathbb{P}^5$///," containing a rational normal quintic curve has codimension 1 in the space of all such complete intersections."}, +EXAMPLE {"K = ZZ/33331; S = PP_K^(1,5);", "X = random({{2},{2},{2}},S);", "time parameterCount(S,X,Verbose=>true)"}, +SeeAlso => {(parameterCount, CubicFourfold), (parameterCount, GushelMukaiFourfold), normalSheaf}} + +document {Key => {(parameterCount, CubicFourfold)}, +Headline => "count of parameters in the moduli space of GM fourfolds", +Usage => "parameterCount X", +Inputs => {"X" => CubicFourfold => {"a special cubic fourfold containing a surface ", TEX///$S$///}}, +Outputs => {ZZ => {"an upper bound for the codimension in the moduli space of cubic fourfolds of the locus of cubic fourfolds that contain a surface belonging to the same irreducible component of the Hilbert scheme containing ", TEX///$[S]$///}, Sequence => {"the triple of integers: ", TEX///$(h^0(I_{S/P^5}(3)), h^0(N_{S/P^5}), h^0(N_{S/X}))$///}}, +PARA{"This function implements a parameter count explained in the paper ", HREF{"https://arxiv.org/abs/1503.05256", "Unirationality of moduli spaces of special cubic fourfolds and K3 surfaces"}, ", by H. Nuer."}, +PARA{"Below, we show that the closure of the locus of cubic fourfolds containing a Veronese surface has codimension at most one (hence exactly one) in the moduli space of cubic fourfolds. Then, by the computation of the discriminant, we deduce that the cubic fourfolds containing a Veronese surface describe the Hassett's divisor ", TEX///$\mathcal{C}_{20}$///}, +EXAMPLE {"K = ZZ/33331; V = PP_K^(2,2);", "X = cubicFourfold V;", "time parameterCount(X,Verbose=>true)", "discriminant X"}, +SeeAlso => {(parameterCount, GushelMukaiFourfold), normalSheaf}} + +document {Key => {(parameterCount, GushelMukaiFourfold)}, +Headline => "count of parameters in the moduli space of GM fourfolds", +Usage => "parameterCount X", +Inputs => {"X" => GushelMukaiFourfold => {"a special GM fourfold containing a surface ", TEX///$S$///, " and contained in a del Pezzo fivefold ", TEX///$Y$///}}, +Outputs => {ZZ => {"an upper bound for the codimension in the moduli space of GM fourfolds of the locus of GM fourfolds that contain a surface belonging to the same irreducible component of the Hilbert scheme of ", TEX///$Y$///, " that contains ", TEX///$[S]$///}, Sequence => {"the triple of integers: ", TEX///$(h^0(I_{S/Y}(2)), h^0(N_{S/Y}), h^0(N_{S/X}))$///}}, +PARA{"This function implements a parameter count explained in the paper ", HREF{"https://arxiv.org/abs/2002.07026", "On some families of Gushel-Mukai fourfolds"}, "."}, +PARA{"Below, we show that the closure of the locus of GM fourfolds containing a cubic scroll has codimension at most one (hence exactly one) in the moduli space of GM fourfolds."}, +EXAMPLE {"G = GG(ZZ/33331,1,4);", "S = (schubertCycle({2,0},G) * random({{1},{1}},0_G))%G", "X = gushelMukaiFourfold S;", "time parameterCount(X,Verbose=>true)", "discriminant X"}, +SeeAlso => {(parameterCount, CubicFourfold), normalSheaf}} + +document {Key => {normalSheaf, (normalSheaf, EmbeddedProjectiveVariety), (normalSheaf, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}, +Headline => "normal sheaf", +Usage => "normalSheaf X"|newline|"normalSheaf(X % Y)"|newline|"normalSheaf(X,Y)", +Inputs => {"X" => EmbeddedProjectiveVariety, "Y" => EmbeddedProjectiveVariety => {" such that ",TEX///$X\subset Y$///," (if not given, it is taken to be the ",TO2{ambientVariety,"ambient variety"}," of ",TEX///$X$///,")"}}, +Outputs => {CoherentSheaf => {"the normal sheaf ", TEX///$\mathcal{N}_{X, Y}$///, " of ", TEX///$X$///, " in ", TEX///$Y$///}}, +EXAMPLE {"X = PP_(ZZ/65521)^(2,2);", "Y = random(2,X);", "N = normalSheaf X;", "N' = normalSheaf(X,Y);", "rank HH^0 N", "rank HH^0 N'"}} + +undocumented {(normalSheaf, MultiprojectiveVariety), (normalSheaf, MultiprojectiveVariety, MultiprojectiveVariety)} + +document {Key => {isAdmissible, (isAdmissible, ZZ), (isAdmissible, CubicFourfold)}, +Headline => "whether an integer is admissible (in the sense of the theory of cubic fourfolds)", +Usage => "isAdmissible d", +Inputs => {"d" => ZZ}, +Outputs => {Boolean => {"whether ", TT"d", " is admissible, i.e., it is an even integer ", TT"d>6", " which is not divisible by 4, 9 or any odd prime congruent to 2 modulo 3"}}, +EXAMPLE{"select(150,isAdmissible)"}, +SeeAlso => {isAdmissibleGM}} + +document {Key => {isAdmissibleGM, (isAdmissibleGM, ZZ), (isAdmissibleGM, GushelMukaiFourfold)}, +Headline => "whether an integer is admissible (in the sense of the theory of GM fourfolds)", +Usage => "isAdmissibleGM d", +Inputs => {"d" => ZZ}, +Outputs => {Boolean => {"whether ",TEX///$d$///," is an integer ",TEX///$>$///," 8 and ",TEX///$\equiv$///," 2 or 4 (mod 8) such that the only odd primes that divide ",TEX///$d$///," are ",TEX///$\equiv$///," 1 (mod 4). In other words, whether a GM fourfold of discriminant ", TT"d", " has an associated K3 surface."}}, +EXAMPLE{"select(140,isAdmissibleGM)"}, +SeeAlso => {isAdmissible}} + +document {Key => {CongruenceOfCurves}, +Headline => "the class of all congruences of secant curves to surfaces", +PARA{"Objects of this type are created by ",TO detectCongruence,"."}} + +document {Key => {(symbol SPACE, CongruenceOfCurves, EmbeddedProjectiveVariety), (symbol SPACE, CongruenceOfCurves, Ideal)}, +Headline => "get the curve of a congruence passing through a point", +Usage => "f(p)", +Inputs => {"f" => CongruenceOfCurves => {"a congruence of curves to a surface inside a variety ", TEX///$Y$///}, "p" => EmbeddedProjectiveVariety => {"a general point on ",TEX///$Y$///," (that is, a point on ",TEX///$Y$///," outside a certain proper Zariski closed subset)"}}, +Outputs => {EmbeddedProjectiveVariety => {"the unique curve of the congruence ", TEX///$f$///, " that passes through ", TEX///$p$///}}, +EXAMPLE {"X = cubicFourfold surface {3,4};", "f = detectCongruence(X,1);","C = f point ambient X;","member(C,f)","assert oo"}, +SeeAlso => {detectCongruence, (member, EmbeddedProjectiveVariety, CongruenceOfCurves)}} + +document {Key => {(map, CongruenceOfCurves)}, +Headline => "compute the parameter space of a congruence", +Usage => "map f", +Inputs => {"f" => CongruenceOfCurves => {"a congruence of curves to a surface inside a variety ", TEX///$Y$///}}, +Outputs => {MultirationalMap => {"a dominant map from ",TEX///$Y$///," to the parameter space of ",TEX///$f$///," whose general fibers are the curves of the congruence"}}, +EXAMPLE {"S = PP_(ZZ/65521)[2,2];","Y = ambient S;","X = cubicFourfold S;","f = detectCongruence(X,1);","F = map f;","Q = target F","f;","p = point Y;","assert(f p == F^* F p)"}, +SeeAlso => {detectCongruence}} + +document {Key => {(member, EmbeddedProjectiveVariety, CongruenceOfCurves)}, +Headline => "test membership in a congruence of curves", +Usage => "member(C,f)", +Inputs => {"C" => EmbeddedProjectiveVariety => {"a curve"}, "f" => CongruenceOfCurves}, +Outputs => {Boolean => {"whether the curve ",TEX///$C$///," belongs to the congruence ", TEX///$f$///}}, +SeeAlso => {(symbol SPACE, CongruenceOfCurves, EmbeddedProjectiveVariety)}} + +undocumented{(toString, CongruenceOfCurves), (net, CongruenceOfCurves), (texMath, CongruenceOfCurves)} + +document {Key => {detectCongruence, (detectCongruence, HodgeSpecialFourfold, ZZ), (detectCongruence, HodgeSpecialFourfold)}, +Headline => "detect and return a congruence of secant curves to a surface", +PARA{"See ",TO (detectCongruence, CubicFourfold)," and ",TO (detectCongruence, GushelMukaiFourfold),"."}} + +document {Key => {(detectCongruence, CubicFourfold, ZZ), (detectCongruence, CubicFourfold)}, +Headline => "detect and return a congruence of (3e-1)-secant curves of degree e", +Usage => "detectCongruence X"|newline|"detectCongruence(X,e)", +Inputs => {"X" => CubicFourfold => {"containing a surface ", TEX///$S\subset\mathbb{P}^5$///}, "e" => ZZ => {"a positive integer (optional but recommended)"}}, +Outputs => {CongruenceOfCurves => {"that is a function which takes a general point ", TEX///$p\in\mathbb{P}^5$///, " (that is, outside a certain proper Zariski closed subset of ",TEX///$\mathbb{P}^5$///,") and returns the unique rational curve of degree ", TEX///$e$///, ", ", TEX///$(3e-1)$///, "-secant to ", TEX///$S$///, ", and passing through ", TEX///$p$///, " (an error is thrown if such a curve does not exist or is not unique)"}}, +EXAMPLE {"-- A general cubic fourfold of discriminant 26"|newline|"X = cubicFourfold(\"3-nodal septic scroll\",ZZ/33331);", "describe X", "time f = detectCongruence(X,Verbose=>true);", "p := point ambient X -- random point on P^5", "time C = f p; -- 5-secant conic to the surface", "assert(dim C == 1 and degree C == 2 and dim(C * surface X) == 0 and degree(C * surface X) == 5 and isSubset(p, C))"}, +SeeAlso => {(detectCongruence, GushelMukaiFourfold, ZZ), coneOfLines}} + +document {Key => {(detectCongruence, GushelMukaiFourfold, ZZ), (detectCongruence, GushelMukaiFourfold)}, +Headline => "detect and return a congruence of (2e-1)-secant curves of degree e inside a del Pezzo fivefold", +Usage => "detectCongruence X"|newline|"detectCongruence(X,e)", +Inputs => {"X" => GushelMukaiFourfold => {"containing a surface ", TEX///$S\subset Y$///,", where ",TEX///$Y$///," denotes the unique del Pezzo fivefold containing the fourfold ",TEX///$X$///}, "e" => ZZ => {"a positive integer (optional but recommended)"}}, +Outputs => {CongruenceOfCurves => {"that is a function which takes a general point ", TEX///$p\in Y$///, "(that is, a point on ",TEX///$Y$///," outside a certain proper Zariski closed subset) and returns the unique rational curve of degree ", TEX///$e$///, ", ", TEX///$(2e-1)$///, "-secant to ", TEX///$S$///, ", contained in ",TEX///$Y$///," and passing through ", TEX///$p$///, " (an error is thrown if such a curve does not exist or is not unique)"}}, +EXAMPLE{"-- A GM fourfold of discriminant 20"|newline|"X = gushelMukaiFourfold(\"17\",ZZ/33331);", "describe X", "time f = detectCongruence(X,Verbose=>true);", "Y = ambientFivefold X; -- del Pezzo fivefold containing X", "p := point Y -- random point on Y", "time C = f p; -- 3-secant conic to the surface", "S = surface X;", "assert(dim C == 1 and degree C == 2 and dim(C*S) == 0 and degree(C*S) == 3 and isSubset(p,C) and isSubset(C,Y))"}, +SeeAlso => {(detectCongruence, CubicFourfold, ZZ), coneOfLines}} + +document {Key => {CubicFourfold}, +Headline => "the class of all special cubic fourfolds", +PARA{"A cubic fourfold is a smooth cubic hypersurface in ", TEX///$\mathbb{P}^5$///, ". A cubic fourfold ", TEX///$X\subset \mathbb{P}^5$///, " is ", EM "special", " of discriminant ", TEX///$d>6$///, " if it contains an algebraic surface ", TEX///$S$///, ", and the discriminant of the saturated lattice spanned by ", TEX///$h^2$///, " and ", TEX///$[S]$///, " in ", TEX///$H^{2,2}(X,\mathbb{Z}):=H^4(X,\mathbb{Z})\cap H^2(\Omega_X^2)$///, " is ", TEX///$d$///, ", where ", TEX///$h$///, " denotes the class of a hyperplane section of ", TEX///$X$///, ". The set ", TEX///$\mathcal{C}_d$///, " of special cubic fourfolds of discriminant ", TEX///$d$///, " is either empty or an irreducible divisor inside the moduli space of cubic fourfolds ", TEX///$\mathcal{C}$///, ". Moreover, ", TEX///$\mathcal{C}_d\neq \emptyset$///, " if and only if ", TEX///$d>6$///, " and ", TEX///$d=$///, "0 or 2 (mod 6). For the general theory, see the papers ", HREF{"https://link.springer.com/article/10.1023/A:1001706324425", "Special cubic fourfolds"}, " and ", HREF{"http://imperium.lenin.ru/~kaledin/math/hasset.pdf", "Some rational cubic fourfolds"}, ", by B. Hassett."}, +PARA{"An object of the class ", TO CubicFourfold, " is basically represented by a pair ", TEX///(S,X)///, ", where ", TEX///$X$///, " is a cubic fourfold and ", TEX///$S$///, " is a surface contained in ", TEX///$X$///, ". The surface ", TEX///$S$///, " is required to be smooth or with at most a finite number ", TEX///$n$///, " of non-normal nodes. This number ", TEX///$n$///, " (if known) can be specified manually using the option ", TT "NumNodes", ". The main constructor for the objects of the class is the function ", TO cubicFourfold,"."}, +SeeAlso => {(discriminant,CubicFourfold)}} + +undocumented{(expression, CubicFourfold), (describe, CubicFourfold)} + +undocumented{InputCheck, NumNodes} + +document {Key => {cubicFourfold, (cubicFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (cubicFourfold, Ideal, Ideal), (cubicFourfold, Ideal, RingElement), [cubicFourfold, NumNodes], [cubicFourfold, InputCheck]}, +Headline => "make a special cubic fourfold", +Usage => "cubicFourfold(S,X)"|newline|"cubicFourfold(S,X,NumNodes=>n)", +Inputs => {"S" => EmbeddedProjectiveVariety => {"an irreducible surface ", TEX///$S\subset\mathbb{P}^5$///, ", which has as singularities only a finite number ",TEX///$n\geq 0$///," of non-normal nodes (this number ",TEX///$n$///," should be passed with the option ", TT "NumNodes",", otherwise it is obtained using a probabilistic method)"}, "X" => EmbeddedProjectiveVariety => {"a smooth cubic fourfold ", TEX///$X\subset \mathbb{P}^5$///, " containing the surface ", TEX///$S$///}}, +Outputs => {CubicFourfold => {"the special cubic fourfold corresponding to the pair ", TEX///$(S,X)$///}}, +PARA{"In the example below, we define a cubic fourfold containing a rational scroll of degree 7 with 3 nodes."}, +EXAMPLE {"K = ZZ/33331; x := gens ring PP_K^5;", "S = projectiveVariety ideal(x_0*x_2*x_3-2*x_1*x_2*x_3-x_1*x_3^2-x_2*x_3^2-x_0*x_1*x_4+2*x_1^2*x_4-x_1*x_2*x_4+x_2^2*x_4+2*x_0*x_3*x_4-x_1*x_3*x_4-x_1*x_4^2+x_1*x_3*x_5, x_1^2*x_3-4*x_1*x_2*x_3-x_0*x_3^2-3*x_1*x_3^2-2*x_2*x_3^2+2*x_0^2*x_4-9*x_0*x_1*x_4+11*x_1^2*x_4-x_0*x_2*x_4-2*x_1*x_2*x_4+2*x_2^2*x_4+12*x_0*x_3*x_4-7*x_1*x_3*x_4-4*x_3^2*x_4+x_0*x_4^2-6*x_1*x_4^2+4*x_2*x_4^2-2*x_3*x_4^2-2*x_4^3-x_0*x_1*x_5+x_1^2*x_5+2*x_1*x_2*x_5+3*x_0*x_3*x_5+2*x_1*x_3*x_5-x_3^2*x_5-x_0*x_4*x_5-4*x_1*x_4*x_5+3*x_2*x_4*x_5+2*x_3*x_4*x_5-x_1*x_5^2, x_0*x_1*x_3-7*x_1*x_2*x_3-3*x_0*x_3^2-4*x_1*x_3^2-3*x_2*x_3^2+x_3^3+3*x_0^2*x_4-14*x_0*x_1*x_4+17*x_1^2*x_4-x_0*x_2*x_4-3*x_1*x_2*x_4+3*x_2^2*x_4+19*x_0*x_3*x_4-9*x_1*x_3*x_4-x_2*x_3*x_4-6*x_3^2*x_4+x_0*x_4^2-9*x_1*x_4^2+6*x_2*x_4^2-3*x_3*x_4^2-3*x_4^3-2*x_0*x_1*x_5+2*x_1^2*x_5+4*x_1*x_2*x_5+5*x_0*x_3*x_5+4*x_1*x_3*x_5-2*x_3^2*x_5-2*x_0*x_4*x_5-7*x_1*x_4*x_5+5*x_2*x_4*x_5+3*x_3*x_4*x_5-2*x_1*x_5^2, x_0^2*x_3-12*x_1*x_2*x_3-6*x_0*x_3^2-6*x_1*x_3^2-5*x_2*x_3^2+2*x_3^3+5*x_0^2*x_4-24*x_0*x_1*x_4+29*x_1^2*x_4-x_0*x_2*x_4-5*x_1*x_2*x_4+5*x_2^2*x_4+32*x_0*x_3*x_4-14*x_1*x_3*x_4-2*x_2*x_3*x_4-10*x_3^2*x_4+x_0*x_4^2-15*x_1*x_4^2+10*x_2*x_4^2-5*x_3*x_4^2-5*x_4^3-3*x_0*x_1*x_5+3*x_1^2*x_5+6*x_1*x_2*x_5+8*x_0*x_3*x_5+7*x_1*x_3*x_5-3*x_3^2*x_5-3*x_0*x_4*x_5-11*x_1*x_4*x_5+8*x_2*x_4*x_5+5*x_3*x_4*x_5-3*x_1*x_5^2, x_1*x_2^2+6*x_1*x_2*x_3+2*x_0*x_3^2+3*x_1*x_3^2+2*x_2*x_3^2-x_3^3-3*x_0^2*x_4+12*x_0*x_1*x_4-14*x_1^2*x_4-2*x_2^2*x_4-15*x_0*x_3*x_4+6*x_1*x_3*x_4+x_2*x_3*x_4+5*x_3^2*x_4+x_0*x_4^2+8*x_1*x_4^2-5*x_2*x_4^2+2*x_3*x_4^2+2*x_4^3+x_0*x_1*x_5-2*x_1^2*x_5-4*x_1*x_2*x_5-4*x_0*x_3*x_5-3*x_1*x_3*x_5+2*x_3^2*x_5+2*x_0*x_4*x_5+7*x_1*x_4*x_5-4*x_2*x_4*x_5-2*x_3*x_4*x_5+2*x_1*x_5^2, x_0*x_2^2+10*x_1*x_2*x_3+3*x_0*x_3^2+5*x_1*x_3^2+4*x_2*x_3^2-x_3^3-5*x_0^2*x_4+19*x_0*x_1*x_4-22*x_1^2*x_4-x_0*x_2*x_4+3*x_1*x_2*x_4-4*x_2^2*x_4-24*x_0*x_3*x_4+9*x_1*x_3*x_4+x_2*x_3*x_4+8*x_3^2*x_4+2*x_0*x_4^2+11*x_1*x_4^2-7*x_2*x_4^2+4*x_3*x_4^2+3*x_4^3+2*x_0*x_1*x_5-4*x_1^2*x_5-7*x_1*x_2*x_5-7*x_0*x_3*x_5-5*x_1*x_3*x_5-x_2*x_3*x_5+3*x_3^2*x_5+4*x_0*x_4*x_5+12*x_1*x_4*x_5-7*x_2*x_4*x_5-3*x_3*x_4*x_5+4*x_1*x_5^2, x_1^2*x_2+17*x_1*x_2*x_3+6*x_0*x_3^2+9*x_1*x_3^2+7*x_2*x_3^2-2*x_3^3-9*x_0^2*x_4+36*x_0*x_1*x_4-44*x_1^2*x_4+3*x_0*x_2*x_4+5*x_1*x_2*x_4-7*x_2^2*x_4-47*x_0*x_3*x_4+21*x_1*x_3*x_4+2*x_2*x_3*x_4+16*x_3^2*x_4+24*x_1*x_4^2-16*x_2*x_4^2+7*x_3*x_4^2+7*x_4^3+3*x_0*x_1*x_5-6*x_1^2*x_5-9*x_1*x_2*x_5-12*x_0*x_3*x_5-8*x_1*x_3*x_5+5*x_3^2*x_5+5*x_0*x_4*x_5+19*x_1*x_4*x_5-12*x_2*x_4*x_5-7*x_3*x_4*x_5+5*x_1*x_5^2, x_0*x_1*x_2+29*x_1*x_2*x_3+11*x_0*x_3^2+15*x_1*x_3^2+12*x_2*x_3^2-4*x_3^3-16*x_0^2*x_4+62*x_0*x_1*x_4-74*x_1^2*x_4+5*x_0*x_2*x_4+9*x_1*x_2*x_4-12*x_2^2*x_4-80*x_0*x_3*x_4+35*x_1*x_3*x_4+4*x_2*x_3*x_4+27*x_3^2*x_4+40*x_1*x_4^2-27*x_2*x_4^2+12*x_3*x_4^2+12*x_4^3+5*x_0*x_1*x_5-10*x_1^2*x_5-16*x_1*x_2*x_5-21*x_0*x_3*x_5-14*x_1*x_3*x_5+9*x_3^2*x_5+9*x_0*x_4*x_5+33*x_1*x_4*x_5-21*x_2*x_4*x_5-12*x_3*x_4*x_5+9*x_1*x_5^2, x_0^2*x_2+49*x_1*x_2*x_3+19*x_0*x_3^2+25*x_1*x_3^2+20*x_2*x_3^2-7*x_3^3-28*x_0^2*x_4+106*x_0*x_1*x_4-124*x_1^2*x_4+8*x_0*x_2*x_4+16*x_1*x_2*x_4-20*x_2^2*x_4-134*x_0*x_3*x_4+58*x_1*x_3*x_4+7*x_2*x_3*x_4+45*x_3^2*x_4+66*x_1*x_4^2-45*x_2*x_4^2+20*x_3*x_4^2+20*x_4^3+9*x_0*x_1*x_5-18*x_1^2*x_5-28*x_1*x_2*x_5-37*x_0*x_3*x_5-23*x_1*x_3*x_5+16*x_3^2*x_5+16*x_0*x_4*x_5+57*x_1*x_4*x_5-36*x_2*x_4*x_5-20*x_3*x_4*x_5+16*x_1*x_5^2, x_1^3+47*x_1*x_2*x_3+18*x_0*x_3^2+23*x_1*x_3^2+19*x_2*x_3^2-7*x_3^3-24*x_0^2*x_4+97*x_0*x_1*x_4-117*x_1^2*x_4+8*x_0*x_2*x_4+16*x_1*x_2*x_4-19*x_2^2*x_4-127*x_0*x_3*x_4+54*x_1*x_3*x_4+7*x_2*x_3*x_4+42*x_3^2*x_4-x_0*x_4^2+62*x_1*x_4^2-42*x_2*x_4^2+19*x_3*x_4^2+19*x_4^3+9*x_0*x_1*x_5-16*x_1^2*x_5-25*x_1*x_2*x_5-33*x_0*x_3*x_5-23*x_1*x_3*x_5+14*x_3^2*x_5+14*x_0*x_4*x_5+51*x_1*x_4*x_5-33*x_2*x_4*x_5-19*x_3*x_4*x_5+14*x_1*x_5^2, x_0*x_1^2+79*x_1*x_2*x_3+29*x_0*x_3^2+40*x_1*x_3^2+32*x_2*x_3^2-11*x_3^3-41*x_0^2*x_4+164*x_0*x_1*x_4-196*x_1^2*x_4+14*x_0*x_2*x_4+26*x_1*x_2*x_4-32*x_2^2*x_4-214*x_0*x_3*x_4+92*x_1*x_3*x_4+11*x_2*x_3*x_4+71*x_3^2*x_4-2*x_0*x_4^2+105*x_1*x_4^2-71*x_2*x_4^2+32*x_3*x_4^2+32*x_4^3+14*x_0*x_1*x_5-26*x_1^2*x_5-41*x_1*x_2*x_5-55*x_0*x_3*x_5-38*x_1*x_3*x_5+23*x_3^2*x_5+23*x_0*x_4*x_5+85*x_1*x_4*x_5-55*x_2*x_4*x_5-32*x_3*x_4*x_5+23*x_1*x_5^2, x_0^2*x_1+133*x_1*x_2*x_3+48*x_0*x_3^2+68*x_1*x_3^2+54*x_2*x_3^2-18*x_3^3-70*x_0^2*x_4+278*x_0*x_1*x_4-330*x_1^2*x_4+24*x_0*x_2*x_4+44*x_1*x_2*x_4-54*x_2^2*x_4-361*x_0*x_3*x_4+156*x_1*x_3*x_4+18*x_2*x_3*x_4+120*x_3^2*x_4-4*x_0*x_4^2+177*x_1*x_4^2-120*x_2*x_4^2+54*x_3*x_4^2+54*x_4^3+23*x_0*x_1*x_5-44*x_1^2*x_5-69*x_1*x_2*x_5-93*x_0*x_3*x_5-63*x_1*x_3*x_5+39*x_3^2*x_5+39*x_0*x_4*x_5+144*x_1*x_4*x_5-93*x_2*x_4*x_5-54*x_3*x_4*x_5+39*x_1*x_5^2, x_0^3+224*x_1*x_2*x_3+80*x_0*x_3^2+115*x_1*x_3^2+91*x_2*x_3^2-30*x_3^3-119*x_0^2*x_4+470*x_0*x_1*x_4-555*x_1^2*x_4+41*x_0*x_2*x_4+75*x_1*x_2*x_4-91*x_2^2*x_4-608*x_0*x_3*x_4+263*x_1*x_3*x_4+30*x_2*x_3*x_4+202*x_3^2*x_4-8*x_0*x_4^2+297*x_1*x_4^2-202*x_2*x_4^2+91*x_3*x_4^2+91*x_4^3+39*x_0*x_1*x_5-76*x_1^2*x_5-118*x_1*x_2*x_5-158*x_0*x_3*x_5-105*x_1*x_3*x_5+67*x_3^2*x_5+68*x_0*x_4*x_5+245*x_1*x_4*x_5-158*x_2*x_4*x_5-91*x_3*x_4*x_5+67*x_1*x_5^2);", "X = projectiveVariety ideal(x_1^2*x_3+x_0*x_2*x_3-6*x_1*x_2*x_3-x_0*x_3^2-4*x_1*x_3^2-3*x_2*x_3^2+2*x_0^2*x_4-10*x_0*x_1*x_4+13*x_1^2*x_4-x_0*x_2*x_4-3*x_1*x_2*x_4+3*x_2^2*x_4+14*x_0*x_3*x_4-8*x_1*x_3*x_4-4*x_3^2*x_4+x_0*x_4^2-7*x_1*x_4^2+4*x_2*x_4^2-2*x_3*x_4^2-2*x_4^3-x_0*x_1*x_5+x_1^2*x_5+2*x_1*x_2*x_5+3*x_0*x_3*x_5+3*x_1*x_3*x_5-x_3^2*x_5-x_0*x_4*x_5-4*x_1*x_4*x_5+3*x_2*x_4*x_5+2*x_3*x_4*x_5-x_1*x_5^2);", "F = cubicFourfold(S,X,NumNodes=>3);", "describe F", "assert(F == X)"}, +SeeAlso => {(cubicFourfold, EmbeddedProjectiveVariety), (cubicFourfold, String, Ring), specialFourfold}} + +document {Key => {(cubicFourfold, EmbeddedProjectiveVariety), (cubicFourfold, Ideal)}, +Headline => "random special cubic fourfold", +Usage => "cubicFourfold S"|newline|"cubicFourfold(S,NumNodes=>n)", +Inputs => {"S" => EmbeddedProjectiveVariety => {"an irreducible surface in ", TEX///$\mathbb{P}^5$///}}, +Outputs => {CubicFourfold => {"a random cubic fourfold containing the given surface"}}, +EXAMPLE {"-- quintic del Pezzo surface"|newline|"S = surface({3,4},ZZ/33331);", "X = cubicFourfold S;", "discriminant X"}, +SeeAlso => {(cubicFourfold, String, Ring)}} + +document {Key => {(cubicFourfold, String, Ring), (cubicFourfold, String)}, +Headline => "random special cubic fourfold of a given type", +Usage => "cubicFourfold(n,K) +cubicFourfold n", +Inputs => {"n" => String => {"the name of some known type of cubic fourfolds"}, "K" => {"the coefficient ring"}}, +Outputs => {CubicFourfold => {"a random special cubic fourfold of the indicated type over ",TT"K"}}, +EXAMPLE {"X = cubicFourfold(\"3-nodal septic scroll\",ZZ/65521);", "describe X"}, +SeeAlso => (cubicFourfold, EmbeddedProjectiveVariety)} + +document {Key => {ambientFivefold, (ambientFivefold, HodgeSpecialFourfold)}, +Headline => "get the ambient fivefold of the Hodge-special fourfold", +Usage => "ambientFivefold X", +Inputs => {"X" => HodgeSpecialFourfold}, +Outputs => {EmbeddedProjectiveVariety => {"the ambient fivefold of ",TT"X"}}, +EXAMPLE {"S = surface {4,5,1};", "V = random(3,S);", "X = V * random(2,S);", "F = specialFourfold(S,X,V);", "ambientFivefold F"}, +PARA {"When ",TEX///$X$///," is a ",TO2{GushelMukaiFourfold,"GM fourfold"},", the ambient fivefold of ",TEX///$X$///," is a fivefold ",TEX///$Y\subset\mathbb{P}^8$///," of degree 5 such that ",TEX///$X\subset Y$///," is a quadric hypersurface. We have that the fourfold ",TEX///$X$///," is of ordinary type if and only if ",TEX///$Y$///," is smooth."}, +EXAMPLE { +"X = specialFourfold(\"21\",ZZ/33331);", +"describe X", +"Y = ambientFivefold X;", +"isSubset(X,Y)", +"Y!"}} + +document {Key => {(map, CubicFourfold)}, +Headline => "associated cubic map", +Usage => "map X", +Inputs => {"X" => CubicFourfold => {"containing a surface ", TEX///$S\subset\mathbb{P}^5$///}}, +Outputs => {RationalMap => {"the rational map from ", TEX///$\mathbb{P}^5$///, " defined by the linear system of cubics through ", TEX///$S$///}}} + +document {Key => {(map, GushelMukaiFourfold)}, +Headline => "associated quadratic map", +Usage => "map X", +Inputs => {"X" => GushelMukaiFourfold => {"containing a surface ", TEX///$S\subset Y$///, ", where ", TEX///$Y\subset\mathbb{P}^8$///, " is the unique del Pezzo fivefold containing ", TEX///$X$///}}, +Outputs => {RationalMap => {"the rational map from ", TEX///$Y$///, " defined by the linear system of quadrics through ", TEX///$S$///}}} + +document {Key => {surface, (surface, HodgeSpecialFourfold)}, +Headline => "get the special surface contained in the fourfold", +Usage => "surface X", +Inputs => {"X" => HodgeSpecialFourfold}, +Outputs => {EmbeddedProjectiveVariety => {"the special surface contained in the fourfold ",TT"X"}}, +EXAMPLE {"X = specialFourfold \"quintic del Pezzo surface\";", "V = ambientFivefold X;", "S = surface X;", "assert isSubset(S,X)"}, +SeeAlso => {(surface,List)}} + +document { +Key => {(surface, List), (surface, VisibleList, Ring), (surface, VisibleList, Option), (surface, VisibleList, Option), (surface, VisibleList, Ring, Option), (surface, VisibleList, Option, Option), (surface, VisibleList, Ring, Option, Option)}, +Headline => "get a rational surface", +Usage => "surface {a,i,j,k,...} +surface({a,i,j,k,...),K) +surface({a,i,j,k,...},K,NumNodes=>n,ambient=>m)", +Inputs => {List => {"a list ",TEX///$\{a,i,j,k,\ldots\}$///," of nonnegative integers"}}, +Outputs => {EmbeddedProjectiveVariety => {"the image of the rational map defined by the linear system of curves of degree ",TEX///$a$///," in ",TEX///$\mathbb{P}_{K}^2$///," having ",TEX///$i$///," random base points of multiplicity 1, ",TEX///$j$///," random base points of multiplicity 2, ",TEX///$k$///," random base points of multiplicity 3, and so on until the last integer in the given list."}}, +PARA{"In the example below, we take the image of the rational map defined by the linear system of septic plane curves with 3 random simple base points and 9 random double points."}, +EXAMPLE { +"S = surface {7,3,9};", +"coefficientRing S", +"T = surface({7,3,9},ZZ/33331);", +"X = cubicFourfold T;", +"coefficientRing X", +"describe X"}, +SeeAlso => {(rationalMap,PolynomialRing,List),(gushelMukaiFourfold,Array,Array)}} + +typValSurf := typicalValues#surface; +typicalValues#surface = Nothing; +document {Key => {(surface, MultiprojectiveVariety, MultiprojectiveVariety)}, +Headline => "make a Hodge-special surface", +Usage => "surface(C,S)", +Inputs => {"C" => MultiprojectiveVariety => {"an irreducible curve"}, "S" => MultiprojectiveVariety => {"a smooth surface ", TEX///$S$///, " containing the curve ", TEX///$C$///}}, +Outputs => {{"the Hodge special surface corresponding to the pair ", TEX///$(C,S)$///}}, +PARA{"The curve ",TEX///$C$///," can be recovered using the function ",TT "curve","."}, +EXAMPLE lines ///K = ZZ/65521; +C = random PP_K^(1,3); -- random twisted cubic in P^3 +j = parametrize PP_K(1,1,1,4); +C = (rationalMap(ambient C,source j) * j) C; +describe C +S = random(8,C); +describe S +S = surface(C,S); +discriminant S +parameterCount(S,Verbose=>true) +f := map(S,1,0) +f = quadricFibration f +discriminant f///, +Caveat => {"This feature is currently under development."}} +typicalValues#surface = typValSurf; + +document {Key => {unirationalParametrization, (unirationalParametrization, CubicFourfold), (unirationalParametrization, CubicFourfold, EmbeddedProjectiveVariety), (unirationalParametrization, GushelMukaiFourfold), (unirationalParametrization, HodgeSpecialFourfold)}, +Headline => "unirational parametrization", +Usage => "unirationalParametrization X", +Inputs => {"X" => CubicFourfold => {"or ", ofClass GushelMukaiFourfold}}, +Outputs => {MultirationalMap => {"a rational map of degree 2 from ",TEX///$\mathbb{P}^4$///," to ",TEX///$X$///}}, +PARA{"The degree of the forms defining the returned map is 10 in the case of cubic fourfolds, and 26 in the case of GM fourfolds."}, +EXAMPLE {"K = ZZ/10000019; S = PP_K^(2,2); -- Veronese surface;", "X = cubicFourfold S;", "time f = unirationalParametrization X;", "degreeSequence f", "degree(f,Strategy=>\"random point\")"}, +SeeAlso => {(parametrize, HodgeSpecialFourfold), (parametrize, MultiprojectiveVariety)}} + +document {Key => {(parametrize, HodgeSpecialFourfold)}, +Headline => "rational parametrization", +Usage => "parametrize X", +Inputs => {"X" => HodgeSpecialFourfold}, +Outputs => {MultirationalMap => {"a birational map from a rational fourfold to ", TT "X"}}, +PARA{"Some Hodge-special fourfolds are known to be rational. In this case, the function tries to obtain a birational map from ", TEX///$\mathbb{P}^4$///, " (or, e.g., from a quadric hypersurface in ", TEX///$\mathbb{P}^5$///, ") to the fourfold."}, +EXAMPLE {"X = specialFourfold surface {3,4};", "phi = parametrize X;", "describe phi"}, +EXAMPLE {"Y = specialFourfold \"tau-quadric\";", "psi = parametrize Y;", "describe psi"}, +EXAMPLE {"Z = specialFourfold \"plane in PP^7\";", "eta = parametrize Z;", "describe eta"}, +SeeAlso => {unirationalParametrization, (parametrize,MultiprojectiveVariety)}} + +document {Key => {fromOrdinaryToGushel, (fromOrdinaryToGushel, GushelMukaiFourfold)}, +Headline => "try to deform to a fourfold of Gushel type", +Usage => "fromOrdinaryToGushel X", +Inputs => {"X" => GushelMukaiFourfold => {"a fourfold of ordinary type"}}, +Outputs => {GushelMukaiFourfold => {"a fourfold of Gushel type, a deformation of ",TT"X"}}, +EXAMPLE {"X = gushelMukaiFourfold \"quintic del Pezzo surface\";", "singularLocus ambientFivefold X", "X' = fromOrdinaryToGushel X;", "support singularLocus ambientFivefold X'"}} + +document {Key => {associatedK3surface, [associatedK3surface, Strategy], building}, +Headline => "K3 surface associated to a rational fourfold", +PARA{"See ",TO (associatedK3surface, CubicFourfold)," and ",TO (associatedK3surface, GushelMukaiFourfold),"."}} + +document {Key => {(associatedK3surface, CubicFourfold)}, +Headline => "K3 surface associated to a rational cubic fourfold", +Usage => "E = associatedK3surface X", +Inputs => {"X" => CubicFourfold => {"containing a surface ", TEX///$S\subset\mathbb{P}^5$///," that admits a ",TO2{CongruenceOfCurves,"congruence"}," of ",TEX///$(3e-1)$///,"-secant curves of degree ",TEX///$e$///}}, +Outputs => {"E" => {"the (minimal) K3 surface associated to ",TEX///$X$///}}, +Consequences => {{{TT"building E"," will return the following four objects:"}, UL{{"the dominant ",TO2{MultirationalMap,"rational map"}," ",TEX///$\mu:\mathbb{P}^5 \dashrightarrow W$///," defined by the linear system of hypersurfaces of degree ",TEX///$3e-1$///," having points of multiplicity ",TEX///$e$///," along ",TEX///$S$///,";"}, {"the ",TO2{EmbeddedProjectiveVariety,"surface"}," ",TEX///$U\subset W$///," determining the inverse map of the restriction of ",TEX///$\mu$///," to ",TEX///$X$///,";"}, {"the ",TO2{List,"list"}," of the exceptional curves on the surface ",TEX///$U$///,";"}, {"a ",TO2{MultirationalMap,"rational map"}," of degree 1 from the surface ",TEX///$U$///," to the minimal K3 surface ",TEX///$\widetilde{U}$///,"."}}}}, +PARA {"For more details and notation, see the papers ",HREF{"https://arxiv.org/abs/1909.01263","Trisecant Flops, their associated K3 surfaces and the rationality of some Fano fourfolds"}," and ",HREF{"https://arxiv.org/abs/2204.11518","Explicit computations with cubic fourfolds, Gushel-Mukai fourfolds, and their associated K3 surfaces"},"."}, +EXAMPLE {"X = cubicFourfold \"quartic scroll\";", "describe X", "E = associatedK3surface(X, Verbose=>true);", "describe X", "(mu,U,exCurves,f) = building E; ? mu", "assert(image f == E)"}, +SeeAlso => {(associatedK3surface, GushelMukaiFourfold), detectCongruence, mirrorFourfold, (polarizedK3surface, DoublySpecialCubicFourfold)}} + +document {Key => {(associatedK3surface, GushelMukaiFourfold)}, +Headline => "K3 surface associated to a rational Gushel-Mukai fourfold", +Usage => "E = associatedK3surface X", +Inputs => {"X" => GushelMukaiFourfold => {"containing a surface ", TEX///$S\subset Y$///," that admits a ",TO2{CongruenceOfCurves,"congruence"}," of ",TEX///$(2e-1)$///,"-secant curves of degree ",TEX///$e$///," inside the ",TO2{ambientFivefold,"ambient fivefold"}," ",TEX///$Y$///," of the fourfold ",TEX///$X$///}}, +Outputs => {"E" => {"the (minimal) K3 surface associated to ",TEX///$X$///}}, +Consequences => {{{TT"building E"," will return the following four objects:"}, UL{{"the dominant ",TO2{MultirationalMap,"rational map"}," ",TEX///$\mu:Y\dashrightarrow W$///," defined by the linear system of hypersurfaces of degree ",TEX///$2e-1$///," having points of multiplicity ",TEX///$e$///," along ",TEX///$S$///,";"}, {"the ",TO2{EmbeddedProjectiveVariety,"surface"}," ",TEX///$U\subset W$///," determining the inverse map of the restriction of ",TEX///$\mu$///," to ",TEX///$X$///,";"}, {"the ",TO2{List,"list"}," of the exceptional curves on the surface ",TEX///$U$///,";"}, {"a ",TO2{MultirationalMap,"rational map"}," of degree 1 from the surface ",TEX///$U$///," to the minimal K3 surface ",TEX///$\widetilde{U}$///,"."}}}}, +PARA {"For more details and notation, see the paper ",HREF{"https://arxiv.org/abs/2204.11518","Explicit computations with cubic fourfolds, Gushel-Mukai fourfolds, and their associated K3 surfaces"},"."}, +EXAMPLE {"X = gushelMukaiFourfold \"tau-quadric\";", "describe X", "E = associatedK3surface(X, Verbose=>true);", "describe X", "(mu,U,exCurves,f) = building E; ? mu", "assert(image f == E)"}, +SeeAlso => {(associatedK3surface, CubicFourfold), detectCongruence, mirrorFourfold, (polarizedK3surface, DoublySpecialCubicFourfold)}} + +document {Key => {parametrizeFanoFourfold, (parametrizeFanoFourfold, EmbeddedProjectiveVariety), [parametrizeFanoFourfold,Strategy]}, +Headline => "rational parametrization of a prime Fano fourfold of coindex at most 3", +Usage => "parametrize X +parametrizeFanoFourfold(X,Strategy=>...)", +Inputs => {"X" => EmbeddedProjectiveVariety => {"a prime Fano fourfold ",TEX///$X$///," of coindex at most 3 having degree ",TEX///$d$///," and genus ",TEX///$g$///," with ",TEX///$(d,g)\in\{(2,0),(4,1),(5,1),(12,7),(14,8),(16,9),(18,10)\}$///}}, +Outputs => {MultirationalMap => {"a birational map from ",TEX///$\mathbb{P}^4$///," to ", TEX///$X$///}}, +PARA{"This function is mainly based on results contained in the classical paper ",HREF{"https://link.springer.com/article/10.1007/BF02413916","Algebraic varieties with canonical curve sections"},", by L. Roth. In some examples, more strategies are available. For instance, if ",TEX///$X\subset\mathbb{P}^7$///," is a 4-dimensional linear section of ",TEX///$\mathbb{G}(1,4)\subset\mathbb{P}^9$///,", then by passing ",TT"Strategy=>1"," (which is the default choice) we get the inverse of the projection from the plane spanned by a conic contained in ",TEX///$X$///,"; while with ",TT"Strategy=>2"," we get the projection from the unique ",TEX///$\sigma_{2,2}$///,"-plane contained in ",TEX///$X$///," (Todd's result)."}, +EXAMPLE {"K = ZZ/65521; X = GG_K(1,4) * random({{1},{1}},0_(GG_K(1,4)));","? X", "time parametrizeFanoFourfold X"}, +SeeAlso => {fanoFourfold,(parametrize,HodgeSpecialFourfold),unirationalParametrization,(parametrize,MultiprojectiveVariety)}} + +document {Key => {fanoFourfold, (fanoFourfold,ZZ,ZZ), [fanoFourfold,CoefficientRing]}, +Headline => "random prime Fano fourfold of coindex at most 3", +Usage => "fanoFourfold(d,g) +fanoFourfold(d,g,CoefficientRing=>K)", +Inputs => {{TT"(d,g)"," a pair of integers belonging to the set ",TEX///$\{(2,0),(3,1),(4,1),(5,1),(4,3),(6,4),(8,5),(10,6),(12,7),(14,8),(16,9),(18,10)\}$///}}, +Outputs => {EmbeddedProjectiveVariety => {"a random prime Fano fourfold of coindex at most 3 having degree ",TEX///$d$///," and genus ",TEX///$g$///}}, +EXAMPLE {"X = fanoFourfold(4,1);", "describe X", "parametrize X"}, +SeeAlso => {parametrizeFanoFourfold}} + +document { +Key => {(clean,HodgeSpecialFourfold)}, +Headline => "clean the internal information of a fourfold", +Usage => "clean X", +Inputs => {"X" => HodgeSpecialFourfold}, +Outputs => {HodgeSpecialFourfold => {"which is mathematically identical to ",TT"X",", but new to the system"}}, +PARA{"This function is only useful for testing."}, +EXAMPLE {"X = specialFourfold \"quartic scroll\"", "X' = clean X", "X === X'"}} + +document {Key => {trisecantFlop}, +Headline => "examples of trisecant flops", +Usage => "trisecantFlop i", +Inputs => {"i" => ZZ => {"an integer between 0 and 17"}}, +Outputs => {{"the i-th example of birational map ",TEX///$X\dashrightarrow W$///," in accordance to the Table 1 in the paper ",HREF{"https://arxiv.org/abs/1909.01263","Trisecant Flops, their associated K3 surfaces and the rationality of some Fano fourfolds"},"."}}, +PARA{"This function requires the package ",HREF{"https://github.com/giovannistagliano/TrisecantFlops","TrisecantFlops"},". If not present the user will be asked to automatically install the package."}, +SeeAlso => {(specialFourfold, String, ZZ)}} +undocumented {(trisecantFlop,ZZ)} + +document {Key => {(specialFourfold, String, ZZ)}, +Headline => "load a prebuilt example of fourfold", +Usage => "specialFourfold(str,i)", +Inputs => {"str" => String => {"such as \"",TT"prebuilt-example-in-P5","\" or \"",TT"prebuilt-example-in-P7","\"."}, "i" => ZZ}, +Outputs => {HodgeSpecialFourfold => {"the i-th example of fourfold in accordance with some classification (e.g., ",TT"specialFourfold(\"prebuilt-example-in-P5\",i)"," is the same as ",TO2{(source,MultirationalMap),"source"}," ",TO trisecantFlop,TT"(i)","."}}, +PARA{"This function requires the package ",HREF{"https://github.com/giovannistagliano/TrisecantFlops","TrisecantFlops"},". If not present the user will be asked to automatically install the package."}, +SeeAlso => trisecantFlop} + +undocumented {(random, HodgeSpecialFourfold), (symbol **, HodgeSpecialFourfold,Ring), (map, HodgeSpecialFourfold), (describe, HodgeSpecialFourfold)} + +document {Key => {(gushelMukaiFourfold, Array, Array, String, Thing), (gushelMukaiFourfold, Array, Array), (gushelMukaiFourfold, Array, Array, String), (gushelMukaiFourfold, Array, Array, Thing)}, +Headline => "construct GM fourfolds by gluing cubic or quartic scrolls to surfaces in PP^6", +Usage => "gushelMukaiFourfold(surface,curve) +gushelMukaiFourfold(surface,curve,scroll) +gushelMukaiFourfold(surface,curve,K) +gushelMukaiFourfold(surface,curve,scroll,K)", +Inputs => {"surface" => Array => {"an array of integers ",TT"[a,i,j,k,...]"," to indicate the rational surface ",TEX///$S\subset\mathbb{P}^6$///," constructed by ",TO2{(surface,List),"surface"},TT"({a,i,j,k,...},K,ambient=>6)"}, + "curve" => Array => {"an array of integers ",TT"[d,l,m,n,...]"," to indicate the plane representation of a curve ",TEX///$C$///," on the surface ",TEX///$S$///," (the command that constructs ",TEX///$C$///," is ",TT///S.cache#"takeCurve"(d,{l,m,n,...})///,")"}, + "scroll" => String => {"which can be either \"cubic scroll\" (the default value) or \"quartic scroll\", to indicate the type of scroll ",TEX///$B\subset\mathbb{P}^6$///," to be used; in the former case ",TEX///$B\simeq\mathbb{P}^1\times\mathbb{P}^2\subset\mathbb{P}^5\subset\mathbb{P}^6$///," while in the latter case ",TEX///$B\subset\mathbb{P}^6$///," is a generic projection of a rational normal quartic scroll of dimension 4 in ",TEX///$\mathbb{P}^7$///}, + "K" => {{"the coefficient ring (",TT"ZZ/65521"," is used by default)"}}}, +Outputs => {GushelMukaiFourfold => {"a GM fourfold ",TEX///$X$///," containing the surface ",TEX///$\overline{\psi_{B}(S)}\subset\mathbb{G}(1,4)\subset\mathbb{P}^9$///,", where ",TEX///$B$///," is a scroll of the indicated type such that ",TEX///$C\subseteq S\cap B$///," and ",TEX///$\psi_{B}:\mathbb{P}^6\dashrightarrow\mathbb{G}(1,4)$///," is the birational map defined by ",TEX///$B$///}}, +PARA {"From the returned fourfold ",TEX///$X$///,", with the following commands we obtain the surface ",TEX///$S$///,", the curve ",TEX///$C$///,", and the scroll ",TEX///$B$///," used in the construction: "},PARA{TT///(B,C) = X.cache#"Construction"; S = ambientVariety C;///},PARA{"Then the surface ",TEX///$\overline{\psi_{B}(S)}\subset\mathbb{G}(1,4)$///," can be constructed with "},PARA{TT///psi = rationalMap B; (psi S)%(image psi);///}, +PARA {"In the following example we construct a GM fourfold containing the image via ",TEX///$\psi_B:\mathbb{P}^6\dashrightarrow\mathbb{G}(1,4)$///," of a quintic del Pezzo surface ",TEX///$S\subset\mathbb{P}^5\subset\mathbb{P}^6$///,", obtained as the image of the plane via the linear system of quartic curves with three general simple base points and two general double points, which cuts ",TEX///$B\simeq\mathbb{P}^1\times\mathbb{P}^2\subset\mathbb{P}^5\subset\mathbb{P}^6$///," along a rational normal quartic curve obtained as the image of a general conic passing through the two double points."}, +EXAMPLE lines ///X = gushelMukaiFourfold([4, 3, 2],[2, 0, 2]); +describe X +(B,C) = X.cache#"Construction"; +S = ambientVariety C; +C; +B; +assert(C == S * B)///, +References => UL{{"G. Staglianò, ",EM"Explicit computations with cubic fourfolds, Gushel-Mukai fourfolds, and their associated K3 surfaces",", available at ",HREF{"https://arxiv.org/abs/2204.11518","arXiv:2204.11518"}," (2022)."}}, +SeeAlso => {(surface,VisibleList,Ring), GMtables}} + +document {Key => {specialFourfold, (specialFourfold, EmbeddedProjectiveVariety), (specialFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (specialFourfold, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety), (specialFourfold, Ideal), (specialFourfold, Ideal, Ideal), (specialFourfold, Ideal, RingElement), (specialFourfold, String), (specialFourfold, String, Ring), [specialFourfold, NumNodes], [specialFourfold, InputCheck]}, +Headline => "make a Hodge-special fourfold", +Usage => "specialFourfold(S,X,V)"|newline|"specialFourfold(S,X)"|newline|"specialFourfold S", +Inputs => {"S" => EmbeddedProjectiveVariety => {"an irreducible ",TO2{(surface,HodgeSpecialFourfold),"surface"}}, "X" => EmbeddedProjectiveVariety => {"a smooth fourfold ", TEX///$X$///, " containing the surface ", TEX///$S$///}, "V" => EmbeddedProjectiveVariety => {"optional, a ",TO2{ambientFivefold,"fivefold"}," where ",TT"X"," is a hypersurface"}}, +Outputs => {HodgeSpecialFourfold => {"which corresponds to the pair ", TEX///$(S,X)$///}}, +PARA{"This can also be used as a shortcut for both ",TO cubicFourfold," and ",TO gushelMukaiFourfold,"."}, +EXAMPLE {"S = surface {3,4};", "X = specialFourfold S -- a random cubic fourfold through S", "describe X", "Y = specialFourfold \"tau-quadric\" -- a random GM fourfold through a tau-quadric", "describe Y", "T = surface {3,2};", "Z = specialFourfold T -- a random c. i. of 3 quadrics through T", "describe Z"}, +SeeAlso => {cubicFourfold, gushelMukaiFourfold}} + +document { +Key => {Singular, [associatedK3surface, Singular], [associatedCastelnuovoSurface, Singular], [mirrorFourfold, Singular]}, +Headline => "whether to transfer computation to Singular", +Usage => "Singular => true or false", +PARA{"This option allows you to transfer part of the computation to Singular."}, +SeeAlso => {associatedK3surface}} + +document {Key => {mirrorFourfold, (mirrorFourfold, CubicFourfold), (mirrorFourfold, GushelMukaiFourfold), (mirrorFourfold, IntersectionOfThreeQuadricsInP7), (mirrorFourfold, HodgeSpecialFourfold), [mirrorFourfold, Strategy]}, +Headline => "associated fourfold to a rational cubic or GM fourfold", +Usage => "mirrorFourfold X", +Inputs => {"X" => CubicFourfold => {"or ", ofClass GushelMukaiFourfold}}, +Outputs => {HodgeSpecialFourfold => {"the fourfold ",TT"W"," containing the surface ",TT"U",", with the same notation as in the function ",TO associatedK3surface,"."}}, +EXAMPLE {"X = specialFourfold(PP_(ZZ/65521)[2,2]);", "W = mirrorFourfold X;", "U = surface W;", "mirrorFourfold W", "(building associatedK3surface X)_1", "assert(oo === U)"}, +EXAMPLE {"X' = specialFourfold \"tau-quadric\";", "W' = mirrorFourfold X';", "U' = surface W';", "mirrorFourfold W'", "(building associatedK3surface X')_1", "assert(oo === U')"}, +SeeAlso => {associatedK3surface}} + +document { +Key => {(toExternalString, HodgeSpecialFourfold)}, +Headline => "convert to a readable string", +Usage => "toExternalString X", +Inputs => {"X" => HodgeSpecialFourfold}, +Outputs => {String => {"a string representation of ",TT "X",", which can be used, in conjunction with ",TO "value",", to read the object back into the program later"}}, +PARA{"Some of the internal data of the input ",TT"X"," are included in the returned string."}, +EXAMPLE {"describe (X = specialFourfold \"tau-quadric\")", "str = toExternalString X;", "describe (value str)"}} + +undocumented {(expression, HodgeSpecialFourfold)} + +document {Key => {HodgeSpecialFourfold}, +Headline => "the class of all Hodge-special fourfolds", +PARA{"An object of the class ", TO HodgeSpecialFourfold, " is basically represented by a pair ", TEX///(S,X)///, ", where ", TEX///$X$///, " is a fourfold and ", TEX///$S$///, " is a surface contained in ", TEX///$X$///,". Such objects are created by the function ",TO specialFourfold,"."}, +SeeAlso => {specialFourfold, CubicFourfold, GushelMukaiFourfold, IntersectionOfThreeQuadricsInP7}} + +document {Key => {(check, ZZ, CongruenceOfCurves), (check, CongruenceOfCurves)}, +Headline => "check that a congruence of curves is well-defined", +Usage => "check f"|newline|"check(n,f)", +Inputs => {"n" => ZZ => {"(optional with default value 5)"}, "f" => CongruenceOfCurves}, +Outputs => {CongruenceOfCurves => {"the same object passed as input, but an error is thrown if the congruence fails when ",TO2{(symbol SPACE, CongruenceOfCurves, EmbeddedProjectiveVariety),"evaluated"}," on ",TT"n"," random points."}}} + +undocumented {(symbol ?, HodgeSpecialFourfold, HodgeSpecialFourfold)} + +document {Key => {IntersectionOfThreeQuadricsInP7}, +Headline => "the class of all special intersection of three quadrics in P^7", +PARA {"This is a subtype of the ",TO HodgeSpecialFourfold," type."}, +SeeAlso => {specialFourfold}} + +undocumented {(expression, IntersectionOfThreeQuadricsInP7)} + +document {Key => {associatedCastelnuovoSurface, (associatedCastelnuovoSurface, IntersectionOfThreeQuadricsInP7), [associatedCastelnuovoSurface, Strategy]}, +Headline => "Castelnuovo surface associated to a rational complete intersection of three quadrics in P^7", +Usage => "E = associatedCastelnuovoSurface X", +Inputs => {"X" => IntersectionOfThreeQuadricsInP7 => {"containing a surface ", TEX///$S\subset Y$///," that admits a ",TO2{CongruenceOfCurves,"congruence"}," of ",TEX///$(2e-1)$///,"-secant curves of degree ",TEX///$e$///," inside the ",TO2{ambientFivefold,"ambient fivefold"}," ",TEX///$Y$///," of the fourfold ",TEX///$X$///}}, +Outputs => {"E" => {"the (minimal) Castelnuovo surface associated to ",TEX///$X$///}}, +Consequences => {{{TT"building E"," will return the following four objects:"}, UL{{"the dominant ",TO2{MultirationalMap,"rational map"}," ",TEX///$\mu:Y\dashrightarrow W$///," defined by the linear system of hypersurfaces of degree ",TEX///$2e-1$///," having points of multiplicity ",TEX///$e$///," along ",TEX///$S$///,";"}, {"the ",TO2{EmbeddedProjectiveVariety,"surface"}," ",TEX///$U\subset W$///," determining the inverse map of the restriction of ",TEX///$\mu$///," to ",TEX///$X$///,";"}, {"the ",TO2{List,"list"}," of the exceptional curves on the surface ",TEX///$U$///,";"}, {"a ",TO2{MultirationalMap,"rational map"}," of degree 1 from the surface ",TEX///$U$///," to the minimal Castelnuovo surface ",TEX///$\widetilde{U}$///,"."}}}}, +PARA {"For more details, see the paper ",HREF{"https://arxiv.org/abs/2312.01773","On complete intersections of three quadrics in P^7"},"."}, +EXAMPLE {"X = specialFourfold random({5:{1}},0_(PP_(ZZ/33331)^7));", "describe X", "E = associatedCastelnuovoSurface(X, Verbose=>true);", "describe X", "(mu,U,exCurves,f) = building E; ? mu", "assert(image f == E)"}, +SeeAlso => {associatedK3surface, detectCongruence}} + +document {Key => {beauvilleMap, (beauvilleMap, IntersectionOfThreeQuadricsInP7)}, +Headline => "construction of Beauville for complete intersections of three quadrics in P^7", +Usage => "f = beauvilleMap X", +Inputs => {"X" => IntersectionOfThreeQuadricsInP7}, +Outputs => {"f" => MultirationalMap => {"a birational map from ",TEX///$X$///," to a fourfold ",TEX///$Y\subset\mathbb{P}^2\times\mathbb{P}^5$///," whose first projection ",TEX///$Y\to\mathbb{P}^2$///," is a quadric surface bundle"}}, +PARA{"Smooth intersections of three quadrics in ",TEX///$\mathbb{P}^7$///," are birational to quadric surface bundles over ",TEX///$\mathbb{P}^2$///," with discriminant curve of degree 8. This is a construction of Beauville; see e.g. Proposition 6 in the paper ",HREF{"https://www.intlpress.com/site/pub/files/_fulltext/journals/sdg/2017/0022/0001/SDG-2017-0022-0001-a009.pdf", "Intersections of three quadrics in P7"}, ", by B. Hassett, A. Pirutka, and Y. Tschinkel."}, +EXAMPLE {"X = specialFourfold random({5:{1}},0_(PP_(ZZ/33331)^7));", "f = beauvilleMap X;", "Y = target f;", "inverse f;", "first projectionMaps target f;"}} + +document {Key => {DoublySpecialCubicFourfold, (symbol &, EmbeddedProjectiveVariety, EmbeddedProjectiveVariety)}, +Headline => "the class of all cubic fourfolds belonging to the intersection of two Hassett divisors", +PARA{"A cubic fourfold ", TEX///$X\subset \mathbb{P}^5$///, " is called ", EM "doubly special", " if it belongs to the intersection of two Hassett divisors ", TEX///$\mathcal{C}_{d_1} \cap \mathcal{C}_{d_2}$///, ". This means that the algebraic part of its middle cohomology, ", TEX///$H^{2,2}(X, \mathbb{Z})$///, ", has rank at least 3, containing the classes of two surfaces ", TEX///$S$///, " and ", TEX///$T$///, " which, together with the square of the hyperplane class ", TEX///$h^2$///, ", span a saturated lattice of rank 3."}, +PARA{"An object of the class ", TO DoublySpecialCubicFourfold, " can be thought of as a triple ", TEX///$(S,T,X)$///, ", where ", TEX///$X$///, " is a cubic fourfold and ", TEX///$S, T$///, " are two surfaces contained in ", TEX///$X$///, ". Internally, it is represented as a nested structure, where the ", TO2{CubicFourfold, "cubic fourfold"}, " containing ", TEX///$S$///, " is built upon the ", TO2{CubicFourfold, "cubic fourfold"}, " containing ", TEX///$T$///, ". This nesting allows the object to inherit the functions and properties of the cubic fourfold containing ", TEX///$S$///, "."}, +PARA{"Such an object is constructed using the operator ", TT "&", " to combine the two surfaces. Specifically, if ", TEX///$S$///, " and ", TEX///$T$///, " are two surfaces and ", TEX///$X$///, " is a cubic fourfold containing both, an object is created via the function ", TO specialFourfold, " (or equivalently ", TO cubicFourfold, ") as follows:"}, +EXAMPLE {"K = ZZ/33331;", "S = PP_K^(2,2); -- the Veronese surface", "T = random({{2},{1},{1}}, sum(3, i -> point S)); -- a 3-secant quadric to S", "X = random(3, S + T);", "X = specialFourfold(S & T, X)", "surface X", "discriminant X"}, +PARA{"If the second argument (the cubic fourfold) is omitted, the constructor will automatically provide a random cubic fourfold containing the union ", TEX///$S \cup T$///, ". A summary of the created fourfold ",TEX///$X$///," is provided by the function ", TT "describe", ", while the pair of surfaces ", TEX///$(S,T)$///, " defining the doubly special structure can be retrieved with the function ", TT "surfaces", "."}, +EXAMPLE {"X = specialFourfold(S & T);", "describe X", "surfaces X"}, +SeeAlso => {CubicFourfold, (polarizedK3surface, DoublySpecialCubicFourfold), surfaces}} + +document {Key => {polarizedK3surface, (polarizedK3surface, DoublySpecialCubicFourfold), FanoMapType, [polarizedK3surface, FanoMapType], [polarizedK3surface, Strategy], latticePolarization}, +Headline => "polarized K3 surface associated to a doubly special cubic fourfold", +Usage => "E = polarizedK3surface X", +Inputs => {"X" => DoublySpecialCubicFourfold => {"whose defining union of surfaces admits a ", TO2{CongruenceOfCurves, "congruence"}, " of curves"}}, +Outputs => {"E" => {"the polarized K3 surface associated to ", TEX///$X$///}}, +PARA {"At the first call, this function initializes a ", EM "container", " object: it computes the underlying ", TO2{EmbeddedProjectiveVariety, "projective surface"}, " but leaves the lattice polarization data empty to save time."}, +PARA {"The full lattice data is computed and stored within the object only upon a second call. Once initialized, ", TO polarize, " can be used as a synonym (using default options)."}, +PARA {"The following methods can be used to access the construction data of ", TEX///$E$///, ":"}, +UL {{TT "building E", " -- returns the four construction objects as in ", TO (associatedK3surface, CubicFourfold), " (the Fano rational map ", TEX///$\mu$///, ", the non-minimal K3 surface ", TEX///$U$///, ", the exceptional curves on ", TEX///$U$///, " and the morphism ", TEX///$f: U \to E$///, " contracting them);"}, {TT "projectiveVariety E", " -- returns the underlying ", TO2{EmbeddedProjectiveVariety, "projective surface"}, ";"}, {TT "latticePolarization E", " -- returns the lattice data induced by the doubly special structure of ", TEX///$X$///, "."}}, +PARA {"Optional inputs:"}, +UL {{TO "Strategy", " -- provides instructions for each step. The first stage handles the construction of the non-minimal K3 surface ", TEX///$U$///, " by inverting the Fano map restricted to the cubic (via ", TT "\"Inverse\"", " or ", TT "\"Approximate\"", "), while the second computes the lattice data (common options include ", TT "\"MapFromW\"", ", ", TT "\"MapFromU\"", ", or ", TT "\"SpecialCurve\"", "). Both can be passed at once as a sequence, e.g., ", TT "Strategy => (\"Inverse\", \"MapFromW\")", ", or provided individually through nested calls."}, {TO "FanoMapType", " -- sets the Fano map to ", TT "\"Standard\"", " (default) or ", TT "\"P2xP2\"", ". Note that switching this type updates the global behavior of ", TEX///$X$///, " (e.g., affecting methods like ", TO mirrorFourfold, "), though previously computed data is preserved;"}, {TO "Verbose", " -- if set to ", TO true, ", provides feedback during the construction."}}, +EXAMPLE {"S = random({3:{1}},0_(PP_(ZZ/65521)^5)); T = random S;", "X = specialFourfold(S & T);", "describe X", "polarizedK3surface(X, Verbose=>true)", "polarizedK3surface(X, Verbose=>true)", "describe X"}, +PARA{"Here is another example."}, +EXAMPLE {"X = specialFourfold surface((2,0,0),(1,0,0));", "describe X", "polarizedK3surface(X, Verbose=>true)", "polarizedK3surface(oo, Verbose=>true)", "latticePolarization oo", "describe X"}, +SeeAlso => {DoublySpecialCubicFourfold, (associatedK3surface, CubicFourfold)}} + +undocumented {(expression, DoublySpecialCubicFourfold), (describe, DoublySpecialCubicFourfold), (check, DoublySpecialCubicFourfold), (clean, DoublySpecialCubicFourfold), (random, DoublySpecialCubicFourfold), (mirrorFourfold, DoublySpecialCubicFourfold), (quadricFibration, DoublySpecialCubicFourfold), (associatedK3surface, DoublySpecialCubicFourfold)} + +undocumented {(surface, VisibleList, VisibleList, Ring, Option), (surface, VisibleList, VisibleList, Option), (surface, VisibleList, VisibleList, Ring), (surface, VisibleList, VisibleList)} + +document {Key => {surfaces, (surfaces, DoublySpecialCubicFourfold)}, +Headline => "get the special surfaces contained in the cubic fourfold", +Usage => "surfaces X", +Inputs => {"X" => DoublySpecialCubicFourfold}, +Outputs => {{"the two surfaces contained in the fourfold ",TT"X"}}, +EXAMPLE {"X = specialFourfold \"DSCF-6\";", "surfaces X"}, +SeeAlso => {(surface,HodgeSpecialFourfold)}} + +document {Key => {swap, (swap, DoublySpecialCubicFourfold)}, +Headline => "swap the order of the two special surfaces", +Usage => "swap X", +Inputs => {"X" => DoublySpecialCubicFourfold}, +Outputs => {DoublySpecialCubicFourfold => {"the same fourfold with the order of the two surfaces reversed."}}, +EXAMPLE {"S = random({3:{1}},0_(PP_(ZZ/65521)^5));", "T = random S;", "X = specialFourfold (S & T);", "surfaces X", "Y = swap X;", "surfaces Y", "assert(X == Y)"}, +SeeAlso => {surfaces}} diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/examples.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/examples.m2 new file mode 100644 index 00000000000..631f0da496a --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/examples.m2 @@ -0,0 +1,152 @@ + +------------------------------------------------------------------------ +-------------------------- Prebuilt Examples --------------------------- +------------------------------------------------------------------------ + +trisecantFlop = method(Options => {Verbose => false}); +trisecantFlop ZZ := o -> i -> ( + try needsPackage "TrisecantFlops" else ( + git := findProgram("git", "git --help"); + dir := temporaryFileName() | "/"; + mkdir dir; + <<"The package TrisecantFlops is not present."< dir); + runProgram(git, "checkout master", RunDirectory => dir | "/TrisecantFlops"); + if not fileExists(dir|"TrisecantFlops/TrisecantFlops.m2") then error "something went wrong in downloading the package TrisecantFlops"; + try needsPackage("TrisecantFlops",FileName => dir|"TrisecantFlops/TrisecantFlops.m2") else error "something went wrong in loading the package TrisecantFlops"; + <<"The package TrisecantFlops has been successfully loaded."< false,FileName => dir|"TrisecantFlops/TrisecantFlops.m2"); + ); + ); + if not member(value "TrisecantFlops",loadedPackages) then error "something went wrong"; + if (value "TrisecantFlops").Options.Version < "1.7" then ( + if o.Verbose then <<"-- removing old version of TrisecantFlops"<"|toString(o.Verbose)|")") +); + +prebuiltExamplesOfRationalFourfolds = memoize(() -> ( + try needsPackage "PrebuiltExamplesOfRationalFourfolds" else ( + curl := findProgram("curl", "curl -h"); + dir := temporaryFileName() | "/"; + mkdir dir; + <<"The package PrebuiltExamplesOfRationalFourfolds is not present."<dir); + if not fileExists(dir|"PrebuiltExamplesOfRationalFourfolds.m2") then error "something went wrong in downloading the package PrebuiltExamplesOfRationalFourfolds"; + try needsPackage("PrebuiltExamplesOfRationalFourfolds",FileName => dir|"PrebuiltExamplesOfRationalFourfolds.m2") else error "something went wrong in loading the package PrebuiltExamplesOfRationalFourfolds"; + <<"The package PrebuiltExamplesOfRationalFourfolds has been successfully loaded."< false,FileName => dir|"PrebuiltExamplesOfRationalFourfolds.m2"); + ); + ); + if not member(value "PrebuiltExamplesOfRationalFourfolds",loadedPackages) then error "something went wrong"; + if (value "PrebuiltExamplesOfRationalFourfolds").Options.Version < "1.1" then ( + <<"-- removing old version of PrebuiltExamplesOfRationalFourfolds"< EmbeddedProjectiveVariety, Options => {CoefficientRing => ZZ/65521}); +fanoFourfold (ZZ,ZZ) := o -> (d,g) -> ( + K := o.CoefficientRing; + if not (instance(K,Ring) and isField K) then error "CoefficientRing option expects a field"; + local Y; local j; local S; local psi; + local X; + dg := {(2,0),(3,1),(4,1),(5,1),(4,3),(6,4),(8,5),(10,6),(12,7),(14,8),(16,9),(18,10)}; + if not member((d,g),dg) then error("expected a pair of integers in the set "|toString(dg)); + if d == 2 and g == 0 then X = random({2},0_(PP_K^5)); + if d == 3 and g == 1 then X = random({3},0_(PP_K^5)); + if d == 4 and g == 1 then X = random({{2},{2}},0_(PP_K^6)); + if d == 5 and g == 1 then ( + Y = GG(K,1,4); + j = parametrize random({{1},{1}},0_Y); + X = j^* Y; + ); + if d == 4 and g == 3 then X = random({4},0_(PP_K^5)); + if d == 6 and g == 4 then X = random({{2},{3}},0_(PP_K^6)); + if d == 8 and g == 5 then X = random({3:{2}},0_(PP_K^7)); + if d == 10 and g == 6 then ( + Y = GG(K,1,4); + j = parametrize random({1},0_Y); + X = (j^* Y) * random({2},0_(source j)); + ); + if d == 12 and g == 7 then ( + S = surface({3,4},K); + psi = rationalMap(S * random({1},0_S),2); + j = parametrize random({1},0_(target psi)); + X = j^* image(2,psi); + ); + if d == 14 and g == 8 then ( + Y = GG(K,1,5); + j = parametrize random({4:{1}},0_Y); + X = j^* Y; + ); + if d == 16 and g == 9 then ( + S = surface({2},K,ambient=>6); + psi = rationalMap(S,3,2); + j = parametrize random({{1},{1}},0_(target psi)); + X = j^* image psi; + ); + if d == 18 and g == 10 then ( + -- p. 4 of [Kapustka and Ranestad - Vector Bundles On Fano Varieties Of Genus Ten] + w := gens ring PP_K^13; + M := matrix {{0,-w_5,w_4,w_6,w_7,w_8,w_0}, + {w_5,0,-w_3,w_12,w_13,w_9,w_1}, + {-w_4,w_3,0,w_10,w_11,-w_6-w_13,w_2}, + {-w_6,-w_12,-w_10,0,w_2,-w_1,w_3}, + {-w_7,-w_13,-w_11,-w_2,0,w_0,w_4}, + {-w_8,-w_9,w_6+w_13,w_1,-w_0,0,w_5}, + {-w_0,-w_1,-w_2,-w_3,-w_4,-w_5,0}}; + Y = Var trim pfaffians(4,M); + j = toRationalMap parametrize random({1},0_Y); + X = j^* Y; + ); + if not (dim X == 4 and degree X == d and sectionalGenus X == g) then error("something went wrong while computing random fourfold of degree "|toString(d)|" and sectional genus "|toString(g)); + return X; +); + +parametrizeFanoFourfold = method(TypicalValue => MultirationalMap, Options => {Strategy => 1}); +parametrizeFanoFourfold EmbeddedProjectiveVariety := o -> X -> ( + X#InverseMethod = inverse3; + if dim X != 4 then error "expected a fourfold"; + if degree X == 5 and sectionalGenus X == 1 then ( + if o.Strategy === 2 then return parametrize X; + if o.Strategy =!= 1 then error("the available strategies are:"|newline|"-- 1: projection from the plane spanned by a conic contained in the fourfold"|newline|"-- 2: projection from the unique sigma_(2,2) plane contained in the fourfold (Todd's result)"); + p := point X; + C := line(X,p) + line(X,p); + if degree C != 2 then error "something went wrong while finding conic on fourfold"; + return inverse3 multirationalMap rationalMap(sub(ideal C,ring X),1); + ); + if o.Strategy =!= 1 then error "strategy not available"; + parametrize X +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/mirrorFourfolds.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/mirrorFourfolds.m2 new file mode 100644 index 00000000000..6557eddcb03 --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/mirrorFourfolds.m2 @@ -0,0 +1,181 @@ + +------------------------------------------------------------------------ +---------------- Mirror fourfolds: computing surfaces U ---------------- +------------------------------------------------------------------------ + +surfaceDeterminingInverseOfFanoMap = method(Options => {Verbose => false, Strategy => null}); + +surfaceDeterminingInverseOfFanoMap HodgeSpecialFourfold := o -> X -> ( + if (surface X).cache#?("surfaceDeterminingInverseOfFanoMap",ideal X) then return (surface X).cache#("surfaceDeterminingInverseOfFanoMap",ideal X); + a := degreeHypersurface X; + Str := o.Strategy; + if Str === null then ( + Str = "Interpolate"; + if member(recognize X,{"planeInPP7", "quinticDelPezzoSurface", "quarticScrollSurface", 1, 6}) then Str = "Inverse"; + if member(recognize X,{"C38Coble", "FarkasVerra", 3, 17, "october2021-26''", 18}) and char coefficientRing X <= 65521 then Str = "F4"; + ); + mu := fanoMap X; + if Str === "Inverse" then ( + if o.Verbose then << "-- computing inverse of the restriction of the Fano map..." << endl << flush; + muInv := inverse3(mu|X); + if o.Verbose then << "-- inverse map computed: defined by forms of degree " << degreeOfDefiningForms muInv << endl << flush; + if not X.cache#?"rationalParametrization" then X.cache#"rationalParametrization" = muInv; + Ui := projectiveVariety(if member(recognize X,{"planeInPP7", 1, 6}) then gens saturate ideal matrix muInv else matrix muInv); + if not isSurfaceUknownToNotNeedRefining X then Ui = refineSurfaceU(Ui, o.Verbose); + Ui.cache#"strategy for surface U" = Str; + if o.Verbose then << "-- result: " << ? Ui << endl << flush; + return (surface X).cache#("surfaceDeterminingInverseOfFanoMap",ideal X) = Ui; + ); + mu = super mu; + if Str === "Interpolate" then (W := Var image mu; iW := lift(3 - (2*(sectionalGenus W)-2)/(degree W),ZZ)); + m := numgens ambient target map X -1; + if o.Verbose then << "-- needed " << m << " steps" << endl << flush; + local mu'; + U := Var trim sum apply(m,j -> + ( + if o.Verbose then <<"-- (step "<o.Verbose) + else error "unrecognized Strategy; available strategies are: \"DirectImage\", \"F4\", \"Interpolate\"" + ) + ); + if recognize X === "NotRecognized" then U = interpolateTop(U,Verbose=>verbosityInterpolateTop(o.Verbose),cache=>true); + if member(recognize X,{"FarkasVerra", 1, "surf-5-6-2-nodal"}) then U = top U; + if recognize X === "surf-7-1-9" then (if o.Verbose then <<"-- removing cubic scroll component from surface"< X -> ( + mu := fanoMapDSCF(X,Verbose=>o.Verbose); + if mu.cache#?("surfaceDeterminingInverseOfFanoMap",X) then return mu.cache#("surfaceDeterminingInverseOfFanoMap",X); + if o.Verbose then << "-- restricting Fano map to the cubic fourfold" << endl; + mu' := mu|X; + local eta; + Str := setStrategyDSCFtoK3(X,o.Strategy); + if Str === "Inverse" then ( + if o.Verbose then << "-- computing inverse of the restricted map..." << endl << flush; + eta = inverse3 mu'; + if o.Verbose then ( + << "-- inverse map computed: defined by forms of degree " << degreeOfDefiningForms eta << endl; + << "-- map stored in cache: retrieve it with importFrom(SpecialFanoFourfolds, \"getInverseFanoMap\")" << endl << flush; + ); + ) else if Str === "Approximate" then ( + if o.Verbose then << "-- computing approximate inverse of the restricted map..." << endl << flush; + eta = multirationalMap approximateInverseMap(toRationalMap mu',Verbose=>o.Verbose,Certify=>false); + if o.Verbose then << "-- approximate inverse computed: defined by forms of degree " << degreeOfDefiningForms eta << endl << flush; + ) else ( + error("invalid strategy: '" | toString(Str) | "'; available options are \"Inverse\" or \"Approximate\""); + ); + if o.Verbose then << "-- analyzing the base locus of the inverse map..." << endl; + B := projectiveVariety(trim lift(ideal matrix eta,ring ambient source eta),Saturate=>true); -- quickBaseLocus + if dim B < 2 then error "base locus dimension of at least 2 required"; + U := B; + if dim B > 2 then ( + topB := interpolateTop(B,Verbose=>verbosityInterpolateTop(o.Verbose),cache=>true); + U = B \ topB; + if dim U != 2 then error "extraction failed: dimension of (B \\ top(B)) is not 2"; + ); + assert(dim U == 2); + if not isSurfaceUknownToBeAlreadyEquidimensional(X,mu) then ( + if o.Verbose then << "-- surface found in base locus; verifying equidimensionality..." << endl; + tU := interpolateTop(U,Verbose=>verbosityInterpolateTop(o.Verbose),cache=>true); + assert(dim tU == 2); + if tU != U then ( + U = tU; + if o.Verbose then << "-- removed components of dimension < 2" << endl; + mu.cache#"surface U is already equidimensional" = false; + ) else ( + if o.Verbose then << "-- equidimensionality verified" << endl; + mu.cache#"surface U is already equidimensional" = true; + ); + ) else ( + if o.Verbose then << "-- surface found in base locus; equidimensionality already known, skipping..." << endl; + ); + if not isSurfaceUknownToNotNeedRefining X then U = refineSurfaceU(U, o.Verbose); + if not U.cache#?"top" then U.cache#"top" = U; + if Str === "Inverse" then U.cache#"birational maps from X to W and from W to X" = (mu',eta); + U.cache#"strategy for surface U" = Str; + if o.Verbose then << "-- result: " << ? U << endl << flush; + mu.cache#("surfaceDeterminingInverseOfFanoMap",X) = U +); + +refineSurfaceU = (U,verb) -> ( + if verb then << "-- projecting to PP^3 for surface decomposition" << endl; + pr := rationalMap for i to 3 list random(1,ring ambient U); + Z := projectiveVariety kernel(map toRationalMap(pr|U),SubringLimit=>1); + Z1 := {}; Z2 := {}; + for D in decompose Z do if dim D == 2 and degree D <= 3 then Z1 = append(Z1,D) else Z2 = append(Z2,D); + if #Z2 != 1 then error("unsupported surface decomposition: found "| toString(#Z2) |" components of degree >= 4"); + if #Z1 == 0 then ( + if verb then << " -- surface was already irreducible" << endl; + return U; + ) else ( + if verb then << " -- removing " << #Z1 << " components of degrees " << apply(Z1, degree) << endl; + ); + W := apply(Z1, D -> support interpolateTop(U * (pr^* D), Verbose=>verbosityInterpolateTop(verb),cache=>true)); + assert all(W, D -> dim D == 2 and degree D <= 3); + vU := U; + for D in W do vU = vU \\ D; + assert(dim vU == 2); + Curves := apply(select(apply(W, D -> vU * D), C -> dim C == 1), E -> support interpolateTop(E, Verbose=>verbosityInterpolateTop(verb),cache=>true)); + Curves = apply(sort apply(Curves, C -> (degree C,C)), last); + if # Curves > 0 then vU.cache#"special curves on U" = Curves; + vU +); + +isSurfaceUknownToNotNeedRefining = method(); +isSurfaceUknownToNotNeedRefining DoublySpecialCubicFourfold := X -> isFanoMapStandard(X) and member(recognizeDSCF X, {"DSCF-V1-1", "DSCF-V1-2", "DSCF-V1-3", "DSCF-V1-7", "DSCF-V1-9", "DSCF-V1-12", "DSCF-V1-16", "DSCF-V1-17", "DSCF-V1-18", "DSCF-V1-20", "DSCF-V1-21", "DSCF-V1-25", "DSCF-V1-26", "DSCF-V1-28", "DSCF-V1-29", "DSCF-V1-30", "DSCF-V1-32", "DSCF-V1-33", "DSCF-V1-35", "DSCF-V1-36", "DSCF-V1-37", "DSCF-V1-38", "DSCF-V1-39", "DSCF-V1-40"}); +isSurfaceUknownToNotNeedRefining IntersectionOfThreeQuadricsInP7 := X -> not member(recognize X, {"surf-7-1-9", "NotRecognized"}); +isSurfaceUknownToNotNeedRefining GushelMukaiFourfold := isSurfaceUknownToNotNeedRefining CubicFourfold := X -> true; +isSurfaceUknownToNotNeedRefining HodgeSpecialFourfold := X -> false; + +getInverseFanoMap = method(); +getInverseFanoMap SurfaceAssociatedToRationalFourfold := Utilde -> ( + (mu,U,C,f) := building Utilde; + if not U.cache#?"birational maps from X to W and from W to X" then error "birational maps between cubic fourfold X and mirror fourfold W not found in cache; the K3 surface may have been computed using an unsuitable strategy"; + last U.cache#"birational maps from X to W and from W to X" +); +getInverseFanoMap DoublySpecialCubicFourfold := X -> ( + (S,P) := surfaces X; + if not S.cache#?("FanoMapDSCF",P) then error "Fano map not found in cache; polarizedK3surface or mirrorFourfold must be called first"; + mu := fanoMapDSCF(X,Verbose=>false); + if not mu.cache#?("surfaceDeterminingInverseOfFanoMap",X) then error "inverse of Fano map not found in cache; polarizedK3surface or mirrorFourfold must be called first"; + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>false); + if not U.cache#?"birational maps from X to W and from W to X" then error "birational maps between cubic fourfold X and mirror fourfold W not found in cache; polarizedK3surface may have been computed using an unsuitable strategy"; + last U.cache#"birational maps from X to W and from W to X" +); + +mirrorFourfold = method(TypicalValue => HodgeSpecialFourfold, Options => {Verbose => false, Strategy => null, Singular => null}); + +mirrorFourfold CubicFourfold := mirrorFourfold GushelMukaiFourfold := mirrorFourfold IntersectionOfThreeQuadricsInP7 := o -> X -> ( + if X.cache#?(surface X,"associatedFourfold") then return X.cache#(surface X,"associatedFourfold"); + if o.Verbose then <<"-- computing the Fano map"<o.Singular,Verbose=>o.Verbose); + if o.Verbose then <<"-- computing the surface U corresponding to the given fourfold"<o.Verbose,Strategy=>o.Strategy); + W := specialFourfold(U,Var target mu,InputCheck=>0); + W.cache#(surface W,"associatedFourfold") = X; + X.cache#(surface X,"associatedFourfold") = W +); + +mirrorFourfold DoublySpecialCubicFourfold := o -> X -> ( + mu := fanoMapDSCF(X,Verbose=>o.Verbose); + if mu.cache#?("mirrorFourfold",X) then return mu.cache#("mirrorFourfold",X); + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>o.Verbose,Strategy=>o.Strategy); + W := specialFourfold(U,target mu,InputCheck=>0); + W.cache#(U,"associatedFourfold") = X; + mu.cache#("mirrorFourfold",X) = W +); + +mirrorFourfold HodgeSpecialFourfold := o -> X -> ( + if not X.cache#?(surface X,"associatedFourfold") then error "can't find associated fourfold"; + X.cache#(surface X,"associatedFourfold") +); diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/tests.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/tests.m2 new file mode 100644 index 00000000000..ea5e9cecb5a --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/tests.m2 @@ -0,0 +1,408 @@ + +------------------------------------------------------------------------ +------------------------------- Tests ---------------------------------- +------------------------------------------------------------------------ + +TEST /// -- Test 0 -- cubic fourfolds from strings: describe, discriminant, parameterCount +strIn := {"quintic del Pezzo surface", "quartic scroll", "3-nodal septic scroll", "one-nodal septic del Pezzo surface", "general cubic 4-fold of discriminant 38", "general cubic 4-fold of discriminant 42", "cubic 4-fold of discriminant 48"}; +strOut := "Special cubic fourfold of discriminant 14 +containing a rational surface of degree 5 and sectional genus 1 +cut out by 5 hypersurfaces of degree 2 +Special cubic fourfold of discriminant 14 +containing a rational surface of degree 4 and sectional genus 0 +cut out by 6 hypersurfaces of degree 2 +Special cubic fourfold of discriminant 26 +containing a 3-nodal surface of degree 7 and sectional genus 0 +cut out by 13 hypersurfaces of degree 3 +Special cubic fourfold of discriminant 26 +containing a 1-nodal surface of degree 7 and sectional genus 1 +cut out by 14 hypersurfaces of degree 3 +Special cubic fourfold of discriminant 38 +containing a rational surface of degree 10 and sectional genus 6 +cut out by 10 hypersurfaces of degree 3 +Special cubic fourfold of discriminant 42 +containing a 5-nodal surface of degree 9 and sectional genus 2 +cut out by 9 hypersurfaces of degree 3 +Special cubic fourfold of discriminant 48 +containing a 6-nodal surface of degree 9 and sectional genus 2 +cut out by 5 hypersurfaces of degrees 2^1 3^4 +"; +X = apply(strIn,cubicFourfold); +-- X = apply(strIn,x -> cubicFourfold(x,InputCheck=>10,Verbose=>true)); +assert all(X,x -> x.cache#?(surface x,"label")); +assert(concatenate apply(X,x -> toString describe x | newline) == strOut); +Y = apply(X,x -> cubicFourfold surface x); +assert all(Y,y -> not y.cache#?(surface y,"label")); +assert(apply(Y,discriminant) == {14,14,26,26,38,42,48}); +assert(concatenate apply(Y,y -> toString describe y | newline) == strOut); +assert(parameterCount(Y_0,Verbose=>true) == (1, (25, 35, 5)) and parameterCount(Y_1,Verbose=>true) == (1, (28, 29, 2))); +/// + +TEST /// -- Test 1 (1/2) -- GM fourfolds from strings: describe, discriminant, parameterCount, toGrass +strIn := {"sigma-plane", "rho-plane", "tau-quadric"}; +strOut := "Special Gushel-Mukai fourfold of discriminant 10('') +containing a plane +and with class in G(1,4) given by s_(3,1) +Type: ordinary +(case 6 of Table 1 in arXiv:2002.07026) +Special Gushel-Mukai fourfold of discriminant 12 +containing a plane +and with class in G(1,4) given by s_(2,2) +Type: ordinary +(case 9 of Table 1 in arXiv:2002.07026) +Special Gushel-Mukai fourfold of discriminant 10(') +containing a surface of degree 2 and sectional genus 0 +cut out by 6 hypersurfaces of degrees 1^5 2^1 +and with class in G(1,4) given by s_(3,1)+s_(2,2) +Type: ordinary +(case 1 of Table 1 in arXiv:2002.07026) +"; +X = apply(strIn,gushelMukaiFourfold); +assert(apply(X,x -> x.cache#(surface x,"label")) == {6, 9, 1}); +assert(concatenate apply(X,x -> toString describe x | newline) == strOut); +Y = apply(X,x -> gushelMukaiFourfold(sub(ideal (toGrass x) surface x,ring target toGrass x),InputCheck=>0)) +assert all(Y,y -> not y.cache#?(surface y,"label")); +assert(apply(Y,discriminant) == {10, 12, 10}); +assert(concatenate apply(Y,y -> toString describe y | newline) == strOut); +assert(parameterCount(Y_0,Verbose=>true) == (2, (34, 4, 0)) and parameterCount(Y_1,Verbose=>true) == (3, (34, 3, 0))); +/// + +TEST /// -- Test 2 (2/2) -- GM fourfolds from strings: describe, discriminant, parameterCount, toGrass +strIn := {"cubic scroll", "quintic del Pezzo surface", "general GM 4-fold of discriminant 20"}; +strOut := "Special Gushel-Mukai fourfold of discriminant 12 +containing a surface of degree 3 and sectional genus 0 +cut out by 7 hypersurfaces of degrees 1^4 2^3 +and with class in G(1,4) given by 2*s_(3,1)+s_(2,2) +Type: ordinary +(case 7 of Table 1 in arXiv:2002.07026) +Special Gushel-Mukai fourfold of discriminant 10('') +containing a surface of degree 5 and sectional genus 1 +cut out by 8 hypersurfaces of degrees 1^3 2^5 +and with class in G(1,4) given by 3*s_(3,1)+2*s_(2,2) +Type: ordinary +(case 4 of Table 1 in arXiv:2002.07026) +Special Gushel-Mukai fourfold of discriminant 20 +containing a surface of degree 9 and sectional genus 2 +cut out by 19 hypersurfaces of degree 2 +and with class in G(1,4) given by 6*s_(3,1)+3*s_(2,2) +Type: ordinary +(case 17 of Table 1 in arXiv:2002.07026) +"; +X = apply(strIn,x -> clean gushelMukaiFourfold x); +debug SpecialFanoFourfolds; +assert(apply(X,recognize) == {7, 4, 17}); +assert(concatenate apply(X,x -> toString describe x | newline) == strOut); +Y = apply(X,x -> gushelMukaiFourfold(sub(ideal (toGrass x) surface x,ring target toGrass x),InputCheck=>0)) +assert all(Y,y -> not y.cache#?(surface y,"label")); +assert(apply(Y,discriminant) == {12, 10, 20}); +assert(concatenate apply(Y,y -> toString describe y | newline) == strOut); +assert(parameterCount(Y_1,Verbose=>true) == (1, (24, 18, 3))); +/// + +TEST /// -- Test 3 -- 21 examples from GMtables +X = for i from 1 to 21 list ( + A = GMtables(i,ZZ/65521); + time gushelMukaiFourfold((rationalMap(ideal A_0,Dominant=>2)) ideal A_1,InputCheck=>0) +); +S = apply(X,x -> surface x); +assert(apply(X,x -> degree surface x) === {2, 4, 14, 5, 9, 1, 3, 7, 1, 10, 10, 14, 12, 8, 9, 11, 9, 7, 10, 4, 12}); +assert(apply(X,x-> sectionalGenus surface x) == {0, 0, 8, 1, 3, 0, 0, 2, 0, 4, 3, 8, 5, 2, 3, 5, 2, 0, 3, 0, 5}); +assert(last cycleClass X_18 == (6,4) and discriminant X_18 == 24); +assert(last cycleClass X_7 == (4,3) and discriminant X_7 == 12); +/// + +TEST /// -- Test 4 -- parametrizations of Fano fourfolds +setRandomSeed 0; +for dg in {(2,0),(3,1),(4,1),(5,1),(4,3),(6,4),(8,5),(10,6),(12,7),(14,8),(16,9),(18,10)} do ( + <<"(d,g) = "<"random point") == 1 and target h === X and ambient source h == source h and h#"inverse" =!= null); +time f = unirationalParametrization X; +assert(# factor f == 1 and target f === X and unique degrees ideal matrix first factor f == {{10}}); +assert isSubset(f point source f,X); +S = schubertCycle({3,1},GG(ZZ/33331,1,4),Standard=>true); +Y = gushelMukaiFourfold S; +time g = parametrize Y; +assert(degree(g,Strategy=>"random point") == 1 and target g === Y and dim ambient source g == 5 and dim source g == 4 and g#"inverse" =!= null); +-- time g = unirationalParametrization Y; +-- assert(# factor g == 1 and target g === Y and unique degrees ideal matrix first factor g == {{26}}) +-- assert isSubset(g point source g,Y) +/// + +TEST /// -- Test 6 (1/3) -- associated K3 surfaces +f = last building associatedK3surface(cubicFourfold "quartic scroll",Verbose=>false); +assert(f#"image" =!= null and dim image f == 2 and degree image f == 14 and dim target f == 8) +/// + +TEST /// -- Test 7 (2/3) -- associated K3 surfaces +g = last building associatedK3surface(cubicFourfold "quintic del Pezzo surface",Verbose=>true,Singular=>false); +assert(g#"image" =!= null and dim image g == 2 and degree image g == 14 and dim target g == 8) +/// + +TEST /// -- Test 8 (3/3) -- associated K3 surfaces +building associatedK3surface(gushelMukaiFourfold "tau-quadric",Verbose=>false); +/// + +TEST /// -- Test 9 -- simple tests on schubertCycle +debug MultiprojectiveVarieties; +S = schubertCycle({2,2},GG(ZZ/33331,1,4),Standard=>true) +assert(idealOfSubvariety S == idealOfSubvariety tangentialChowForm(projectiveVariety ideal((Grass(0,4,ZZ/33331,Variable=>"x"))_0,(Grass(0,4,ZZ/33331,Variable=>"x"))_1),1,1)) +S = schubertCycle({3,2,1},GG(ZZ/33331,2,5),Standard=>true) +use ring ambientVariety S; +assert(idealOfSubvariety S == ideal(x_(0,4,5),x_(0,3,5),x_(1,2,5),x_(0,2,5),x_(0,1,5),x_(2,3,4),x_(1,3,4),x_(0,3,4),x_(1,2,4),x_(0,2,4),x_(0,1,4),x_(1,2,3),x_(0,2,3),x_(0,1,3),x_(0,1,2))) +/// + +TEST /// -- Test 10 (1/2) -- detectCongruence +X = cubicFourfold("quintic del Pezzo surface",ZZ/33331); +detectCongruence(X,Verbose=>true); +/// + +TEST /// -- Test 11 (2/2) -- detectCongruence +use Grass(1,4,ZZ/33331); +S31 = ideal(p_(3,4),p_(2,4),p_(1,4),p_(0,4),p_(2,3),p_(1,3),p_(1,2)); +Y = gushelMukaiFourfold(S31,InputCheck=>0); +assert(not Y.cache#?(surface Y,"label")); Y.cache#(surface Y,"label") = 6; +detectCongruence(Y,Verbose=>true); +-- Y = gushelMukaiFourfold("18",ZZ/3331); +-- detectCongruence Y; +/// + +TEST /// -- Test 12 (1/2) -- GM fourfolds containing nodal surfaces +debug SpecialFanoFourfolds; +K = ZZ/65521; +X = makeGMfromCurveOnSurfaceInP6((surface({2,0,0,0},K,ambient=>6)).cache#"takeCurve" (1,(0,0,0)),InputCheck=>0); +assert(discriminant X == 36); +assert(numberNodes surface X == 1); +X' = random X; +assert(surface X === surface X' and ambientFivefold X === ambientFivefold X' and isSubset(surface X',X') and dim(X*X') == 3); +assert(discriminant X' == 44 and discriminant X == 44); +/// + +TEST /// -- Test 13 (2/2) -- GM fourfolds containing nodal surfaces +X = gushelMukaiFourfold("nodal surface of degree 11 and genus 3 with class (7,4)",ZZ/33331,InputCheck=>0); +assert(discriminant X == 26 and last cycleClass X == (7,4) and degree surface X == 11 and sectionalGenus surface X == 3); +Y = gushelMukaiFourfold("nodal D44",ZZ/33331,InputCheck=>0); +assert(discriminant Y == 44 and last cycleClass Y == (6,3) and degree surface Y == 9 and sectionalGenus surface Y == 1); +/// + +TEST /// -- Test 14 -- gluing scrolls along curves +debug SpecialFanoFourfolds +S = surface({3,4,0,0},ambient=>6); +for a in {(1,0),(2,0),(3,0),(4,0),(5,0),(5,1)} do ( + (d,g) := a; + E := curvesOnSurface(S,d,g); + assert(#E>0); + for C in E do ( + <<"(d,g) = "<<(d,g)<<", curve: "<6),InputCheck=>0,"Gluing"=>"cubic scroll",Degrees=>hashTable{1=>(1,1),2=>(19,infinity),3=>(0,0)}); +X = first L; +assert(#L == 1 and discriminant X == 18 and last cycleClass X == (5,3)) +-- L = takeGMsfromSurfaceInP6(surface({3,1,1,0},ambient=>6),InputCheck=>0,"Gluing"=>"quartic scroll",Degrees=>hashTable{1=>(1,1),2=>(19,infinity),3=>(0,0)}); +-- X = first L; +-- assert(#L == 1 and discriminant X == 20 and last cycleClass X == (4,3)) +/// + +TEST /// -- Test 16 -- new description for fourfolds in v.2.8 +X = cubicFourfold "quartic scroll"; +mirrorFourfold X; +s1 = "Special cubic fourfold of discriminant 14 +containing a rational surface of degree 4 and sectional genus 0 +cut out by 6 hypersurfaces of degree 2 +K3 status: [â–“â–“â–“â–‘â–‘ / â–“â–“â–“â–“â–“] +Mirror fourfold: hypersurface in PP^5 of degree 2 +Surface U of degree 10, sectional genus 7, χ(O_U) = 2, cut out by 7 hypersurfaces of degrees 2^1 3^6 "; +assert(toString describe X === s1) +associatedK3surface X +s2 = "Special cubic fourfold of discriminant 14 +containing a rational surface of degree 4 and sectional genus 0 +cut out by 6 hypersurfaces of degree 2 +K3 status: [â–“â–“â–“â–“â–“ / â–“â–“â–“â–“â–“] +Mirror fourfold: hypersurface in PP^5 of degree 2 +Surface U of degree 10, sectional genus 7, χ(O_U) = 2, cut out by 7 hypersurfaces of degrees 2^1 3^6 +Exceptional curves: an irreducible conic curve +Minimal K3 surface Ũ: degree 14 and sectional genus 8 in PP^8 cut out by 15 hypersurfaces of degree 2"; +assert(toString describe X === s2) +/// + +TEST /// -- Test 17 +X = specialFourfold "plane in PP^7"; +assert(discriminant X == 31); +S = associatedCastelnuovoSurface X; +assert((dim S,dim ambient S,degree S,sectionalGenus S,degrees S) == (2, 4, 9, 9, {({3}, 1), ({4}, 3)})) +/// + +TEST /// -- Test 18 +debug MultiprojectiveVarieties; +K := ZZ/65521; +P := PP_K(1,1,1,4); +C = random({{2},{3}},0_P) +assert(discriminant(surface(C,random(8,C)),Algorithm=>2) == -7) +D = internalProjection_2 (surface({3, 1, 1, 0},K)).cache#"takeCurve"(3,{0, 0, 0}); +x := gens ring P; +f := (rationalMap {{x_0^4,x_0^3*x_1,x_0^3*x_2,x_3}}) << (ambient D); +E = f^* D; +assert(discriminant(surface(E,random(8,E)),Algorithm=>1) == -43) +E' = f^* random D; +assert(discriminant(surface(E',random(8,E')),Algorithm=>2) == -43) +/// + +TEST /// -- test 19 DSCF +-- generateInputsForRunExampleTest 6 +debug SpecialFanoFourfolds; +runExampleTest(6,64997,4,0,20,6,4,2,4,1,0,0,0,4,matrix {{6, 5}, {5, -2}}) +/// + +TEST /// -- test 20 DSCF +debug SpecialFanoFourfolds; +(B,V,C) = GMtables(1,ZZ/65521); +assert(B * V == C and dim C == 1) +X = specialFourfold(B & V); +assert(surfaceIntersectionNumber X == 1) +assert(latticeIntersectionMatrix3x3 X == matrix {{3, 3, 2}, {3, 7, 1}, {2, 1, 4}}) +/// + +TEST /// -- test 21 DSCF +debug SpecialFanoFourfolds; +debug MultiprojectiveVarieties; +S = nodalProjection surface {3,3}; +n = inverseNormalizationMapRaw(S,"S",1,true); +assert(dim target n == 6 and dim image n == 2 and degree image n == 6 and euler hilbertPolynomial image n == 1) +S' = nodalProjection surface {3,1}; +n' = inverseNormalizationMapExperimentalRaw(S',"S'",1,true) +assert(dim target n' == 8 and dim image n' == 2 and degree image n' == 8 and euler hilbertPolynomial image n' == 1) +/// + +TEST /// -- test 22 DSCF +debug SpecialFanoFourfolds; +L = discoverCubicFourfoldsInC8({4,4,3,2,1},1,4,5,7,"test_file",true,300,{}); +assert(apply(L,discriminant) == {14, 20, 24, 32, 38}) +assert(fileExists "test_file.txt" and fileExists "test_file_commands.m2") +removeFile "test_file.txt" +removeFile "test_file_commands.m2" +/// + +TEST /// -- test 23 DSCF -- switch between Fano map types +debug SpecialFanoFourfolds; +X = specialFourfold("DSCF-"|(toString 6)) +E = polarizedK3surface(X,Verbose=>true,Strategy=>("Approximate","MapFromU-Virtual"),FanoMapType=>"P2xP2") +assert(computationStatus X == 2); +(mu,U,LC,f) = building E; +assert(U === surface mirrorFourfold X); +assert(mirrorFourfold mirrorFourfold X === X); +assert(degree U == 19 and sectionalGenus U == 15 and euler hilbertPolynomial U == 4); +W = target mu; +assert(W == mirrorFourfold X); +assert isDeformationP2P2 mirrorFourfold X +assert(isFanoMapToP2xP2 X and fanoMapDSCF X === mu); +assert(E#"LatticePolarization" === null); +polarizedK3surface(X,Verbose=>true); +assert(E#"LatticePolarization" =!= null); +assert(computationStatus X == 2); +assert(isVirtualLatticeK3 E); +assert instance(latticePolarization E, LatticePolarizationOnK3Surface); +assert(latticeMatrix latticePolarization E == matrix {{3, 7}, {7, 2}}); +assert(recoverFourfold E === X and recoverFourfold projectiveVariety E === X); +assert("Approximate" === U.cache#"strategy for surface U"); +assert(not U.cache#?"birational maps from X to W and from W to X"); +E' = polarizedK3surface(X,Verbose=>true,Strategy=>("Inverse","MapFromW-Virtual"),FanoMapType=>"Standard") +W' = mirrorFourfold X; +U' = (building E')_1; +assert(not isDeformationP2P2 W'); +assert(isFanoMapStandard X and fanoMapDSCF X === first building E'); +assert(E'#"LatticePolarization" === null); +polarizedK3surface(X,Verbose=>true); +assert(E'#"LatticePolarization" =!= null); +assert(computationStatus X == 3); +assert(isVirtualLatticeK3 E'); +assert instance(latticePolarization E', LatticePolarizationOnK3Surface); +assert(latticeMatrix latticePolarization E' == matrix {{6, 12}, {12, 2}}); +assert(recoverFourfold E' === X and recoverFourfold projectiveVariety E' === X); +assert("Inverse" === U'.cache#"strategy for surface U"); +assert(U'.cache#?"birational maps from X to W and from W to X"); +E'' = polarizedK3surface polarizedK3surface(X,Verbose=>true,Strategy=>"Approximate",FanoMapType=>"P2xP2") +assert(isDeformationP2P2 mirrorFourfold X); +assert(latticeMatrix latticePolarization E'' == matrix {{3, 7}, {7, 2}}); +/// + +TEST /// -- test 24 DSCF +-- reduced complexity to avoid memory allocation failures +debug SpecialFanoFourfolds; +for i in {1,5,7,20,40} do assert(recognizeDSCF specialFourfold("DSCF-"|(toString i)) === "DSCF-V1-"|toString(i)) +/// + +TEST /// -- test 25 DSCF -- Tregub +debug SpecialFanoFourfolds; +K = ZZ/65521; +S = PP_K^(2,2); +P = linearSpan {point S,point S,point S}; +X = specialFourfold(S & P); +describe X +assert(latticeIntersectionMatrix3x3 X == matrix {{3, 4, 1}, {4, 12, 3}, {1, 3, 3}}) +Utilde = polarizedK3surface(X,Strategy=>"Approximate",Verbose=>true) +assert(X.cache#(first surfaces X,last surfaces X,"labelDSCF") === "Tregub1") +assert(recognizeDSCF X === "Tregub1") +(mu,U,C,f) = building Utilde; +assert(degree U == 18 and sectionalGenus U == 10 and euler hilbertPolynomial U == -1) +/// + +TEST /// -- test 26 DSCF +-- generateInputsForRunExampleTest 33 +debug SpecialFanoFourfolds; +runExampleTest(33,42169,8,1,56,13,8,2,7,7,3,1,0,8,matrix {{14, 9}, {9, 2}}) +/// + +TEST /// -- test 27 DSCF +-- generateInputsForRunExampleTest 21 +debug SpecialFanoFourfolds; +runExampleTest(21,38113,7,1,38,16,10,2,8,8,3,2,0,10,matrix {{18, 9}, {9, 2}}) +/// + +TEST /// -- test 28 DSCF +debug SpecialFanoFourfolds; +X = specialFourfold surface((2,0,0),(1,0,0),ZZ/61001); +assert(char coefficientRing X === 61001); +T = polarizedK3surface polarizedK3surface X; +assert(computationStatus T == 4) +f = map(T,1,1); +assert(source f === projectiveVariety T and dim ambient source f == 4 and dim ambient target f == 8) +E = T(1,1); +assert(E === image f and degree E == 14 and sectionalGenus E == 8) +assert((latticePolarization T)(1,1) === E) +T' = polarizedK3surface(T,Strategy=>"MapFromU-Virtual") +assert(computationStatus T == 3) +assert instance(T'(1,1),LatticePolarizationOnK3Surface) +assert((polarizedK3surface(T,Strategy=>"SpecialCurve"))(1,1) === E) +/// diff --git a/M2/Macaulay2/packages/SpecialFanoFourfolds/utils.m2 b/M2/Macaulay2/packages/SpecialFanoFourfolds/utils.m2 new file mode 100644 index 00000000000..9a35dc9bb6b --- /dev/null +++ b/M2/Macaulay2/packages/SpecialFanoFourfolds/utils.m2 @@ -0,0 +1,580 @@ + +eulerCharacteristic = method(Options => {Algorithm => null}); +eulerCharacteristic EmbeddedProjectiveVariety := o -> X -> ( + if X.cache#?"euler" then return X.cache#"euler"; + if codim linearSpan X > 0 then return X.cache#"euler" = eulerCharacteristic((parametrize linearSpan X)^^ X,Algorithm=>o.Algorithm); + if o.Algorithm === "Hodge" then return X.cache#"euler" = euler variety X; + if o.Algorithm === "CremonaCertifyTrue" then return euler(X,Verify=>true); + K := coefficientRing X; + if K === QQ then return X.cache#"euler" = eulerCharacteristic(X ** (ZZ/65521),Algorithm=>o.Algorithm); + if char K < 1000 and K === ZZ/(char K) then <<"--warning: base field too small to use probabilistic methods"<false); + if o.Algorithm === "CharacteristicClasses" then return X.cache#"euler" = Euler(ideal X,InputIsSmooth=>true); + if o.Algorithm === null then ( + X' := if max flatten degrees ideal X > 2 and dim X == 2 then isomorphicProjectionOfSurfaceInP5 X else X; + return X.cache#"euler" = if codim X' > 0 then Euler(ideal X',InputIsSmooth=>true) else euler(X',Verify=>false); + ); + error(///Algorithm option: Expected method to compute the topological Euler characteristic. +Possible methods are the following: +"Hodge" -- command: euler variety X -- package: Core; +"CremonaCertifyTrue" -- command: EulerCharacteristic(ideal X,Certify=>true) -- package: Cremona; +"CremonaCertifyFalse" -- command: EulerCharacteristic ideal X -- package: Cremona; +"CharacteristicClasses" -- command: Euler(ideal X,InputIsSmooth=>true) -- package: CharacteristicClasses///); +); + +inverse3 = method(); +inverse3 RationalMap := psi -> ( + if psi#"inverseRationalMap" =!= null then return psi#"inverseRationalMap"; + phi := rationalMap map inverseOfMap(map psi,CheckBirational=>false,AssumeDominant=>true,QuickRank=>false,MinorsLimit=>0,Verbosity=>0); + forceInverseMap(phi,psi); + phi +); +inverse3 MultirationalMap := Psi -> ( + if Psi#"inverse" =!= null then return Psi#"inverse"; + Phi := multirationalMap inverse3 toRationalMap Psi; + if ring source Phi =!= ring target Psi then error "internal error encountered"; + Phi#"source" = target Psi; + if ring target Phi =!= ring source Psi then error "internal error encountered"; + Phi = multirationalMap(Phi,source Psi); + Phi#"inverse" = Psi; + Psi#"inverse" = Phi +); +inverse3 (RationalMap,Option) := (psi,opt) -> inverse3 psi; +inverse3 (MultirationalMap,Option) := (Psi,opt) -> inverse3 Psi; + +VirtualInverseWeightedRationalMap = new Type of WeightedRationalMap; +virtualInverseWeightedRationalMap = method(); +virtualInverseWeightedRationalMap WeightedRationalMap := Phi -> ( + new VirtualInverseWeightedRationalMap from { + symbol cache => new CacheTable, + "maps" => {}, + "target" => source Phi, + "source" => target Phi, + "image" => source Phi, + "isDominant" => true, + "isBirational" => true, + "graph" => null, + "multidegree" => null, + "baseLocus" => null, + "inverse" => Phi + } +); +inverse3 VirtualInverseWeightedRationalMap := inverse VirtualInverseWeightedRationalMap := Phi -> Phi#"inverse"; +super VirtualInverseWeightedRationalMap := Phi -> ( + Psi := virtualInverseWeightedRationalMap inverse Phi; + Psi#"target" = ambient target Psi; + Psi#"isDominant" = null; + Psi#"isBirational" = null; + return Psi; +); +VirtualInverseWeightedRationalMap EmbeddedProjectiveVariety := (Phi,Z) -> ( + -- Provisional implementation: thought specifically for computing images of curves + -- under the inverse normalization of a surface. + if Phi#"inverse" === null and Phi.cache#?"Composition of VirtualInverseWeightedRationalMap and WeightedRationalMap" then ( + (f,g) := Phi.cache#"Composition of VirtualInverseWeightedRationalMap and WeightedRationalMap"; + return g (f Z); + ); + h := inverse Phi; + decZ := if Z.cache#?"Decomposition" then decompose Z else {Z}; + W := for L in decZ list h^* L; + return sum W; +); +VirtualInverseWeightedRationalMap * MultirationalMap := (Phi,Psi) -> ( + if Psi#"image" === null then ( + << "-- virtual maps used; computing image and composition..." << endl; + image Psi; + ); + Eta := new VirtualInverseWeightedRationalMap from { + symbol cache => new CacheTable, + "maps" => {}, + "target" => target Psi, + "source" => source Phi, + "image" => image Psi, + "isDominant" => null, + "isBirational" => null, + "graph" => null, + "multidegree" => null, + "baseLocus" => null, + "inverse" => null + }; + Eta.cache#"Composition of VirtualInverseWeightedRationalMap and WeightedRationalMap" = (Phi,Psi); + return Eta; +); +MultirationalMap * VirtualInverseWeightedRationalMap := (Phi,Psi) -> virtualInverseWeightedRationalMap((inverse Psi) * (inverse3 Phi)); + +normalization = method(Options => {Verbose => true}); +normalization EmbeddedProjectiveVariety := o -> X -> ( + if X.cache#?"Normalization" then return X.cache#"Normalization"; + if o.Verbose then <<"-- computing normalization of "|toString(? ideal X)< {Verbose => true}); +experimentalNormalizationInv EmbeddedProjectiveVariety := o -> X -> ( + if X.cache#?"ExperimentalNormalizationInv" then return X.cache#"ExperimentalNormalizationInv"; + if not (dim X == 2 and dim linearSpan X > 5) then error "expected a surface with linear span of dimension > 5"; + if o.Verbose then <<"-- computing experimental normalization of "|toString(? ideal X)< point X); + pr := rationalMap((linearSpan pts)_X,Dominant=>true); + if degree target pr != degree X - (dim ambient X - 5) then error "something went wrong"; + n := multirationalMap normalization(target pr,Verbose=>o.Verbose); + if o.Verbose then <<"-- inverting normalization"< (n' pr'^* p,1))); + if dim ambient target h != dim ambient source h + (dim ambient X - 5) then error "something went wrong"; + N := pr * n' * h; + X.cache#"ExperimentalNormalizationInv" = N +); +*- + +MinimumCodimensionForExperimentalNormalization = 7; -- 5; +inverseNormalizationMapRaw = (Y,strY,expEulOut,verb) -> ( + if codim Y >= MinimumCodimensionForExperimentalNormalization then return inverseNormalizationMapExperimentalRaw(Y,strY,expEulOut,verb); + -- ( + -- try return inverseNormalizationMapExperimentalRaw(Y,strY,expEulOut,verb); + -- if verb then << "-- experimental normalization failed; falling back to standard method..." << endl; + -- ); + if verb then << "-- computing normalization of " << strY << "..." << endl << flush; + normU := multirationalMap normalization(Y,Verbose=>false); + X := source normU; + if instance(normU,WeightedRationalMap) then ( + if verb then << "-- warning: normalization of " << strY << " has unsupported type: " << class X << endl; + if verb then << "-- trying to use a virtual map to bypass this limitation."< ( + -- A variant and improvement of experimentalNormalizationInv. + if not (dim Y == 2 and dim linearSpan Y > 5) then error "experimental normalization requires a surface with linear span dimension > 5"; + if verb then << "-- computing experimental normalization of " << strY << "..." << endl; + pts := apply(dim ambient Y - 5, i -> point Y); + pr := rationalMap((linearSpan pts)_Y, Dominant=>true); + if degree target pr != degree Y - (dim ambient Y - 5) then error "something went wrong"; + if verb then << " -- computing normalization of " << (? target pr) << endl << flush; + n := multirationalMap normalization(target pr, Verbose=>false); + if verb then << " -- inverting normalization map..." << endl << flush; + local n'; + if instance(n,WeightedRationalMap) then ( + if verb then << " -- warning: normalization of the surface has unsupported type: " << class source n << endl; + if verb then << " -- trying to use a virtual map to bypass this limitation."< (n' pr'^* p,1))); + if dim ambient target h != dim ambient source h + (dim ambient Y - 5) then << " -- unexpected behavior: expected map to PP^" << dim ambient source h + (dim ambient Y - 5) << " but obtained map to PP^" << dim ambient target h << endl; + if verb then << " -- computing image of map to PP^" << dim target h << endl; + if instance(h,WeightedRationalMap) then image h else image(h,"F4"); + if verb then << " -- composing maps" << endl; + N := (pr * n') * h; + if verb then ( + << "-- experimental normalization complete; result: map from " << strY << " to " << ? image N << endl << flush; + if not instance(image N,WeightedProjectiveVariety) then ( + if euler hilbertPolynomial image N != expEulOut then << "-- warning: normalization has unexpected invariants" << endl; + ); + ); + N +); + +interpolateImage = method(Options => {Verbose => false, cache => true}); +interpolateImage (MultirationalMap,List,ZZ) := o -> (Phi,D,j) -> ( + if Phi#"image" =!= null then return Phi#"image"; + if Phi#"isDominant" === true then return target Phi; + if not all(D,d -> instance(d,ZZ)) then error "expected a list of integers"; + doPrint := (v,c) -> ( + if not v then return false; + if c <= 5 then return true; + if c <= 15 then return c % 3 == 0; + if c <= 50 then return c % 10 == 0; + if c <= 150 then return c % 30 == 0; + c % 100 == 0 + ); + cont := 0; + W := Phi point source Phi; + while select(flatten degrees ideal W,d -> d <= j) =!= D do ( + if doPrint(o.Verbose,cont) then <<" -- image "< first degree w <= j),MinimalGenerators=>true,Saturate=>false); + if flatten degrees ideal W =!= D then error "something went wrong: the degrees of the generators are wrong"; + if o.cache then ( + if Phi#"image" === null then Phi#"image" = W; + Phi#"isDominant" = Phi#"image" == target Phi; + if Phi#"isDominant" then Phi#"image" = target Phi; + return Phi#"image"; + ) else return W; +); +interpolateImage (RationalMap,List,ZZ) := o -> (Phi,D,j) -> ( + if Phi#"idealImage" =!= null then return Phi#"idealImage"; + W := interpolateImage(multirationalMap Phi,D,j,Verbose=>o.Verbose,cache=>false); + if o.cache then forceImage(Phi,ideal W); + ideal W +); + +interpolateTop = method(Options => {Verbose => false, cache => true, "Deep" => 3}); +interpolateTop (EmbeddedProjectiveVariety,List) := o -> (X,j) -> ( + if X.cache#?"top" then return X.cache#"top"; + assert(#j == 1 and instance(first j,ZZ)); + j = first j; + cont := 0; m := o#"Deep"; local f; + W := 0_X; + D := toList(1..m); + while # unique take(D,-m) != 1 do ( + cont = cont + 1; + if o.Verbose then <<" -- top "< d <= j)); + ); + gW := select(flatten entries gens ideal W,w -> first degree w <= j); + if # gW > 0 then ( + W = projectiveVariety(ideal gW,MinimalGenerators=>true,Saturate=>false); + if o.cache then X.cache#"top" = W; + return W; + ) else return ambient W; +); +interpolateTop (ZZ,EmbeddedProjectiveVariety) := o -> (i,X) -> ( + W := 0_X; + while not (dim W == dim X and degree W == degree X) do ( + i = i + 1; + W = interpolateTop(X,{i},Verbose=>o.Verbose,cache=>false,"Deep"=>o#"Deep"); + ); + if o.cache then X.cache#"top" = W else W +); +interpolateTop EmbeddedProjectiveVariety := o -> X -> interpolateTop(0,X,Verbose=>o.Verbose,cache=>o.cache,"Deep"=>o#"Deep"); + +DefaultVerbosityInterpolateTop = false; +verbosityInterpolateTop = v -> v and DefaultVerbosityInterpolateTop; + +mapDefinedByDivisor = method(); +mapDefinedByDivisor (QuotientRing,VisibleList) := (R,D) -> rationalMap(R,new Tally from apply(select(D,l -> last l > 0),d -> first d => last d)); +mapDefinedByDivisor (MultiprojectiveVariety,VisibleList) := (X,D) -> rationalMap(X,new Tally from apply(select(D,l -> last l > 0),d -> first d => last d)); + +-- sufficient conditions for smoothness ('Y' is assumed to be equidimensional) +isSmooth EmbeddedProjectiveVariety := {} >> o -> (cacheValue "isSmooth") (Y -> ( + if Y.cache#?"singularLocus" or Y.cache#?"nonSaturatedSingularLocus" then return (dim singLocus Y == -1); + X := fitVariety Y; + isXsm := dim singLocus X == -1; + if isXsm then Y.cache#"singularLocus" = 0_Y; + isXsm +)); + +singLocus = method(); +singLocus EmbeddedProjectiveVariety := X -> singularLocus(X,Saturate=>false); + +numberNodes = method(Options => {Verbose => false}); +numberNodes EmbeddedProjectiveVariety := o -> Y -> ( + if Y.cache#?"FiniteNumberOfNodes" then return Y.cache#"FiniteNumberOfNodes"; + X := if not(Y.cache#?"singularLocus" or Y.cache#?"nonSaturatedSingularLocus") then fitVariety Y else Y; + if dim singLocus X >= 1 then error "expected at most a finite number of nodes"; + n := if dim singLocus X == -1 then 0 else ( + if (singLocus X).cache#?"Support" then + degree support singLocus X + else + degree support (rationalMap random({{1},{1}},0_X)) singLocus X + ); + if o.Verbose then <<"-- calculated number of nodes (got "<< n <<" nodes)"< ( + if coefficientRing X === QQ then X = X ** (ZZ/nextPrime random(1000,11000000)); + if codim linearSpan X > 0 then X = (parametrize linearSpan X)^^ X; + n := dim ambient X; k := dim X; + if k > 0 and k <= 2 and 2*k+1 < n then ( + pr := rationalMap linearSpan apply(n-(2*k+1),i -> point X); + if dim target pr != 2*k+1 then error "internal error encountered"; + X = pr X; + ); + return X; +)); + +isomorphicProjectionOfSurfaceInP5 = method(); +isomorphicProjectionOfSurfaceInP5 EmbeddedProjectiveVariety := X -> ( + if dim X != 2 then error "expected a surface"; + if codim linearSpan X > 0 then X = (parametrize linearSpan X)^^ X; + if dim ambient X <= 5 then return X; + pr := rationalMap apply(6,i -> random(1,ring ambient X)); + Var pr (ideal X) +); + +varietyDefinedBylinearSyzygies = method(); +varietyDefinedBylinearSyzygies EmbeddedProjectiveVariety := (cacheValue "varietyDefinedBylinearSyzygies") (Y -> ( + G := transpose syz gens ideal Y; + M := matrix select(entries G,g -> max flatten degrees ideal g == 1); + K := mingens kernel M; + I := unique apply(entries transpose K,g -> trim ideal g); + Var first select(I,i -> dim i >= 1) +)); + +mapY5 = memoize (K -> ( + X := GG(K,1,4); + h := (parametrize projectiveVariety ideal sum gens ring ambient X)||X; + -- assert(dim singLocus source h == -1); + h +)); +Y5 = K -> source mapY5 K; + +mapY4 = memoize (K -> ( + y := gens ring ambient source mapY5 K; + h := (parametrize projectiveVariety ideal(y_0-y_1+y_2-y_3+y_4-y_5+y_6))||(Y5 K); + h = h * (mapY5 K); + -- assert(dim singLocus source h == -1); + h +)); +Y4 = K -> source mapY4 K; + +Var = method(Options => {MinimalGenerators => false}); +Var Ideal := o -> I -> projectiveVariety(I,MinimalGenerators=>o.MinimalGenerators,Saturate=>false); +Var Ring := o -> R -> projectiveVariety(R,MinimalGenerators=>o.MinimalGenerators,Saturate=>false); +Var Matrix := o -> M -> projectiveVariety(M,MinimalGenerators=>o.MinimalGenerators,Saturate=>false); + +isStandardK3surface = method(); +isStandardK3surface EmbeddedProjectiveVariety := (cacheValue "is standard K3 surface") ( + S -> ( + if dim S != 2 then return false; + g := sectionalGenus S; + if g != dim ambient S then return false; + if degree S != 2*g-2 then return false; + if euler hilbertPolynomial S != 2 then return false; + if dim linearSpan S < dim ambient S then return false; + if g >= 6 and degrees S =!= {({2},binomial(g-2,2))} then << "-- notice: ideal generators deviate from expected quadrics for a K3 of genus " << g << endl; + return true; + ) +); + +isPlaneInP5 = P -> degrees P === {({1}, 3)} and codim P == 3 and dim P == 2; + +isPresumedRationalNormalCurve = method(); +isPresumedRationalNormalCurve EmbeddedProjectiveVariety := C -> ( + degs := flatten degrees ideal C; + if max degs > 2 then return false; + d1 := number(degs, i -> i == 1); + d2 := number(degs, i -> i == 2); + n := (dim ambient C) - d1; + if d2 != binomial(n,2) then return false; + if dim C != 1 then return false; + if degree C != n then return false; + if sectionalGenus C != 0 then return false; + if n <= 3 then return isSmooth C; + true +); + +commonNameRationalNormalCurve = method(); +commonNameRationalNormalCurve EmbeddedProjectiveVariety := C -> ( + if not isPresumedRationalNormalCurve C then error "expected a rational normal curve"; + d := degree C; + if d == 1 then return "a line"; + if d == 2 then return "an irreducible conic curve"; + if d == 3 then return "a twisted cubic curve"; + "a rational normal curve of degree " | (toString d) +); + +surfaceDescription = method(); +surfaceDescription (ZZ,EmbeddedProjectiveVariety,Boolean) := (m,S,withNodes) -> ( + if dim S != 2 then error "expected a surface"; + degs := flatten degrees ideal S; + if degree S == 1 and unique degs === {1} then return "plane"; + n := if withNodes or S.cache#?"FiniteNumberOfNodes" then numberNodes(S,Verbose=>false) else -1; + nodal := if n > 0 then (toString n)|"-nodal " else (if S.cache#?"singularLocus" and dim(singularLocus S) == -1 then "smooth " else ""); + rational := if S.cache#?"rationalParametrization" then "rational " else ""; + surfaceHeader := nodal | rational | "surface of degree " | (toString degree S) | " and sectional genus " | (toString sectionalGenus S); + cutOutLine := "cut out by "|(if same degs then (toString(#degs)|" hypersurfaces of degree "|(toString first degs)) else (toString(#degs)|" hypersurfaces of degrees "|toStringDegreesVar(S))); + sp := if m < 0 then " " else (newline|concatenate(m:" ")); + surfaceHeader|sp|cutOutLine +); +surfaceDescription (ZZ,EmbeddedProjectiveVariety) := (m,S) -> surfaceDescription(m,S,S.cache#?"FiniteNumberOfNodes" or S.cache#?"singularLocus" or S.cache#?"nonSaturatedSingularLocus" or (S.cache#?"fitVariety" and (S.cache#"fitVariety").cache#?"nonSaturatedSingularLocus")); +surfaceDescription EmbeddedProjectiveVariety := S -> surfaceDescription(-1,S); + +degreeOfDefiningForms = method(); +degreeOfDefiningForms MultirationalMap := f -> ( + u := unique degrees ideal compress matrix f; + if #u != 1 then error "internal error encountered"; + unsequence toSequence first u +); + +humanReadableSeconds = t -> ( + assert(instance(t,ZZ) and t >= 0); + d := t // 86400; + r := t % 86400; + h := r // 3600; + r = r % 3600; + m := r // 60; + s := r % 60; + units := {}; + if d > 0 then units = append(units, toString d | " day" | (if d == 1 then "" else "s")); + if h > 0 then units = append(units, toString h | " hour" | (if h == 1 then "" else "s")); + if m > 0 then units = append(units, toString m | " minute" | (if m == 1 then "" else "s")); + if s > 0 or #units == 0 then units = append(units, toString s | " second" | (if s == 1 then "" else "s")); + n := #units; + if n == 1 then units#0 + else if n == 2 then units#0 | " and " | units#1 + else ( + out := units#0; + for i from 1 to n-2 do out = out | ", " | units#i; + out | ", and " | units#(n-1) + ) +); + +computationStatus = method(); +-- -1: Fano map not found +-- 0: surface U missing from cache: no computation performed +-- 1: Fano map and U computed; exceptional curves missing +-- 2: exceptional curves computed; associated K3/Castelnuovo surface missing +-- 3: surface Utilde computed; lattice polarization missing +-- 4: computation complete +computationStatus DoublySpecialCubicFourfold := X -> ( + (S,P) := surfaces X; + if not S.cache#?("FanoMapDSCF",P) then return -1; + mu := S.cache#("FanoMapDSCF",P); + if not mu.cache#?("surfaceDeterminingInverseOfFanoMap",X) then return 0; + U := mu.cache#("surfaceDeterminingInverseOfFanoMap",X); + if not U.cache#?"exceptionalCurves" then return 1; + if not(mu.cache#?("AssociatedUnderlyingK3",X) and mu.cache#?("K3SurfaceFromDoublySpecialCubicFourfold",X)) then return 2; + E := mu.cache#("K3SurfaceFromDoublySpecialCubicFourfold",X); + if E#"LatticePolarization" === null or isVirtualLatticeK3 E then return 3; + return 4; +); +computationStatus HodgeSpecialFourfold := X -> ( + S := surface X; + if not S.cache#?("fanoMap",ambientFivefold X) then return -1; + if not S.cache#?("surfaceDeterminingInverseOfFanoMap",ideal X) then return 0; + U := S.cache#("surfaceDeterminingInverseOfFanoMap",ideal X); + if not U.cache#?"exceptionalCurves" then return 1; + if not X.cache#?"AssociatedSurfaceCompleteData" then return 2; + return 3; +); +computationStatus K3SurfaceFromDoublySpecialCubicFourfold := S -> computationStatus recoverFourfold S; + +computationStatusLog = method(); +computationStatusLog HodgeSpecialFourfold := X -> ( + s := computationStatus X; + if s >= 0 and (not instance(X,DoublySpecialCubicFourfold)) then s = s + 1; + st := if s == -1 then "â–‘â–‘â–‘â–‘â–‘" + else if s == 0 then "â–“â–‘â–‘â–‘â–‘" + else if s == 1 then "â–“â–“â–‘â–‘â–‘" + else if s == 2 then "â–“â–“â–“â–‘â–‘" + else if s == 3 then "â–“â–“â–“â–“â–‘" + else if s == 4 then "â–“â–“â–“â–“â–“" + else error "internal error: computationStatus returned an invalid value"; + (if instance(X,IntersectionOfThreeQuadricsInP7) then "Castelnuovo" else "K3") | " status: " | "["|st|" / â–“â–“â–“â–“â–“]" +); +computationStatusLog K3SurfaceFromDoublySpecialCubicFourfold := S -> computationStatusLog recoverFourfold S; + +describeMirrorFourfoldAndK3 = method(); +describeMirrorFourfoldAndK3 HodgeSpecialFourfold := X -> ( + s := computationStatus X; + if s <= 0 then return ""; + mu := if instance(X,DoublySpecialCubicFourfold) then fanoMapDSCF(X,Verbose=>true) else multirationalMap fanoMap(X,Verbose=>true); -- already in cache + W := target mu; + U := surfaceDeterminingInverseOfFanoMap(X,Verbose=>true); -- already in cache + cutOutLine := Y -> ( + degs := flatten degrees ideal Y; + " cut out by "|(if same degs then (toString(#degs)|" hypersurfaces of degree "|(toString first degs)) else (toString(#degs)|" hypersurfaces of degrees "|toStringDegreesVar(Y))) + ); + strW := if codim W == 0 + then "PP^4" + else if codim W == 1 + then ("hypersurface in PP^5 of degree " | (toString degree W)) + else if codim W == numgens ideal W and degree W == product flatten degrees ideal W + then ("complete intersection of type " | (toString toSequence flatten degrees ideal W) | " in PP^" | (toString dim ambient W)) + else if isDeformationP2P2 W + then "≋ ℙ² × ℙ² ⊂ â„™â¸" + else if isDeformationDP5 W + then "≋ ð”¾(1,4) ∩ ℙⷠ⊂ â„™â·" + else (("fourfold of degree " | (toString degree W) | " and sectional genus " | (toString sectionalGenus W) | " in PP^" | (toString dim ambient W)) | (cutOutLine W)); + descr := "Mirror fourfold: " | strW | newline | "Surface U of degree " | (toString degree U) | ", sectional genus " | (toString sectionalGenus U) | ", χ(O_U) = " | (toString euler hilbertPolynomial U) | "," | (cutOutLine U); + if s <= 1 then return descr; + (L,C) := exceptionalCurves(X,Verbose=>false); -- already in cache + descr = descr | newline | printInfoOnExceptionalCurves(L,C); + if s <= 2 then return descr; + local Utilde; + if instance(X,DoublySpecialCubicFourfold) then ( + Utilde = mu.cache#("AssociatedUnderlyingK3",X); + ) else if instance(X,CubicFourfold) or instance(X,GushelMukaiFourfold) or instance(X,IntersectionOfThreeQuadricsInP7) then ( + Utilde = X.cache#"AssociatedSurfaceCompleteData"; + ) else error "unsupported fourfold type"; + strSurf := if instance(X,IntersectionOfThreeQuadricsInP7) then "Castelnuovo" else "K3"; + descr = descr | newline | "Minimal " | strSurf | " surface Ũ: "; + if instance(Utilde,EmbeddedProjectiveVariety) then ( + descr = descr | "degree " | (toString degree Utilde) | " and sectional genus " | (toString sectionalGenus Utilde) | " in " | toString(? ambient Utilde) | (cutOutLine Utilde); + ) else ( + descr = descr | toString(? Utilde); + ); + if s <= 3 then return descr; + pUtilde := polarizedK3surface(X,Verbose=>true); -- already in cache + descr || ((net "Lattice intersection matrix on Ũ: ") | (net latticeMatrix pUtilde)) +); + +isDeformationP2P2 = X -> ( + if degrees X =!= {({2}, 9)} or dim X != 4 or codim X != 4 or degree X != 6 then return false; + p := hilbertPolynomial(X,Projective=>false); + i := first gens ring p; + p == (1/4)*i^4+(3/2)*i^3+(13/4)*i^2+3*i+1 +); + +isDeformationDP5 = X -> ( + if degrees X =!= {({2}, 5)} or dim X != 4 or codim X != 3 or degree X != 5 then return false; + p := hilbertPolynomial(X,Projective=>false); + i := first gens ring p; + p == (5/24)*i^4+(5/4)*i^3+(67/24)*i^2+(11/4)*i+1 +); + +printInfoOnExceptionalCurves = (L,C) -> ( + if dim L == -1 and dim C == -1 then return "No exceptional curves"; + if dim L == -1 and dim C != -1 then ( + if isPresumedRationalNormalCurve C then return "Exceptional curves: " | (commonNameRationalNormalCurve C); + return "Exceptional curves: only curves of degree > 1"; + ); + if dim L != -1 and dim C == -1 then ( + if degree L == 1 then return "Exceptional curves: 1 line"; + return ("Exceptional curves: " | (toString degree L) | " lines"); + ); + assert(dim L != -1 and dim C != -1); + if isPresumedRationalNormalCurve C then ( + if degree L == 1 then return "Exceptional curves: 1 line and " | (commonNameRationalNormalCurve C); + return ("Exceptional curves: " | (toString degree L) | " lines and " | (commonNameRationalNormalCurve C)); + ); + if degree L == 1 then return "Exceptional curves: 1 line and other curves of degree > 1"; + "Exceptional curves: " | (toString degree L) | " lines and other curves of degree > 1" +); diff --git a/M2/Macaulay2/packages/SpectralSequences.m2 b/M2/Macaulay2/packages/SpectralSequences.m2 index 5da4de6ece1..44d8b76b91a 100644 --- a/M2/Macaulay2/packages/SpectralSequences.m2 +++ b/M2/Macaulay2/packages/SpectralSequences.m2 @@ -1,6 +1,6 @@ -- -*- coding: utf-8 -*- -------------------------------------------------------------------------------- --- Copyright 2012 Gregory G. Smith +-- Copyright 2026 the joint authors -- -- This program is free software: you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free Software @@ -15,11 +15,24 @@ -- You should have received a copy of the GNU General Public License along with -- this program. If not, see . -------------------------------------------------------------------------------- +--- This is an updated version of the package SpectralSequences.m2. +-- We have made SpectralSequences.m2 forward +-- compatible with the changes to the ChainComplex type and its intended eventual replacement +-- by the Complexes type which is currently being developed by Mike and Greg. +-- All of the existing methods and examples, from SpectralSequences.m2 v. 1 work correctly as advertised and +-- so this remains the case with the migration to the Complexes +-- framework as well. Some older "legacy" and other "patch" code has been removed +-- while other methods have been optimized and/or required other forms of syntax +-- and/or type changes. +-- The current version seems to install OK and appears to be ready to +-- issue a pull request to push this to the M2 repo for distribution with the next M2 release +------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- newPackage( "SpectralSequences", -- AuxiliaryFiles => true, - Version => "1.0", - Date => "20 September 2016", + Version => "2.02", + Date => "9 March 2026", Authors => { { Name => "David Berlekamp", @@ -31,12 +44,12 @@ newPackage( HomePage => "http://www.math.utah.edu/~boocher"}, { Name => "Nathan Grieve", - Email => "n.grieve@unb.ca", - HomePage => "http://www.math.unb.ca/~ngrieve"}, + Email => "nathan.m.grieve@gmail.com", + HomePage => "https://sites.google.com/view/nathan-grieve"}, { Name => "Eloisa Grifo", - Email => "eloisa.grifo@virginia.edu", - HomePage => "http://people.virginia.edu/~er2eq/"}, + Email => "grifo@unl.edu", + HomePage => "https://eloisagrifo.github.io"}, { Name => "Gregory G. Smith", Email => "ggsmith@mast.queensu.ca", @@ -47,8 +60,8 @@ newPackage( HomePage => "http://math.berkeley.edu/~thanh"}}, Headline => "spectral sequences", Keywords => {"Homological Algebra"}, - PackageImports => {"Truncations"}, - PackageExports => {"SimplicialComplexes", "ChainComplexExtras", "PushForward"} + PackageImports => {}, + PackageExports => {"Complexes","PushForward", "SimplicialComplexes"} ) export { @@ -91,148 +104,32 @@ ReverseDictionary = value Core#"private dictionary"#"ReverseDictionary" -------------------------------------------------------------------------------- -- CODE -------------------------------------------------------------------------------- ------------------------------------------------------------------------------------- --- ChainComplexExtraExtras -- Several people have worked on this portion of the code --------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------------------------- +-- We have removed almost all of the "patch code" that is now part of the "Complexes" package -- +------------------------------------------------------------------------------------------------- +-- The writing of some methods are much improved -- -- since things are mutable we don't want to cache spots spots = method() +spots Complex := List => C -> sort keys C.module -spots ChainComplex := List => ( - C -> sort select(keys complete C,i -> class i === ZZ)) - -max ChainComplex := K -> max spots K -min ChainComplex := K -> min spots K - -support ChainComplex := List => ( - C -> sort select (spots C, i -> C_i != 0)) - - --- Computes the graded pieces of the total complex of a Hom double complex --- (just as a graded module, so no maps!) -Hom (GradedModule, GradedModule) := GradedModule => opts -> (C, D) -> ( - R := C.ring; if R =!= D.ring then error "expected graded modules over the same ring"; - (c,d) := (spots C, spots D); - pairs := new MutableHashTable; - scan(c, i -> scan(d, j -> ( - k := j-i; - p := if not pairs#?k then pairs#k = new MutableHashTable else pairs#k; - p#(i,j) = 1;))); - scan(keys pairs, k -> pairs#k = sort keys pairs#k); - E := new GradedModule; - E.ring = R; - scan(keys pairs, k-> ( - p := pairs#k; - E#k = directSum(apply(p, v -> v => Hom(C_(v#0), D_(v#1), opts)));)); - E) - - - -isWellDefined ChainComplexMap := Boolean => f -> ( - (F,G):= (source f, target f); - all(drop(spots F,1), i -> G.dd_i * f#i == f#(i-1) * F.dd_i)) - --- Computes the total complex of the Hom double complex of two chain complexes --- This code is different from that in ChainComplexExtras. We need this version --- so that the indices are cached. -Hom (ChainComplex, ChainComplex) := ChainComplex => opts -> (C, D) -> ( - if C.ring =!= D.ring then error "expected chain complexes over the same ring"; - hom := lookup(Hom, GradedModule, GradedModule); - E := chainComplex (hom opts)(C, D); - scan(spots E, i -> if E#?i and E#?(i-1) then E.dd#i = - map(E#(i-1), E#i, - matrix table( - E#(i-1).cache.indices, E#i.cache.indices, - (j,k) -> map(E#(i-1).cache.components#(E#(i-1).cache.indexComponents#j), - (E#i).cache.components#((E#i).cache.indexComponents#k), - if j#0 === k#0 and j#1 === k#1-1 then (-1)^(k#0)*Hom(C_(k#0), D.dd_(k#1), opts) - else if j#0 === k#0 + 1 and j#1 === k#1 then Hom(C.dd_(j#0), D_(k#1), opts) - else 0)))); - E -) - -Hom (ChainComplex, ChainComplexMap) := ChainComplexMap => opts -> (C, f) -> ( - (F, G) := (Hom(C, source f, opts), Hom(C, target f, opts)); - map(G,F, i -> map(G_i,F_i, matrix table( G_i.cache.indices,F_i.cache.indices, - (j,k) -> map(G#i.cache.components#(G#i.cache.indexComponents#j), - F#i.cache.components#(F#i.cache.indexComponents#k), - if j === k then Hom(C_(j#0), f_(j#1), opts) - else 0))))) - -Hom (ChainComplexMap, ChainComplex) := ChainComplexMap => opts -> (f, C) -> ( - (F, G) := (Hom(target f, C, opts), Hom(source f, C, opts)); - map(G,F, i -> map (G_i,F_i, matrix table(G_i.cache.indices,F_i.cache.indices, - (j,k) -> map(G#i.cache.components#(G#i.cache.indexComponents#j), - F#i.cache.components#(F#i.cache.indexComponents#k), - if j === k then Hom(f_(j#0), C_(j#1), opts) - else 0))))) - -ChainComplexMap ** ChainComplex := ChainComplexMap => (f,C) -> ( - (F,G) := ((source f) ** C, (target f) ** C); - map(G,F, i -> map (G_i,F_i, matrix table(G_i.cache.indices,F_i.cache.indices, - (j,k) -> map(G#i.cache.components#(G#i.cache.indexComponents#j), - F#i.cache.components#(F#i.cache.indexComponents#k), - if j === k then f_(j#0) ** C_(j#1) - else 0))))) - -ChainComplex ** ChainComplexMap := ChainComplexMap => (C,f) -> ( - (F,G) := (C ** source f, C ** target f); - map(G,F, i -> map (G_i,F_i, matrix table(G_i.cache.indices,F_i.cache.indices, - (j,k) -> map(G#i.cache.components#(G#i.cache.indexComponents#j), - F#i.cache.components#(F#i.cache.indexComponents#k), - if j === k then C_(j#0) ** f_(j#1) - else 0))))) - --- truncate a chain complex at a given homological degree -truncate(ChainComplex,ZZ):= {} >> o -> (C,q) ->( - if q == 0 then return C - else ( - m := min support C; - n := max support C; - l := length C; - if q < -l or q > l then return image(0*id_C) - else K:=new ChainComplex; - K.ring=C.ring; - if q < 0 then for i from min C + 1 to max C do ( - if i <= n + q then K.dd_i = C.dd_i - else if i-1 > n + q then K.dd_i = inducedMap(0*C_(i-1),0*C_i,C.dd_i) - else K.dd_i = inducedMap(C_(i-1), 0*C_i, C.dd_i) ) - else for i from min C+1 to max C do ( - if i-1 >= q + m then K.dd_i = C.dd_i - else if i < q + m then K.dd_i = inducedMap(0*C_(i-1),0*C_i,C.dd_i) - else K.dd_i = map(0*C_(i-1), C_i, 0*C.dd_i) )); - K) - +support Complex := List => C -> select(spots C, i -> C_i != 0) -- the following relies on the pushFwd method from the package "PushForward.m2" +-- perhaps this should be added to the Complexes package -- -pushFwd(RingMap,ChainComplex):=o->(f,C) -> -( pushFwdC := chainComplex(source f); - maps := apply(spots C, i-> (i,pushFwd(f,C.dd_i))); - for i from min C to max C do ( - pushFwdC.dd_(maps#i_0) = maps#i_1 - ); - pushFwdC - ) +pushFwd(RingMap, Complex) := o -> (f, C) -> ( + (lo, hi) := concentration C; + if dd^C == 0 + then complex(for i from lo to hi list pushFwd(f, C_i, o), Base => lo) + else complex applyValues(C.dd.map, m -> pushFwd(f, m, o))) - --- New method for tensor that returns the tensor product of a complex via a ring map -tensor(RingMap, ChainComplex) := ChainComplex => {} >> opts -> (f,C) -> ( - k := min C; - D := chainComplex( - if even(k) then apply( - drop(select(keys complete C, - i -> instance(i,ZZ)),1), - j -> f ** C.dd_j) - else apply( - drop(select(keys complete C, - i -> instance(i,ZZ)),1), - j -> (-1) * (f ** C.dd_j))); - D[-k] - ) - - ----------------------------------------------------------------------------------- +-- e.g. n = 2 means cut 2 from the beginning +-- and n = -2 means cut 2 from the tail; n = 0 does nothing +naiveTruncation(Complex, ZZ) := Complex => (C, n) -> ( + (lo, hi) := concentration C; + if n > 0 then naiveTruncation(C, (lo + n, infinity)) else + if n < 0 then naiveTruncation(C, (-infinity, hi + n)) else C) ------------------------------------------------------------------------------------- -- filtered complexes @@ -249,21 +146,20 @@ min FilteredComplex := K -> min spots K support FilteredComplex := List => ( K -> sort select (spots K, i -> K#i != 0)) - FilteredComplex _ InfiniteNumber := -FilteredComplex _ ZZ := ChainComplex => (K,p) -> ( +FilteredComplex _ ZZ := Complex => (K,p) -> ( if K#?p then K#p else if p < min K then K#(min K) else if p > max K then K#(max K) ) FilteredComplex ^ InfiniteNumber := -FilteredComplex ^ ZZ := ChainComplex => (K,p) -> K_(-p) +FilteredComplex ^ ZZ := Complex => (K,p) -> K_(-p) -chainComplex FilteredComplex := ChainComplex => K -> K_infinity +complex FilteredComplex := Complex => {} >> o -> K -> K_infinity -- Returns the inclusion map from the pth subcomplex to the top -inducedMap (FilteredComplex, ZZ) := ChainComplexMap => opts -> (K,p) -> ( +inducedMap (FilteredComplex, ZZ) := ComplexMap => opts -> (K,p) -> ( if not K.cache#?inducedMaps then K.cache.inducedMaps = new MutableHashTable; if not K.cache.inducedMaps#?p then K.cache.inducedMaps#p = inducedMap(K_infinity, K_p); K.cache.inducedMaps#p) @@ -273,54 +169,54 @@ net FilteredComplex := K -> ( v := between("", apply(spots K, p -> p | " : " | net K_p)); if #v === 0 then "0" else stack v) - --- Primitive constructor, takes a list eg {m_n,m_(n-1), ...,m_0} --- defining inclusion maps C=F_(n+1)C > F_(n)C > ... > F_0 C --- of subcomplexes of a chain complex (or simplicial complexes) --- and produces a filtered complex with integer keys the --- corresponding chain complex. --- If F_0C is not zero then by default F_(-1)C is added and is 0. --- THIS IS THE CONVENTION WE WANT BY DEFAULT. SEE --- THE HOPF FIBRATION EXAMPLE. TO GET THE CORRECT INDICES ON THE E2 PAGE --- WE WANT THE ZERO COMPLEX TO HAVE "FILTRATION DEGREE -1". +---- What follows is a provisional fix for the ``master constructor method" ---- +---- This seems to be fine now --- +-- The constructor is basically the same as in the previous version -- +-- Note that the "default" SimplicialComplexes package is still based on +-- ChainComplexes and not Complexes so we will need to make additional +-- updates in due time but at least for now we have a forward compatible +-- provisional fix. filteredComplex = method(Options => { Shift => 0, ReducedHomology => true}) filteredComplex(List) := FilteredComplex => opts -> L -> ( - local maps; - local C; - if #L === 0 - then error "expected at least one chain complex map or simplicial complex"; - if all(#L, p -> class L#p === SimplicialComplex) then ( - kk := coefficientRing L#0; - if opts.ReducedHomology == true then ( - C = chainComplex complex L#0; -- By default the ambient simplicial complex is the first element of the list - maps = apply(#L-1, p -> map(C, chainComplex complex L#(p+1), - i -> sub(contract(transpose matrix{faces(i,L#0)}, matrix{faces(i,L#(p+1))}), kk)))) - else (C = truncate(chainComplex complex L#0,1); -- By default the ambient simplicial complex is the first element of the list - maps = apply(#L-1, p -> map(C, truncate(chainComplex complex L#(p+1),1), - i -> sub(contract(transpose matrix{faces(i,L#0)}, matrix{faces(i,L#(p+1))}), kk)))) - ) - else ( - maps = L; - if any(#maps, p -> class maps#p =!= ChainComplexMap) then ( - error "expected sequence of chain complexes"); - C = target maps#0;-- By default the ambient chain complex is target of first map. - if any(#maps, p -> target maps#p != C) then ( - error "expected all map to have the same target")); - Z := image map(C, C, i -> 0*id_(C#i)); -- make zero subcomplex as a subcomplex of ambient complex - P := {}; - myList := {}; - for p from 0 to #maps - 1 do ( - myList = myList | - {#maps - (p+1) -opts.Shift => image maps#p}; - ); - if myList != {} then (P = {(#maps-opts.Shift) => C} | myList) - else P = { - opts.Shift => C} ; - if (last P)#1 != Z then (P = P | {(-1-opts.Shift) => Z}); - return new FilteredComplex from P | {symbol zero => (ring C)^0, symbol cache => new CacheTable}) + if #L == 0 then error "expected at least one complex map or simplicial complex"; + if not uniform L then error "expected a list of complex maps or simplicial complexes"; + -- + maps := if instance(L#0, SimplicialComplex) then ( + kk := coefficientRing L#0; + if opts.ReducedHomology == true then ( + -- By default the ambient simplicial complex is the first element of the list + C := complex L#0; + apply(#L-1, p -> map(C, complex L#(p+1), + i -> sub(contract(transpose matrix{faces(i,L#0)}, matrix{faces(i,L#(p+1))}), kk)))) + else ( + -- By default the ambient simplicial complex is the first element of the list + C = complex L#0; + C = naiveTruncation(C, 1); + apply(#L-1, p -> map(C, naiveTruncation(complex L#(p+1), 1), + i -> sub(contract(transpose matrix{faces(i,L#0)}, matrix{faces(i,L#(p+1))}), kk)))) + ) + else if instance(L#0, ComplexMap) then ( + -- By default the ambient chain complex is target of first map. + C = target L#0; + if same apply(L, target) then L + else error "expected all maps to have the same target") + else error "expected a list of complex maps or simplicial complexes"; + -- + Z := image map(C, C, i -> 0*id_(C_i)); -- make zero subcomplex as a subcomplex of ambient complex + P := {}; + myList := {}; + for p from 0 to #maps - 1 do ( + myList = myList | + {#maps - (p+1) -opts.Shift => image maps#p}; + ); + if myList != {} then (P = {(#maps-opts.Shift) => C} | myList) + else P = { - opts.Shift => C} ; + if (last P)#1 != Z then (P = P | {(-1-opts.Shift) => Z}); + return new FilteredComplex from P | {symbol zero => (ring C)^0, symbol cache => new CacheTable}) -------------------------------------------------------------------------------- @@ -329,33 +225,28 @@ filteredComplex(List) := FilteredComplex => opts -> L -> ( -- make the filtered complex associated to the "naive truncation of a chain complex" -filteredComplex ChainComplex := FilteredComplex => opts-> C->( complete C; - n := max support C; - m := min support C; - p := length C; - if p > 0 then ( - H := for i from 1 to p list inducedMap(C,truncate(C,-i)); - filteredComplex( H, Shift => - m) ) - else filteredComplex {map(C, image(0 * id_C), id_C)}--{map(C, id_C} -- now the constructor supports the zero chain complex - ) - +filteredComplex Complex := FilteredComplex => opts -> C -> ( + (lo, hi) := concentration C; + if dd^C == 0 + then filteredComplex{ map(C, image(0 * id_C), id_C) } + else filteredComplex(Shift => -lo, + apply(hi-lo, i -> inducedMap(C, naiveTruncation(C, lo, hi-i-1))))) --produce the "x-filtration" of the tensor product complex. -FilteredComplex ** ChainComplex := FilteredComplex => (K,C) -> ( - xTensormodules := (p,q,T)->(apply( (T#q).cache.indices, - i-> if (i#0) <=p then - image (id_(((T#q).cache.components)#(((T#q).cache.indexComponents)#i))) - else image(0* id_(((T#q).cache.components)#(((T#q).cache.indexComponents)#i)))) ); - xTensorComplex := (T,p) ->(K := new ChainComplex; - K.ring = T.ring; - for i from min T to max T do ( - if T#?(i-1) then - K.dd_i = inducedMap( - directSum(xTensormodules(p,i-1,T) - ), - directSum(xTensormodules(p,i,T)),T.dd_i)); - K - ); +xTensormodules := (p,q,T) -> ( + apply(indices T_q, components T_q, + (ind, M) -> if ind#0 <= p + then image id_M + else image(0 * id_M))) + +xTensorComplex := (T,p) ->( + (lo, hi) := concentration T; + if lo == hi + then complex(directSum xTensormodules(p, lo, T), Base => lo) + else complex applyPairs(T.dd.map, + (i,f) -> i => inducedMap(directSum(xTensormodules(p, i-1, T)), directSum(xTensormodules(p, i, T)), f))) + +FilteredComplex ** Complex := FilteredComplex => (K,C) -> ( supp := support K_infinity; -- try to handle the boundary cases -- if supp != {} and #supp > 1 then ( @@ -377,19 +268,22 @@ filteredComplex(reverse for i from P to (N-1) list ) ) +--- Here is the proposed update from ChainComplex ** FilteredComplex to Complex ** FilteredComplex --produce the "y-filtration" of the tensor product complex. -ChainComplex ** FilteredComplex := FilteredComplex => (C,K) -> ( - yTensorModules := (p,q,T)->(apply( (T#q).cache.indices, - i-> if (i#1) <=p then image (id_(((T#q).cache.components)#(((T#q).cache.indexComponents)#i))) - else image(0* id_(((T#q).cache.components)#(((T#q).cache.indexComponents)#i)))) ); - yTensorComplex := (T,p) -> (K := new ChainComplex; - K.ring = T.ring; - for i from min T to max T do ( - if T#?(i-1) then - K.dd_i = inducedMap(directSum(yTensorModules(p,i-1,T)), - directSum(yTensorModules(p,i,T)),T.dd_i)); - K - ); +yTensorModules := (p,q,T)->( + apply(indices T_q, components T_q, + (ind, M) -> if ind#1 <= p + then image id_M + else image(0 * id_M))) + +yTensorComplex := (T,p) -> ( + (lo, hi) := concentration T; + if lo == hi + then complex(directSum(yTensorModules(p, lo, T), Base => lo)) + else complex applyPairs(T.dd.map, + (i,f) -> i => inducedMap(directSum(yTensorModules(p, i-1, T)), directSum(yTensorModules(p, i, T)), f))) + +Complex ** FilteredComplex := FilteredComplex => (C,K) -> ( supp := support K_infinity; -- try to handle the boundary cases -- if supp != {} and #supp > 1 then ( @@ -409,43 +303,39 @@ filteredComplex(reverse for i from P to (N-1) list filteredComplex({id_tt}) ) ) - ) + ) -- produce the "x-filtration" of the Hom complex. -xmodules := (n, d, H)->( +xHomModules := (n, d, H)->( -- want components {p,q} = Hom(-p, q) with p + q = d and p <= n - apply( (H#d).cache.indices, - i -> if - (i#0) <= n then - image (id_(((H#d).cache.components)#(((H#d).cache.indexComponents)#i))) - else image(0* id_(((H#d).cache.components)#(((H#d).cache.indexComponents)#i)))) ); - - -xComplex := (T,n) -> - (K := new ChainComplex; - K.ring = T.ring; - for i from min T to max T do ( - if T#?(i-1) then - K.dd_i = inducedMap(directSum(xmodules(n,i-1,T)),directSum(xmodules(n,i,T)),T.dd_i)); - K - ) + apply(indices H_d, components H_d, + (ind, M) -> if -ind#0 <= n + then image id_M + else image(0 * id_M))) + +xHomComplex := (T,n) -> ( + (lo, hi) := concentration T; + if lo == hi + then complex(directSum(xHomModules(n, lo, T), Base => lo)) + else complex applyPairs(T.dd.map, + (i,f) -> i => inducedMap(directSum(xHomModules(n, i-1, T)), directSum(xHomModules(n, i, T)), f))) -- produce the "x-filtration" of the Hom complex. -Hom (FilteredComplex, ChainComplex):= FilteredComplex => opts -> (K, D) -> ( - C := complete D; +Hom (FilteredComplex, Complex):= FilteredComplex => opts -> (K, C) -> ( supp := support K_infinity; -- try to handle the boundary cases -- if supp != {} and #supp > 1 then ( N := - max support K_infinity; P := - min support K_infinity; H := Hom(K_infinity, C, opts); - filteredComplex(reverse for i from N to P - 1 list inducedMap(H, xComplex(H,i)), + filteredComplex(reverse for i from N to P - 1 list inducedMap(H, xHomComplex(H,i)), Shift => - N) ) else ( if #supp == 1 then ( p := min supp; h := Hom(K_infinity, C, opts); - filteredComplex( {inducedMap(h, xComplex(h, p))}, Shift => p + 1 ) + filteredComplex( {inducedMap(h, xHomComplex(h, p))}, Shift => p + 1 ) ) else( hhh := Hom(K_infinity, C, opts); @@ -454,42 +344,40 @@ Hom (FilteredComplex, ChainComplex):= FilteredComplex => opts -> (K, D) -> ( ) ) --- next are some functions used in the "y-filtration" of the Hom complex. - -ymodules := (n, d, H) -> ( - -- want components {p,q} = Hom(-p, q) with p + q = d and q <= n - apply( (H#d).cache.indices, - i -> if (i#1) <= n then - image (id_(((H#d).cache.components)#(((H#d).cache.indexComponents)#i))) - else image(0* id_(((H#d).cache.components)#(((H#d).cache.indexComponents)#i)))) - ) + -yComplex := (T,n) -> - (K := new ChainComplex; - K.ring = T.ring; - for i from min T to max T do ( - if T#?(i-1) then - K.dd_i = inducedMap(directSum(ymodules(n,i-1,T)),directSum(ymodules(n,i,T)),T.dd_i)); - K - ) +-- next are some functions used in the "y-filtration" of the Hom complex. -Hom (ChainComplex, FilteredComplex) := FilteredComplex => opts -> (D, K) -> ( - C := complete D; +yHomModules := (n, d, H) -> ( + -- want components {p,q} = Hom(-p, q) with p + q = d and q <= n + apply(indices H_d, components H_d, + (ind, M) -> if ind#1 <= n + then image id_M + else image(0 * id_M))) + +yHomComplex := (T,n) -> ( + (lo, hi) := concentration T; + if lo == hi + then complex(directSum(yHomModules(n, lo, T), Base => lo)) + else complex applyPairs(T.dd.map, + (i,f) -> i => inducedMap(directSum(yHomModules(n, i-1, T)), directSum(yHomModules(n, i, T)), f))) + +Hom (Complex, FilteredComplex) := FilteredComplex => opts -> (C, K) -> ( supp := support K_infinity; -- try to handle the boundary cases -- if supp != {} and #supp > 1 then ( N := max support K_infinity; P := min support K_infinity; H := Hom(C, K_infinity, opts); - filteredComplex(reverse for i from P to N - 1 list inducedMap(H, yComplex(H,i)), + filteredComplex(reverse for i from P to N - 1 list inducedMap(H, yHomComplex(H,i)), Shift => - P) ) else ( if #supp == 1 then ( p := min supp; h := Hom(C, K_infinity, opts); - filteredComplex( {inducedMap(h, yComplex(h, p))}, Shift => - p + 1 ) + filteredComplex( {inducedMap(h, yHomComplex(h, p))}, Shift => - p + 1 ) ) else( hhh := Hom(C, K_infinity, opts); @@ -499,21 +387,37 @@ Hom (ChainComplex, FilteredComplex) := FilteredComplex => opts -> (D, K) -> ( ) +--- The following updates the I-adic filtration constructors --- + + -- I-adic filtration code -- -- the following script allows us to multiply a chain complex by an ideal -Ideal * ChainComplex := ChainComplex => (I,C) -> ( - D := new ChainComplex; - D.ring = C.ring; - apply(drop(spots C, 1), i -> D.dd_i = inducedMap(I * C_(i-1), I * C_i, C.dd_i)); - D - ) +-- this is a slightly updated script +-- but somehow it hasn't been included already in the standalone "Complexes" package? +Ideal * Complex := Complex => (I,C) -> ( + (lo, hi) := concentration C; + if dd^C == 0 + then complex(for i from lo to hi list I * C_i, Base => lo) + else ( + f := inducedMap(I * C_(lo-1), I * C_lo, dd^C_lo); + complex hashTable for i from lo+1 to hi list i => ( + f = inducedMap(source f, I * C_i, dd^C_i)))) -filteredComplex(Ideal,ChainComplex,ZZ) := FilteredComplex => opts -> (I,C,n) ->( +filteredComplex(Ideal,Complex,ZZ) := FilteredComplex => opts -> (I,C,n) ->( if n < 0 then error "expected a non-negative integer" else filteredComplex(apply(n, i -> inducedMap(C, I^(i+1) * C)), Shift => n) ) +---- Unless we are forgetting some, it seems that all other filtered complexes constructors which need updating +--- to be compatible with the "Complexes" update +--- are omitted for now ------ + +--- All of the "core" code needed to work with spectral sequences +--- Appears to run correctly given the updates to make the "main constructor" +-- "Complexes" compatible +-- We just need to check that we aren't forgetting anything. + ------------------------------------ -- Pages and Sequences -- ------------------------------------ @@ -529,7 +433,7 @@ Page.GlobalReleaseHook = globalReleaseFunction describe Page := E -> net expression E new Page := Page => (cl) -> ( - C := newClass(Page,new MutableHashTable); -- sigh + C := newClass(Page, new MutableHashTable); -- sigh C.cache = new CacheTable; b := C.dd = new PageMap; b.degree = {}; @@ -538,7 +442,6 @@ new Page := Page => (cl) -> ( ring Page := C -> C.ring degree Page := C -> C.dd.degree - netPage = method() netPage(Page,List,List) := (E,mins,maxs) -> ( newmaxQ := maxs#1; @@ -700,7 +603,7 @@ SpectralSequence ^ InfiniteNumber:= -- again trying to handle the case of the zero complex -- if min K_(infinity) < infinity and max K_infinity > - infinity then ( for p from min K to max K do ( - for q from - p + min K_(infinity) to max K_(infinity) do ( + for q from -p + min K_(infinity) to max K_(infinity) + 1 do ( if E.Prune == false then H#{p,q} = epq(K,p,q,s) else H#{p,q} = prune epq(K,p,q,s) ); @@ -724,6 +627,23 @@ minimalPresentation SpectralSequence := prune SpectralSequence := SpectralSequen spectralSequence(E.filteredComplex, Prune => true) ) + + +edgeComplex = method() + +edgeComplex(SpectralSequence) := (E) -> ( + if E.Prune then error "not currently implemented for pruned spectral sequences"; + M := select(spots E^2 .dd, i -> E^2_i != 0); + l := min apply(M, i -> i#0); + m := min apply(M, i -> i#1); + C := complex E; + if M != {} then ( + complex {inducedMap(E^2_{l + 1, m}, HH_(l + m + 1) C, id_(C_(l + m + 1))), + inducedMap(HH_(l + m + 1) C, E^2_{l,m + 1}, id_(C_(l + m + 1))), + E^2 .dd_{l + 2,m}, inducedMap(E^2_{l + 2, m}, HH_(l + m + 2) C, id_(C_(l + m + 2)))}) + else complex C.ring^0) + + ---------------------------------------------------------------------------- -------------------------------------------------------------------------------- @@ -750,10 +670,10 @@ minimalPresentation SpectralSequencePage := prune SpectralSequencePage := Spectr spectralSequencePage(E.filteredComplex, E.number, Prune => true) ) -SpectralSequencePage _ List := Module => (E,i)-> ( source(E.dd _i) ) +SpectralSequencePage _ List := Module => (E,i) -> ( source(E.dd _i) ) -SpectralSequencePage ^ List := Module => (E,i)-> (E_(-i)) +SpectralSequencePage ^ List := Module => (E,i) -> (E_(-i)) -- view the modules on a Spectral Sequence Page. We are referring to these -- as the support of the page. @@ -770,7 +690,7 @@ page SpectralSequencePage := Page => opts -> E -> ( -- again trying to handle the case of the zero complex -- if min K_(infinity) < infinity and max K_infinity > - infinity then ( for p from min K to max K do ( - for q from -p + min K_(infinity) to max K_(infinity) do ( + for q from -p + min K_(infinity) to max K_(infinity) + 1 do ( -- H#{p,q} = E^s_{p,q} if E.Prune == false then H#{p,q} = epq(K,p,q,s) else H#{p,q} = prune epq(K,p,q,s) @@ -909,24 +829,34 @@ SpectralSequencePageMap _ List := Matrix => (d,i)-> (if (d)#?i then d#i pruneEpqrMaps(d.filteredComplex,i#0,i#1,- d.degree #0) ) -SpectralSequencePageMap ^ List := Matrix => (d,i)-> (d_(-i)) +SpectralSequencePageMap ^ List := Matrix => (d,i) -> (d_(-i)) +pruningMaps = method() +pruningMaps(SpectralSequencePage) := (E) -> ( if not E.Prune then error "page is not pruned" + else + P := new PageMap; + P.degree = E.dd.degree; + scan(spots E.dd, i -> P#i = E.dd_i .cache.sourcePruningMap); + P + ) -- auxiliary spectral sequence stuff. filteredComplex SpectralSequence := FilteredComplex => opts -> E -> E.filteredComplex -chainComplex SpectralSequence := ChainComplex => E -> chainComplex filteredComplex E +complex SpectralSequence := Complex => {} >> opts -> E -> complex E.filteredComplex + -- given a morphism f: A --> B -- compute the connecting map -- HH_{n+1}( coker f) --> HH_n (im f) connectingMorphism = method() -connectingMorphism(ChainComplexMap,ZZ) := (a,n) -> ( +connectingMorphism(ComplexMap,ZZ) := (a,n) -> ( K := filteredComplex ({a}) ; e := spectralSequence K ; e^1 .dd_{1, n} ) + -- here are some needed functions related to Hilbert polynomials -- hilbertPolynomial ZZ := ProjectiveHilbertPolynomial => o -> (M) -> ( if M == 0 then new ProjectiveHilbertPolynomial from {} else @@ -945,47 +875,17 @@ hilbertPolynomial (SpectralSequencePage) := Page => o -> (E) -> ( P ) -pruningMaps = method() -pruningMaps(SpectralSequencePage) := (E) -> ( if E.Prune == false then error "page is not pruned" - else - P := new PageMap; - P.degree = E.dd.degree; - apply(spots E.dd, i -> P#i = E.dd_i .cache.sourcePruningMap); - P - ) - -basis (ZZ,SpectralSequencePage) := opts -> (deg,E) -> ( +basis (ZZ, SpectralSequencePage) := opts -> (deg,E) -> ( P := new Page; apply(spots E.dd, i -> P#i = basis(deg,E_i)); P ) -basis (List,SpectralSequencePage) := opts -> (deg,E) -> ( +basis (List, SpectralSequencePage) := opts -> (deg,E) -> ( P := new Page; apply(spots E.dd, i -> P#i = basis(deg,E_i)); P ) --- --- --- - -edgeComplex = method() - -edgeComplex(SpectralSequence) := (E) -> ( - if E.Prune == true then error "not currently implemented for pruned spectral sequences"; - if E.Prune == true then error "not currently implemented for pruned spectral sequences"; - M := select(spots E^2 .dd, i -> E^2_i != 0); - l := min apply(M, i -> i#0); - m := min apply(M, i -> i#1); - C := chainComplex E; - if M != {} then ( - chainComplex {inducedMap(E^2_{l + 1, m}, HH_(l + m + 1) C, id_(C_(l + m + 1))), - inducedMap(HH_(l + m + 1) C, E^2_{l,m + 1}, id_(C_(l + m + 1))), - E^2 .dd_{l + 2,m}, inducedMap(E^2_{l + 2, m}, HH_(l + m + 2) C, id_(C_(l + m + 2)))}) - else - (c := new ChainComplex; c.ring = E.filteredComplex _infinity .ring; - c) - ) filteredHomologyObject = method() @@ -994,20 +894,12 @@ filteredHomologyObject(ZZ, ZZ,FilteredComplex) := (p,n,K) -> ( image(inducedMap(HH_n K_infinity, HH_n K_p, id_(K_infinity _n))) ) - associatedGradedHomologyObject = method() associatedGradedHomologyObject(ZZ,ZZ,FilteredComplex) := (p,n,K) -> ( filteredHomologyObject(p,n,K) / filteredHomologyObject(p-1,n,K) ) - - ------------------------------------------------------------ ------------------------------------------------------------ - - - beginDocumentation() undocumented { @@ -1152,6 +1044,7 @@ document { } + doc /// Key "Examples of filtered complexes and spectral sequences" @@ -1215,7 +1108,7 @@ doc /// Example B = QQ[a..d] J = ideal vars B - C = complete res monomialCurveIdeal(B,{1,3,4}) + C = freeResolution monomialCurveIdeal(B,{1,3,4}) K = filteredComplex(J,C,4) Text Here are some higher pages of the associated spectral sequence: @@ -1228,7 +1121,6 @@ doc /// E^4 .dd /// - doc /// Key "Filtered complexes and simplicial complexes" @@ -1274,7 +1166,6 @@ doc /// "Filtrations and homomorphism complexes" /// - doc /// Key "Filtrations and homomorphism complexes" @@ -1310,8 +1201,8 @@ doc /// Example A = QQ[x,y,z,w]; - B = res monomialCurveIdeal(A, {1,2,3}); - C = res monomialCurveIdeal(A, {1,3,4}); + B = freeResolution monomialCurveIdeal(A, {1,2,3}); + C = freeResolution monomialCurveIdeal(A, {1,3,4}); F' = Hom(filteredComplex B, C) F'' = Hom(B,filteredComplex C) Text @@ -1362,8 +1253,8 @@ doc /// --$ B\otimes(filteredComplex C)$. Example A = QQ[x,y,z,w]; - B = res monomialCurveIdeal(A,{1,2,3}); - C = res monomialCurveIdeal(A,{1,3,4}); + B = freeResolution monomialCurveIdeal(A,{1,2,3}); + C = freeResolution monomialCurveIdeal(A,{1,3,4}); F' = (filteredComplex B) ** C F'' = B ** (filteredComplex C) Text @@ -1378,7 +1269,7 @@ doc /// SeeAlso "Balancing Tor" /// - + doc /// Key "How to make filtered complexes from chain complex maps" @@ -1409,25 +1300,25 @@ doc /// R = QQ[x,y,z,w] ; c2 = matrix(R,{{1},{0}}) ; c1 = matrix(R,{{0,1}}) ; - C = chainComplex({c1,c2}) + C = complex ({c1,c2}) D_2 = image matrix(R,{{1}}); D_1 = image matrix(R,{{1,0},{0,0}}); D_0 = image matrix(R,{{1}}); - D = chainComplex({inducedMap(D_0,D_1,C.dd_1),inducedMap(D_1,D_2,C.dd_2)}) + D = complex({inducedMap(D_0,D_1,C.dd_1),inducedMap(D_1,D_2,C.dd_2)}) E_2 = image matrix(R,{{0}}); E_1 = image matrix(R,{{1,0},{0,0}}); E_0 = image matrix(R,{{1}}); - E = chainComplex({inducedMap(E_0,E_1,C.dd_1),inducedMap(E_1,E_2,C.dd_2)}) + E = complex({inducedMap(E_0,E_1,C.dd_1),inducedMap(E_1,E_2,C.dd_2)}) Text We now make our chain complex maps. Example - d = chainComplexMap(C,D,apply(spots C, i-> inducedMap(C_i,D_i,id_C _i))) - e = chainComplexMap(C,E,apply(spots C, i->inducedMap(C_i,E_i, id_C _i))) + d = map(C,D,apply(spots C, i-> inducedMap(C_i,D_i,id_C _i))) + e = map(C,E,apply(spots C, i->inducedMap(C_i,E_i, id_C _i))) Text We can check that these are indeed chain complex maps: Example - isChainComplexMap d - isChainComplexMap e + isWellDefined d + isWellDefined e Text Now, given the list of chain complex maps $\{d, e\}$, we obtain a filtration of $C$ by: @@ -1510,8 +1401,8 @@ doc /// R = S/I; kR = coker vars R; kS = coker vars S; - CS = res kS; - CR = res(kR,LengthLimit=>6); + CS = freeResolution kS; + CR = freeResolution(kR,LengthLimit=>6); CS' = CS**R; E = prune spectralSequence (CS' ** filteredComplex CR); Text @@ -1544,6 +1435,7 @@ doc /// appears on the $E_1$ cancels in two steps via an $E_2$ map with $k^6$ and via an $E_3$ map with a $k^2$. /// + doc /// Key "Identifying anti-podal points of the two sphere" @@ -1566,7 +1458,7 @@ doc /// Text We can check that the homology of the simplicial complex twoSphere agrees with that of $\mathbb{S}^2$. Example - C = truncate(chainComplex complex twoSphere,1) + C = naiveTruncation(complex twoSphere,1,infinity) prune HH C Text We now write down our simplicial complex whose topological realization @@ -1578,7 +1470,7 @@ doc /// Again we can check that we've entered a simplicial complex whose homology agrees with that of the real projective plane. Example - B = truncate(chainComplex complex realProjectivePlane,1) + B = naiveTruncation(complex realProjectivePlane, 1,infinity) prune HH B Text We now compute the fibers of the anti-podal quotient map @@ -1614,6 +1506,7 @@ doc /// E^2 .dd /// + doc/// Key "The fibration of the Klein Bottle over the sphere with fibers the sphere" @@ -1633,7 +1526,7 @@ doc/// We can check that the homology of this simplicial complex agrees with that of the Klein Bottle: Example - C = truncate(chainComplex complex Delta,1) + C = naiveTruncation(complex Delta,1, infinity) prune HH C Text Let $S$ be the simplicial complex with facets $\{A_0 A_1, A_0 A_2, A_1 A_2\}$. Then $S$ is a triangulation of $S^1$. The simplicial map @@ -1662,6 +1555,7 @@ doc/// homology of the Klein bottle /// + doc /// Key "The trivial fibration over the sphere with fibers the sphere"--"The trivial fibration over the sphere with fiber the sphere" @@ -1684,7 +1578,7 @@ doc /// $\Delta$ agrees with that of the torus $\mathbb{S}^1 \times \mathbb{S}^1 $ Example - C = truncate(chainComplex complex Delta,1) + C = naiveTruncation(complex Delta,1, infinity) prune HH C Text Let $S$ be the simplicial complex with facets $\{A_0 A_1, A_0 A_2, A_1 A_2\}$. Then $S$ is a triangulation of $S^1$. The simplicial map @@ -1707,6 +1601,7 @@ doc /// E^2 /// + doc /// Key "Spectral sequences and non-Koszul syzygies" @@ -1730,12 +1625,12 @@ doc /// p_1 = y^2*w; p_2 = y^2*z+x^2*w; I = ideal(p_0,p_1,p_2); - -- make the frobenious power of the irrelevant ideal + -- make the Frobenius power of the irrelevant ideal B = B_*/(x -> x^2)//ideal; -- need to take a large enough power. -- it turns out that 2 is large enough for this example - G = complete res image gens B; - F = koszul gens I; + G = freeResolution image gens B; + F = koszulComplex gens I; K = Hom(G, filteredComplex(F)); E = prune spectralSequence K; E^1 @@ -1763,7 +1658,8 @@ doc /// -- this is what is predicted by the paper. isIsomorphism(E^2 .dd_{3, -1}) /// - + + doc /// Key "Spectral sequences and connecting morphisms" @@ -1787,17 +1683,17 @@ doc /// $C$ is a general divisor of type $(3,3)$ on $X$. This connecting morphism is an isomorphism. Example - R = ZZ/101[a_0..b_1, Degrees=>{2:{1,0},2:{0,1}}]; -- PP^1 x PP^1 - M = intersect(ideal(a_0,a_1),ideal(b_0,b_1)) ; -- irrelevant ideal - M = M_*/(x -> x^5)//ideal ; -- Suitably high Frobenius power of M - G = res image gens M ; - I = ideal random(R^1, R^{{-3,-3}}) -- ideal of C - b = chainComplex gradedModule R^{{1,0}} -- make line bundle a chain complex - a = chainComplex gradedModule R^{{-2,-3}} + R = ZZ/101[a_0..b_1, Degrees=>{2:{1,0},2:{0,1}}] -- PP^1 x PP^1 + M = intersect(ideal(a_0,a_1),ideal(b_0,b_1)) -- irrelevant ideal + M = M_*/(x -> x^5)//ideal -- Suitably high Frobenius power of M + G = freeResolution image gens M + I = ideal random(R^1, R^{{-3,-3}}) -- ideal of C -- but we don't use this in what follows + B = complex R^{{1,0}} -- make line bundle a chain complex + A = complex R^{{-2,-3}} -- make the map OO(-2, -3) --> OO(1,0) - f = chainComplexMap(b, a,{random(R^1, R^{{-3,-3}})}) ; - K = filteredComplex ({Hom(G,f)}) ; -- the two step filtered complex we want - E = prune spectralSequence K ; + f = randomComplexMap(B, A, Degree => 0) + K = filteredComplex ({Hom(G,f)}) -- the two step filtered complex we want + E = prune spectralSequence K; Text The degree zero piece of the map $E^1 .dd_{1, -2}$ below is the desired connecting morphism $H^1(C, OO_C(1,0)) \rightarrow H^2(X, OO_X(-2,-3))$. @@ -1813,8 +1709,9 @@ doc /// prune connectingMorphism(Hom(G, f), - 2) ; prune connectingMorphism(Hom(G, f), - 2) == E^1 .dd_{1, -2} -/// - +/// + + doc /// Key "Spectral sequences and hypercohomology calculations" @@ -1830,7 +1727,7 @@ doc /// $H^i(X, \mathcal{F})$ can be realized as the degree zero piece of the multigraded module $Ext^i(B^{[l]}, F)$ for sufficiently large $l$; here $B^{[l]}$ denotes - the $l$th Frobenius power of $B$ and $F$ is any multigraded module whose + the $l$th Forbenius power of $B$ and $F$ is any multigraded module whose corresponding sheaf on $X$ is $\mathcal{F}$. Given the fan of @@ -1848,7 +1745,7 @@ doc /// We first make the multi-graded coordinate ring of $\mathbb{P}^1 \times \mathbb{P}^1$, the - irrelevant ideal, and a sufficentily high Frobenus power of the + irrelevant ideal, and a sufficiently high Frobenius power of the irrelevant ideal needed for our calculations. Also the complex $G$ below is a resolution of the irrelevant ideal. Example @@ -1857,14 +1754,14 @@ doc /// R = ZZ/101[a_0..b_1, Degrees=>{2:{1,0},2:{0,1}}]; -- PP^1 x PP^1 B = intersect(ideal(a_0,a_1),ideal(b_0,b_1)) ; -- irrelevant ideal B = B_*/(x -> x^5)//ideal ; -- Sufficentily high Frobenius power - G = res image gens B ; + G = freeResolution image gens B ; Text We next make the ideal, denoted by $I$ below, of a general divisor of type $(3,3)$ on $\mathbb{P}^1 \times \mathbb{P}^1$. Also the chain complex $F$ below is a resolution of this ideal. Example I = ideal random(R^1, R^{{-3,-3}}) ; -- ideal of C - F = res comodule I + F = freeResolution comodule I Text To use hypercohomology to compute the cohomology groups of the line bundle $\mathcal{O}_C(1,0)$ on $C$ we twist the @@ -2006,7 +1903,8 @@ doc /// Text Thus the E^3 page appears to have been computed correctly. /// - + + doc /// Key "Balancing Tor" @@ -2021,8 +1919,8 @@ doc /// To compute $Tor^A_i(M,N)$ we resolve the modules, tensor appropriately, and then take homology. Example - K = res M - J = res N + K = freeResolution M + J = freeResolution N Text The spectral sequence that computes $Tor^A_i(M,N)$ by tensoring $K$ with $N$ and taking homology is given by @@ -2074,16 +1972,15 @@ doc /// there is a spectral sequence E with E^2_{p,q} = Tor^S_p(Tor^R_q(M,S),N) that abuts to Tor^R_{p+q}(M,N). Example --- First example k=QQ; R=k[a,b,c]; S=k[s,t]; f = map(S,R,{s^2,s*t,t^2}); N = coker vars S; M = coker vars R --; - F := complete res N; + F := freeResolution N; pushFwdF := pushFwd(f,F); - G := complete res M; + G := freeResolution M; E := spectralSequence(filteredComplex(G) ** pushFwdF); EE := spectralSequence(G ** (filteredComplex pushFwdF)); e = prune E; @@ -2095,9 +1992,9 @@ doc /// ee^0 SeeAlso "Filtrations and tensor product complexes" - /// + -------------------------------------------- -- Documentation of methods and functions -- -------------------------------------------- @@ -2139,6 +2036,7 @@ doc /// "Filtrations and homomorphism complexes" /// + doc /// Key SpectralSequence @@ -2452,7 +2350,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text The infinity page of the resulting spectral sequence is computed below. @@ -2542,7 +2440,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute an example of a pruning map below. @@ -2572,13 +2470,13 @@ doc /// doc /// Key - (spots, ChainComplex) + (spots, Complex) Headline which spots does the given chain complex has a module. Usage s = spots L Inputs - L:ChainComplex + L:Complex Outputs s:List Description @@ -2599,7 +2497,7 @@ doc /// Inputs L:List or - L:ChainComplex + L:Complex or L:SpectralSequence ReducedHomology => Boolean @@ -2695,7 +2593,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); E = spectralSequence K Text @@ -2731,7 +2629,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); E = spectralSequence K Text @@ -2746,24 +2644,36 @@ doc /// "Examples of filtered complexes and spectral sequences" /// - - doc /// - Key - (truncate, ChainComplex, ZZ) - Headline - compute the hard truncation of a chain complex - Description - Text - Computes the hard truncation of a chain complex as a specified homological degree. - Example - B = QQ[a..d]; - C = koszul vars B - truncate(C,1) - truncate(C,-1) - truncate(C,-10) - truncate(C,10) -/// + Key + (naiveTruncation, Complex, ZZ) + Headline + compute the hard truncation of a chain complex + Usage + naiveTruncation(C, n) + Inputs + C:Complex + n:ZZ + Outputs + :Complex + Description + Text + This method returns the naive truncation of $C$ by truncating + the low homological degrees if $n$ is positive (i.e. from left) + and high homological degrees if $n$ is negative (i.e. from right). + Example + B = QQ[a..d]; + C = (koszulComplex vars B)[2] + naiveTruncation(C, 10) + naiveTruncation(C, 2) + naiveTruncation(C, 1) + naiveTruncation(C, 0) + naiveTruncation(C,-1) + naiveTruncation(C,-2) + naiveTruncation(C,-10) + SeeAlso + (naiveTruncation, Complex, ZZ, ZZ) +/// doc /// Key @@ -2785,7 +2695,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute an example of a pruning map below. @@ -2801,7 +2711,7 @@ doc /// doc /// Key - (support,ChainComplex) + (support,Complex) Headline nonzero parts of a chain complex Description @@ -2810,13 +2720,13 @@ doc /// Example A = QQ[x,y]; - C = koszul vars A + C = koszulComplex vars A support C - D = truncate(C,1) + D = naiveTruncation(C,1, infinity) spots D support D SeeAlso - (spots, ChainComplex) + (spots, Complex) /// @@ -2834,7 +2744,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute an example of a pruning map below. @@ -2846,7 +2756,8 @@ doc /// (prune, SpectralSequence) SpectralSequencePage PageMap -/// +/// + doc /// Key @@ -2873,38 +2784,38 @@ doc /// R = QQ[x,y,z,w] d2 = matrix(R,{{1},{0}}) d1 = matrix(R,{{0,1}}) - C = chainComplex({d1,d2}) + C = complex ({d1,d2}) Text We now make the modules of the another chain complex which we will label D. Example - D_2 = image matrix(R,{{1}}) - D_1 = image matrix(R,{{1,0},{0,0}}) - D_0 = image matrix(R,{{1}}) - D = chainComplex({inducedMap(D_0,D_1,C.dd_1),inducedMap(D_1,D_2,C.dd_2)}) + D2 = image matrix(R,{{1}}) + D1 = image matrix(R,{{1,0},{0,0}}) + D0 = image matrix(R,{{1}}) + D = complex({inducedMap(D0,D1,C.dd_1),inducedMap(D1,D2,C.dd_2)}) Text Now make a chain complex map. Example - d = chainComplexMap(C,D,apply(spots C, i-> inducedMap(C_i,D_i,id_C _i))) - isChainComplexMap d - d == chainComplexMap(C,D,{inducedMap(C_0,D_0,id_(C_0)),inducedMap(C_1,D_1,id_(C_1)),inducedMap(C_2,D_2,id_(C_2))}) + d = map(C,D,{inducedMap(C_0,D0,id_(C_0)),inducedMap(C_1,D1,id_(C_1)),inducedMap(C_2,D2,id_(C_2))}) + isWellDefined d Text We now make the modules of another chain complex which we will label E. Example - E_2 = image matrix(R,{{0}}) - E_1 = image matrix(R,{{1,0},{0,0}}) - E_0 = image matrix(R,{{1}}) - E = chainComplex({inducedMap(E_0,E_1,C.dd_1),inducedMap(E_1,E_2,C.dd_2)}) + E2 = image matrix(R,{{0}}) + E1 = image matrix(R,{{1,0},{0,0}}) + E0 = image matrix(R,{{1}}) + E = complex ({inducedMap(E0,E1,C.dd_1),inducedMap(E1,E2,C.dd_2)}) Text Now make a chain complex map. Example - e = chainComplexMap(C,E,apply(spots C, i->inducedMap(C_i,D_i, id_C _i))) + e = map(C,E,{inducedMap(C_0,E0,id_(C_0)),inducedMap(C_1,E1,id_(C_1)),inducedMap(C_2,E_2,id_(C_2))}) + isWellDefined e Text Now make a filtered complex from a list of chain complex maps. Example K = filteredComplex({d,e}) Text We can make a filtered complex, with a specified minimum filtration degree - from a list of ChainComplexMaps by using the Shift option. + from a list of ComplexMaps by using the Shift option. Example L = filteredComplex({d,e},Shift => 1) M = filteredComplex({d,e},Shift => -1) @@ -2924,16 +2835,17 @@ doc /// SeeAlso "maps between chain complexes" /// + doc /// Key - (filteredComplex, ChainComplex) + (filteredComplex, Complex) Headline obtain a filtered complex from a chain complex Usage K = filteredComplex C Inputs - C: ChainComplex + C: Complex -- these options don't do anything for this constructor. ReducedHomology => Boolean Shift => ZZ @@ -2943,14 +2855,14 @@ doc /// Text Produces the filtered complex obtained by successively truncating the complex. Example - needsPackage "SpectralSequences" A = QQ[x,y] - C = koszul vars A + C = koszulComplex vars A K = filteredComplex C - SeeAlso - (truncate, ChainComplex,ZZ) + SeeAlso + (naiveTruncation, Complex,ZZ) /// + doc /// Key (filteredComplex, SpectralSequence) @@ -2983,11 +2895,11 @@ doc /// can be recovered from the spectral sequence by: Example - C = filteredComplex E - SeeAlso - --(_, FilteredComplex,InfiniteNumber) - --(^,FilteredComplex,InfiniteNumber) -/// + C = filteredComplex E + SeeAlso + (symbol _, FilteredComplex, InfiniteNumber) + (symbol ^, FilteredComplex, InfiniteNumber) +/// doc /// @@ -3013,7 +2925,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute the degree $0$ piece of the $E^3$ page below. @@ -3045,7 +2957,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute the degree $0$ piece of the $E^3$ page below. @@ -3055,32 +2967,33 @@ doc /// /// doc /// - Key - (chainComplex, FilteredComplex) - Headline - the ambient chain complex of a filtered complex - Usage - C = chainComplex K - Inputs - K:FilteredComplex - Outputs - C:ChainComplex - Description - Text + Key + (complex, FilteredComplex) + Headline + the ambient chain complex of a filtered complex + Usage + C = complex K + Inputs + K:FilteredComplex + Outputs + C:Complex + Description + Text Returns the ambient chain complex of the filtered complex. Example A = QQ[x,y]; - C = koszul vars A + C = koszulComplex vars A K = filteredComplex C; - chainComplex K - K_infinity - SeeAlso - (symbol _, FilteredComplex, ZZ) + complex K + K_infinity + SeeAlso + (symbol _, FilteredComplex, ZZ) (symbol _, FilteredComplex, InfiniteNumber) (symbol ^, FilteredComplex, ZZ) - (symbol ^, FilteredComplex, InfiniteNumber) + (symbol ^, FilteredComplex, InfiniteNumber) /// + doc /// Key (minimalPresentation, SpectralSequence) @@ -3105,7 +3018,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text Compare some pages of the non-pruned version of the spectral sequence @@ -3147,7 +3060,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text Compare some pruned and non-prunded pages the spectral sequence $E$ below. @@ -3182,22 +3095,22 @@ doc /// Returns the spectral sequence associated to the filtered complex. Example A = QQ[x,y]; - C = koszul vars A + C = koszulComplex vars A K = filteredComplex C; E = spectralSequence K /// doc /// Key - (Hom, FilteredComplex, ChainComplex) - (Hom, ChainComplex, FilteredComplex) + (Hom, FilteredComplex, Complex) + (Hom, Complex, FilteredComplex) Headline the filtered Hom complex Usage f = Hom(K,C) Inputs K:FilteredComplex - C:ChainComplex + C:Complex Outputs f:FilteredComplex Description @@ -3206,8 +3119,8 @@ doc /// an example which illustrates the syntax. Example A = QQ[x,y,z,w]; - B = res monomialCurveIdeal(A, {1,2,3}); - C = res monomialCurveIdeal(A, {1,3,4}); + B = freeResolution monomialCurveIdeal(A, {1,2,3}); + C = freeResolution monomialCurveIdeal(A, {1,3,4}); F' = Hom(filteredComplex B, C) F'' = Hom(B,filteredComplex C) SeeAlso @@ -3215,25 +3128,25 @@ doc /// /// doc /// - Key - (chainComplex, SpectralSequence) - Headline - the underlying chain complex of a Spectral Sequence - Usage - K = chainComplex E - Inputs - E:SpectralSequence - Outputs - K:ChainComplex - Description - Text + Key + (complex, SpectralSequence) + Headline + the underlying chain complex of a Spectral Sequence + Usage + K = complex E + Inputs + E:SpectralSequence + Outputs + K:Complex + Description + Text Returns the underlying chain complex of a spectral sequence. Example A = QQ[x,y]; - C = koszul vars A + C = koszulComplex vars A K = filteredComplex C; E = spectralSequence K - chainComplex E + complex E /// doc /// @@ -3259,7 +3172,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text @@ -3304,9 +3217,9 @@ doc /// S = R/ideal"x2"; N = S^1/ideal"x"; M = R^1/R_0; - C = res M; + C = freeResolution M; C' = C ** S; - D = res(N,LengthLimit => 10); + D = freeResolution(N,LengthLimit => 10); E0 = C' ** (filteredComplex D); E = prune spectralSequence E0; Text @@ -3321,9 +3234,9 @@ doc /// S = R/ideal"x2"; N = S^1/ideal"x"; M = R^1/R_0; - C = res M; + C = freeResolution M; C' = C ** S; - D = res(N,LengthLimit => 10); + D = freeResolution(N,LengthLimit => 10); E0 = C' ** (filteredComplex D); E = prune spectralSequence E0; netPage(E_2,{-5,0},{7,1}) @@ -3351,7 +3264,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text @@ -3392,7 +3305,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute a map on the third page of the spectral sequence associated to $K$. @@ -3426,7 +3339,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text We compute a map on the third page of the spectral sequence associated to $K$. @@ -3460,7 +3373,7 @@ doc /// Example B = QQ[a..d]; J = ideal vars B; - C = complete res monomialCurveIdeal(B,{1,3,4}); + C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); Text @@ -3515,7 +3428,7 @@ doc /// (Using cohomological or upper indexing conventions.) The relationship $E^{-i,-j} = E_{i,j}$ holds. Example A = QQ[x,y] - C = koszul vars A; + C = koszulComplex vars A; K = filteredComplex C; E = spectralSequence K E_0 @@ -3545,7 +3458,7 @@ doc /// (Using homological or lower indexing conventions.) The relationship $E_{i,j} = E^{-i,-j}$ holds. Example A = QQ[x,y] - C = koszul vars A; + C = koszulComplex vars A; K = filteredComplex C; E = spectralSequence K E^0 @@ -3558,15 +3471,15 @@ doc /// doc /// Key - (symbol **, ChainComplex, FilteredComplex) - (symbol **, FilteredComplex, ChainComplex) + (symbol **, Complex, FilteredComplex) + (symbol **, FilteredComplex, Complex) Headline filtered tensor product of complexes Usage KK = C ** K KK = K ** C Inputs - C:ChainComplex + C:Complex K:FilteredComplex Outputs KK:FilteredComplex @@ -3577,26 +3490,26 @@ doc /// The following example illustrates the syntax. Example A = QQ[x,y]; - B = koszul vars A; - C = koszul vars A; + B = koszulComplex vars A; + C = koszulComplex vars A; F' = (filteredComplex B) ** C F'' = B ** (filteredComplex C) SeeAlso "Filtrations and tensor product complexes" /// - + doc /// Key - (tensor, RingMap, ChainComplex) + (tensor, RingMap, Complex) Headline tensor product of a chain complex by a ring map Usage D = tensor(f,C) Inputs f:RingMap - C:ChainComplex + C:Complex Outputs - D:ChainComplex + D:Complex Description Text Given a ring map R -> S and a chain complex over R, @@ -3605,7 +3518,7 @@ doc /// R = QQ[x]; M = R^1/(x^2); S = R/(x^4); - C = res M + C = freeResolution M f = map(S,R,{1}); tensor(f,C) SeeAlso @@ -3625,7 +3538,7 @@ doc /// K:FilteredComplex i:ZZ Outputs - f:ChainComplexMap + f:ComplexMap Description Text Returns the chain complex map specifying the inclusion of the i piece @@ -3633,11 +3546,11 @@ doc /// complex to the ambient chain complex. Example A = QQ[x,y]; - C = koszul vars A; + C = koszulComplex vars A; K = filteredComplex C inducedMap(K,1) /// - + doc /// Key (symbol _, FilteredComplex, ZZ) @@ -3651,14 +3564,14 @@ doc /// j:ZZ an integer, infinity, or -infinity Outputs - C:ChainComplex + C:Complex Description Text Returns the chain complex in (homological) filtration degree j. The relationship $K _ j = K ^{(-j)}$ holds. Example A = QQ[x,y]; - C = koszul vars A; + C = koszulComplex vars A; K = filteredComplex C K_0 K_1 @@ -3687,14 +3600,14 @@ doc /// j:ZZ an integer, infinity, or -infinity Outputs - C:ChainComplex + C:Complex Description Text Returns the chain complex in (cohomological) filtration degree j. The relationship $K ^ j = K _{(-j)}$ holds. Example A = QQ[x,y]; - C = koszul vars A; + C = koszulComplex vars A; K = filteredComplex C K_0 K_1 @@ -3719,7 +3632,7 @@ doc /// Usage g = connectingMorphism(f, n) Inputs - f:ChainComplexMap + f:ComplexMap n:ZZ Outputs g:Matrix @@ -3731,13 +3644,13 @@ doc /// doc /// Key - (connectingMorphism, ChainComplexMap,ZZ) + (connectingMorphism, ComplexMap,ZZ) Headline use spectral sequences to compute connecting morphisms Usage g = connectingMorphism(f, n) Inputs - f:ChainComplexMap + f:ComplexMap n:ZZ Outputs g:Matrix @@ -3880,10 +3793,6 @@ doc /// /// ---doc /// --- Key --- associatedGradedHomologyObject ---/// doc /// Key @@ -3903,7 +3812,7 @@ doc /// Text Computes the associated graded homology object determined by the filtered chain complex /// - + doc /// Key "Edge homomorphisms" @@ -3958,7 +3867,7 @@ doc /// "Examples of filtered complexes and spectral sequences" /// - + doc /// Key @@ -3971,7 +3880,7 @@ doc /// Inputs E: SpectralSequence Outputs - C: ChainComplex + C: Complex Description Text Suppose that $E$ is a spectral sequence with the properties that: @@ -4021,16 +3930,17 @@ doc /// The method currently does not support pruned spectral sequences. /// + doc /// Key - (filteredComplex, Ideal, ChainComplex, ZZ) + (filteredComplex, Ideal, Complex, ZZ) Headline I-adic filtrations of chain complexes Usage K = filteredComplex(I,C,n) Inputs I: Ideal - C: ChainComplex + C: Complex n: ZZ Outputs K: FilteredComplex @@ -4040,7 +3950,7 @@ doc /// Example B = QQ[a..d] J = ideal vars B - C = complete res monomialCurveIdeal(B,{1,3,4}) + C = freeResolution monomialCurveIdeal(B,{1,3,4}) K = filteredComplex(J,C,4) Text Here are higher some pages of the associated spectral sequence: @@ -4092,9 +4002,9 @@ doc /// Example rank ker E^2 .dd_{2,-1} rank image E^2 .dd_{2,-1} -/// +/// + --- We might want to not include this next example doc /// Key "Example 2" @@ -4127,7 +4037,6 @@ doc /// E^infinity /// --- We might want to not include this next example doc /// Key "Example 3" @@ -4155,24 +4064,19 @@ doc /// E^2 .dd E^infinity prune HH K_infinity -/// - +/// +-------------------------------------------------------------------------------- TEST /// -restart; -needsPackage "SpectralSequences"; A = QQ[a,b,c]; -C = new ChainComplex; -C.ring = A; +C = complex(A^0); K = filteredComplex C; -assert(K_0 == C); -assert(K_1 == C); +assert(K_0 == C) +assert(K_1 == C) /// TEST /// -restart; -needsPackage "SpectralSequences"; A = QQ[a,b,c]; D = simplicialComplex {a*b*c}; F2D = D; @@ -4196,8 +4100,6 @@ assert(all(keys support e^5, j -> isIsomorphism homologyIsomorphism(e,j#0,j#1,5) /// TEST /// -restart -needsPackage "SpectralSequences"; -- The following example is taken from p. 127, Fig 7.2 of -- Zomorodian's "Topology for computing" A = ZZ [s,t,u,v,w] ; @@ -4252,8 +4154,6 @@ assert(all(keys support e^12, j -> isIsomorphism homologyIsomorphism(e,j#0,j#1,1 /// TEST /// -restart -needsPackage "SpectralSequences"; A = QQ[a,b,c,d]; D = simplicialComplex {a*d*c, a*b, a*c, b*c}; F2D = D; @@ -4291,11 +4191,9 @@ assert(all(keys support e^12, j -> isIsomorphism homologyIsomorphism(e,j#0,j#1,1 /// TEST /// -restart -needsPackage "SpectralSequences"; B = QQ[a..d]; J = ideal vars B; -C = complete res monomialCurveIdeal(B,{1,3,4}); +C = freeResolution monomialCurveIdeal(B,{1,3,4}); K = filteredComplex(J,C,4); e = prune spectralSequence K; assert(all(keys support e^0, j -> isIsomorphism homologyIsomorphism(e,j#0,j#1,0))) @@ -4307,15 +4205,13 @@ assert(all(keys support e^4, j -> isIsomorphism homologyIsomorphism(e,j#0,j#1,4) TEST /// -restart -needsPackage "SpectralSequences"; S = ZZ/101[x,y]; I = ideal(x^2,x*y,y^2); R = S/I; kR = coker vars R; kS = coker vars S; -CS = res kS; -CR = res(kR,LengthLimit=>6); +CS = freeResolution kS; +CR = freeResolution(kR,LengthLimit=>6); CS' = CS**R; E = prune spectralSequence (CS' ** filteredComplex CR); assert(all(keys support E^0, j -> isIsomorphism homologyIsomorphism(E,j#0,j#1,0))) @@ -4326,9 +4222,11 @@ assert(all(keys support E^4, j -> isIsomorphism homologyIsomorphism(E,j#0,j#1,4) /// end ---- --- scratch code -- ---- + + + +--the end-- + -------------------------------------------------------------------------------- restart @@ -4338,7 +4236,3 @@ installPackage("SpectralSequences", RemakeAllDocumentation => true) check "SpectralSequences"; viewHelp SpectralSequences ------------------------------------------ - -Status API Training Shop Blog About -© 2016 GitHub, Inc. Terms Privacy Security Contact Help - diff --git a/M2/Macaulay2/packages/Style/katex/contrib/auto-render.min.js b/M2/Macaulay2/packages/Style/katex/contrib/auto-render.min.js index 32a7dd8a405..0a1f35df54e 100644 --- a/M2/Macaulay2/packages/Style/katex/contrib/auto-render.min.js +++ b/M2/Macaulay2/packages/Style/katex/contrib/auto-render.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};r.d(o,{default:function(){return p}});var i=r(757),a=r.n(i);const l=function(e,t,n){let r=n,o=0;const i=e.length;for(;re.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;n=e.search(o),-1!==n;){n>0&&(r.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const o=t.findIndex((t=>e.startsWith(t.left)));if(n=l(t[o].right,e,t[o].left.length),-1===n)break;const i=e.slice(0,n+t[o].right.length),a=s.test(i)?i:e.slice(t[o].left.length,n);r.push({type:"math",data:a,rawData:i,display:t[o].display}),e=e.slice(n+t[o].right.length)}return""!==e&&r.push({type:"text",data:e}),r};const c=function(e,t){const n=d(e,t.delimiters);if(1===n.length&&"text"===n[0].type)return null;const r=document.createDocumentFragment();for(let e=0;e-1===e.indexOf(" "+t+" ")))&&f(r,t)}}};var p=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},f(e,n)};return o=o.default}()})); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};r.d(o,{default:function(){return p}});var i=r(757),a=r.n(i);const l=function(e,t,n){let r=n,o=0;const i=e.length;for(;re.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")).join("|")+")");for(;n=e.search(o),-1!==n;){n>0&&(r.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const o=t.findIndex(t=>e.startsWith(t.left));if(n=l(t[o].right,e,t[o].left.length),-1===n)break;const i=e.slice(0,n+t[o].right.length),a=s.test(i)?i:e.slice(t[o].left.length,n);r.push({type:"math",data:a,rawData:i,display:t[o].display}),e=e.slice(n+t[o].right.length)}return""!==e&&r.push({type:"text",data:e}),r};const c=function(e,t){const n=d(e,t.delimiters);if(1===n.length&&"text"===n[0].type)return null;const r=document.createDocumentFragment();for(let e=0;e!e.includes(" "+t+" "))&&f(i,t)}}};var p=function(e,t){if(!e)throw new Error("No element provided to render");const n={};Object.assign(n,t),n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=new Set((null==t?void 0:t.ignoredTags)||["script","noscript","style","textarea","pre","code","option"]),n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},f(e,n)};return o=o.default}()}); \ No newline at end of file diff --git a/M2/Macaulay2/packages/Style/katex/contrib/copy-tex.min.js b/M2/Macaulay2/packages/Style/katex/contrib/copy-tex.min.js index a826f4f57e2..88f9c300553 100644 --- a/M2/Macaulay2/packages/Style/katex/contrib/copy-tex.min.js +++ b/M2/Macaulay2/packages/Style/katex/contrib/copy-tex.min.js @@ -1 +1 @@ -!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={};const t={inline:["$","$"],display:["$$","$$"]};var n=function(e,n){void 0===n&&(n=t);const o=e.querySelectorAll(".katex-mathml + .katex-html");for(let e=0;ee instanceof Text?e.textContent:e.outerHTML)).join("");r.setData("text/html",c),r.setData("text/plain",n(s).textContent),e.preventDefault()})),e=e.default}()})); \ No newline at end of file +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}("undefined"!=typeof self?self:this,function(){return function(){"use strict";var e={};const t={inline:["$","$"],display:["$$","$$"]};var n=function(e,n){void 0===n&&(n=t);const o=e.querySelectorAll(".katex-mathml + .katex-html");for(let e=0;ee instanceof Text?e.textContent:e.outerHTML).join("");r.setData("text/html",c),r.setData("text/plain",n(s).textContent),e.preventDefault()}),e=e.default}()}); \ No newline at end of file diff --git a/M2/Macaulay2/packages/Style/katex/contrib/render-a11y-string.min.js b/M2/Macaulay2/packages/Style/katex/contrib/render-a11y-string.min.js index 314ce26d711..13c61bb8121 100644 --- a/M2/Macaulay2/packages/Style/katex/contrib/render-a11y-string.min.js +++ b/M2/Macaulay2/packages/Style/katex/contrib/render-a11y-string.min.js @@ -1 +1 @@ -!function(e,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r(require("katex"));else if("function"==typeof define&&define.amd)define(["katex"],r);else{var t="object"==typeof exports?r(require("katex")):r(e.katex);for(var a in t)("object"==typeof exports?exports:e)[a]=t[a]}}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var r={757:function(r){r.exports=e}},t={};function a(e){var o=t[e];if(void 0!==o)return o.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,a),n.exports}a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,{a:r}),r},a.d=function(e,r){for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)};var o={},n=a(757),s=a.n(n);const l={"(":"left parenthesis",")":"right parenthesis","[":"open bracket","]":"close bracket","\\{":"left brace","\\}":"right brace","\\lvert":"open vertical bar","\\rvert":"close vertical bar","|":"vertical bar","\\uparrow":"up arrow","\\Uparrow":"up arrow","\\downarrow":"down arrow","\\Downarrow":"down arrow","\\updownarrow":"up down arrow","\\leftarrow":"left arrow","\\Leftarrow":"left arrow","\\rightarrow":"right arrow","\\Rightarrow":"right arrow","\\langle":"open angle","\\rangle":"close angle","\\lfloor":"open floor","\\rfloor":"close floor","\\int":"integral","\\intop":"integral","\\lim":"limit","\\ln":"natural log","\\log":"log","\\sin":"sine","\\cos":"cosine","\\tan":"tangent","\\cot":"cotangent","\\sum":"sum","/":"slash",",":"comma",".":"point","-":"negative","+":"plus","~":"tilde",":":"colon","?":"question mark","'":"apostrophe","\\%":"percent"," ":"space","\\ ":"space","\\$":"dollar sign","\\angle":"angle","\\degree":"degree","\\circ":"circle","\\vec":"vector","\\triangle":"triangle","\\pi":"pi","\\prime":"prime","\\infty":"infinity","\\alpha":"alpha","\\beta":"beta","\\gamma":"gamma","\\omega":"omega","\\theta":"theta","\\sigma":"sigma","\\lambda":"lambda","\\tau":"tau","\\Delta":"delta","\\delta":"delta","\\mu":"mu","\\rho":"rho","\\nabla":"del","\\ell":"ell","\\ldots":"dots","\\hat":"hat","\\acute":"acute"},i={prime:"prime",degree:"degrees",circle:"degrees",2:"squared",3:"cubed"},c={"|":"open vertical bar",".":""},p={"|":"close vertical bar",".":""},u={"+":"plus","-":"minus","\\pm":"plus minus","\\cdot":"dot","*":"times","/":"divided by","\\times":"times","\\div":"divided by","\\circ":"circle","\\bullet":"bullet"},d={"=":"equals","\\approx":"approximately equals","\u2260":"does not equal","\\geq":"is greater than or equal to","\\ge":"is greater than or equal to","\\leq":"is less than or equal to","\\le":"is less than or equal to",">":"is greater than","<":"is less than","\\leftarrow":"left arrow","\\Leftarrow":"left arrow","\\rightarrow":"right arrow","\\Rightarrow":"right arrow",":":"colon"},h={"\\underleftarrow":"left arrow","\\underrightarrow":"right arrow","\\underleftrightarrow":"left-right arrow","\\undergroup":"group","\\underlinesegment":"line segment","\\utilde":"tilde"},b=(e,r,t)=>{if(!e)return;let a;a="open"===r?e in c?c[e]:l[e]||e:"close"===r?e in p?p[e]:l[e]||e:"bin"===r?u[e]||e:"rel"===r?d[e]||e:l[e]||e,/^\d+$/.test(a)&&t.length>0&&/^\d+$/.test(t[t.length-1])?t[t.length-1]+=a:a&&t.push(a)},m=(e,r)=>{const t=[];e.push(t),r(t)},f=function(e,r,t){if(void 0===r&&(r=[]),e instanceof Array)for(let a=0;a{switch(e.type){case"accent":m(r,(r=>{f(e.base,r,t),r.push("with"),b(e.label,"normal",r),r.push("on top")}));break;case"accentUnder":m(r,(r=>{f(e.base,r,t),r.push("with"),b(h[e.label],"normal",r),r.push("underneath")}));break;case"accent-token":case"color-token":case"kern":case"leftright-right":case"size":case"infix":case"internal":break;case"atom":{const{text:t}=e;switch(e.family){case"bin":b(t,"bin",r);break;case"close":b(t,"close",r);break;case"inner":b(e.text,"inner",r);break;case"open":b(t,"open",r);break;case"punct":b(t,"punct",r);break;case"rel":b(t,"rel",r);break;default:throw e.family,new Error('"'+e.family+'" is not a valid atom type')}break}case"color":{const a=e.color.replace(/katex-/,"");m(r,(r=>{r.push("start color "+a),f(e.body,r,t),r.push("end color "+a)}));break}case"delimsizing":e.delim&&"."!==e.delim&&b(e.delim,"normal",r);break;case"genfrac":m(r,(r=>{const{leftDelim:a,rightDelim:o}=e;e.hasBarLine?(r.push("start fraction"),a&&b(a,"open",r),f(e.numer,r,t),r.push("divided by"),f(e.denom,r,t),o&&b(o,"close",r),r.push("end fraction")):(r.push("start binomial"),a&&b(a,"open",r),f(e.numer,r,t),r.push("over"),f(e.denom,r,t),o&&b(o,"close",r),r.push("end binomial"))}));break;case"hbox":case"lap":case"ordgroup":case"raisebox":case"sizing":case"styling":case"smash":case"vcenter":case"operatorname":case"font":case"html":f(e.body,r,t);break;case"leftright":m(r,(r=>{b(e.left,"open",r),f(e.body,r,t),b(e.right,"close",r)}));break;case"mathord":b(e.text,"normal",r);break;case"op":{const{body:a,name:o}=e;a?f(a,r,t):o&&b(o,"normal",r);break}case"op-token":case"textord":b(e.text,t,r);break;case"overline":m(r,(function(r){r.push("start overline"),f(e.body,r,t),r.push("end overline")}));break;case"pmb":r.push("bold");break;case"phantom":r.push("empty space");break;case"rule":r.push("rectangle");break;case"spacing":r.push("space");break;case"sqrt":m(r,(r=>{const{body:a,index:o}=e;if(o)return"3"===w(f(o,[],t)).join(",")?(r.push("cube root of"),f(a,r,t),void r.push("end cube root")):(r.push("root"),r.push("start index"),f(o,r,t),void r.push("end index"));r.push("square root of"),f(a,r,t),r.push("end square root")}));break;case"supsub":{const{base:a,sub:o,sup:n}=e;let s=!1;if(a&&(f(a,r,t),s="op"===a.type&&"\\log"===a.name),o){const e=s?"base":"subscript";m(r,(function(r){r.push("start "+e),f(o,r,t),r.push("end "+e)}))}n&&m(r,(function(e){const r=w(f(n,[],t)).join(",");r in i?e.push(i[r]):(e.push("start superscript"),f(n,e,t),e.push("end superscript"))}));break}case"text":if("\\textbf"===e.font){m(r,(function(r){r.push("start bold text"),f(e.body,r,t),r.push("end bold text")}));break}m(r,(function(r){r.push("start text"),f(e.body,r,t),r.push("end text")}));break;case"enclose":if(/cancel/.test(e.label)){m(r,(function(r){r.push("start cancel"),f(e.body,r,t),r.push("end cancel")}));break}if(/box/.test(e.label)){m(r,(function(r){r.push("start box"),f(e.body,r,t),r.push("end box")}));break}if(/sout/.test(e.label)){m(r,(function(r){r.push("start strikeout"),f(e.body,r,t),r.push("end strikeout")}));break}if(/phase/.test(e.label)){m(r,(function(r){r.push("start phase angle"),f(e.body,r,t),r.push("end phase angle")}));break}throw new Error("KaTeX-a11y: enclose node with "+e.label+" not supported yet");case"vphantom":throw new Error("KaTeX-a11y: vphantom not implemented yet");case"hphantom":throw new Error("KaTeX-a11y: hphantom not implemented yet");case"array":throw new Error("KaTeX-a11y: array not implemented yet");case"raw":throw new Error("KaTeX-a11y: raw not implemented yet");case"url":throw new Error("KaTeX-a11y: url not implemented yet");case"tag":throw new Error("KaTeX-a11y: tag not implemented yet");case"verb":b("start verbatim","normal",r),b(e.body,"normal",r),b("end verbatim","normal",r);break;case"environment":throw new Error("KaTeX-a11y: environment not implemented yet");case"horizBrace":b("start "+e.label.slice(1),"normal",r),f(e.base,r,t),b("end "+e.label.slice(1),"normal",r);break;case"includegraphics":throw new Error("KaTeX-a11y: includegraphics not implemented yet");case"href":throw new Error("KaTeX-a11y: href not implemented yet");case"cr":throw new Error("KaTeX-a11y: cr not implemented yet");case"underline":m(r,(function(r){r.push("start underline"),f(e.body,r,t),r.push("end underline")}));break;case"xArrow":throw new Error("KaTeX-a11y: xArrow not implemented yet");case"cdlabel":throw new Error("KaTeX-a11y: cdlabel not implemented yet");case"cdlabelparent":throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");case"mclass":{const t=e.mclass.slice(1);f(e.body,r,t);break}case"mathchoice":f(e.text,r,t);break;case"htmlmathml":f(e.mathml,r,t);break;case"middle":b(e.delim,t,r);break;default:throw e.type,new Error("KaTeX a11y un-recognized type: "+e.type)}})(e,r,t);return r},w=function(e){let r=[];return e.forEach((function(e){e instanceof Array?r=r.concat(w(e)):r.push(e)})),r};return o.default=function(e,r){const t=s().__parse(e,r),a=f(t,[],"normal");return w(a).join(", ")},o=o.default}()})); \ No newline at end of file +!function(e,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r(require("katex"));else if("function"==typeof define&&define.amd)define(["katex"],r);else{var t="object"==typeof exports?r(require("katex")):r(e.katex);for(var a in t)("object"==typeof exports?exports:e)[a]=t[a]}}("undefined"!=typeof self?self:this,function(e){return function(){"use strict";var r={757:function(r){r.exports=e}},t={};function a(e){var o=t[e];if(void 0!==o)return o.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,a),n.exports}a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,{a:r}),r},a.d=function(e,r){for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)};var o={},n=a(757),s=a.n(n);const l={"(":"left parenthesis",")":"right parenthesis","[":"open bracket","]":"close bracket","\\{":"left brace","\\}":"right brace","\\lvert":"open vertical bar","\\rvert":"close vertical bar","|":"vertical bar","\\uparrow":"up arrow","\\Uparrow":"up arrow","\\downarrow":"down arrow","\\Downarrow":"down arrow","\\updownarrow":"up down arrow","\\leftarrow":"left arrow","\\Leftarrow":"left arrow","\\rightarrow":"right arrow","\\Rightarrow":"right arrow","\\langle":"open angle","\\rangle":"close angle","\\lfloor":"open floor","\\rfloor":"close floor","\\int":"integral","\\intop":"integral","\\lim":"limit","\\ln":"natural log","\\log":"log","\\sin":"sine","\\cos":"cosine","\\tan":"tangent","\\cot":"cotangent","\\sum":"sum","/":"slash",",":"comma",".":"point","-":"negative","+":"plus","~":"tilde",":":"colon","?":"question mark","'":"apostrophe","\\%":"percent"," ":"space","\\ ":"space","\\$":"dollar sign","\\angle":"angle","\\degree":"degree","\\circ":"circle","\\vec":"vector","\\triangle":"triangle","\\pi":"pi","\\prime":"prime","\\infty":"infinity","\\alpha":"alpha","\\beta":"beta","\\gamma":"gamma","\\omega":"omega","\\theta":"theta","\\sigma":"sigma","\\lambda":"lambda","\\tau":"tau","\\Delta":"delta","\\delta":"delta","\\mu":"mu","\\rho":"rho","\\nabla":"del","\\ell":"ell","\\ldots":"dots","\\hat":"hat","\\acute":"acute"},i={prime:"prime",degree:"degrees",circle:"degrees",2:"squared",3:"cubed"},c={"|":"open vertical bar",".":""},u={"|":"close vertical bar",".":""},p={"+":"plus","-":"minus","\\pm":"plus minus","\\cdot":"dot","*":"times","/":"divided by","\\times":"times","\\div":"divided by","\\circ":"circle","\\bullet":"bullet"},d={"=":"equals","\\approx":"approximately equals","\u2260":"does not equal","\\geq":"is greater than or equal to","\\ge":"is greater than or equal to","\\leq":"is less than or equal to","\\le":"is less than or equal to",">":"is greater than","<":"is less than","\\leftarrow":"left arrow","\\Leftarrow":"left arrow","\\rightarrow":"right arrow","\\Rightarrow":"right arrow",":":"colon"},h={"\\underleftarrow":"left arrow","\\underrightarrow":"right arrow","\\underleftrightarrow":"left-right arrow","\\undergroup":"group","\\underlinesegment":"line segment","\\utilde":"tilde"},b=(e,r,t)=>{if(!e)return;let a;a="open"===r?e in c?c[e]:l[e]||e:"close"===r?e in u?u[e]:l[e]||e:"bin"===r?p[e]||e:"rel"===r?d[e]||e:l[e]||e;const o=t[t.length-1];/^\d+$/.test(a)&&t.length>0&&"string"==typeof o&&/^\d+$/.test(o)?t[t.length-1]+=a:a&&t.push(a)},m=(e,r)=>{const t=[];e.push(t),r(t)},f=function(e,r,t){if(void 0===r&&(r=[]),e instanceof Array)for(let a=0;a{switch(e.type){case"accent":m(r,r=>{f(e.base,r,t),r.push("with"),b(e.label,"normal",r),r.push("on top")});break;case"accentUnder":m(r,r=>{f(e.base,r,t),r.push("with"),b(h[e.label],"normal",r),r.push("underneath")});break;case"accent-token":case"color-token":case"kern":case"leftright-right":case"size":case"infix":case"internal":break;case"atom":{const{text:t}=e;switch(e.family){case"bin":b(t,"bin",r);break;case"close":b(t,"close",r);break;case"inner":b(e.text,"inner",r);break;case"open":b(t,"open",r);break;case"punct":b(t,"punct",r);break;case"rel":b(t,"rel",r);break;default:throw e.family,new Error('"'+e.family+'" is not a valid atom type')}break}case"color":{const a=e.color.replace(/katex-/,"");m(r,r=>{r.push("start color "+a),f(e.body,r,t),r.push("end color "+a)});break}case"delimsizing":e.delim&&"."!==e.delim&&b(e.delim,"normal",r);break;case"genfrac":m(r,r=>{const{leftDelim:a,rightDelim:o}=e;e.hasBarLine?(r.push("start fraction"),a&&b(a,"open",r),f(e.numer,r,t),r.push("divided by"),f(e.denom,r,t),o&&b(o,"close",r),r.push("end fraction")):(r.push("start binomial"),a&&b(a,"open",r),f(e.numer,r,t),r.push("over"),f(e.denom,r,t),o&&b(o,"close",r),r.push("end binomial"))});break;case"hbox":case"lap":case"ordgroup":case"raisebox":case"sizing":case"styling":case"smash":case"vcenter":case"operatorname":case"font":case"html":f(e.body,r,t);break;case"leftright":m(r,r=>{b(e.left,"open",r),f(e.body,r,t),b(e.right,"close",r)});break;case"mathord":b(e.text,"normal",r);break;case"op":{const{body:a,name:o}=e;a?f(a,r,t):o&&b(o,"normal",r);break}case"op-token":case"textord":b(e.text,t,r);break;case"overline":m(r,function(r){r.push("start overline"),f(e.body,r,t),r.push("end overline")});break;case"pmb":r.push("bold");break;case"phantom":r.push("empty space");break;case"rule":r.push("rectangle");break;case"spacing":r.push("space");break;case"sqrt":m(r,r=>{const{body:a,index:o}=e;if(o)return"3"===w(f(o,[],t)).join(",")?(r.push("cube root of"),f(a,r,t),void r.push("end cube root")):(r.push("root"),r.push("start index"),f(o,r,t),void r.push("end index"));r.push("square root of"),f(a,r,t),r.push("end square root")});break;case"supsub":{const{base:a,sub:o,sup:n}=e;let s=!1;if(a&&(f(a,r,t),s="op"===a.type&&"\\log"===a.name),o){const e=s?"base":"subscript";m(r,function(r){r.push("start "+e),f(o,r,t),r.push("end "+e)})}n&&m(r,function(e){const r=w(f(n,[],t)).join(",");r in i?e.push(i[r]):(e.push("start superscript"),f(n,e,t),e.push("end superscript"))});break}case"text":if("\\textbf"===e.font){m(r,function(r){r.push("start bold text"),f(e.body,r,t),r.push("end bold text")});break}m(r,function(r){r.push("start text"),f(e.body,r,t),r.push("end text")});break;case"enclose":if(/cancel/.test(e.label)){m(r,function(r){r.push("start cancel"),f(e.body,r,t),r.push("end cancel")});break}if(/box/.test(e.label)){m(r,function(r){r.push("start box"),f(e.body,r,t),r.push("end box")});break}if(/sout/.test(e.label)){m(r,function(r){r.push("start strikeout"),f(e.body,r,t),r.push("end strikeout")});break}if(/phase/.test(e.label)){m(r,function(r){r.push("start phase angle"),f(e.body,r,t),r.push("end phase angle")});break}throw new Error("KaTeX-a11y: enclose node with "+e.label+" not supported yet");case"vphantom":throw new Error("KaTeX-a11y: vphantom not implemented yet");case"array":throw new Error("KaTeX-a11y: array not implemented yet");case"raw":throw new Error("KaTeX-a11y: raw not implemented yet");case"url":throw new Error("KaTeX-a11y: url not implemented yet");case"tag":throw new Error("KaTeX-a11y: tag not implemented yet");case"verb":b("start verbatim","normal",r),b(e.body,"normal",r),b("end verbatim","normal",r);break;case"environment":throw new Error("KaTeX-a11y: environment not implemented yet");case"horizBrace":b("start "+e.label.slice(1),"normal",r),f(e.base,r,t),b("end "+e.label.slice(1),"normal",r);break;case"includegraphics":throw new Error("KaTeX-a11y: includegraphics not implemented yet");case"href":throw new Error("KaTeX-a11y: href not implemented yet");case"cr":throw new Error("KaTeX-a11y: cr not implemented yet");case"underline":m(r,function(r){r.push("start underline"),f(e.body,r,t),r.push("end underline")});break;case"xArrow":throw new Error("KaTeX-a11y: xArrow not implemented yet");case"cdlabel":throw new Error("KaTeX-a11y: cdlabel not implemented yet");case"cdlabelparent":throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");case"mclass":{const t=e.mclass.slice(1);f(e.body,r,t);break}case"mathchoice":f(e.text,r,t);break;case"htmlmathml":f(e.mathml,r,t);break;case"middle":b(e.delim,t,r);break;default:throw new Error("KaTeX a11y un-recognized type: "+e.type)}})(e,r,t);return r},w=function(e){let r=[];return e.forEach(function(e){Array.isArray(e)?r=r.concat(w(e)):r.push(e)}),r};return o.default=function(e,r){const t=s().__parse(e,r),a=f(t,[],"normal");return w(a).join(", ")},o=o.default}()}); \ No newline at end of file diff --git a/M2/Macaulay2/packages/Style/katex/katex.min.css b/M2/Macaulay2/packages/Style/katex/katex.min.css index 865330a732f..59c5e059797 100644 --- a/M2/Macaulay2/packages/Style/katex/katex.min.css +++ b/M2/Macaulay2/packages/Style/katex/katex.min.css @@ -1 +1 @@ -@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.25"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo} +@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.45"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo} diff --git a/M2/Macaulay2/packages/Style/katex/katex.min.js b/M2/Macaulay2/packages/Style/katex/katex.min.js index babf9595fc0..32d63a39204 100644 --- a/M2/Macaulay2/packages/Style/katex/katex.min.js +++ b/M2/Macaulay2/packages/Style/katex/katex.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.katex=t():e.katex=t()}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={d:function(t,r){for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return Wn}});class r{constructor(e,t){this.name=void 0,this.position=void 0,this.length=void 0,this.rawMessage=void 0;let n,o,s="KaTeX parse error: "+e;const i=t&&t.loc;if(i&&i.start<=i.end){const e=i.lexer.input;n=i.start,o=i.end,n===e.length?s+=" at end of input: ":s+=" at position "+(n+1)+": ";const t=e.slice(n,o).replace(/[^]/g,"$&\u0332");let r,a;r=n>15?"\u2026"+e.slice(n-15,n):e.slice(0,n),a=o+15":">","<":"<",'"':""","'":"'"},i=/[&><"']/g;const a=function(e){return"ordgroup"===e.type||"color"===e.type?1===e.body.length?a(e.body[0]):e:"font"===e.type?a(e.body):e};var l={deflt:function(e,t){return void 0===e?t:e},escape:function(e){return String(e).replace(i,(e=>s[e]))},hyphenate:function(e){return e.replace(o,"-$1").toLowerCase()},getBaseElem:a,isCharacterBox:function(e){const t=a(e);return"mathord"===t.type||"textord"===t.type||"atom"===t.type},protocolFromUrl:function(e){const t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?":"!==t[2]?null:/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?t[1].toLowerCase():null:"_relative"}};const h={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:e=>"#"+e},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(e,t)=>(t.push(e),t)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:e=>Math.max(0,e),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:e=>Math.max(0,e),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:e=>Math.max(0,e),cli:"-e, --max-expand ",cliProcessor:e=>"Infinity"===e?1/0:parseInt(e)},globalGroup:{type:"boolean",cli:!1}};function c(e){if(e.default)return e.default;const t=e.type,r=Array.isArray(t)?t[0]:t;if("string"!=typeof r)return r.enum[0];switch(r){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}class m{constructor(e){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(const t in h)if(h.hasOwnProperty(t)){const r=h[t];this[t]=void 0!==e[t]?r.processor?r.processor(e[t]):e[t]:c(r)}}reportNonstrict(e,t,r){let o=this.strict;if("function"==typeof o&&(o=o(e,t,r)),o&&"ignore"!==o){if(!0===o||"error"===o)throw new n("LaTeX-incompatible input and strict mode is set to 'error': "+t+" ["+e+"]",r);"warn"===o?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+o+"': "+t+" ["+e+"]")}}useStrictBehavior(e,t,r){let n=this.strict;if("function"==typeof n)try{n=n(e,t,r)}catch(e){n="error"}return!(!n||"ignore"===n)&&(!0===n||"error"===n||("warn"===n?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+t+" ["+e+"]"),!1)))}isTrusted(e){if(e.url&&!e.protocol){const t=l.protocolFromUrl(e.url);if(null==t)return!1;e.protocol=t}const t="function"==typeof this.trust?this.trust(e):this.trust;return Boolean(t)}}class p{constructor(e,t,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=t,this.cramped=r}sup(){return u[d[this.id]]}sub(){return u[g[this.id]]}fracNum(){return u[f[this.id]]}fracDen(){return u[b[this.id]]}cramp(){return u[y[this.id]]}text(){return u[x[this.id]]}isTight(){return this.size>=2}}const u=[new p(0,0,!1),new p(1,0,!0),new p(2,1,!1),new p(3,1,!0),new p(4,2,!1),new p(5,2,!0),new p(6,3,!1),new p(7,3,!0)],d=[4,5,4,5,6,7,6,7],g=[5,5,5,5,7,7,7,7],f=[2,3,4,5,6,7,6,7],b=[3,3,5,5,7,7,7,7],y=[1,1,3,3,5,5,7,7],x=[0,1,2,3,2,3,2,3];var w={DISPLAY:u[0],TEXT:u[2],SCRIPT:u[4],SCRIPTSCRIPT:u[6]};const v=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];const k=[];function S(e){for(let t=0;t=k[t]&&e<=k[t+1])return!0;return!1}v.forEach((e=>e.blocks.forEach((e=>k.push(...e)))));const M=80,z={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:"M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z",leftmapsto:"M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z",leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:"M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z",midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:"M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z",rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"};var A={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}};const T={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},B={"\xc5":"A","\xd0":"D","\xde":"o","\xe5":"a","\xf0":"d","\xfe":"o","\u0410":"A","\u0411":"B","\u0412":"B","\u0413":"F","\u0414":"A","\u0415":"E","\u0416":"K","\u0417":"3","\u0418":"N","\u0419":"N","\u041a":"K","\u041b":"N","\u041c":"M","\u041d":"H","\u041e":"O","\u041f":"N","\u0420":"P","\u0421":"C","\u0422":"T","\u0423":"y","\u0424":"O","\u0425":"X","\u0426":"U","\u0427":"h","\u0428":"W","\u0429":"W","\u042a":"B","\u042b":"X","\u042c":"B","\u042d":"3","\u042e":"X","\u042f":"R","\u0430":"a","\u0431":"b","\u0432":"a","\u0433":"r","\u0434":"y","\u0435":"e","\u0436":"m","\u0437":"e","\u0438":"n","\u0439":"n","\u043a":"n","\u043b":"n","\u043c":"m","\u043d":"n","\u043e":"o","\u043f":"n","\u0440":"p","\u0441":"c","\u0442":"o","\u0443":"y","\u0444":"b","\u0445":"x","\u0446":"n","\u0447":"n","\u0448":"w","\u0449":"w","\u044a":"a","\u044b":"m","\u044c":"a","\u044d":"e","\u044e":"m","\u044f":"r"};function C(e,t,r){if(!A[t])throw new Error("Font metrics not found for font: "+t+".");let n=e.charCodeAt(0),o=A[t][n];if(!o&&e[0]in B&&(n=B[e[0]].charCodeAt(0),o=A[t][n]),o||"text"!==r||S(n)&&(o=A[t][77]),o)return{depth:o[0],height:o[1],italic:o[2],skew:o[3],width:o[4]}}const N={};const q=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],I=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],R=function(e,t){return t.size<2?e:q[e-1][t.size-1]};class H{constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||H.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=I[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){const t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(const r in e)e.hasOwnProperty(r)&&(t[r]=e[r]);return new H(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:R(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:I[e-1]})}havingBaseStyle(e){e=e||this.style.text();const t=R(H.BASESIZE,e);return this.size===t&&this.textSize===H.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){let e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==H.BASESIZE?["sizing","reset-size"+this.size,"size"+H.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=function(e){let t;if(t=e>=5?0:e>=3?1:2,!N[t]){const e=N[t]={cssEmPerMu:T.quad[t]/18};for(const r in T)T.hasOwnProperty(r)&&(e[r]=T[r][t])}return N[t]}(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}}H.BASESIZE=6;var O=H;const E={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},L={ex:!0,em:!0,mu:!0},D=function(e){return"string"!=typeof e&&(e=e.unit),e in E||e in L||"ex"===e},V=function(e,t){let r;if(e.unit in E)r=E[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if("mu"===e.unit)r=t.fontMetrics().cssEmPerMu;else{let o;if(o=t.style.isTight()?t.havingStyle(t.style.text()):t,"ex"===e.unit)r=o.fontMetrics().xHeight;else{if("em"!==e.unit)throw new n("Invalid unit: '"+e.unit+"'");r=o.fontMetrics().quad}o!==t&&(r*=o.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*r,t.maxSize)},P=function(e){return+e.toFixed(4)+"em"},F=function(e){return e.filter((e=>e)).join(" ")},G=function(e,t,r){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},t){t.style.isTight()&&this.classes.push("mtight");const e=t.getColor();e&&(this.style.color=e)}},U=function(e){const t=document.createElement(e);t.className=F(this.classes);for(const e in this.style)this.style.hasOwnProperty(e)&&(t.style[e]=this.style[e]);for(const e in this.attributes)this.attributes.hasOwnProperty(e)&&t.setAttribute(e,this.attributes[e]);for(let e=0;e/=\x00-\x1f]/,X=function(e){let t="<"+e;this.classes.length&&(t+=' class="'+l.escape(F(this.classes))+'"');let r="";for(const e in this.style)this.style.hasOwnProperty(e)&&(r+=l.hyphenate(e)+":"+this.style[e]+";");r&&(t+=' style="'+l.escape(r)+'"');for(const e in this.attributes)if(this.attributes.hasOwnProperty(e)){if(Y.test(e))throw new n("Invalid attribute name '"+e+"'");t+=" "+e+'="'+l.escape(this.attributes[e])+'"'}t+=">";for(let e=0;e",t};class W{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,G.call(this,e,r,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return this.classes.includes(e)}toNode(){return U.call(this,"span")}toMarkup(){return X.call(this,"span")}}class _{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,G.call(this,t,n),this.children=r||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return this.classes.includes(e)}toNode(){return U.call(this,"a")}toMarkup(){return X.call(this,"a")}}class j{constructor(e,t,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=t,this.src=e,this.classes=["mord"],this.style=r}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(const t in this.style)this.style.hasOwnProperty(t)&&(e.style[t]=this.style[t]);return e}toMarkup(){let e=''+l.escape(this.alt)+'=n[0]&&e<=n[1])return r.name}}return null}(this.text.charCodeAt(0));l&&this.classes.push(l+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=$[this.text])}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createTextNode(this.text);let t=null;this.italic>0&&(t=document.createElement("span"),t.style.marginRight=P(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=F(this.classes));for(const e in this.style)this.style.hasOwnProperty(e)&&(t=t||document.createElement("span"),t.style[e]=this.style[e]);return t?(t.appendChild(e),t):e}toMarkup(){let e=!1,t="0&&(r+="margin-right:"+this.italic+"em;");for(const e in this.style)this.style.hasOwnProperty(e)&&(r+=l.hyphenate(e)+":"+this.style[e]+";");r&&(e=!0,t+=' style="'+l.escape(r)+'"');const n=l.escape(this.text);return e?(t+=">",t+=n,t+="",t):n}}class K{constructor(e,t){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=t||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","svg");for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);for(let t=0;t':''}}class Q{constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","line");for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);return e}toMarkup(){let e="","\\gt",!0),se(ie,le,ye,"\u2208","\\in",!0),se(ie,le,ye,"\ue020","\\@not"),se(ie,le,ye,"\u2282","\\subset",!0),se(ie,le,ye,"\u2283","\\supset",!0),se(ie,le,ye,"\u2286","\\subseteq",!0),se(ie,le,ye,"\u2287","\\supseteq",!0),se(ie,he,ye,"\u2288","\\nsubseteq",!0),se(ie,he,ye,"\u2289","\\nsupseteq",!0),se(ie,le,ye,"\u22a8","\\models"),se(ie,le,ye,"\u2190","\\leftarrow",!0),se(ie,le,ye,"\u2264","\\le"),se(ie,le,ye,"\u2264","\\leq",!0),se(ie,le,ye,"<","\\lt",!0),se(ie,le,ye,"\u2192","\\rightarrow",!0),se(ie,le,ye,"\u2192","\\to"),se(ie,he,ye,"\u2271","\\ngeq",!0),se(ie,he,ye,"\u2270","\\nleq",!0),se(ie,le,xe,"\xa0","\\ "),se(ie,le,xe,"\xa0","\\space"),se(ie,le,xe,"\xa0","\\nobreakspace"),se(ae,le,xe,"\xa0","\\ "),se(ae,le,xe,"\xa0"," "),se(ae,le,xe,"\xa0","\\space"),se(ae,le,xe,"\xa0","\\nobreakspace"),se(ie,le,xe,null,"\\nobreak"),se(ie,le,xe,null,"\\allowbreak"),se(ie,le,be,",",","),se(ie,le,be,";",";"),se(ie,he,me,"\u22bc","\\barwedge",!0),se(ie,he,me,"\u22bb","\\veebar",!0),se(ie,le,me,"\u2299","\\odot",!0),se(ie,le,me,"\u2295","\\oplus",!0),se(ie,le,me,"\u2297","\\otimes",!0),se(ie,le,we,"\u2202","\\partial",!0),se(ie,le,me,"\u2298","\\oslash",!0),se(ie,he,me,"\u229a","\\circledcirc",!0),se(ie,he,me,"\u22a1","\\boxdot",!0),se(ie,le,me,"\u25b3","\\bigtriangleup"),se(ie,le,me,"\u25bd","\\bigtriangledown"),se(ie,le,me,"\u2020","\\dagger"),se(ie,le,me,"\u22c4","\\diamond"),se(ie,le,me,"\u22c6","\\star"),se(ie,le,me,"\u25c3","\\triangleleft"),se(ie,le,me,"\u25b9","\\triangleright"),se(ie,le,fe,"{","\\{"),se(ae,le,we,"{","\\{"),se(ae,le,we,"{","\\textbraceleft"),se(ie,le,pe,"}","\\}"),se(ae,le,we,"}","\\}"),se(ae,le,we,"}","\\textbraceright"),se(ie,le,fe,"{","\\lbrace"),se(ie,le,pe,"}","\\rbrace"),se(ie,le,fe,"[","\\lbrack",!0),se(ae,le,we,"[","\\lbrack",!0),se(ie,le,pe,"]","\\rbrack",!0),se(ae,le,we,"]","\\rbrack",!0),se(ie,le,fe,"(","\\lparen",!0),se(ie,le,pe,")","\\rparen",!0),se(ae,le,we,"<","\\textless",!0),se(ae,le,we,">","\\textgreater",!0),se(ie,le,fe,"\u230a","\\lfloor",!0),se(ie,le,pe,"\u230b","\\rfloor",!0),se(ie,le,fe,"\u2308","\\lceil",!0),se(ie,le,pe,"\u2309","\\rceil",!0),se(ie,le,we,"\\","\\backslash"),se(ie,le,we,"\u2223","|"),se(ie,le,we,"\u2223","\\vert"),se(ae,le,we,"|","\\textbar",!0),se(ie,le,we,"\u2225","\\|"),se(ie,le,we,"\u2225","\\Vert"),se(ae,le,we,"\u2225","\\textbardbl"),se(ae,le,we,"~","\\textasciitilde"),se(ae,le,we,"\\","\\textbackslash"),se(ae,le,we,"^","\\textasciicircum"),se(ie,le,ye,"\u2191","\\uparrow",!0),se(ie,le,ye,"\u21d1","\\Uparrow",!0),se(ie,le,ye,"\u2193","\\downarrow",!0),se(ie,le,ye,"\u21d3","\\Downarrow",!0),se(ie,le,ye,"\u2195","\\updownarrow",!0),se(ie,le,ye,"\u21d5","\\Updownarrow",!0),se(ie,le,ge,"\u2210","\\coprod"),se(ie,le,ge,"\u22c1","\\bigvee"),se(ie,le,ge,"\u22c0","\\bigwedge"),se(ie,le,ge,"\u2a04","\\biguplus"),se(ie,le,ge,"\u22c2","\\bigcap"),se(ie,le,ge,"\u22c3","\\bigcup"),se(ie,le,ge,"\u222b","\\int"),se(ie,le,ge,"\u222b","\\intop"),se(ie,le,ge,"\u222c","\\iint"),se(ie,le,ge,"\u222d","\\iiint"),se(ie,le,ge,"\u220f","\\prod"),se(ie,le,ge,"\u2211","\\sum"),se(ie,le,ge,"\u2a02","\\bigotimes"),se(ie,le,ge,"\u2a01","\\bigoplus"),se(ie,le,ge,"\u2a00","\\bigodot"),se(ie,le,ge,"\u222e","\\oint"),se(ie,le,ge,"\u222f","\\oiint"),se(ie,le,ge,"\u2230","\\oiiint"),se(ie,le,ge,"\u2a06","\\bigsqcup"),se(ie,le,ge,"\u222b","\\smallint"),se(ae,le,ue,"\u2026","\\textellipsis"),se(ie,le,ue,"\u2026","\\mathellipsis"),se(ae,le,ue,"\u2026","\\ldots",!0),se(ie,le,ue,"\u2026","\\ldots",!0),se(ie,le,ue,"\u22ef","\\@cdots",!0),se(ie,le,ue,"\u22f1","\\ddots",!0),se(ie,le,we,"\u22ee","\\varvdots"),se(ae,le,we,"\u22ee","\\varvdots"),se(ie,le,ce,"\u02ca","\\acute"),se(ie,le,ce,"\u02cb","\\grave"),se(ie,le,ce,"\xa8","\\ddot"),se(ie,le,ce,"~","\\tilde"),se(ie,le,ce,"\u02c9","\\bar"),se(ie,le,ce,"\u02d8","\\breve"),se(ie,le,ce,"\u02c7","\\check"),se(ie,le,ce,"^","\\hat"),se(ie,le,ce,"\u20d7","\\vec"),se(ie,le,ce,"\u02d9","\\dot"),se(ie,le,ce,"\u02da","\\mathring"),se(ie,le,de,"\ue131","\\@imath"),se(ie,le,de,"\ue237","\\@jmath"),se(ie,le,we,"\u0131","\u0131"),se(ie,le,we,"\u0237","\u0237"),se(ae,le,we,"\u0131","\\i",!0),se(ae,le,we,"\u0237","\\j",!0),se(ae,le,we,"\xdf","\\ss",!0),se(ae,le,we,"\xe6","\\ae",!0),se(ae,le,we,"\u0153","\\oe",!0),se(ae,le,we,"\xf8","\\o",!0),se(ae,le,we,"\xc6","\\AE",!0),se(ae,le,we,"\u0152","\\OE",!0),se(ae,le,we,"\xd8","\\O",!0),se(ae,le,ce,"\u02ca","\\'"),se(ae,le,ce,"\u02cb","\\`"),se(ae,le,ce,"\u02c6","\\^"),se(ae,le,ce,"\u02dc","\\~"),se(ae,le,ce,"\u02c9","\\="),se(ae,le,ce,"\u02d8","\\u"),se(ae,le,ce,"\u02d9","\\."),se(ae,le,ce,"\xb8","\\c"),se(ae,le,ce,"\u02da","\\r"),se(ae,le,ce,"\u02c7","\\v"),se(ae,le,ce,"\xa8",'\\"'),se(ae,le,ce,"\u02dd","\\H"),se(ae,le,ce,"\u25ef","\\textcircled");const ve={"--":!0,"---":!0,"``":!0,"''":!0};se(ae,le,we,"\u2013","--",!0),se(ae,le,we,"\u2013","\\textendash"),se(ae,le,we,"\u2014","---",!0),se(ae,le,we,"\u2014","\\textemdash"),se(ae,le,we,"\u2018","`",!0),se(ae,le,we,"\u2018","\\textquoteleft"),se(ae,le,we,"\u2019","'",!0),se(ae,le,we,"\u2019","\\textquoteright"),se(ae,le,we,"\u201c","``",!0),se(ae,le,we,"\u201c","\\textquotedblleft"),se(ae,le,we,"\u201d","''",!0),se(ae,le,we,"\u201d","\\textquotedblright"),se(ie,le,we,"\xb0","\\degree",!0),se(ae,le,we,"\xb0","\\degree"),se(ae,le,we,"\xb0","\\textdegree",!0),se(ie,le,we,"\xa3","\\pounds"),se(ie,le,we,"\xa3","\\mathsterling",!0),se(ae,le,we,"\xa3","\\pounds"),se(ae,le,we,"\xa3","\\textsterling",!0),se(ie,he,we,"\u2720","\\maltese"),se(ae,he,we,"\u2720","\\maltese");const ke='0123456789/@."';for(let e=0;e<14;e++){const t=ke.charAt(e);se(ie,le,we,t,t)}const Se='0123456789!@*()-=+";:?/.,';for(let e=0;e<25;e++){const t=Se.charAt(e);se(ae,le,we,t,t)}const Me="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";for(let e=0;e<52;e++){const t=Me.charAt(e);se(ie,le,de,t,t),se(ae,le,we,t,t)}se(ie,he,we,"C","\u2102"),se(ae,he,we,"C","\u2102"),se(ie,he,we,"H","\u210d"),se(ae,he,we,"H","\u210d"),se(ie,he,we,"N","\u2115"),se(ae,he,we,"N","\u2115"),se(ie,he,we,"P","\u2119"),se(ae,he,we,"P","\u2119"),se(ie,he,we,"Q","\u211a"),se(ae,he,we,"Q","\u211a"),se(ie,he,we,"R","\u211d"),se(ae,he,we,"R","\u211d"),se(ie,he,we,"Z","\u2124"),se(ae,he,we,"Z","\u2124"),se(ie,le,de,"h","\u210e"),se(ae,le,de,"h","\u210e");let ze="";for(let e=0;e<52;e++){const t=Me.charAt(e);ze=String.fromCharCode(55349,56320+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56372+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56424+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56580+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56684+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56736+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56788+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56840+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56944+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),e<26&&(ze=String.fromCharCode(55349,56632+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,56476+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze))}ze=String.fromCharCode(55349,56668),se(ie,le,de,"k",ze),se(ae,le,we,"k",ze);for(let e=0;e<10;e++){const t=e.toString();ze=String.fromCharCode(55349,57294+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,57314+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,57324+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze),ze=String.fromCharCode(55349,57334+e),se(ie,le,de,t,ze),se(ae,le,we,t,ze)}const Ae="\xd0\xde\xfe";for(let e=0;e<3;e++){const t=Ae.charAt(e);se(ie,le,de,t,t),se(ae,le,we,t,t)}const Te=[["mathbf","textbf","Main-Bold"],["mathbf","textbf","Main-Bold"],["mathnormal","textit","Math-Italic"],["mathnormal","textit","Math-Italic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["mathscr","textscr","Script-Regular"],["","",""],["","",""],["","",""],["mathfrak","textfrak","Fraktur-Regular"],["mathfrak","textfrak","Fraktur-Regular"],["mathbb","textbb","AMS-Regular"],["mathbb","textbb","AMS-Regular"],["mathboldfrak","textboldfrak","Fraktur-Regular"],["mathboldfrak","textboldfrak","Fraktur-Regular"],["mathsf","textsf","SansSerif-Regular"],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathitsf","textitsf","SansSerif-Italic"],["mathitsf","textitsf","SansSerif-Italic"],["","",""],["","",""],["mathtt","texttt","Typewriter-Regular"],["mathtt","texttt","Typewriter-Regular"]],Be=[["mathbf","textbf","Main-Bold"],["","",""],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathtt","texttt","Typewriter-Regular"]];class Ce{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createDocumentFragment();for(let t=0;te.toText())).join("")}}const Ne=function(e,t,r){return oe[r][e]&&oe[r][e].replace&&(e=oe[r][e].replace),{value:e,metrics:C(e,t,r)}},qe=function(e,t,r,n,o){const s=Ne(e,t,r),i=s.metrics;let a;if(e=s.value,i){let t=i.italic;("text"===r||n&&"mathit"===n.font)&&(t=0),a=new Z(e,i.height,i.depth,t,i.skew,i.width,o)}else"undefined"!=typeof console&&console.warn("No character metrics for '"+e+"' in style '"+t+"' and mode '"+r+"'"),a=new Z(e,0,0,0,0,0,o);if(n){a.maxFontSize=n.sizeMultiplier,n.style.isTight()&&a.classes.push("mtight");const e=n.getColor();e&&(a.style.color=e)}return a},Ie=(e,t)=>{if(F(e.classes)!==F(t.classes)||e.skew!==t.skew||e.maxFontSize!==t.maxFontSize)return!1;if(1===e.classes.length){const t=e.classes[0];if("mbin"===t||"mord"===t)return!1}for(const r in e.style)if(e.style.hasOwnProperty(r)&&e.style[r]!==t.style[r])return!1;for(const r in t.style)if(t.style.hasOwnProperty(r)&&e.style[r]!==t.style[r])return!1;return!0},Re=function(e){let t=0,r=0,n=0;for(let o=0;ot&&(t=s.height),s.depth>r&&(r=s.depth),s.maxFontSize>n&&(n=s.maxFontSize)}e.height=t,e.depth=r,e.maxFontSize=n},He=function(e,t,r,n){const o=new W(e,t,r,n);return Re(o),o},Oe=(e,t,r,n)=>new W(e,t,r,n),Ee=function(e){const t=new Ce(e);return Re(t),t},Le=function(e,t,r){let n,o="";switch(e){case"amsrm":o="AMS";break;case"textrm":o="Main";break;case"textsf":o="SansSerif";break;case"texttt":o="Typewriter";break;default:o=e}return n="textbf"===t&&"textit"===r?"BoldItalic":"textbf"===t?"Bold":"textit"===t?"Italic":"Regular",o+"-"+n},De={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathsfit:{variant:"sans-serif-italic",fontName:"SansSerif-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},Ve={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]};var Pe={fontMap:De,makeSymbol:qe,mathsym:function(e,t,r,n){return void 0===n&&(n=[]),"boldsymbol"===r.font&&Ne(e,"Main-Bold",t).metrics?qe(e,"Main-Bold",t,r,n.concat(["mathbf"])):"\\"===e||"main"===oe[t][e].font?qe(e,"Main-Regular",t,r,n):qe(e,"AMS-Regular",t,r,n.concat(["amsrm"]))},makeSpan:He,makeSvgSpan:Oe,makeLineSpan:function(e,t,r){const n=He([e],[],t);return n.height=Math.max(r||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=P(n.height),n.maxFontSize=1,n},makeAnchor:function(e,t,r,n){const o=new _(e,t,r,n);return Re(o),o},makeFragment:Ee,wrapFragment:function(e,t){return e instanceof Ce?He([],[e],t):e},makeVList:function(e,t){const{children:r,depth:n}=function(e){if("individualShift"===e.positionType){const t=e.children,r=[t[0]],n=-t[0].shift-t[0].elem.depth;let o=n;for(let e=1;e0)return qe(s,h,o,t,i.concat(c));if(l){let e,n;if("boldsymbol"===l){const t=function(e,t,r,n,o){return"textord"!==o&&Ne(e,"Math-BoldItalic",t).metrics?{fontName:"Math-BoldItalic",fontClass:"boldsymbol"}:{fontName:"Main-Bold",fontClass:"mathbf"}}(s,o,0,0,r);e=t.fontName,n=[t.fontClass]}else a?(e=De[l].fontName,n=[l]):(e=Le(l,t.fontWeight,t.fontShape),n=[l,t.fontWeight,t.fontShape]);if(Ne(s,e,o).metrics)return qe(s,e,o,t,i.concat(n));if(ve.hasOwnProperty(s)&&"Typewriter"===e.slice(0,10)){const r=[];for(let a=0;a{const r=He(["mspace"],[],t),n=V(e,t);return r.style.marginRight=P(n),r},staticSvg:function(e,t){const[r,n,o]=Ve[e],s=new J(r),i=new K([s],{width:P(n),height:P(o),style:"width:"+P(n),viewBox:"0 0 "+1e3*n+" "+1e3*o,preserveAspectRatio:"xMinYMin"}),a=Oe(["overlay"],[i],t);return a.height=o,a.style.height=P(o),a.style.width=P(n),a},svgData:Ve,tryCombineChars:e=>{for(let t=0;t{const r=t.classes[0],n=e.classes[0];"mbin"===r&&tt.includes(n)?t.classes[0]="mord":"mbin"===n&&et.includes(r)&&(e.classes[0]="mord")}),{node:i},a,l),st(o,((e,t)=>{const r=lt(t),n=lt(e),o=r&&n?e.hasClass("mtight")?Xe[r][n]:Ye[r][n]:null;if(o)return Pe.makeGlue(o,s)}),{node:i},a,l),o},st=function(e,t,r,n,o){n&&e.push(n);let s=0;for(;sr=>{e.splice(t+1,0,r),s++})(s)}n&&e.pop()},it=function(e){return e instanceof Ce||e instanceof _||e instanceof W&&e.hasClass("enclosing")?e:null},at=function(e,t){const r=it(e);if(r){const e=r.children;if(e.length){if("right"===t)return at(e[e.length-1],"right");if("left"===t)return at(e[0],"left")}}return e},lt=function(e,t){return e?(t&&(e=at(e,t)),nt[e.classes[0]]||null):null},ht=function(e,t){const r=["nulldelimiter"].concat(e.baseSizingClasses());return Qe(t.concat(r))},ct=function(e,t,r){if(!e)return Qe();if(_e[e.type]){let n=_e[e.type](e,t);if(r&&t.size!==r.size){n=Qe(t.sizingClasses(r),[n],t);const e=t.sizeMultiplier/r.sizeMultiplier;n.height*=e,n.depth*=e}return n}throw new n("Got group of unknown type: '"+e.type+"'")};function mt(e,t){const r=Qe(["base"],e,t),n=Qe(["strut"]);return n.style.height=P(r.height+r.depth),r.depth&&(n.style.verticalAlign=P(-r.depth)),r.children.unshift(n),r}function pt(e,t){let r=null;1===e.length&&"tag"===e[0].type&&(r=e[0].tag,e=e[0].body);const n=ot(e,t,"root");let o;2===n.length&&n[1].hasClass("tag")&&(o=n.pop());const s=[];let i,a=[];for(let e=0;e0&&(s.push(mt(a,t)),a=[]),s.push(n[e]));a.length>0&&s.push(mt(a,t)),r?(i=mt(ot(r,t,!0)),i.classes=["tag"],s.push(i)):o&&s.push(o);const l=Qe(["katex-html"],s);if(l.setAttribute("aria-hidden","true"),i){const e=i.children[0];e.style.height=P(l.height+l.depth),l.depth&&(e.style.verticalAlign=P(-l.depth))}return l}function ut(e){return new Ce(e)}class dt{constructor(e,t,r){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=t||[],this.classes=r||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){const e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);this.classes.length>0&&(e.className=F(this.classes));for(let t=0;t0&&(e+=' class ="'+l.escape(F(this.classes))+'"'),e+=">";for(let t=0;t",e}toText(){return this.children.map((e=>e.toText())).join("")}}class gt{constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return l.escape(this.toText())}toText(){return this.text}}var ft={MathNode:dt,TextNode:gt,SpaceNode:class{constructor(e){this.width=void 0,this.character=void 0,this.width=e,this.character=e>=.05555&&e<=.05556?"\u200a":e>=.1666&&e<=.1667?"\u2009":e>=.2222&&e<=.2223?"\u2005":e>=.2777&&e<=.2778?"\u2005\u200a":e>=-.05556&&e<=-.05555?"\u200a\u2063":e>=-.1667&&e<=-.1666?"\u2009\u2063":e>=-.2223&&e<=-.2222?"\u205f\u2063":e>=-.2778&&e<=-.2777?"\u2005\u2063":null}toNode(){if(this.character)return document.createTextNode(this.character);{const e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",P(this.width)),e}}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},newDocumentFragment:ut};const bt=function(e,t,r){return!oe[t][e]||!oe[t][e].replace||55349===e.charCodeAt(0)||ve.hasOwnProperty(e)&&r&&(r.fontFamily&&"tt"===r.fontFamily.slice(4,6)||r.font&&"tt"===r.font.slice(4,6))||(e=oe[t][e].replace),new ft.TextNode(e)},yt=function(e){return 1===e.length?e[0]:new ft.MathNode("mrow",e)},xt=function(e,t){if("texttt"===t.fontFamily)return"monospace";if("textsf"===t.fontFamily)return"textit"===t.fontShape&&"textbf"===t.fontWeight?"sans-serif-bold-italic":"textit"===t.fontShape?"sans-serif-italic":"textbf"===t.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===t.fontShape&&"textbf"===t.fontWeight)return"bold-italic";if("textit"===t.fontShape)return"italic";if("textbf"===t.fontWeight)return"bold";const r=t.font;if(!r||"mathnormal"===r)return null;const n=e.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"textord"===e.type?"bold":"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathsfit"===r)return"sans-serif-italic";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";let o=e.text;if(["\\imath","\\jmath"].includes(o))return null;oe[n][o]&&oe[n][o].replace&&(o=oe[n][o].replace);return C(o,Pe.fontMap[r].fontName,n)?Pe.fontMap[r].variant:null};function wt(e){if(!e)return!1;if("mi"===e.type&&1===e.children.length){const t=e.children[0];return t instanceof gt&&"."===t.text}if("mo"===e.type&&1===e.children.length&&"true"===e.getAttribute("separator")&&"0em"===e.getAttribute("lspace")&&"0em"===e.getAttribute("rspace")){const t=e.children[0];return t instanceof gt&&","===t.text}return!1}const vt=function(e,t,r){if(1===e.length){const n=St(e[0],t);return r&&n instanceof dt&&"mo"===n.type&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}const n=[];let o;for(let r=0;r=1&&("mn"===o.type||wt(o))){const e=s.children[0];e instanceof dt&&"mn"===e.type&&(e.children=[...o.children,...e.children],n.pop())}else if("mi"===o.type&&1===o.children.length){const e=o.children[0];if(e instanceof gt&&"\u0338"===e.text&&("mo"===s.type||"mi"===s.type||"mn"===s.type)){const e=s.children[0];e instanceof gt&&e.text.length>0&&(e.text=e.text.slice(0,1)+"\u0338"+e.text.slice(1),n.pop())}}}n.push(s),o=s}return n},kt=function(e,t,r){return yt(vt(e,t,r))},St=function(e,t){if(!e)return new ft.MathNode("mrow");if(je[e.type]){return je[e.type](e,t)}throw new n("Got group of unknown type: '"+e.type+"'")};function Mt(e,t,r,n,o){const s=vt(e,r);let i;i=1===s.length&&s[0]instanceof dt&&["mrow","mtable"].includes(s[0].type)?s[0]:new ft.MathNode("mrow",s);const a=new ft.MathNode("annotation",[new ft.TextNode(t)]);a.setAttribute("encoding","application/x-tex");const l=new ft.MathNode("semantics",[i,a]),h=new ft.MathNode("math",[l]);h.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&h.setAttribute("display","block");const c=o?"katex":"katex-mathml";return Pe.makeSpan([c],[h])}const zt=function(e){return new O({style:e.displayMode?w.DISPLAY:w.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},At=function(e,t){if(t.displayMode){const r=["katex-display"];t.leqno&&r.push("leqno"),t.fleqn&&r.push("fleqn"),e=Pe.makeSpan(r,[e])}return e},Tt=function(e,t,r){const n=zt(r);let o;if("mathml"===r.output)return Mt(e,t,n,r.displayMode,!0);if("html"===r.output){const t=pt(e,n);o=Pe.makeSpan(["katex"],[t])}else{const s=Mt(e,t,n,r.displayMode,!1),i=pt(e,n);o=Pe.makeSpan(["katex"],[s,i])}return At(o,r)};const Bt={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},Ct={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]};var Nt=function(e,t,r,n,o){let s;const i=e.height+e.depth+r+n;if(/fbox|color|angl/.test(t)){if(s=Pe.makeSpan(["stretchy",t],[],o),"fbox"===t){const e=o.color&&o.getColor();e&&(s.style.borderColor=e)}}else{const e=[];/^[bx]cancel$/.test(t)&&e.push(new Q({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&e.push(new Q({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));const r=new K(e,{width:"100%",height:P(i)});s=Pe.makeSvgSpan([],[r],o)}return s.height=i,s.style.height=P(i),s},qt=function(e){const t=new ft.MathNode("mo",[new ft.TextNode(Bt[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},It=function(e,t){const{span:r,minWidth:n,height:o}=function(){let r=4e5;const n=e.label.slice(1);if(["widehat","widecheck","widetilde","utilde"].includes(n)){const s="ordgroup"===(o=e.base).type?o.body.length:1;let i,a,l;if(s>5)"widehat"===n||"widecheck"===n?(i=420,r=2364,l=.42,a=n+"4"):(i=312,r=2340,l=.34,a="tilde4");else{const e=[1,1,2,2,3,3][s];"widehat"===n||"widecheck"===n?(r=[0,1062,2364,2364,2364][e],i=[0,239,300,360,420][e],l=[0,.24,.3,.3,.36,.42][e],a=n+e):(r=[0,600,1033,2339,2340][e],i=[0,260,286,306,312][e],l=[0,.26,.286,.3,.306,.34][e],a="tilde"+e)}const h=new J(a),c=new K([h],{width:"100%",height:P(l),viewBox:"0 0 "+r+" "+i,preserveAspectRatio:"none"});return{span:Pe.makeSvgSpan([],[c],t),minWidth:0,height:l}}{const e=[],o=Ct[n],[s,i,a]=o,l=a/1e3,h=s.length;let c,m;if(1===h){c=["hide-tail"],m=[o[3]]}else if(2===h)c=["halfarrow-left","halfarrow-right"],m=["xMinYMin","xMaxYMin"];else{if(3!==h)throw new Error("Correct katexImagesData or update code here to support\n "+h+" children.");c=["brace-left","brace-center","brace-right"],m=["xMinYMin","xMidYMin","xMaxYMin"]}for(let n=0;n0&&(r.style.minWidth=P(n)),r};function Rt(e,t){if(!e||e.type!==t)throw new Error("Expected node of type "+t+", but got "+(e?"node of type "+e.type:String(e)));return e}function Ht(e){const t=Ot(e);if(!t)throw new Error("Expected node of symbol group type, but got "+(e?"node of type "+e.type:String(e)));return t}function Ot(e){return e&&("atom"===e.type||re.hasOwnProperty(e.type))?e:null}const Et=(e,t)=>{let r,n,o;e&&"supsub"===e.type?(n=Rt(e.base,"accent"),r=n.base,e.base=r,o=function(e){if(e instanceof W)return e;throw new Error("Expected span but got "+String(e)+".")}(ct(e,t)),e.base=n):(n=Rt(e,"accent"),r=n.base);const s=ct(r,t.havingCrampedStyle());let i=0;if(n.isShifty&&l.isCharacterBox(r)){const e=l.getBaseElem(r);i=ee(ct(e,t.havingCrampedStyle())).skew}const a="\\c"===n.label;let h,c=a?s.height+s.depth:Math.min(s.height,t.fontMetrics().xHeight);if(n.isStretchy)h=It(n,t),h=Pe.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:h,wrapperClasses:["svg-align"],wrapperStyle:i>0?{width:"calc(100% - "+P(2*i)+")",marginLeft:P(2*i)}:void 0}]},t);else{let e,r;"\\vec"===n.label?(e=Pe.staticSvg("vec",t),r=Pe.svgData.vec[1]):(e=Pe.makeOrd({mode:n.mode,text:n.label},t,"textord"),e=ee(e),e.italic=0,r=e.width,a&&(c+=e.depth)),h=Pe.makeSpan(["accent-body"],[e]);const o="\\textcircled"===n.label;o&&(h.classes.push("accent-full"),c=s.height);let l=i;o||(l-=r/2),h.style.left=P(l),"\\textcircled"===n.label&&(h.style.top=".2em"),h=Pe.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-c},{type:"elem",elem:h}]},t)}const m=Pe.makeSpan(["mord","accent"],[h],t);return o?(o.children[0]=m,o.height=Math.max(m.height,o.height),o.classes[0]="mord",o):m},Lt=(e,t)=>{const r=e.isStretchy?qt(e.label):new ft.MathNode("mo",[bt(e.label,e.mode)]),n=new ft.MathNode("mover",[St(e.base,t),r]);return n.setAttribute("accent","true"),n},Dt=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map((e=>"\\"+e)).join("|"));$e({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(e,t)=>{const r=Ke(t[0]),n=!Dt.test(e.funcName),o=!n||"\\widehat"===e.funcName||"\\widetilde"===e.funcName||"\\widecheck"===e.funcName;return{type:"accent",mode:e.parser.mode,label:e.funcName,isStretchy:n,isShifty:o,base:r}},htmlBuilder:Et,mathmlBuilder:Lt}),$e({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(e,t)=>{const r=t[0];let n=e.parser.mode;return"math"===n&&(e.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+e.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:e.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Et,mathmlBuilder:Lt}),$e({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"accentUnder",mode:r.mode,label:n,base:o}},htmlBuilder:(e,t)=>{const r=ct(e.base,t),n=It(e,t),o="\\utilde"===e.label?.12:0,s=Pe.makeVList({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:o},{type:"elem",elem:r}]},t);return Pe.makeSpan(["mord","accentunder"],[s],t)},mathmlBuilder:(e,t)=>{const r=qt(e.label),n=new ft.MathNode("munder",[St(e.base,t),r]);return n.setAttribute("accentunder","true"),n}});const Vt=e=>{const t=new ft.MathNode("mpadded",e?[e]:[]);return t.setAttribute("width","+0.6em"),t.setAttribute("lspace","0.3em"),t};$e({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n,funcName:o}=e;return{type:"xArrow",mode:n.mode,label:o,body:t[0],below:r[0]}},htmlBuilder(e,t){const r=t.style;let n=t.havingStyle(r.sup());const o=Pe.wrapFragment(ct(e.body,n,t),t),s="\\x"===e.label.slice(0,2)?"x":"cd";let i;o.classes.push(s+"-arrow-pad"),e.below&&(n=t.havingStyle(r.sub()),i=Pe.wrapFragment(ct(e.below,n,t),t),i.classes.push(s+"-arrow-pad"));const a=It(e,t),l=-t.fontMetrics().axisHeight+.5*a.height;let h,c=-t.fontMetrics().axisHeight-.5*a.height-.111;if((o.depth>.25||"\\xleftequilibrium"===e.label)&&(c-=o.depth),i){const e=-t.fontMetrics().axisHeight+i.height+.5*a.height+.111;h=Pe.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:c},{type:"elem",elem:a,shift:l},{type:"elem",elem:i,shift:e}]},t)}else h=Pe.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:c},{type:"elem",elem:a,shift:l}]},t);return h.children[0].children[0].children[1].classes.push("svg-align"),Pe.makeSpan(["mrel","x-arrow"],[h],t)},mathmlBuilder(e,t){const r=qt(e.label);let n;if(r.setAttribute("minsize","x"===e.label.charAt(0)?"1.75em":"3.0em"),e.body){const o=Vt(St(e.body,t));if(e.below){const s=Vt(St(e.below,t));n=new ft.MathNode("munderover",[r,s,o])}else n=new ft.MathNode("mover",[r,o])}else if(e.below){const o=Vt(St(e.below,t));n=new ft.MathNode("munder",[r,o])}else n=Vt(),n=new ft.MathNode("mover",[r,n]);return n}});const Pt=Pe.makeSpan;function Ft(e,t){const r=ot(e.body,t,!0);return Pt([e.mclass],r,t)}function Gt(e,t){let r;const n=vt(e.body,t);return"minner"===e.mclass?r=new ft.MathNode("mpadded",n):"mord"===e.mclass?e.isCharacterBox?(r=n[0],r.type="mi"):r=new ft.MathNode("mi",n):(e.isCharacterBox?(r=n[0],r.type="mo"):r=new ft.MathNode("mo",n),"mbin"===e.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"===e.mclass||"mclose"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0em"):"minner"===e.mclass&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em")),r}$e({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"mclass",mode:r.mode,mclass:"m"+n.slice(5),body:Je(o),isCharacterBox:l.isCharacterBox(o)}},htmlBuilder:Ft,mathmlBuilder:Gt});const Ut=e=>{const t="ordgroup"===e.type&&e.body.length?e.body[0]:e;return"atom"!==t.type||"bin"!==t.family&&"rel"!==t.family?"mord":"m"+t.family};$e({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(e,t){let{parser:r}=e;return{type:"mclass",mode:r.mode,mclass:Ut(t[0]),body:Je(t[1]),isCharacterBox:l.isCharacterBox(t[1])}}}),$e({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(e,t){let{parser:r,funcName:n}=e;const o=t[1],s=t[0];let i;i="\\stackrel"!==n?Ut(o):"mrel";const a={type:"op",mode:o.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:Je(o)},h={type:"supsub",mode:s.mode,base:a,sup:"\\underset"===n?null:s,sub:"\\underset"===n?s:null};return{type:"mclass",mode:r.mode,mclass:i,body:[h],isCharacterBox:l.isCharacterBox(h)}},htmlBuilder:Ft,mathmlBuilder:Gt}),$e({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"pmb",mode:r.mode,mclass:Ut(t[0]),body:Je(t[0])}},htmlBuilder(e,t){const r=ot(e.body,t,!0),n=Pe.makeSpan([e.mclass],r,t);return n.style.textShadow="0.02em 0.01em 0.04px",n},mathmlBuilder(e,t){const r=vt(e.body,t),n=new ft.MathNode("mstyle",r);return n.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),n}});const Yt={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},Xt=e=>"textord"===e.type&&"@"===e.text;function Wt(e,t,r){const n=Yt[e];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{const e={type:"atom",text:n,mode:"math",family:"rel"},o={type:"ordgroup",mode:"math",body:[r.callFunction("\\\\cdleft",[t[0]],[]),r.callFunction("\\Big",[e],[]),r.callFunction("\\\\cdright",[t[1]],[])]};return r.callFunction("\\\\cdparent",[o],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{const e={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[e],[])}default:return{type:"textord",text:" ",mode:"math"}}}$e({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"cdlabel",mode:r.mode,side:n.slice(4),label:t[0]}},htmlBuilder(e,t){const r=t.havingStyle(t.style.sup()),n=Pe.wrapFragment(ct(e.label,r,t),t);return n.classes.push("cd-label-"+e.side),n.style.bottom=P(.8-n.depth),n.height=0,n.depth=0,n},mathmlBuilder(e,t){let r=new ft.MathNode("mrow",[St(e.label,t)]);return r=new ft.MathNode("mpadded",[r]),r.setAttribute("width","0"),"left"===e.side&&r.setAttribute("lspace","-1width"),r.setAttribute("voffset","0.7em"),r=new ft.MathNode("mstyle",[r]),r.setAttribute("displaystyle","false"),r.setAttribute("scriptlevel","1"),r}}),$e({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(e,t){let{parser:r}=e;return{type:"cdlabelparent",mode:r.mode,fragment:t[0]}},htmlBuilder(e,t){const r=Pe.wrapFragment(ct(e.fragment,t),t);return r.classes.push("cd-vert-arrow"),r},mathmlBuilder(e,t){return new ft.MathNode("mrow",[St(e.fragment,t)])}}),$e({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;const o=Rt(t[0],"ordgroup").body;let s="";for(let e=0;e=1114111)throw new n("\\@char with invalid code point "+s);return a<=65535?i=String.fromCharCode(a):(a-=65536,i=String.fromCharCode(55296+(a>>10),56320+(1023&a))),{type:"textord",mode:r.mode,text:i}}});const _t=(e,t)=>{const r=ot(e.body,t.withColor(e.color),!1);return Pe.makeFragment(r)},jt=(e,t)=>{const r=vt(e.body,t.withColor(e.color)),n=new ft.MathNode("mstyle",r);return n.setAttribute("mathcolor",e.color),n};$e({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(e,t){let{parser:r}=e;const n=Rt(t[0],"color-token").color,o=t[1];return{type:"color",mode:r.mode,color:n,body:Je(o)}},htmlBuilder:_t,mathmlBuilder:jt}),$e({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(e,t){let{parser:r,breakOnTokenText:n}=e;const o=Rt(t[0],"color-token").color;r.gullet.macros.set("\\current@color",o);const s=r.parseExpression(!0,n);return{type:"color",mode:r.mode,color:o,body:s}},htmlBuilder:_t,mathmlBuilder:jt}),$e({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(e,t,r){let{parser:n}=e;const o="["===n.gullet.future().text?n.parseSizeGroup(!0):null,s=!n.settings.displayMode||!n.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:n.mode,newLine:s,size:o&&Rt(o,"size").value}},htmlBuilder(e,t){const r=Pe.makeSpan(["mspace"],[],t);return e.newLine&&(r.classes.push("newline"),e.size&&(r.style.marginTop=P(V(e.size,t)))),r},mathmlBuilder(e,t){const r=new ft.MathNode("mspace");return e.newLine&&(r.setAttribute("linebreak","newline"),e.size&&r.setAttribute("height",P(V(e.size,t)))),r}});const $t={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},Zt=e=>{const t=e.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(t))throw new n("Expected a control sequence",e);return t},Kt=(e,t,r,n)=>{let o=e.gullet.macros.get(r.text);null==o&&(r.noexpand=!0,o={tokens:[r],numArgs:0,unexpandable:!e.gullet.isExpandable(r.text)}),e.gullet.macros.set(t,o,n)};$e({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(e){let{parser:t,funcName:r}=e;t.consumeSpaces();const o=t.fetch();if($t[o.text])return"\\global"!==r&&"\\\\globallong"!==r||(o.text=$t[o.text]),Rt(t.parseFunction(),"internal");throw new n("Invalid token after macro prefix",o)}}),$e({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e,o=t.gullet.popToken();const s=o.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(s))throw new n("Expected a control sequence",o);let i,a=0;const l=[[]];for(;"{"!==t.gullet.future().text;)if(o=t.gullet.popToken(),"#"===o.text){if("{"===t.gullet.future().text){i=t.gullet.future(),l[a].push("{");break}if(o=t.gullet.popToken(),!/^[1-9]$/.test(o.text))throw new n('Invalid argument number "'+o.text+'"');if(parseInt(o.text)!==a+1)throw new n('Argument number "'+o.text+'" out of order');a++,l.push([])}else{if("EOF"===o.text)throw new n("Expected a macro definition");l[a].push(o.text)}let{tokens:h}=t.gullet.consumeArg();return i&&h.unshift(i),"\\edef"!==r&&"\\xdef"!==r||(h=t.gullet.expandTokens(h),h.reverse()),t.gullet.macros.set(s,{tokens:h,numArgs:a,delimiters:l},r===$t[r]),{type:"internal",mode:t.mode}}}),$e({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=Zt(t.gullet.popToken());t.gullet.consumeSpaces();const o=(e=>{let t=e.gullet.popToken();return"="===t.text&&(t=e.gullet.popToken()," "===t.text&&(t=e.gullet.popToken())),t})(t);return Kt(t,n,o,"\\\\globallet"===r),{type:"internal",mode:t.mode}}}),$e({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=Zt(t.gullet.popToken()),o=t.gullet.popToken(),s=t.gullet.popToken();return Kt(t,n,s,"\\\\globalfuture"===r),t.gullet.pushToken(s),t.gullet.pushToken(o),{type:"internal",mode:t.mode}}});const Jt=function(e,t,r){const n=C(oe.math[e]&&oe.math[e].replace||e,t,r);if(!n)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return n},Qt=function(e,t,r,n){const o=r.havingBaseStyle(t),s=Pe.makeSpan(n.concat(o.sizingClasses(r)),[e],r),i=o.sizeMultiplier/r.sizeMultiplier;return s.height*=i,s.depth*=i,s.maxFontSize=o.sizeMultiplier,s},er=function(e,t,r){const n=t.havingBaseStyle(r),o=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=P(o),e.height-=o,e.depth+=o},tr=function(e,t,r,n,o,s){const i=function(e,t,r,n){return Pe.makeSymbol(e,"Size"+t+"-Regular",r,n)}(e,t,o,n),a=Qt(Pe.makeSpan(["delimsizing","size"+t],[i],n),w.TEXT,n,s);return r&&er(a,n,w.TEXT),a},rr=function(e,t,r){let n;n="Size1-Regular"===t?"delim-size1":"delim-size4";return{type:"elem",elem:Pe.makeSpan(["delimsizinginner",n],[Pe.makeSpan([],[Pe.makeSymbol(e,t,r)])])}},nr=function(e,t,r){const n=A["Size4-Regular"][e.charCodeAt(0)]?A["Size4-Regular"][e.charCodeAt(0)][4]:A["Size1-Regular"][e.charCodeAt(0)][4],o=new J("inner",function(e,t){switch(e){case"\u239c":return"M291 0 H417 V"+t+" H291z M291 0 H417 V"+t+" H291z";case"\u2223":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z";case"\u2225":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145zM367 0 H410 V"+t+" H367z M367 0 H410 V"+t+" H367z";case"\u239f":return"M457 0 H583 V"+t+" H457z M457 0 H583 V"+t+" H457z";case"\u23a2":return"M319 0 H403 V"+t+" H319z M319 0 H403 V"+t+" H319z";case"\u23a5":return"M263 0 H347 V"+t+" H263z M263 0 H347 V"+t+" H263z";case"\u23aa":return"M384 0 H504 V"+t+" H384z M384 0 H504 V"+t+" H384z";case"\u23d0":return"M312 0 H355 V"+t+" H312z M312 0 H355 V"+t+" H312z";case"\u2016":return"M257 0 H300 V"+t+" H257z M257 0 H300 V"+t+" H257zM478 0 H521 V"+t+" H478z M478 0 H521 V"+t+" H478z";default:return""}}(e,Math.round(1e3*t))),s=new K([o],{width:P(n),height:P(t),style:"width:"+P(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),i=Pe.makeSvgSpan([],[s],r);return i.height=t,i.style.height=P(t),i.style.width=P(n),{type:"elem",elem:i}},or={type:"kern",size:-.008},sr=["|","\\lvert","\\rvert","\\vert"],ir=["\\|","\\lVert","\\rVert","\\Vert"],ar=function(e,t,r,n,o,s){let i,a,l,h,c="",m=0;i=l=h=e,a=null;let p="Size1-Regular";"\\uparrow"===e?l=h="\u23d0":"\\Uparrow"===e?l=h="\u2016":"\\downarrow"===e?i=l="\u23d0":"\\Downarrow"===e?i=l="\u2016":"\\updownarrow"===e?(i="\\uparrow",l="\u23d0",h="\\downarrow"):"\\Updownarrow"===e?(i="\\Uparrow",l="\u2016",h="\\Downarrow"):sr.includes(e)?(l="\u2223",c="vert",m=333):ir.includes(e)?(l="\u2225",c="doublevert",m=556):"["===e||"\\lbrack"===e?(i="\u23a1",l="\u23a2",h="\u23a3",p="Size4-Regular",c="lbrack",m=667):"]"===e||"\\rbrack"===e?(i="\u23a4",l="\u23a5",h="\u23a6",p="Size4-Regular",c="rbrack",m=667):"\\lfloor"===e||"\u230a"===e?(l=i="\u23a2",h="\u23a3",p="Size4-Regular",c="lfloor",m=667):"\\lceil"===e||"\u2308"===e?(i="\u23a1",l=h="\u23a2",p="Size4-Regular",c="lceil",m=667):"\\rfloor"===e||"\u230b"===e?(l=i="\u23a5",h="\u23a6",p="Size4-Regular",c="rfloor",m=667):"\\rceil"===e||"\u2309"===e?(i="\u23a4",l=h="\u23a5",p="Size4-Regular",c="rceil",m=667):"("===e||"\\lparen"===e?(i="\u239b",l="\u239c",h="\u239d",p="Size4-Regular",c="lparen",m=875):")"===e||"\\rparen"===e?(i="\u239e",l="\u239f",h="\u23a0",p="Size4-Regular",c="rparen",m=875):"\\{"===e||"\\lbrace"===e?(i="\u23a7",a="\u23a8",h="\u23a9",l="\u23aa",p="Size4-Regular"):"\\}"===e||"\\rbrace"===e?(i="\u23ab",a="\u23ac",h="\u23ad",l="\u23aa",p="Size4-Regular"):"\\lgroup"===e||"\u27ee"===e?(i="\u23a7",h="\u23a9",l="\u23aa",p="Size4-Regular"):"\\rgroup"===e||"\u27ef"===e?(i="\u23ab",h="\u23ad",l="\u23aa",p="Size4-Regular"):"\\lmoustache"===e||"\u23b0"===e?(i="\u23a7",h="\u23ad",l="\u23aa",p="Size4-Regular"):"\\rmoustache"!==e&&"\u23b1"!==e||(i="\u23ab",h="\u23a9",l="\u23aa",p="Size4-Regular");const u=Jt(i,p,o),d=u.height+u.depth,g=Jt(l,p,o),f=g.height+g.depth,b=Jt(h,p,o),y=b.height+b.depth;let x=0,v=1;if(null!==a){const e=Jt(a,p,o);x=e.height+e.depth,v=2}const k=d+y+x,S=k+Math.max(0,Math.ceil((t-k)/(v*f)))*v*f;let M=n.fontMetrics().axisHeight;r&&(M*=n.sizeMultiplier);const z=S/2-M,A=[];if(c.length>0){const e=S-d-y,t=Math.round(1e3*S),r=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v1759 h347 v-84\nH403z M403 1759 V0 H319 V1759 v"+t+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v1759 H0 v84 H347z\nM347 1759 V0 H263 V1759 v"+t+" v1759 h84z";case"vert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z";case"doublevert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z\nM367 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M410 15 H367 v585 v"+t+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+t+" v1715 h263 v84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+t+" v1799 H0 v-84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v602 h84z\nM403 1759 V0 H319 V1759 v"+t+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v602 h84z\nM347 1759 V0 h-84 V1759 v"+t+" v602 h84z";case"lparen":return"M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1\nc-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349,\n-36,557 l0,"+(t+84)+"c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210,\n949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9\nc0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5,\n-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189\nl0,-"+(t+92)+"c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3,\n-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z";case"rparen":return"M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3,\n63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5\nc11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,"+(t+9)+"\nc-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664\nc-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11\nc0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17\nc242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558\nl0,-"+(t+144)+"c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7,\n-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z";default:throw new Error("Unknown stretchy delimiter.")}}(c,Math.round(1e3*e)),o=new J(c,r),s=(m/1e3).toFixed(3)+"em",i=(t/1e3).toFixed(3)+"em",a=new K([o],{width:s,height:i,viewBox:"0 0 "+m+" "+t}),l=Pe.makeSvgSpan([],[a],n);l.height=t/1e3,l.style.width=s,l.style.height=i,A.push({type:"elem",elem:l})}else{if(A.push(rr(h,p,o)),A.push(or),null===a){const e=S-d-y+.016;A.push(nr(l,e,n))}else{const e=(S-d-y-x)/2+.016;A.push(nr(l,e,n)),A.push(or),A.push(rr(a,p,o)),A.push(or),A.push(nr(l,e,n))}A.push(or),A.push(rr(i,p,o))}const T=n.havingBaseStyle(w.TEXT),B=Pe.makeVList({positionType:"bottom",positionData:z,children:A},T);return Qt(Pe.makeSpan(["delimsizing","mult"],[B],T),w.TEXT,n,s)},lr=.08,hr=function(e,t,r,n,o){const s=function(e,t,r){t*=1e3;let n="";switch(e){case"sqrtMain":n=function(e,t){return"M95,"+(622+e+t)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+e/2.075+" -"+e+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+e)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize1":n=function(e,t){return"M263,"+(601+e+t)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+e/2.084+" -"+e+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+e)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize2":n=function(e,t){return"M983 "+(10+e+t)+"\nl"+e/3.13+" -"+e+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+e)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize3":n=function(e,t){return"M424,"+(2398+e+t)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+e/4.223+" -"+e+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+e)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+e)+" "+t+"\nh400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize4":n=function(e,t){return"M473,"+(2713+e+t)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+e/5.298+" -"+e+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+e)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"}(t,M);break;case"sqrtTall":n=function(e,t,r){return"M702 "+(e+t)+"H400000"+(40+e)+"\nH742v"+(r-54-t-e)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+t+"H400000v"+(40+e)+"H742z"}(t,M,r)}return n}(e,n,r),i=new J(e,s),a=new K([i],{width:"400em",height:P(t),viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return Pe.makeSvgSpan(["hide-tail"],[a],o)},cr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"],mr=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"],pr=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],ur=[0,1.2,1.8,2.4,3],dr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],gr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"stack"}],fr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],br=function(e){if("small"===e.type)return"Main-Regular";if("large"===e.type)return"Size"+e.size+"-Regular";if("stack"===e.type)return"Size4-Regular";throw new Error("Add support for delim type '"+e.type+"' here.")},yr=function(e,t,r,n){for(let o=Math.min(2,3-n.style.size);ot)return r[o]}return r[r.length-1]},xr=function(e,t,r,n,o,s){let i;"<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),i=pr.includes(e)?dr:cr.includes(e)?fr:gr;const a=yr(e,t,i,n);return"small"===a.type?function(e,t,r,n,o,s){const i=Pe.makeSymbol(e,"Main-Regular",o,n),a=Qt(i,t,n,s);return r&&er(a,n,t),a}(e,a.style,r,n,o,s):"large"===a.type?tr(e,a.size,r,n,o,s):ar(e,t,r,n,o,s)};var wr={sqrtImage:function(e,t){const r=t.havingBaseSizing(),n=yr("\\surd",e*r.sizeMultiplier,fr,r);let o=r.sizeMultiplier;const s=Math.max(0,t.minRuleThickness-t.fontMetrics().sqrtRuleThickness);let i,a,l=0,h=0,c=0;return"small"===n.type?(c=1e3+1e3*s+80,e<1?o=1:e<1.4&&(o=.7),l=(1+s+lr)/o,h=(1+s)/o,i=hr("sqrtMain",l,c,s,t),i.style.minWidth="0.853em",a=.833/o):"large"===n.type?(c=1080*ur[n.size],h=(ur[n.size]+s)/o,l=(ur[n.size]+s+lr)/o,i=hr("sqrtSize"+n.size,l,c,s,t),i.style.minWidth="1.02em",a=1/o):(l=e+s+lr,h=e+s,c=Math.floor(1e3*e+s)+80,i=hr("sqrtTall",l,c,s,t),i.style.minWidth="0.742em",a=1.056),i.height=h,i.style.height=P(l),{span:i,advanceWidth:a,ruleWidth:(t.fontMetrics().sqrtRuleThickness+s)*o}},sizedDelim:function(e,t,r,o,s){if("<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),cr.includes(e)||pr.includes(e))return tr(e,t,!1,r,o,s);if(mr.includes(e))return ar(e,ur[t],!1,r,o,s);throw new n("Illegal delimiter: '"+e+"'")},sizeToMaxHeight:ur,customSizedDelim:xr,leftRightDelim:function(e,t,r,n,o,s){const i=n.fontMetrics().axisHeight*n.sizeMultiplier,a=5/n.fontMetrics().ptPerEm,l=Math.max(t-i,r+i),h=Math.max(l/500*901,2*l-a);return xr(e,h,!0,n,o,s)}};const vr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},kr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function Sr(e,t){const r=Ot(e);if(r&&kr.includes(r.text))return r;throw new n(r?"Invalid delimiter '"+r.text+"' after '"+t.funcName+"'":"Invalid delimiter type '"+e.type+"'",e)}function Mr(e){if(!e.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}$e({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(e,t)=>{const r=Sr(t[0],e);return{type:"delimsizing",mode:e.parser.mode,size:vr[e.funcName].size,mclass:vr[e.funcName].mclass,delim:r.text}},htmlBuilder:(e,t)=>"."===e.delim?Pe.makeSpan([e.mclass]):wr.sizedDelim(e.delim,e.size,t,e.mode,[e.mclass]),mathmlBuilder:e=>{const t=[];"."!==e.delim&&t.push(bt(e.delim,e.mode));const r=new ft.MathNode("mo",t);"mopen"===e.mclass||"mclose"===e.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r.setAttribute("stretchy","true");const n=P(wr.sizeToMaxHeight[e.size]);return r.setAttribute("minsize",n),r.setAttribute("maxsize",n),r}}),$e({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=e.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new n("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:e.parser.mode,delim:Sr(t[0],e).text,color:r}}}),$e({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=Sr(t[0],e),n=e.parser;++n.leftrightDepth;const o=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);const s=Rt(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:o,left:r.text,right:s.delim,rightColor:s.color}},htmlBuilder:(e,t)=>{Mr(e);const r=ot(e.body,t,!0,["mopen","mclose"]);let n,o,s=0,i=0,a=!1;for(let e=0;e{Mr(e);const r=vt(e.body,t);if("."!==e.left){const t=new ft.MathNode("mo",[bt(e.left,e.mode)]);t.setAttribute("fence","true"),r.unshift(t)}if("."!==e.right){const t=new ft.MathNode("mo",[bt(e.right,e.mode)]);t.setAttribute("fence","true"),e.rightColor&&t.setAttribute("mathcolor",e.rightColor),r.push(t)}return yt(r)}}),$e({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=Sr(t[0],e);if(!e.parser.leftrightDepth)throw new n("\\middle without preceding \\left",r);return{type:"middle",mode:e.parser.mode,delim:r.text}},htmlBuilder:(e,t)=>{let r;if("."===e.delim)r=ht(t,[]);else{r=wr.sizedDelim(e.delim,1,t,e.mode,[]);const n={delim:e.delim,options:t};r.isMiddle=n}return r},mathmlBuilder:(e,t)=>{const r="\\vert"===e.delim||"|"===e.delim?bt("|","text"):bt(e.delim,e.mode),n=new ft.MathNode("mo",[r]);return n.setAttribute("fence","true"),n.setAttribute("lspace","0.05em"),n.setAttribute("rspace","0.05em"),n}});const zr=(e,t)=>{const r=Pe.wrapFragment(ct(e.body,t),t),n=e.label.slice(1);let o,s=t.sizeMultiplier,i=0;const a=l.isCharacterBox(e.body);if("sout"===n)o=Pe.makeSpan(["stretchy","sout"]),o.height=t.fontMetrics().defaultRuleThickness/s,i=-.5*t.fontMetrics().xHeight;else if("phase"===n){const e=V({number:.6,unit:"pt"},t),n=V({number:.35,unit:"ex"},t);s/=t.havingBaseSizing().sizeMultiplier;const a=r.height+r.depth+e+n;r.style.paddingLeft=P(a/2+e);const l=Math.floor(1e3*a*s),c="M400000 "+(h=l)+" H0 L"+h/2+" 0 l65 45 L145 "+(h-80)+" H400000z",m=new K([new J("phase",c)],{width:"400em",height:P(l/1e3),viewBox:"0 0 400000 "+l,preserveAspectRatio:"xMinYMin slice"});o=Pe.makeSvgSpan(["hide-tail"],[m],t),o.style.height=P(a),i=r.depth+e+n}else{/cancel/.test(n)?a||r.classes.push("cancel-pad"):"angl"===n?r.classes.push("anglpad"):r.classes.push("boxpad");let s=0,l=0,h=0;/box/.test(n)?(h=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness),s=t.fontMetrics().fboxsep+("colorbox"===n?0:h),l=s):"angl"===n?(h=Math.max(t.fontMetrics().defaultRuleThickness,t.minRuleThickness),s=4*h,l=Math.max(0,.25-r.depth)):(s=a?.2:0,l=s),o=Nt(r,n,s,l,t),/fbox|boxed|fcolorbox/.test(n)?(o.style.borderStyle="solid",o.style.borderWidth=P(h)):"angl"===n&&.049!==h&&(o.style.borderTopWidth=P(h),o.style.borderRightWidth=P(h)),i=r.depth+l,e.backgroundColor&&(o.style.backgroundColor=e.backgroundColor,e.borderColor&&(o.style.borderColor=e.borderColor))}var h;let c;if(e.backgroundColor)c=Pe.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:i},{type:"elem",elem:r,shift:0}]},t);else{const e=/cancel|phase/.test(n)?["svg-align"]:[];c=Pe.makeVList({positionType:"individualShift",children:[{type:"elem",elem:r,shift:0},{type:"elem",elem:o,shift:i,wrapperClasses:e}]},t)}return/cancel/.test(n)&&(c.height=r.height,c.depth=r.depth),/cancel/.test(n)&&!a?Pe.makeSpan(["mord","cancel-lap"],[c],t):Pe.makeSpan(["mord"],[c],t)},Ar=(e,t)=>{let r=0;const n=new ft.MathNode(e.label.indexOf("colorbox")>-1?"mpadded":"menclose",[St(e.body,t)]);switch(e.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\phase":n.setAttribute("notation","phasorangle");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\angl":n.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(r=t.fontMetrics().fboxsep*t.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),"\\fcolorbox"===e.label){const r=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness);n.setAttribute("style","border: "+r+"em solid "+String(e.borderColor))}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return e.backgroundColor&&n.setAttribute("mathbackground",e.backgroundColor),n};$e({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=Rt(t[0],"color-token").color,i=t[1];return{type:"enclose",mode:n.mode,label:o,backgroundColor:s,body:i}},htmlBuilder:zr,mathmlBuilder:Ar}),$e({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=Rt(t[0],"color-token").color,i=Rt(t[1],"color-token").color,a=t[2];return{type:"enclose",mode:n.mode,label:o,backgroundColor:i,borderColor:s,body:a}},htmlBuilder:zr,mathmlBuilder:Ar}),$e({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\fbox",body:t[0]}}}),$e({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout","\\phase"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}},htmlBuilder:zr,mathmlBuilder:Ar}),$e({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\angl",body:t[0]}}});const Tr={};function Br(e){let{type:t,names:r,props:n,handler:o,htmlBuilder:s,mathmlBuilder:i}=e;const a={type:t,numArgs:n.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:o};for(let e=0;e{if(!e.parser.settings.displayMode)throw new n("{"+e.envName+"} can be used only in display mode.")};function Or(e){if(-1===e.indexOf("ed"))return-1===e.indexOf("*")}function Er(e,t,r){let{hskipBeforeAndAfter:o,addJot:s,cols:i,arraystretch:a,colSeparationType:l,autoTag:h,singleRow:c,emptySingleRow:m,maxNumCols:p,leqno:u}=t;if(e.gullet.beginGroup(),c||e.gullet.macros.set("\\cr","\\\\\\relax"),!a){const t=e.gullet.expandMacroAsText("\\arraystretch");if(null==t)a=1;else if(a=parseFloat(t),!a||a<0)throw new n("Invalid \\arraystretch: "+t)}e.gullet.beginGroup();let d=[];const g=[d],f=[],b=[],y=null!=h?[]:void 0;function x(){h&&e.gullet.macros.set("\\@eqnsw","1",!0)}function w(){y&&(e.gullet.macros.get("\\df@tag")?(y.push(e.subparse([new Ir("\\df@tag")])),e.gullet.macros.set("\\df@tag",void 0,!0)):y.push(Boolean(h)&&"1"===e.gullet.macros.get("\\@eqnsw")))}for(x(),b.push(Rr(e));;){let t=e.parseExpression(!1,c?"\\end":"\\\\");e.gullet.endGroup(),e.gullet.beginGroup(),t={type:"ordgroup",mode:e.mode,body:t},r&&(t={type:"styling",mode:e.mode,style:r,body:[t]}),d.push(t);const o=e.fetch().text;if("&"===o){if(p&&d.length===p){if(c||l)throw new n("Too many tab characters: &",e.nextToken);e.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}e.consume()}else{if("\\end"===o){w(),1===d.length&&"styling"===t.type&&0===t.body[0].body.length&&(g.length>1||!m)&&g.pop(),b.length0&&(x+=.25),c.push({pos:x,isDashed:e[t]})}for(v(i[0]),r=0;r0&&(p+=y,le)))for(r=0;r=a)continue;(o>0||e.hskipBeforeAndAfter)&&(i=l.deflt(c.pregap,u),0!==i&&(z=Pe.makeSpan(["arraycolsep"],[]),z.style.width=P(i),M.push(z)));let d=[];for(r=0;r0){const e=Pe.makeLineSpan("hline",t,m),r=Pe.makeLineSpan("hdashline",t,m),n=[{type:"elem",elem:h,shift:0}];for(;c.length>0;){const t=c.pop(),o=t.pos-k;t.isDashed?n.push({type:"elem",elem:r,shift:o}):n.push({type:"elem",elem:e,shift:o})}h=Pe.makeVList({positionType:"individualShift",children:n},t)}if(0===T.length)return Pe.makeSpan(["mord"],[h],t);{let e=Pe.makeVList({positionType:"individualShift",children:T},t);return e=Pe.makeSpan(["tag"],[e],t),Pe.makeFragment([h,e])}},Vr={c:"center ",l:"left ",r:"right "},Pr=function(e,t){const r=[],n=new ft.MathNode("mtd",[],["mtr-glue"]),o=new ft.MathNode("mtd",[],["mml-eqn-num"]);for(let s=0;s0){const t=e.cols;let r="",n=!1,o=0,i=t.length;"separator"===t[0].type&&(a+="top ",o=1),"separator"===t[t.length-1].type&&(a+="bottom ",i-=1);for(let e=o;e0?"left ":"",a+=c[c.length-1].length>0?"right ":"";for(let e=1;e-1?"alignat":"align",s="split"===e.envName,i=Er(e.parser,{cols:r,addJot:!0,autoTag:s?void 0:Or(e.envName),emptySingleRow:!0,colSeparationType:o,maxNumCols:s?2:void 0,leqno:e.parser.settings.leqno},"display");let a,l=0;const h={type:"ordgroup",mode:e.mode,body:[]};if(t[0]&&"ordgroup"===t[0].type){let e="";for(let r=0;r0&&c&&(n=1),r[e]={type:"align",align:t,pregap:n,postgap:0}}return i.colSeparationType=c?"align":"alignat",i};Br({type:"array",names:["array","darray"],props:{numArgs:1},handler(e,t){const r=(Ot(t[0])?[t[0]]:Rt(t[0],"ordgroup").body).map((function(e){const t=Ht(e).text;if(-1!=="lcr".indexOf(t))return{type:"align",align:t};if("|"===t)return{type:"separator",separator:"|"};if(":"===t)return{type:"separator",separator:":"};throw new n("Unknown column alignment: "+t,e)})),o={cols:r,hskipBeforeAndAfter:!0,maxNumCols:r.length};return Er(e.parser,o,Lr(e.envName))},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(e){const t={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[e.envName.replace("*","")];let r="c";const o={hskipBeforeAndAfter:!1,cols:[{type:"align",align:r}]};if("*"===e.envName.charAt(e.envName.length-1)){const t=e.parser;if(t.consumeSpaces(),"["===t.fetch().text){if(t.consume(),t.consumeSpaces(),r=t.fetch().text,-1==="lcr".indexOf(r))throw new n("Expected l or c or r",t.nextToken);t.consume(),t.consumeSpaces(),t.expect("]"),t.consume(),o.cols=[{type:"align",align:r}]}}const s=Er(e.parser,o,Lr(e.envName)),i=Math.max(0,...s.body.map((e=>e.length)));return s.cols=new Array(i).fill({type:"align",align:r}),t?{type:"leftright",mode:e.mode,body:[s],left:t[0],right:t[1],rightColor:void 0}:s},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(e){const t=Er(e.parser,{arraystretch:.5},"script");return t.colSeparationType="small",t},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["subarray"],props:{numArgs:1},handler(e,t){const r=(Ot(t[0])?[t[0]]:Rt(t[0],"ordgroup").body).map((function(e){const t=Ht(e).text;if(-1!=="lc".indexOf(t))return{type:"align",align:t};throw new n("Unknown column alignment: "+t,e)}));if(r.length>1)throw new n("{subarray} can contain only one column");let o={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5};if(o=Er(e.parser,o,"script"),o.body.length>0&&o.body[0].length>1)throw new n("{subarray} can contain only one column");return o},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=Er(e.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},Lr(e.envName));return{type:"leftright",mode:e.mode,body:[t],left:e.envName.indexOf("r")>-1?".":"\\{",right:e.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:Fr,htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){["gather","gather*"].includes(e.envName)&&Hr(e);const t={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:Or(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return Er(e.parser,t,"display")},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:Fr,htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){Hr(e);const t={autoTag:Or(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:e.parser.settings.leqno};return Er(e.parser,t,"display")},htmlBuilder:Dr,mathmlBuilder:Pr}),Br({type:"array",names:["CD"],props:{numArgs:0},handler(e){return Hr(e),function(e){const t=[];for(e.gullet.beginGroup(),e.gullet.macros.set("\\cr","\\\\\\relax"),e.gullet.beginGroup();;){t.push(e.parseExpression(!1,"\\\\")),e.gullet.endGroup(),e.gullet.beginGroup();const r=e.fetch().text;if("&"!==r&&"\\\\"!==r){if("\\end"===r){0===t[t.length-1].length&&t.pop();break}throw new n("Expected \\\\ or \\cr or \\end",e.nextToken)}e.consume()}let r=[];const o=[r];for(let a=0;a-1);else{if(!("<>AV".indexOf(o)>-1))throw new n('Expected one of "<>AV=|." after @',l[t]);for(let e=0;e<2;e++){let r=!0;for(let h=t+1;h{const r=e.font,n=t.withFont(r);return ct(e.body,n)},Yr=(e,t)=>{const r=e.font,n=t.withFont(r);return St(e.body,n)},Xr={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};$e({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathsfit","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=Ke(t[0]);let s=n;return s in Xr&&(s=Xr[s]),{type:"font",mode:r.mode,font:s.slice(1),body:o}},htmlBuilder:Ur,mathmlBuilder:Yr}),$e({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:(e,t)=>{let{parser:r}=e;const n=t[0],o=l.isCharacterBox(n);return{type:"mclass",mode:r.mode,mclass:Ut(n),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:n}],isCharacterBox:o}}}),$e({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n,breakOnTokenText:o}=e;const{mode:s}=r,i=r.parseExpression(!0,o);return{type:"font",mode:s,font:"math"+n.slice(1),body:{type:"ordgroup",mode:r.mode,body:i}}},htmlBuilder:Ur,mathmlBuilder:Yr});const Wr=(e,t)=>{let r=t;return"display"===e?r=r.id>=w.SCRIPT.id?r.text():w.DISPLAY:"text"===e&&r.size===w.DISPLAY.size?r=w.TEXT:"script"===e?r=w.SCRIPT:"scriptscript"===e&&(r=w.SCRIPTSCRIPT),r},_r=(e,t)=>{const r=Wr(e.size,t.style),n=r.fracNum(),o=r.fracDen();let s;s=t.havingStyle(n);const i=ct(e.numer,s,t);if(e.continued){const e=8.5/t.fontMetrics().ptPerEm,r=3.5/t.fontMetrics().ptPerEm;i.height=i.height0?3*c:7*c,u=t.fontMetrics().denom1):(h>0?(m=t.fontMetrics().num2,p=c):(m=t.fontMetrics().num3,p=3*c),u=t.fontMetrics().denom2),l){const e=t.fontMetrics().axisHeight;m-i.depth-(e+.5*h){let r=new ft.MathNode("mfrac",[St(e.numer,t),St(e.denom,t)]);if(e.hasBarLine){if(e.barSize){const n=V(e.barSize,t);r.setAttribute("linethickness",P(n))}}else r.setAttribute("linethickness","0px");const n=Wr(e.size,t.style);if(n.size!==t.style.size){r=new ft.MathNode("mstyle",[r]);const e=n.size===w.DISPLAY.size?"true":"false";r.setAttribute("displaystyle",e),r.setAttribute("scriptlevel","0")}if(null!=e.leftDelim||null!=e.rightDelim){const t=[];if(null!=e.leftDelim){const r=new ft.MathNode("mo",[new ft.TextNode(e.leftDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}if(t.push(r),null!=e.rightDelim){const r=new ft.MathNode("mo",[new ft.TextNode(e.rightDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}return yt(t)}return r};$e({type:"genfrac",names:["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];let i,a=null,l=null,h="auto";switch(n){case"\\dfrac":case"\\frac":case"\\tfrac":i=!0;break;case"\\\\atopfrac":i=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":i=!1,a="(",l=")";break;case"\\\\bracefrac":i=!1,a="\\{",l="\\}";break;case"\\\\brackfrac":i=!1,a="[",l="]";break;default:throw new Error("Unrecognized genfrac command")}switch(n){case"\\dfrac":case"\\dbinom":h="display";break;case"\\tfrac":case"\\tbinom":h="text"}return{type:"genfrac",mode:r.mode,continued:!1,numer:o,denom:s,hasBarLine:i,leftDelim:a,rightDelim:l,size:h,barSize:null}},htmlBuilder:_r,mathmlBuilder:jr}),$e({type:"genfrac",names:["\\cfrac"],props:{numArgs:2},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];return{type:"genfrac",mode:r.mode,continued:!0,numer:o,denom:s,hasBarLine:!0,leftDelim:null,rightDelim:null,size:"display",barSize:null}}}),$e({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(e){let t,{parser:r,funcName:n,token:o}=e;switch(n){case"\\over":t="\\frac";break;case"\\choose":t="\\binom";break;case"\\atop":t="\\\\atopfrac";break;case"\\brace":t="\\\\bracefrac";break;case"\\brack":t="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:t,token:o}}});const $r=["display","text","script","scriptscript"],Zr=function(e){let t=null;return e.length>0&&(t=e,t="."===t?null:t),t};$e({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(e,t){let{parser:r}=e;const n=t[4],o=t[5],s=Ke(t[0]),i="atom"===s.type&&"open"===s.family?Zr(s.text):null,a=Ke(t[1]),l="atom"===a.type&&"close"===a.family?Zr(a.text):null,h=Rt(t[2],"size");let c,m=null;h.isBlank?c=!0:(m=h.value,c=m.number>0);let p="auto",u=t[3];if("ordgroup"===u.type){if(u.body.length>0){const e=Rt(u.body[0],"textord");p=$r[Number(e.text)]}}else u=Rt(u,"textord"),p=$r[Number(u.text)];return{type:"genfrac",mode:r.mode,numer:n,denom:o,continued:!1,hasBarLine:c,barSize:m,leftDelim:i,rightDelim:l,size:p}},htmlBuilder:_r,mathmlBuilder:jr}),$e({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(e,t){let{parser:r,funcName:n,token:o}=e;return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:Rt(t[0],"size").value,token:o}}}),$e({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e}(Rt(t[1],"infix").size),i=t[2],a=s.number>0;return{type:"genfrac",mode:r.mode,numer:o,denom:i,continued:!1,hasBarLine:a,barSize:s,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:_r,mathmlBuilder:jr});const Kr=(e,t)=>{const r=t.style;let n,o;"supsub"===e.type?(n=e.sup?ct(e.sup,t.havingStyle(r.sup()),t):ct(e.sub,t.havingStyle(r.sub()),t),o=Rt(e.base,"horizBrace")):o=Rt(e,"horizBrace");const s=ct(o.base,t.havingBaseStyle(w.DISPLAY)),i=It(o,t);let a;if(o.isOver?(a=Pe.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:i}]},t),a.children[0].children[0].children[1].classes.push("svg-align")):(a=Pe.makeVList({positionType:"bottom",positionData:s.depth+.1+i.height,children:[{type:"elem",elem:i},{type:"kern",size:.1},{type:"elem",elem:s}]},t),a.children[0].children[0].children[0].classes.push("svg-align")),n){const e=Pe.makeSpan(["mord",o.isOver?"mover":"munder"],[a],t);a=o.isOver?Pe.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:e},{type:"kern",size:.2},{type:"elem",elem:n}]},t):Pe.makeVList({positionType:"bottom",positionData:e.depth+.2+n.height+n.depth,children:[{type:"elem",elem:n},{type:"kern",size:.2},{type:"elem",elem:e}]},t)}return Pe.makeSpan(["mord",o.isOver?"mover":"munder"],[a],t)};$e({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"horizBrace",mode:r.mode,label:n,isOver:/^\\over/.test(n),base:t[0]}},htmlBuilder:Kr,mathmlBuilder:(e,t)=>{const r=qt(e.label);return new ft.MathNode(e.isOver?"mover":"munder",[St(e.base,t),r])}}),$e({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[1],o=Rt(t[0],"url").url;return r.settings.isTrusted({command:"\\href",url:o})?{type:"href",mode:r.mode,href:o,body:Je(n)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:(e,t)=>{const r=ot(e.body,t,!1);return Pe.makeAnchor(e.href,[],r,t)},mathmlBuilder:(e,t)=>{let r=kt(e.body,t);return r instanceof dt||(r=new dt("mrow",[r])),r.setAttribute("href",e.href),r}}),$e({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=Rt(t[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");const o=[];for(let e=0;e{let{parser:r,funcName:o,token:s}=e;const i=Rt(t[0],"raw").string,a=t[1];let l;r.settings.strict&&r.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");const h={};switch(o){case"\\htmlClass":h.class=i,l={command:"\\htmlClass",class:i};break;case"\\htmlId":h.id=i,l={command:"\\htmlId",id:i};break;case"\\htmlStyle":h.style=i,l={command:"\\htmlStyle",style:i};break;case"\\htmlData":{const e=i.split(",");for(let t=0;t{const r=ot(e.body,t,!1),n=["enclosing"];e.attributes.class&&n.push(...e.attributes.class.trim().split(/\s+/));const o=Pe.makeSpan(n,r,t);for(const t in e.attributes)"class"!==t&&e.attributes.hasOwnProperty(t)&&o.setAttribute(t,e.attributes[t]);return o},mathmlBuilder:(e,t)=>kt(e.body,t)}),$e({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"htmlmathml",mode:r.mode,html:Je(t[0]),mathml:Je(t[1])}},htmlBuilder:(e,t)=>{const r=ot(e.html,t,!1);return Pe.makeFragment(r)},mathmlBuilder:(e,t)=>kt(e.mathml,t)});const Jr=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};{const t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new n("Invalid size: '"+e+"' in \\includegraphics");const r={number:+(t[1]+t[2]),unit:t[3]};if(!D(r))throw new n("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r}};$e({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:(e,t,r)=>{let{parser:o}=e,s={number:0,unit:"em"},i={number:.9,unit:"em"},a={number:0,unit:"em"},l="";if(r[0]){const e=Rt(r[0],"raw").string.split(",");for(let t=0;t{const r=V(e.height,t);let n=0;e.totalheight.number>0&&(n=V(e.totalheight,t)-r);let o=0;e.width.number>0&&(o=V(e.width,t));const s={height:P(r+n)};o>0&&(s.width=P(o)),n>0&&(s.verticalAlign=P(-n));const i=new j(e.src,e.alt,s);return i.height=r,i.depth=n,i},mathmlBuilder:(e,t)=>{const r=new ft.MathNode("mglyph",[]);r.setAttribute("alt",e.alt);const n=V(e.height,t);let o=0;if(e.totalheight.number>0&&(o=V(e.totalheight,t)-n,r.setAttribute("valign",P(-o))),r.setAttribute("height",P(n+o)),e.width.number>0){const n=V(e.width,t);r.setAttribute("width",P(n))}return r.setAttribute("src",e.src),r}}),$e({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=Rt(t[0],"size");if(r.settings.strict){const e="m"===n[1],t="mu"===o.value.unit;e?(t||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, not "+o.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):t&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:o.value}},htmlBuilder(e,t){return Pe.makeGlue(e.dimension,t)},mathmlBuilder(e,t){const r=V(e.dimension,t);return new ft.SpaceNode(r)}}),$e({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:o}},htmlBuilder:(e,t)=>{let r;"clap"===e.alignment?(r=Pe.makeSpan([],[ct(e.body,t)]),r=Pe.makeSpan(["inner"],[r],t)):r=Pe.makeSpan(["inner"],[ct(e.body,t)]);const n=Pe.makeSpan(["fix"],[]);let o=Pe.makeSpan([e.alignment],[r,n],t);const s=Pe.makeSpan(["strut"]);return s.style.height=P(o.height+o.depth),o.depth&&(s.style.verticalAlign=P(-o.depth)),o.children.unshift(s),o=Pe.makeSpan(["thinbox"],[o],t),Pe.makeSpan(["mord","vbox"],[o],t)},mathmlBuilder:(e,t)=>{const r=new ft.MathNode("mpadded",[St(e.body,t)]);if("rlap"!==e.alignment){const t="llap"===e.alignment?"-1":"-0.5";r.setAttribute("lspace",t+"width")}return r.setAttribute("width","0px"),r}}),$e({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){let{funcName:r,parser:n}=e;const o=n.mode;n.switchMode("math");const s="\\("===r?"\\)":"$",i=n.parseExpression(!1,s);return n.expect(s),n.switchMode(o),{type:"styling",mode:n.mode,style:"text",body:i}}}),$e({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){throw new n("Mismatched "+e.funcName)}});const Qr=(e,t)=>{switch(t.style.size){case w.DISPLAY.size:return e.display;case w.TEXT.size:return e.text;case w.SCRIPT.size:return e.script;case w.SCRIPTSCRIPT.size:return e.scriptscript;default:return e.text}};$e({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"mathchoice",mode:r.mode,display:Je(t[0]),text:Je(t[1]),script:Je(t[2]),scriptscript:Je(t[3])}},htmlBuilder:(e,t)=>{const r=Qr(e,t),n=ot(r,t,!1);return Pe.makeFragment(n)},mathmlBuilder:(e,t)=>{const r=Qr(e,t);return kt(r,t)}});const en=(e,t,r,n,o,s,i)=>{e=Pe.makeSpan([],[e]);const a=r&&l.isCharacterBox(r);let h,c,m;if(t){const e=ct(t,n.havingStyle(o.sup()),n);c={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-e.depth)}}if(r){const e=ct(r,n.havingStyle(o.sub()),n);h={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-e.height)}}if(c&&h){const t=n.fontMetrics().bigOpSpacing5+h.elem.height+h.elem.depth+h.kern+e.depth+i;m=Pe.makeVList({positionType:"bottom",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:h.elem,marginLeft:P(-s)},{type:"kern",size:h.kern},{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:P(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}else if(h){const t=e.height-i;m=Pe.makeVList({positionType:"top",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:h.elem,marginLeft:P(-s)},{type:"kern",size:h.kern},{type:"elem",elem:e}]},n)}else{if(!c)return e;{const t=e.depth+i;m=Pe.makeVList({positionType:"bottom",positionData:t,children:[{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:P(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}}const p=[m];if(h&&0!==s&&!a){const e=Pe.makeSpan(["mspace"],[],n);e.style.marginRight=P(s),p.unshift(e)}return Pe.makeSpan(["mop","op-limits"],p,n)},tn=["\\smallint"],rn=(e,t)=>{let r,n,o,s=!1;"supsub"===e.type?(r=e.sup,n=e.sub,o=Rt(e.base,"op"),s=!0):o=Rt(e,"op");const i=t.style;let a,l=!1;if(i.size===w.DISPLAY.size&&o.symbol&&!tn.includes(o.name)&&(l=!0),o.symbol){const e=l?"Size2-Regular":"Size1-Regular";let r="";if("\\oiint"!==o.name&&"\\oiiint"!==o.name||(r=o.name.slice(1),o.name="oiint"===r?"\\iint":"\\iiint"),a=Pe.makeSymbol(o.name,e,"math",t,["mop","op-symbol",l?"large-op":"small-op"]),r.length>0){const e=a.italic,n=Pe.staticSvg(r+"Size"+(l?"2":"1"),t);a=Pe.makeVList({positionType:"individualShift",children:[{type:"elem",elem:a,shift:0},{type:"elem",elem:n,shift:l?.08:0}]},t),o.name="\\"+r,a.classes.unshift("mop"),a.italic=e}}else if(o.body){const e=ot(o.body,t,!0);1===e.length&&e[0]instanceof Z?(a=e[0],a.classes[0]="mop"):a=Pe.makeSpan(["mop"],e,t)}else{const e=[];for(let r=1;r{let r;if(e.symbol)r=new dt("mo",[bt(e.name,e.mode)]),tn.includes(e.name)&&r.setAttribute("largeop","false");else if(e.body)r=new dt("mo",vt(e.body,t));else{r=new dt("mi",[new gt(e.name.slice(1))]);const t=new dt("mo",[bt("\u2061","text")]);r=e.parentIsSupSub?new dt("mrow",[r,t]):ut([r,t])}return r},on={"\u220f":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22c0":"\\bigwedge","\u22c1":"\\bigvee","\u22c2":"\\bigcap","\u22c3":"\\bigcup","\u2a00":"\\bigodot","\u2a01":"\\bigoplus","\u2a02":"\\bigotimes","\u2a04":"\\biguplus","\u2a06":"\\bigsqcup"};$e({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220f","\u2210","\u2211","\u22c0","\u22c1","\u22c2","\u22c3","\u2a00","\u2a01","\u2a02","\u2a04","\u2a06"],props:{numArgs:0},handler:(e,t)=>{let{parser:r,funcName:n}=e,o=n;return 1===o.length&&(o=on[o]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:o}},htmlBuilder:rn,mathmlBuilder:nn}),$e({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:Je(n)}},htmlBuilder:rn,mathmlBuilder:nn});const sn={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};$e({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:rn,mathmlBuilder:nn}),$e({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:rn,mathmlBuilder:nn}),$e({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e,n=r;return 1===n.length&&(n=sn[n]),{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:rn,mathmlBuilder:nn});const an=(e,t)=>{let r,n,o,s,i=!1;if("supsub"===e.type?(r=e.sup,n=e.sub,o=Rt(e.base,"operatorname"),i=!0):o=Rt(e,"operatorname"),o.body.length>0){const e=o.body.map((e=>{const t=e.text;return"string"==typeof t?{type:"textord",mode:e.mode,text:t}:e})),r=ot(e,t.withFont("mathrm"),!0);for(let e=0;e{let{parser:r,funcName:n}=e;const o=t[0];return{type:"operatorname",mode:r.mode,body:Je(o),alwaysHandleSupSub:"\\operatornamewithlimits"===n,limits:!1,parentIsSupSub:!1}},htmlBuilder:an,mathmlBuilder:(e,t)=>{let r=vt(e.body,t.withFont("mathrm")),n=!0;for(let e=0;ee.toText())).join("");r=[new ft.TextNode(e)]}const o=new ft.MathNode("mi",r);o.setAttribute("mathvariant","normal");const s=new ft.MathNode("mo",[bt("\u2061","text")]);return e.parentIsSupSub?new ft.MathNode("mrow",[o,s]):ft.newDocumentFragment([o,s])}}),Nr("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),Ze({type:"ordgroup",htmlBuilder(e,t){return e.semisimple?Pe.makeFragment(ot(e.body,t,!1)):Pe.makeSpan(["mord"],ot(e.body,t,!0),t)},mathmlBuilder(e,t){return kt(e.body,t,!0)}}),$e({type:"overline",names:["\\overline"],props:{numArgs:1},handler(e,t){let{parser:r}=e;const n=t[0];return{type:"overline",mode:r.mode,body:n}},htmlBuilder(e,t){const r=ct(e.body,t.havingCrampedStyle()),n=Pe.makeLineSpan("overline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Pe.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*o},{type:"elem",elem:n},{type:"kern",size:o}]},t);return Pe.makeSpan(["mord","overline"],[s],t)},mathmlBuilder(e,t){const r=new ft.MathNode("mo",[new ft.TextNode("\u203e")]);r.setAttribute("stretchy","true");const n=new ft.MathNode("mover",[St(e.body,t),r]);return n.setAttribute("accent","true"),n}}),$e({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"phantom",mode:r.mode,body:Je(n)}},htmlBuilder:(e,t)=>{const r=ot(e.body,t.withPhantom(),!1);return Pe.makeFragment(r)},mathmlBuilder:(e,t)=>{const r=vt(e.body,t);return new ft.MathNode("mphantom",r)}}),$e({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"hphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{let r=Pe.makeSpan([],[ct(e.body,t.withPhantom())]);if(r.height=0,r.depth=0,r.children)for(let e=0;e{const r=vt(Je(e.body),t),n=new ft.MathNode("mphantom",r),o=new ft.MathNode("mpadded",[n]);return o.setAttribute("height","0px"),o.setAttribute("depth","0px"),o}}),$e({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"vphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{const r=Pe.makeSpan(["inner"],[ct(e.body,t.withPhantom())]),n=Pe.makeSpan(["fix"],[]);return Pe.makeSpan(["mord","rlap"],[r,n],t)},mathmlBuilder:(e,t)=>{const r=vt(Je(e.body),t),n=new ft.MathNode("mphantom",r),o=new ft.MathNode("mpadded",[n]);return o.setAttribute("width","0px"),o}}),$e({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;const n=Rt(t[0],"size").value,o=t[1];return{type:"raisebox",mode:r.mode,dy:n,body:o}},htmlBuilder(e,t){const r=ct(e.body,t),n=V(e.dy,t);return Pe.makeVList({positionType:"shift",positionData:-n,children:[{type:"elem",elem:r}]},t)},mathmlBuilder(e,t){const r=new ft.MathNode("mpadded",[St(e.body,t)]),n=e.dy.number+e.dy.unit;return r.setAttribute("voffset",n),r}}),$e({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0,allowedInArgument:!0},handler(e){let{parser:t}=e;return{type:"internal",mode:t.mode}}}),$e({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["size","size","size"]},handler(e,t,r){let{parser:n}=e;const o=r[0],s=Rt(t[0],"size"),i=Rt(t[1],"size");return{type:"rule",mode:n.mode,shift:o&&Rt(o,"size").value,width:s.value,height:i.value}},htmlBuilder(e,t){const r=Pe.makeSpan(["mord","rule"],[],t),n=V(e.width,t),o=V(e.height,t),s=e.shift?V(e.shift,t):0;return r.style.borderRightWidth=P(n),r.style.borderTopWidth=P(o),r.style.bottom=P(s),r.width=n,r.height=o+s,r.depth=-s,r.maxFontSize=1.125*o*t.sizeMultiplier,r},mathmlBuilder(e,t){const r=V(e.width,t),n=V(e.height,t),o=e.shift?V(e.shift,t):0,s=t.color&&t.getColor()||"black",i=new ft.MathNode("mspace");i.setAttribute("mathbackground",s),i.setAttribute("width",P(r)),i.setAttribute("height",P(n));const a=new ft.MathNode("mpadded",[i]);return o>=0?a.setAttribute("height",P(o)):(a.setAttribute("height",P(o)),a.setAttribute("depth",P(-o))),a.setAttribute("voffset",P(o)),a}});const hn=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];$e({type:"sizing",names:hn,props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!1,r);return{type:"sizing",mode:o.mode,size:hn.indexOf(n)+1,body:s}},htmlBuilder:(e,t)=>{const r=t.havingSize(e.size);return ln(e.body,r,t)},mathmlBuilder:(e,t)=>{const r=t.havingSize(e.size),n=vt(e.body,r),o=new ft.MathNode("mstyle",n);return o.setAttribute("mathsize",P(r.sizeMultiplier)),o}}),$e({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:(e,t,r)=>{let{parser:n}=e,o=!1,s=!1;const i=r[0]&&Rt(r[0],"ordgroup");if(i){let e="";for(let t=0;t{const r=Pe.makeSpan([],[ct(e.body,t)]);if(!e.smashHeight&&!e.smashDepth)return r;if(e.smashHeight&&(r.height=0,r.children))for(let e=0;e{const r=new ft.MathNode("mpadded",[St(e.body,t)]);return e.smashHeight&&r.setAttribute("height","0px"),e.smashDepth&&r.setAttribute("depth","0px"),r}}),$e({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n}=e;const o=r[0],s=t[0];return{type:"sqrt",mode:n.mode,body:s,index:o}},htmlBuilder(e,t){let r=ct(e.body,t.havingCrampedStyle());0===r.height&&(r.height=t.fontMetrics().xHeight),r=Pe.wrapFragment(r,t);const n=t.fontMetrics().defaultRuleThickness;let o=n;t.style.idr.height+r.depth+s&&(s=(s+c-r.height-r.depth)/2);const m=a.height-r.height-s-l;r.style.paddingLeft=P(h);const p=Pe.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+m)},{type:"elem",elem:a},{type:"kern",size:l}]},t);if(e.index){const r=t.havingStyle(w.SCRIPTSCRIPT),n=ct(e.index,r,t),o=.6*(p.height-p.depth),s=Pe.makeVList({positionType:"shift",positionData:-o,children:[{type:"elem",elem:n}]},t),i=Pe.makeSpan(["root"],[s]);return Pe.makeSpan(["mord","sqrt"],[i,p],t)}return Pe.makeSpan(["mord","sqrt"],[p],t)},mathmlBuilder(e,t){const{body:r,index:n}=e;return n?new ft.MathNode("mroot",[St(r,t),St(n,t)]):new ft.MathNode("msqrt",[St(r,t)])}});const cn={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT};$e({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e,t){let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!0,r),i=n.slice(1,n.length-5);return{type:"styling",mode:o.mode,style:i,body:s}},htmlBuilder(e,t){const r=cn[e.style],n=t.havingStyle(r).withFont("");return ln(e.body,n,t)},mathmlBuilder(e,t){const r=cn[e.style],n=t.havingStyle(r),o=vt(e.body,n),s=new ft.MathNode("mstyle",o),i={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[e.style];return s.setAttribute("scriptlevel",i[0]),s.setAttribute("displaystyle",i[1]),s}});Ze({type:"supsub",htmlBuilder(e,t){const r=function(e,t){const r=e.base;if(r)return"op"===r.type?r.limits&&(t.style.size===w.DISPLAY.size||r.alwaysHandleSupSub)?rn:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(t.style.size===w.DISPLAY.size||r.limits)?an:null:"accent"===r.type?l.isCharacterBox(r.base)?Et:null:"horizBrace"===r.type&&!e.sub===r.isOver?Kr:null;return null}(e,t);if(r)return r(e,t);const{base:n,sup:o,sub:s}=e,i=ct(n,t);let a,h;const c=t.fontMetrics();let m=0,p=0;const u=n&&l.isCharacterBox(n);if(o){const e=t.havingStyle(t.style.sup());a=ct(o,e,t),u||(m=i.height-e.fontMetrics().supDrop*e.sizeMultiplier/t.sizeMultiplier)}if(s){const e=t.havingStyle(t.style.sub());h=ct(s,e,t),u||(p=i.depth+e.fontMetrics().subDrop*e.sizeMultiplier/t.sizeMultiplier)}let d;d=t.style===w.DISPLAY?c.sup1:t.style.cramped?c.sup3:c.sup2;const g=t.sizeMultiplier,f=P(.5/c.ptPerEm/g);let b,y=null;if(h){const t=e.base&&"op"===e.base.type&&e.base.name&&("\\oiint"===e.base.name||"\\oiiint"===e.base.name);(i instanceof Z||t)&&(y=P(-i.italic))}if(a&&h){m=Math.max(m,d,a.depth+.25*c.xHeight),p=Math.max(p,c.sub2);const e=4*c.defaultRuleThickness;if(m-a.depth-(h.height-p)0&&(m+=t,p-=t)}const r=[{type:"elem",elem:h,shift:p,marginRight:f,marginLeft:y},{type:"elem",elem:a,shift:-m,marginRight:f}];b=Pe.makeVList({positionType:"individualShift",children:r},t)}else if(h){p=Math.max(p,c.sub1,h.height-.8*c.xHeight);const e=[{type:"elem",elem:h,marginLeft:y,marginRight:f}];b=Pe.makeVList({positionType:"shift",positionData:p,children:e},t)}else{if(!a)throw new Error("supsub must have either sup or sub.");m=Math.max(m,d,a.depth+.25*c.xHeight),b=Pe.makeVList({positionType:"shift",positionData:-m,children:[{type:"elem",elem:a,marginRight:f}]},t)}const x=lt(i,"right")||"mord";return Pe.makeSpan([x],[i,Pe.makeSpan(["msupsub"],[b])],t)},mathmlBuilder(e,t){let r,n,o=!1;e.base&&"horizBrace"===e.base.type&&(n=!!e.sup,n===e.base.isOver&&(o=!0,r=e.base.isOver)),!e.base||"op"!==e.base.type&&"operatorname"!==e.base.type||(e.base.parentIsSupSub=!0);const s=[St(e.base,t)];let i;if(e.sub&&s.push(St(e.sub,t)),e.sup&&s.push(St(e.sup,t)),o)i=r?"mover":"munder";else if(e.sub)if(e.sup){const r=e.base;i=r&&"op"===r.type&&r.limits&&t.style===w.DISPLAY||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.style===w.DISPLAY||r.limits)?"munderover":"msubsup"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===w.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===w.DISPLAY)?"munder":"msub"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===w.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===w.DISPLAY)?"mover":"msup"}return new ft.MathNode(i,s)}}),Ze({type:"atom",htmlBuilder(e,t){return Pe.mathsym(e.text,e.mode,t,["m"+e.family])},mathmlBuilder(e,t){const r=new ft.MathNode("mo",[bt(e.text,e.mode)]);if("bin"===e.family){const n=xt(e,t);"bold-italic"===n&&r.setAttribute("mathvariant",n)}else"punct"===e.family?r.setAttribute("separator","true"):"open"!==e.family&&"close"!==e.family||r.setAttribute("stretchy","false");return r}});const mn={mi:"italic",mn:"normal",mtext:"normal"};Ze({type:"mathord",htmlBuilder(e,t){return Pe.makeOrd(e,t,"mathord")},mathmlBuilder(e,t){const r=new ft.MathNode("mi",[bt(e.text,e.mode,t)]),n=xt(e,t)||"italic";return n!==mn[r.type]&&r.setAttribute("mathvariant",n),r}}),Ze({type:"textord",htmlBuilder(e,t){return Pe.makeOrd(e,t,"textord")},mathmlBuilder(e,t){const r=bt(e.text,e.mode,t),n=xt(e,t)||"normal";let o;return o="text"===e.mode?new ft.MathNode("mtext",[r]):/[0-9]/.test(e.text)?new ft.MathNode("mn",[r]):"\\prime"===e.text?new ft.MathNode("mo",[r]):new ft.MathNode("mi",[r]),n!==mn[o.type]&&o.setAttribute("mathvariant",n),o}});const pn={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},un={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};Ze({type:"spacing",htmlBuilder(e,t){if(un.hasOwnProperty(e.text)){const r=un[e.text].className||"";if("text"===e.mode){const n=Pe.makeOrd(e,t,"textord");return n.classes.push(r),n}return Pe.makeSpan(["mspace",r],[Pe.mathsym(e.text,e.mode,t)],t)}if(pn.hasOwnProperty(e.text))return Pe.makeSpan(["mspace",pn[e.text]],[],t);throw new n('Unknown type of space "'+e.text+'"')},mathmlBuilder(e,t){let r;if(!un.hasOwnProperty(e.text)){if(pn.hasOwnProperty(e.text))return new ft.MathNode("mspace");throw new n('Unknown type of space "'+e.text+'"')}return r=new ft.MathNode("mtext",[new ft.TextNode("\xa0")]),r}});const dn=()=>{const e=new ft.MathNode("mtd",[]);return e.setAttribute("width","50%"),e};Ze({type:"tag",mathmlBuilder(e,t){const r=new ft.MathNode("mtable",[new ft.MathNode("mtr",[dn(),new ft.MathNode("mtd",[kt(e.body,t)]),dn(),new ft.MathNode("mtd",[kt(e.tag,t)])])]);return r.setAttribute("width","100%"),r}});const gn={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},fn={"\\textbf":"textbf","\\textmd":"textmd"},bn={"\\textit":"textit","\\textup":"textup"},yn=(e,t)=>{const r=e.font;return r?gn[r]?t.withTextFontFamily(gn[r]):fn[r]?t.withTextFontWeight(fn[r]):"\\emph"===r?"textit"===t.fontShape?t.withTextFontShape("textup"):t.withTextFontShape("textit"):t.withTextFontShape(bn[r]):t};$e({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"text",mode:r.mode,body:Je(o),font:n}},htmlBuilder(e,t){const r=yn(e,t),n=ot(e.body,r,!0);return Pe.makeSpan(["mord","text"],n,r)},mathmlBuilder(e,t){const r=yn(e,t);return kt(e.body,r)}}),$e({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"underline",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=ct(e.body,t),n=Pe.makeLineSpan("underline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Pe.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:o},{type:"elem",elem:n},{type:"kern",size:3*o},{type:"elem",elem:r}]},t);return Pe.makeSpan(["mord","underline"],[s],t)},mathmlBuilder(e,t){const r=new ft.MathNode("mo",[new ft.TextNode("\u203e")]);r.setAttribute("stretchy","true");const n=new ft.MathNode("munder",[St(e.body,t),r]);return n.setAttribute("accentunder","true"),n}}),$e({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"vcenter",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=ct(e.body,t),n=t.fontMetrics().axisHeight,o=.5*(r.height-n-(r.depth+n));return Pe.makeVList({positionType:"shift",positionData:o,children:[{type:"elem",elem:r}]},t)},mathmlBuilder(e,t){return new ft.MathNode("mpadded",[St(e.body,t)],["vcenter"])}}),$e({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(e,t,r){throw new n("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(e,t){const r=xn(e),n=[],o=t.havingStyle(t.style.text());for(let t=0;te.body.replace(/ /g,e.star?"\u2423":"\xa0");var wn=We;const vn="[ \r\n\t]",kn="(\\\\[a-zA-Z@]+)"+vn+"*",Sn="[\u0300-\u036f]",Mn=new RegExp(Sn+"+$"),zn="("+vn+"+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff]"+Sn+"*|[\ud800-\udbff][\udc00-\udfff]"+Sn+"*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|"+kn+"|\\\\[^\ud800-\udfff])";class An{constructor(e,t){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=t,this.tokenRegex=new RegExp(zn,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){const e=this.input,t=this.tokenRegex.lastIndex;if(t===e.length)return new Ir("EOF",new qr(this,t,t));const r=this.tokenRegex.exec(e);if(null===r||r.index!==t)throw new n("Unexpected character: '"+e[t]+"'",new Ir(e[t],new qr(this,t,t+1)));const o=r[6]||r[3]||(r[2]?"\\ ":" ");if(14===this.catcodes[o]){const t=e.indexOf("\n",this.tokenRegex.lastIndex);return-1===t?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=t+1,this.lex()}return new Ir(o,new qr(this,t,this.tokenRegex.lastIndex))}}class Tn{constructor(e,t){void 0===e&&(e={}),void 0===t&&(t={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(0===this.undefStack.length)throw new n("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");const e=this.undefStack.pop();for(const t in e)e.hasOwnProperty(t)&&(null==e[t]?delete this.current[t]:this.current[t]=e[t])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,t,r){if(void 0===r&&(r=!1),r){for(let t=0;t0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{const t=this.undefStack[this.undefStack.length-1];t&&!t.hasOwnProperty(e)&&(t[e]=this.current[e])}null==t?delete this.current[e]:this.current[e]=t}}var Bn=Cr;Nr("\\noexpand",(function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}})),Nr("\\expandafter",(function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}})),Nr("\\@firstoftwo",(function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}})),Nr("\\@secondoftwo",(function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}})),Nr("\\@ifnextchar",(function(e){const t=e.consumeArgs(3);e.consumeSpaces();const r=e.future();return 1===t[0].length&&t[0][0].text===r.text?{tokens:t[1],numArgs:0}:{tokens:t[2],numArgs:0}})),Nr("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Nr("\\TextOrMath",(function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}}));const Cn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};Nr("\\char",(function(e){let t,r=e.popToken(),o="";if("'"===r.text)t=8,r=e.popToken();else if('"'===r.text)t=16,r=e.popToken();else if("`"===r.text)if(r=e.popToken(),"\\"===r.text[0])o=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new n("\\char` missing argument");o=r.text.charCodeAt(0)}else t=10;if(t){if(o=Cn[r.text],null==o||o>=t)throw new n("Invalid base-"+t+" digit "+r.text);let s;for(;null!=(s=Cn[e.future().text])&&s{let s=e.consumeArg().tokens;if(1!==s.length)throw new n("\\newcommand's first argument must be a macro name");const i=s[0].text,a=e.isDefined(i);if(a&&!t)throw new n("\\newcommand{"+i+"} attempting to redefine "+i+"; use \\renewcommand");if(!a&&!r)throw new n("\\renewcommand{"+i+"} when command "+i+" does not yet exist; use \\newcommand");let l=0;if(s=e.consumeArg().tokens,1===s.length&&"["===s[0].text){let t="",r=e.expandNextToken();for(;"]"!==r.text&&"EOF"!==r.text;)t+=r.text,r=e.expandNextToken();if(!t.match(/^\s*[0-9]+\s*$/))throw new n("Invalid number of arguments: "+t);l=parseInt(t),s=e.consumeArg().tokens}return a&&o||e.macros.set(i,{tokens:s,numArgs:l}),""};Nr("\\newcommand",(e=>Nn(e,!1,!0,!1))),Nr("\\renewcommand",(e=>Nn(e,!0,!1,!1))),Nr("\\providecommand",(e=>Nn(e,!0,!0,!0))),Nr("\\message",(e=>{const t=e.consumeArgs(1)[0];return console.log(t.reverse().map((e=>e.text)).join("")),""})),Nr("\\errmessage",(e=>{const t=e.consumeArgs(1)[0];return console.error(t.reverse().map((e=>e.text)).join("")),""})),Nr("\\show",(e=>{const t=e.popToken(),r=t.text;return console.log(t,e.macros.get(r),wn[r],oe.math[r],oe.text[r]),""})),Nr("\\bgroup","{"),Nr("\\egroup","}"),Nr("~","\\nobreakspace"),Nr("\\lq","`"),Nr("\\rq","'"),Nr("\\aa","\\r a"),Nr("\\AA","\\r A"),Nr("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),Nr("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),Nr("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),Nr("\u212c","\\mathscr{B}"),Nr("\u2130","\\mathscr{E}"),Nr("\u2131","\\mathscr{F}"),Nr("\u210b","\\mathscr{H}"),Nr("\u2110","\\mathscr{I}"),Nr("\u2112","\\mathscr{L}"),Nr("\u2133","\\mathscr{M}"),Nr("\u211b","\\mathscr{R}"),Nr("\u212d","\\mathfrak{C}"),Nr("\u210c","\\mathfrak{H}"),Nr("\u2128","\\mathfrak{Z}"),Nr("\\Bbbk","\\Bbb{k}"),Nr("\xb7","\\cdotp"),Nr("\\llap","\\mathllap{\\textrm{#1}}"),Nr("\\rlap","\\mathrlap{\\textrm{#1}}"),Nr("\\clap","\\mathclap{\\textrm{#1}}"),Nr("\\mathstrut","\\vphantom{(}"),Nr("\\underbar","\\underline{\\text{#1}}"),Nr("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'),Nr("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),Nr("\\ne","\\neq"),Nr("\u2260","\\neq"),Nr("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),Nr("\u2209","\\notin"),Nr("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),Nr("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),Nr("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),Nr("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),Nr("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),Nr("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),Nr("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),Nr("\u27c2","\\perp"),Nr("\u203c","\\mathclose{!\\mkern-0.8mu!}"),Nr("\u220c","\\notni"),Nr("\u231c","\\ulcorner"),Nr("\u231d","\\urcorner"),Nr("\u231e","\\llcorner"),Nr("\u231f","\\lrcorner"),Nr("\xa9","\\copyright"),Nr("\xae","\\textregistered"),Nr("\ufe0f","\\textregistered"),Nr("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}'),Nr("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}'),Nr("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}'),Nr("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}'),Nr("\\vdots","{\\varvdots\\rule{0pt}{15pt}}"),Nr("\u22ee","\\vdots"),Nr("\\varGamma","\\mathit{\\Gamma}"),Nr("\\varDelta","\\mathit{\\Delta}"),Nr("\\varTheta","\\mathit{\\Theta}"),Nr("\\varLambda","\\mathit{\\Lambda}"),Nr("\\varXi","\\mathit{\\Xi}"),Nr("\\varPi","\\mathit{\\Pi}"),Nr("\\varSigma","\\mathit{\\Sigma}"),Nr("\\varUpsilon","\\mathit{\\Upsilon}"),Nr("\\varPhi","\\mathit{\\Phi}"),Nr("\\varPsi","\\mathit{\\Psi}"),Nr("\\varOmega","\\mathit{\\Omega}"),Nr("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),Nr("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),Nr("\\boxed","\\fbox{$\\displaystyle{#1}$}"),Nr("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),Nr("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),Nr("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;"),Nr("\\dddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}"),Nr("\\ddddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}");const qn={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};Nr("\\dots",(function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in qn?t=qn[r]:("\\not"===r.slice(0,4)||r in oe.math&&["bin","rel"].includes(oe.math[r].group))&&(t="\\dotsb"),t}));const In={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};Nr("\\dotso",(function(e){return e.future().text in In?"\\ldots\\,":"\\ldots"})),Nr("\\dotsc",(function(e){const t=e.future().text;return t in In&&","!==t?"\\ldots\\,":"\\ldots"})),Nr("\\cdots",(function(e){return e.future().text in In?"\\@cdots\\,":"\\@cdots"})),Nr("\\dotsb","\\cdots"),Nr("\\dotsm","\\cdots"),Nr("\\dotsi","\\!\\cdots"),Nr("\\dotsx","\\ldots\\,"),Nr("\\DOTSI","\\relax"),Nr("\\DOTSB","\\relax"),Nr("\\DOTSX","\\relax"),Nr("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Nr("\\,","\\tmspace+{3mu}{.1667em}"),Nr("\\thinspace","\\,"),Nr("\\>","\\mskip{4mu}"),Nr("\\:","\\tmspace+{4mu}{.2222em}"),Nr("\\medspace","\\:"),Nr("\\;","\\tmspace+{5mu}{.2777em}"),Nr("\\thickspace","\\;"),Nr("\\!","\\tmspace-{3mu}{.1667em}"),Nr("\\negthinspace","\\!"),Nr("\\negmedspace","\\tmspace-{4mu}{.2222em}"),Nr("\\negthickspace","\\tmspace-{5mu}{.277em}"),Nr("\\enspace","\\kern.5em "),Nr("\\enskip","\\hskip.5em\\relax"),Nr("\\quad","\\hskip1em\\relax"),Nr("\\qquad","\\hskip2em\\relax"),Nr("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Nr("\\tag@paren","\\tag@literal{({#1})}"),Nr("\\tag@literal",(e=>{if(e.macros.get("\\df@tag"))throw new n("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"})),Nr("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),Nr("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Nr("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Nr("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Nr("\\newline","\\\\\\relax"),Nr("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");const Rn=P(A["Main-Regular"]["T".charCodeAt(0)][1]-.7*A["Main-Regular"]["A".charCodeAt(0)][1]);Nr("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+Rn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),Nr("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+Rn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),Nr("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Nr("\\@hspace","\\hskip #1\\relax"),Nr("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Nr("\\ordinarycolon",":"),Nr("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),Nr("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),Nr("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),Nr("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),Nr("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),Nr("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),Nr("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),Nr("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),Nr("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),Nr("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),Nr("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),Nr("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),Nr("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),Nr("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),Nr("\u2237","\\dblcolon"),Nr("\u2239","\\eqcolon"),Nr("\u2254","\\coloneqq"),Nr("\u2255","\\eqqcolon"),Nr("\u2a74","\\Coloneqq"),Nr("\\ratio","\\vcentcolon"),Nr("\\coloncolon","\\dblcolon"),Nr("\\colonequals","\\coloneqq"),Nr("\\coloncolonequals","\\Coloneqq"),Nr("\\equalscolon","\\eqqcolon"),Nr("\\equalscoloncolon","\\Eqqcolon"),Nr("\\colonminus","\\coloneq"),Nr("\\coloncolonminus","\\Coloneq"),Nr("\\minuscolon","\\eqcolon"),Nr("\\minuscoloncolon","\\Eqcolon"),Nr("\\coloncolonapprox","\\Colonapprox"),Nr("\\coloncolonsim","\\Colonsim"),Nr("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Nr("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Nr("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Nr("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Nr("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),Nr("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Nr("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Nr("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Nr("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Nr("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}"),Nr("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}"),Nr("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}"),Nr("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}"),Nr("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),Nr("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),Nr("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),Nr("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),Nr("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),Nr("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),Nr("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),Nr("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),Nr("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),Nr("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),Nr("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),Nr("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),Nr("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),Nr("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),Nr("\\imath","\\html@mathml{\\@imath}{\u0131}"),Nr("\\jmath","\\html@mathml{\\@jmath}{\u0237}"),Nr("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),Nr("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),Nr("\u27e6","\\llbracket"),Nr("\u27e7","\\rrbracket"),Nr("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),Nr("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),Nr("\u2983","\\lBrace"),Nr("\u2984","\\rBrace"),Nr("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29b5}}"),Nr("\u29b5","\\minuso"),Nr("\\darr","\\downarrow"),Nr("\\dArr","\\Downarrow"),Nr("\\Darr","\\Downarrow"),Nr("\\lang","\\langle"),Nr("\\rang","\\rangle"),Nr("\\uarr","\\uparrow"),Nr("\\uArr","\\Uparrow"),Nr("\\Uarr","\\Uparrow"),Nr("\\N","\\mathbb{N}"),Nr("\\R","\\mathbb{R}"),Nr("\\Z","\\mathbb{Z}"),Nr("\\alef","\\aleph"),Nr("\\alefsym","\\aleph"),Nr("\\Alpha","\\mathrm{A}"),Nr("\\Beta","\\mathrm{B}"),Nr("\\bull","\\bullet"),Nr("\\Chi","\\mathrm{X}"),Nr("\\clubs","\\clubsuit"),Nr("\\cnums","\\mathbb{C}"),Nr("\\Complex","\\mathbb{C}"),Nr("\\Dagger","\\ddagger"),Nr("\\diamonds","\\diamondsuit"),Nr("\\empty","\\emptyset"),Nr("\\Epsilon","\\mathrm{E}"),Nr("\\Eta","\\mathrm{H}"),Nr("\\exist","\\exists"),Nr("\\harr","\\leftrightarrow"),Nr("\\hArr","\\Leftrightarrow"),Nr("\\Harr","\\Leftrightarrow"),Nr("\\hearts","\\heartsuit"),Nr("\\image","\\Im"),Nr("\\infin","\\infty"),Nr("\\Iota","\\mathrm{I}"),Nr("\\isin","\\in"),Nr("\\Kappa","\\mathrm{K}"),Nr("\\larr","\\leftarrow"),Nr("\\lArr","\\Leftarrow"),Nr("\\Larr","\\Leftarrow"),Nr("\\lrarr","\\leftrightarrow"),Nr("\\lrArr","\\Leftrightarrow"),Nr("\\Lrarr","\\Leftrightarrow"),Nr("\\Mu","\\mathrm{M}"),Nr("\\natnums","\\mathbb{N}"),Nr("\\Nu","\\mathrm{N}"),Nr("\\Omicron","\\mathrm{O}"),Nr("\\plusmn","\\pm"),Nr("\\rarr","\\rightarrow"),Nr("\\rArr","\\Rightarrow"),Nr("\\Rarr","\\Rightarrow"),Nr("\\real","\\Re"),Nr("\\reals","\\mathbb{R}"),Nr("\\Reals","\\mathbb{R}"),Nr("\\Rho","\\mathrm{P}"),Nr("\\sdot","\\cdot"),Nr("\\sect","\\S"),Nr("\\spades","\\spadesuit"),Nr("\\sub","\\subset"),Nr("\\sube","\\subseteq"),Nr("\\supe","\\supseteq"),Nr("\\Tau","\\mathrm{T}"),Nr("\\thetasym","\\vartheta"),Nr("\\weierp","\\wp"),Nr("\\Zeta","\\mathrm{Z}"),Nr("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Nr("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Nr("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),Nr("\\bra","\\mathinner{\\langle{#1}|}"),Nr("\\ket","\\mathinner{|{#1}\\rangle}"),Nr("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Nr("\\Bra","\\left\\langle#1\\right|"),Nr("\\Ket","\\left|#1\\right\\rangle");const Hn=e=>t=>{const r=t.consumeArg().tokens,n=t.consumeArg().tokens,o=t.consumeArg().tokens,s=t.consumeArg().tokens,i=t.macros.get("|"),a=t.macros.get("\\|");t.macros.beginGroup();const l=t=>r=>{e&&(r.macros.set("|",i),o.length&&r.macros.set("\\|",a));let s=t;if(!t&&o.length){"|"===r.future().text&&(r.popToken(),s=!0)}return{tokens:s?o:n,numArgs:0}};t.macros.set("|",l(!1)),o.length&&t.macros.set("\\|",l(!0));const h=t.consumeArg().tokens,c=t.expandTokens([...s,...h,...r]);return t.macros.endGroup(),{tokens:c.reverse(),numArgs:0}};Nr("\\bra@ket",Hn(!1)),Nr("\\bra@set",Hn(!0)),Nr("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),Nr("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),Nr("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),Nr("\\angln","{\\angl n}"),Nr("\\blue","\\textcolor{##6495ed}{#1}"),Nr("\\orange","\\textcolor{##ffa500}{#1}"),Nr("\\pink","\\textcolor{##ff00af}{#1}"),Nr("\\red","\\textcolor{##df0030}{#1}"),Nr("\\green","\\textcolor{##28ae7b}{#1}"),Nr("\\gray","\\textcolor{gray}{#1}"),Nr("\\purple","\\textcolor{##9d38bd}{#1}"),Nr("\\blueA","\\textcolor{##ccfaff}{#1}"),Nr("\\blueB","\\textcolor{##80f6ff}{#1}"),Nr("\\blueC","\\textcolor{##63d9ea}{#1}"),Nr("\\blueD","\\textcolor{##11accd}{#1}"),Nr("\\blueE","\\textcolor{##0c7f99}{#1}"),Nr("\\tealA","\\textcolor{##94fff5}{#1}"),Nr("\\tealB","\\textcolor{##26edd5}{#1}"),Nr("\\tealC","\\textcolor{##01d1c1}{#1}"),Nr("\\tealD","\\textcolor{##01a995}{#1}"),Nr("\\tealE","\\textcolor{##208170}{#1}"),Nr("\\greenA","\\textcolor{##b6ffb0}{#1}"),Nr("\\greenB","\\textcolor{##8af281}{#1}"),Nr("\\greenC","\\textcolor{##74cf70}{#1}"),Nr("\\greenD","\\textcolor{##1fab54}{#1}"),Nr("\\greenE","\\textcolor{##0d923f}{#1}"),Nr("\\goldA","\\textcolor{##ffd0a9}{#1}"),Nr("\\goldB","\\textcolor{##ffbb71}{#1}"),Nr("\\goldC","\\textcolor{##ff9c39}{#1}"),Nr("\\goldD","\\textcolor{##e07d10}{#1}"),Nr("\\goldE","\\textcolor{##a75a05}{#1}"),Nr("\\redA","\\textcolor{##fca9a9}{#1}"),Nr("\\redB","\\textcolor{##ff8482}{#1}"),Nr("\\redC","\\textcolor{##f9685d}{#1}"),Nr("\\redD","\\textcolor{##e84d39}{#1}"),Nr("\\redE","\\textcolor{##bc2612}{#1}"),Nr("\\maroonA","\\textcolor{##ffbde0}{#1}"),Nr("\\maroonB","\\textcolor{##ff92c6}{#1}"),Nr("\\maroonC","\\textcolor{##ed5fa6}{#1}"),Nr("\\maroonD","\\textcolor{##ca337c}{#1}"),Nr("\\maroonE","\\textcolor{##9e034e}{#1}"),Nr("\\purpleA","\\textcolor{##ddd7ff}{#1}"),Nr("\\purpleB","\\textcolor{##c6b9fc}{#1}"),Nr("\\purpleC","\\textcolor{##aa87ff}{#1}"),Nr("\\purpleD","\\textcolor{##7854ab}{#1}"),Nr("\\purpleE","\\textcolor{##543b78}{#1}"),Nr("\\mintA","\\textcolor{##f5f9e8}{#1}"),Nr("\\mintB","\\textcolor{##edf2df}{#1}"),Nr("\\mintC","\\textcolor{##e0e5cc}{#1}"),Nr("\\grayA","\\textcolor{##f6f7f7}{#1}"),Nr("\\grayB","\\textcolor{##f0f1f2}{#1}"),Nr("\\grayC","\\textcolor{##e3e5e6}{#1}"),Nr("\\grayD","\\textcolor{##d6d8da}{#1}"),Nr("\\grayE","\\textcolor{##babec2}{#1}"),Nr("\\grayF","\\textcolor{##888d93}{#1}"),Nr("\\grayG","\\textcolor{##626569}{#1}"),Nr("\\grayH","\\textcolor{##3b3e40}{#1}"),Nr("\\grayI","\\textcolor{##21242c}{#1}"),Nr("\\kaBlue","\\textcolor{##314453}{#1}"),Nr("\\kaGreen","\\textcolor{##71B307}{#1}");const On={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class En{constructor(e,t,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new Tn(Bn,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new An(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){let t,r,n;if(e){if(this.consumeSpaces(),"["!==this.future().text)return null;t=this.popToken(),({tokens:n,end:r}=this.consumeArg(["]"]))}else({tokens:n,start:t,end:r}=this.consumeArg());return this.pushToken(new Ir("EOF",r.loc)),this.pushTokens(n),new Ir("",qr.range(t,r))}consumeSpaces(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}}consumeArg(e){const t=[],r=e&&e.length>0;r||this.consumeSpaces();const o=this.future();let s,i=0,a=0;do{if(s=this.popToken(),t.push(s),"{"===s.text)++i;else if("}"===s.text){if(--i,-1===i)throw new n("Extra }",s)}else if("EOF"===s.text)throw new n("Unexpected end of input in a macro argument, expected '"+(e&&r?e[a]:"}")+"'",s);if(e&&r)if((0===i||1===i&&"{"===e[a])&&s.text===e[a]){if(++a,a===e.length){t.splice(-a,a);break}}else a=0}while(0!==i||r);return"{"===o.text&&"}"===t[t.length-1].text&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:o,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new n("The length of delimiters doesn't match the number of args!");const r=t[0];for(let e=0;ethis.settings.maxExpand)throw new n("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){const t=this.popToken(),r=t.text,o=t.noexpand?null:this._getExpansion(r);if(null==o||e&&o.unexpandable){if(e&&null==o&&"\\"===r[0]&&!this.isDefined(r))throw new n("Undefined control sequence: "+r);return this.pushToken(t),!1}this.countExpansion(1);let s=o.tokens;const i=this.consumeArgs(o.numArgs,o.delimiters);if(o.numArgs){s=s.slice();for(let e=s.length-1;e>=0;--e){let t=s[e];if("#"===t.text){if(0===e)throw new n("Incomplete placeholder at end of macro body",t);if(t=s[--e],"#"===t.text)s.splice(e+1,1);else{if(!/^[1-9]$/.test(t.text))throw new n("Not a valid argument number",t);s.splice(e,2,...i[+t.text-1])}}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(!1===this.expandOnce()){const e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new Ir(e)]):void 0}expandTokens(e){const t=[],r=this.stack.length;for(this.pushTokens(e);this.stack.length>r;)if(!1===this.expandOnce(!0)){const e=this.stack.pop();e.treatAsRelax&&(e.noexpand=!1,e.treatAsRelax=!1),t.push(e)}return this.countExpansion(t.length),t}expandMacroAsText(e){const t=this.expandMacro(e);return t?t.map((e=>e.text)).join(""):t}_getExpansion(e){const t=this.macros.get(e);if(null==t)return t;if(1===e.length){const t=this.lexer.catcodes[e];if(null!=t&&13!==t)return}const r="function"==typeof t?t(this):t;if("string"==typeof r){let e=0;if(-1!==r.indexOf("#")){const t=r.replace(/##/g,"");for(;-1!==t.indexOf("#"+(e+1));)++e}const t=new An(r,this.settings),n=[];let o=t.lex();for(;"EOF"!==o.text;)n.push(o),o=t.lex();n.reverse();return{tokens:n,numArgs:e}}return r}isDefined(e){return this.macros.has(e)||wn.hasOwnProperty(e)||oe.math.hasOwnProperty(e)||oe.text.hasOwnProperty(e)||On.hasOwnProperty(e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:wn.hasOwnProperty(e)&&!wn[e].primitive}}const Ln=/^[\u208a\u208b\u208c\u208d\u208e\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2090\u2091\u2095\u1d62\u2c7c\u2096\u2097\u2098\u2099\u2092\u209a\u1d63\u209b\u209c\u1d64\u1d65\u2093\u1d66\u1d67\u1d68\u1d69\u1d6a]/,Dn=Object.freeze({"\u208a":"+","\u208b":"-","\u208c":"=","\u208d":"(","\u208e":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1d62":"i","\u2c7c":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209a":"p","\u1d63":"r","\u209b":"s","\u209c":"t","\u1d64":"u","\u1d65":"v","\u2093":"x","\u1d66":"\u03b2","\u1d67":"\u03b3","\u1d68":"\u03c1","\u1d69":"\u03d5","\u1d6a":"\u03c7","\u207a":"+","\u207b":"-","\u207c":"=","\u207d":"(","\u207e":")","\u2070":"0","\xb9":"1","\xb2":"2","\xb3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1d2c":"A","\u1d2e":"B","\u1d30":"D","\u1d31":"E","\u1d33":"G","\u1d34":"H","\u1d35":"I","\u1d36":"J","\u1d37":"K","\u1d38":"L","\u1d39":"M","\u1d3a":"N","\u1d3c":"O","\u1d3e":"P","\u1d3f":"R","\u1d40":"T","\u1d41":"U","\u2c7d":"V","\u1d42":"W","\u1d43":"a","\u1d47":"b","\u1d9c":"c","\u1d48":"d","\u1d49":"e","\u1da0":"f","\u1d4d":"g","\u02b0":"h","\u2071":"i","\u02b2":"j","\u1d4f":"k","\u02e1":"l","\u1d50":"m","\u207f":"n","\u1d52":"o","\u1d56":"p","\u02b3":"r","\u02e2":"s","\u1d57":"t","\u1d58":"u","\u1d5b":"v","\u02b7":"w","\u02e3":"x","\u02b8":"y","\u1dbb":"z","\u1d5d":"\u03b2","\u1d5e":"\u03b3","\u1d5f":"\u03b4","\u1d60":"\u03d5","\u1d61":"\u03c7","\u1dbf":"\u03b8"}),Vn={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"},"\u0327":{text:"\\c"}},Pn={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u1e09":"c\u0327\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\xe7":"c\u0327","\u010f":"d\u030c","\u1e0b":"d\u0307","\u1e11":"d\u0327","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u1e1d":"e\u0327\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u0229":"e\u0327","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u0123":"g\u0327","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\u1e29":"h\u0327","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u0137":"k\u0327","\u013a":"l\u0301","\u013e":"l\u030c","\u013c":"l\u0327","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\u0146":"n\u0327","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u0157":"r\u0327","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u015f":"s\u0327","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\u0163":"t\u0327","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u1e08":"C\u0327\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\xc7":"C\u0327","\u010e":"D\u030c","\u1e0a":"D\u0307","\u1e10":"D\u0327","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u1e1c":"E\u0327\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u0228":"E\u0327","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u0122":"G\u0327","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\u1e28":"H\u0327","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0136":"K\u0327","\u0139":"L\u0301","\u013d":"L\u030c","\u013b":"L\u0327","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\u0145":"N\u0327","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u0156":"R\u0327","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u015e":"S\u0327","\u0164":"T\u030c","\u1e6a":"T\u0307","\u0162":"T\u0327","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"};class Fn{constructor(e,t){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new En(e,t,this.mode),this.settings=t,this.leftrightDepth=0}expect(e,t){if(void 0===t&&(t=!0),this.fetch().text!==e)throw new n("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{const e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){const t=this.nextToken;this.consume(),this.gullet.pushToken(new Ir("}")),this.gullet.pushTokens(e);const r=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,r}parseExpression(e,t){const r=[];for(;;){"math"===this.mode&&this.consumeSpaces();const n=this.fetch();if(-1!==Fn.endOfExpression.indexOf(n.text))break;if(t&&n.text===t)break;if(e&&wn[n.text]&&wn[n.text].infix)break;const o=this.parseAtom(t);if(!o)break;"internal"!==o.type&&r.push(o)}return"text"===this.mode&&this.formLigatures(r),this.handleInfixNodes(r)}handleInfixNodes(e){let t,r=-1;for(let o=0;o=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+t[0]+'" used in math mode',e);const r=oe[this.mode][t].group,n=qr.range(e);let s;if(te.hasOwnProperty(r)){const e=r;s={type:"atom",mode:this.mode,family:e,loc:n,text:t}}else s={type:r,mode:this.mode,loc:n,text:t};o=s}else{if(!(t.charCodeAt(0)>=128))return null;this.settings.strict&&(S(t.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'" ('+t.charCodeAt(0)+")",e)),o={type:"textord",mode:"text",loc:qr.range(e),text:t}}if(this.consume(),r)for(let t=0;t15?"\u2026"+e.slice(n-15,n):e.slice(0,n),l=o+15e.replace(o,"-$1").toLowerCase(),i={"&":"&",">":">","<":"<",'"':""","'":"'"},l=/[&><"']/g,a=e=>String(e).replace(l,e=>i[e]),c=e=>"ordgroup"===e.type||"color"===e.type?1===e.body.length?c(e.body[0]):e:"font"===e.type?c(e.body):e,h=new Set(["mathord","textord","atom"]),m=e=>h.has(c(e).type),u={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:e=>"#"+e},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(e,t)=>(t.push(e),t)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:e=>Math.max(0,e),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:e=>Math.max(0,e),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:e=>Math.max(0,e),cli:"-e, --max-expand ",cliProcessor:e=>"Infinity"===e?1/0:parseInt(e)},globalGroup:{type:"boolean",cli:!1}};function p(e){if("default"in e)return e.default;const t=e.type,r=Array.isArray(t)?t[0]:t;if("string"!=typeof r)return r.enum[0];switch(r){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}class d{constructor(e){void 0===e&&(e={}),e=e||{};for(const t of Object.keys(u)){const r=u[t],n=e[t];this[t]=void 0!==n?r.processor?r.processor(n):n:p(r)}}reportNonstrict(e,t,r){let o=this.strict;if("function"==typeof o&&(o=o(e,t,r)),o&&"ignore"!==o){if(!0===o||"error"===o)throw new n("LaTeX-incompatible input and strict mode is set to 'error': "+t+" ["+e+"]",r);"warn"===o?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+o+"': "+t+" ["+e+"]")}}useStrictBehavior(e,t,r){let n=this.strict;if("function"==typeof n)try{n=n(e,t,r)}catch(e){n="error"}return!(!n||"ignore"===n)&&(!0===n||"error"===n||("warn"===n?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+t+" ["+e+"]"),!1)))}isTrusted(e){if("url"in e&&e.url&&!e.protocol){const t=(e=>{const t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?":"!==t[2]?null:/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?t[1].toLowerCase():null:"_relative"})(e.url);if(null==t)return!1;e.protocol=t}const t="function"==typeof this.trust?this.trust(e):this.trust;return Boolean(t)}}class g{constructor(e,t,r){this.id=e,this.size=t,this.cramped=r}sup(){return f[b[this.id]]}sub(){return f[y[this.id]]}fracNum(){return f[x[this.id]]}fracDen(){return f[w[this.id]]}cramp(){return f[v[this.id]]}text(){return f[k[this.id]]}isTight(){return this.size>=2}}const f=[new g(0,0,!1),new g(1,0,!0),new g(2,1,!1),new g(3,1,!0),new g(4,2,!1),new g(5,2,!0),new g(6,3,!1),new g(7,3,!0)],b=[4,5,4,5,6,7,6,7],y=[5,5,5,5,7,7,7,7],x=[2,3,4,5,6,7,6,7],w=[3,3,5,5,7,7,7,7],v=[1,1,3,3,5,5,7,7],k=[0,1,2,3,2,3,2,3];var S={DISPLAY:f[0],TEXT:f[2],SCRIPT:f[4],SCRIPTSCRIPT:f[6]};const z=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];const M=[];function A(e){for(let t=0;t=M[t]&&e<=M[t+1])return!0;return!1}z.forEach(e=>e.blocks.forEach(e=>M.push(...e)));const T=e=>e+" "+e,B=80,q={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:T("M40 281 V428 H0 V94 H40 V241 H400000 v40z"),leftbracketunder:T("M0 0 h120 V290 H399995 v120 H0z"),leftbracketover:T("M0 440 h120 V150 H399995 v-120 H0z"),leftmapsto:T("M40 281 V448H0V74H40V241H400000v40z"),leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:T("M0 50 h400000 v40H0z m0 194h40000v40H0z"),midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:T("M399960 241 V94 h40 V428 h-40 V281 H0 v-40z"),rightbracketunder:T("M399995 0 h-120 V290 H0 v120 H400000z"),rightbracketover:T("M399995 440 h-120 V150 H0 v-120 H399995z"),rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"};class C{constructor(e){this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createDocumentFragment();for(let t=0;te.toText()).join("")}}const I={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},R={ex:!0,em:!0,mu:!0},H=function(e){return"string"!=typeof e&&(e=e.unit),e in I||e in R||"ex"===e},E=function(e,t){let r;if(e.unit in I)r=I[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if("mu"===e.unit)r=t.fontMetrics().cssEmPerMu;else{let o;if(o=t.style.isTight()?t.havingStyle(t.style.text()):t,"ex"===e.unit)r=o.fontMetrics().xHeight;else{if("em"!==e.unit)throw new n("Invalid unit: '"+e.unit+"'");r=o.fontMetrics().quad}o!==t&&(r*=o.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*r,t.maxSize)},O=function(e){return+e.toFixed(4)+"em"},N=function(e){return e.filter(e=>e).join(" ")},D=function(e,t,r){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},t){t.style.isTight()&&this.classes.push("mtight");const e=t.getColor();e&&(this.style.color=e)}},L=function(e){const t=document.createElement(e);t.className=N(this.classes);for(const e of Object.keys(this.style))t.style[e]=this.style[e];for(const e of Object.keys(this.attributes))t.setAttribute(e,this.attributes[e]);for(let e=0;e/=\x00-\x1f]/,F=function(e){let t="<"+e;this.classes.length&&(t+=' class="'+a(N(this.classes))+'"');let r="";for(const e of Object.keys(this.style))r+=s(e)+":"+this.style[e]+";";r&&(t+=' style="'+a(r)+'"');for(const e of Object.keys(this.attributes)){if(P.test(e))throw new n("Invalid attribute name '"+e+"'");t+=" "+e+'="'+a(this.attributes[e])+'"'}t+=">";for(let e=0;e",t};class V{constructor(e,t,r,n){D.call(this,e,r,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return this.classes.includes(e)}toNode(){return L.call(this,"span")}toMarkup(){return F.call(this,"span")}}class G{constructor(e,t,r,n){D.call(this,t,n),this.children=r||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return this.classes.includes(e)}toNode(){return L.call(this,"a")}toMarkup(){return F.call(this,"a")}}class U{constructor(e,t,r){this.alt=t,this.src=e,this.classes=["mord"],this.height=0,this.depth=0,this.maxFontSize=0,this.style=r}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(const t of Object.keys(this.style))e.style[t]=this.style[t];return e}toMarkup(){let e=''+a(this.alt)+'=n[0]&&e<=n[1])return r.name}}return null}(this.text.charCodeAt(0));a&&this.classes.push(a+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=X[this.text])}hasClass(e){return this.classes.includes(e)}toNode(){const e=document.createTextNode(this.text);let t=null;this.italic>0&&(t=document.createElement("span"),t.style.marginRight=O(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=N(this.classes));for(const e of Object.keys(this.style))t=t||document.createElement("span"),t.style[e]=this.style[e];return t?(t.appendChild(e),t):e}toMarkup(){let e=!1,t="0&&(r+="margin-right:"+O(this.italic)+";");for(const e of Object.keys(this.style))r+=s(e)+":"+this.style[e]+";";r&&(e=!0,t+=' style="'+a(r)+'"');const n=a(this.text);return e?(t+=">",t+=n,t+="",t):n}}class W{constructor(e,t){this.children=e||[],this.attributes=t||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","svg");for(const t of Object.keys(this.attributes))e.setAttribute(t,this.attributes[t]);for(let t=0;t':''}}class _{constructor(e){this.attributes=e||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","line");for(const t of Object.keys(this.attributes))e.setAttribute(t,this.attributes[t]);return e}toMarkup(){let e="","\\gt",!0),oe(se,le,be,"\u2208","\\in",!0),oe(se,le,be,"\ue020","\\@not"),oe(se,le,be,"\u2282","\\subset",!0),oe(se,le,be,"\u2283","\\supset",!0),oe(se,le,be,"\u2286","\\subseteq",!0),oe(se,le,be,"\u2287","\\supseteq",!0),oe(se,ae,be,"\u2288","\\nsubseteq",!0),oe(se,ae,be,"\u2289","\\nsupseteq",!0),oe(se,le,be,"\u22a8","\\models"),oe(se,le,be,"\u2190","\\leftarrow",!0),oe(se,le,be,"\u2264","\\le"),oe(se,le,be,"\u2264","\\leq",!0),oe(se,le,be,"<","\\lt",!0),oe(se,le,be,"\u2192","\\rightarrow",!0),oe(se,le,be,"\u2192","\\to"),oe(se,ae,be,"\u2271","\\ngeq",!0),oe(se,ae,be,"\u2270","\\nleq",!0),oe(se,le,ye,"\xa0","\\ "),oe(se,le,ye,"\xa0","\\space"),oe(se,le,ye,"\xa0","\\nobreakspace"),oe(ie,le,ye,"\xa0","\\ "),oe(ie,le,ye,"\xa0"," "),oe(ie,le,ye,"\xa0","\\space"),oe(ie,le,ye,"\xa0","\\nobreakspace"),oe(se,le,ye,null,"\\nobreak"),oe(se,le,ye,null,"\\allowbreak"),oe(se,le,fe,",",","),oe(se,le,fe,";",";"),oe(se,ae,he,"\u22bc","\\barwedge",!0),oe(se,ae,he,"\u22bb","\\veebar",!0),oe(se,le,he,"\u2299","\\odot",!0),oe(se,le,he,"\u2295","\\oplus",!0),oe(se,le,he,"\u2297","\\otimes",!0),oe(se,le,xe,"\u2202","\\partial",!0),oe(se,le,he,"\u2298","\\oslash",!0),oe(se,ae,he,"\u229a","\\circledcirc",!0),oe(se,ae,he,"\u22a1","\\boxdot",!0),oe(se,le,he,"\u25b3","\\bigtriangleup"),oe(se,le,he,"\u25bd","\\bigtriangledown"),oe(se,le,he,"\u2020","\\dagger"),oe(se,le,he,"\u22c4","\\diamond"),oe(se,le,he,"\u22c6","\\star"),oe(se,le,he,"\u25c3","\\triangleleft"),oe(se,le,he,"\u25b9","\\triangleright"),oe(se,le,ge,"{","\\{"),oe(ie,le,xe,"{","\\{"),oe(ie,le,xe,"{","\\textbraceleft"),oe(se,le,me,"}","\\}"),oe(ie,le,xe,"}","\\}"),oe(ie,le,xe,"}","\\textbraceright"),oe(se,le,ge,"{","\\lbrace"),oe(se,le,me,"}","\\rbrace"),oe(se,le,ge,"[","\\lbrack",!0),oe(ie,le,xe,"[","\\lbrack",!0),oe(se,le,me,"]","\\rbrack",!0),oe(ie,le,xe,"]","\\rbrack",!0),oe(se,le,ge,"(","\\lparen",!0),oe(se,le,me,")","\\rparen",!0),oe(ie,le,xe,"<","\\textless",!0),oe(ie,le,xe,">","\\textgreater",!0),oe(se,le,ge,"\u230a","\\lfloor",!0),oe(se,le,me,"\u230b","\\rfloor",!0),oe(se,le,ge,"\u2308","\\lceil",!0),oe(se,le,me,"\u2309","\\rceil",!0),oe(se,le,xe,"\\","\\backslash"),oe(se,le,xe,"\u2223","|"),oe(se,le,xe,"\u2223","\\vert"),oe(ie,le,xe,"|","\\textbar",!0),oe(se,le,xe,"\u2225","\\|"),oe(se,le,xe,"\u2225","\\Vert"),oe(ie,le,xe,"\u2225","\\textbardbl"),oe(ie,le,xe,"~","\\textasciitilde"),oe(ie,le,xe,"\\","\\textbackslash"),oe(ie,le,xe,"^","\\textasciicircum"),oe(se,le,be,"\u2191","\\uparrow",!0),oe(se,le,be,"\u21d1","\\Uparrow",!0),oe(se,le,be,"\u2193","\\downarrow",!0),oe(se,le,be,"\u21d3","\\Downarrow",!0),oe(se,le,be,"\u2195","\\updownarrow",!0),oe(se,le,be,"\u21d5","\\Updownarrow",!0),oe(se,le,de,"\u2210","\\coprod"),oe(se,le,de,"\u22c1","\\bigvee"),oe(se,le,de,"\u22c0","\\bigwedge"),oe(se,le,de,"\u2a04","\\biguplus"),oe(se,le,de,"\u22c2","\\bigcap"),oe(se,le,de,"\u22c3","\\bigcup"),oe(se,le,de,"\u222b","\\int"),oe(se,le,de,"\u222b","\\intop"),oe(se,le,de,"\u222c","\\iint"),oe(se,le,de,"\u222d","\\iiint"),oe(se,le,de,"\u220f","\\prod"),oe(se,le,de,"\u2211","\\sum"),oe(se,le,de,"\u2a02","\\bigotimes"),oe(se,le,de,"\u2a01","\\bigoplus"),oe(se,le,de,"\u2a00","\\bigodot"),oe(se,le,de,"\u222e","\\oint"),oe(se,le,de,"\u222f","\\oiint"),oe(se,le,de,"\u2230","\\oiiint"),oe(se,le,de,"\u2a06","\\bigsqcup"),oe(se,le,de,"\u222b","\\smallint"),oe(ie,le,ue,"\u2026","\\textellipsis"),oe(se,le,ue,"\u2026","\\mathellipsis"),oe(ie,le,ue,"\u2026","\\ldots",!0),oe(se,le,ue,"\u2026","\\ldots",!0),oe(se,le,ue,"\u22ef","\\@cdots",!0),oe(se,le,ue,"\u22f1","\\ddots",!0),oe(se,le,xe,"\u22ee","\\varvdots"),oe(ie,le,xe,"\u22ee","\\varvdots"),oe(se,le,ce,"\u02ca","\\acute"),oe(se,le,ce,"\u02cb","\\grave"),oe(se,le,ce,"\xa8","\\ddot"),oe(se,le,ce,"~","\\tilde"),oe(se,le,ce,"\u02c9","\\bar"),oe(se,le,ce,"\u02d8","\\breve"),oe(se,le,ce,"\u02c7","\\check"),oe(se,le,ce,"^","\\hat"),oe(se,le,ce,"\u20d7","\\vec"),oe(se,le,ce,"\u02d9","\\dot"),oe(se,le,ce,"\u02da","\\mathring"),oe(se,le,pe,"\ue131","\\@imath"),oe(se,le,pe,"\ue237","\\@jmath"),oe(se,le,xe,"\u0131","\u0131"),oe(se,le,xe,"\u0237","\u0237"),oe(ie,le,xe,"\u0131","\\i",!0),oe(ie,le,xe,"\u0237","\\j",!0),oe(ie,le,xe,"\xdf","\\ss",!0),oe(ie,le,xe,"\xe6","\\ae",!0),oe(ie,le,xe,"\u0153","\\oe",!0),oe(ie,le,xe,"\xf8","\\o",!0),oe(ie,le,xe,"\xc6","\\AE",!0),oe(ie,le,xe,"\u0152","\\OE",!0),oe(ie,le,xe,"\xd8","\\O",!0),oe(ie,le,ce,"\u02ca","\\'"),oe(ie,le,ce,"\u02cb","\\`"),oe(ie,le,ce,"\u02c6","\\^"),oe(ie,le,ce,"\u02dc","\\~"),oe(ie,le,ce,"\u02c9","\\="),oe(ie,le,ce,"\u02d8","\\u"),oe(ie,le,ce,"\u02d9","\\."),oe(ie,le,ce,"\xb8","\\c"),oe(ie,le,ce,"\u02da","\\r"),oe(ie,le,ce,"\u02c7","\\v"),oe(ie,le,ce,"\xa8",'\\"'),oe(ie,le,ce,"\u02dd","\\H"),oe(ie,le,ce,"\u25ef","\\textcircled");const we={"--":!0,"---":!0,"``":!0,"''":!0};oe(ie,le,xe,"\u2013","--",!0),oe(ie,le,xe,"\u2013","\\textendash"),oe(ie,le,xe,"\u2014","---",!0),oe(ie,le,xe,"\u2014","\\textemdash"),oe(ie,le,xe,"\u2018","`",!0),oe(ie,le,xe,"\u2018","\\textquoteleft"),oe(ie,le,xe,"\u2019","'",!0),oe(ie,le,xe,"\u2019","\\textquoteright"),oe(ie,le,xe,"\u201c","``",!0),oe(ie,le,xe,"\u201c","\\textquotedblleft"),oe(ie,le,xe,"\u201d","''",!0),oe(ie,le,xe,"\u201d","\\textquotedblright"),oe(se,le,xe,"\xb0","\\degree",!0),oe(ie,le,xe,"\xb0","\\degree"),oe(ie,le,xe,"\xb0","\\textdegree",!0),oe(se,le,xe,"\xa3","\\pounds"),oe(se,le,xe,"\xa3","\\mathsterling",!0),oe(ie,le,xe,"\xa3","\\pounds"),oe(ie,le,xe,"\xa3","\\textsterling",!0),oe(se,ae,xe,"\u2720","\\maltese"),oe(ie,ae,xe,"\u2720","\\maltese");const ve='0123456789/@."';for(let e=0;e<14;e++){const t=ve.charAt(e);oe(se,le,xe,t,t)}const ke='0123456789!@*()-=+";:?/.,';for(let e=0;e<25;e++){const t=ke.charAt(e);oe(ie,le,xe,t,t)}const Se="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";for(let e=0;e<52;e++){const t=Se.charAt(e);oe(se,le,pe,t,t),oe(ie,le,xe,t,t)}oe(se,ae,xe,"C","\u2102"),oe(ie,ae,xe,"C","\u2102"),oe(se,ae,xe,"H","\u210d"),oe(ie,ae,xe,"H","\u210d"),oe(se,ae,xe,"N","\u2115"),oe(ie,ae,xe,"N","\u2115"),oe(se,ae,xe,"P","\u2119"),oe(ie,ae,xe,"P","\u2119"),oe(se,ae,xe,"Q","\u211a"),oe(ie,ae,xe,"Q","\u211a"),oe(se,ae,xe,"R","\u211d"),oe(ie,ae,xe,"R","\u211d"),oe(se,ae,xe,"Z","\u2124"),oe(ie,ae,xe,"Z","\u2124"),oe(se,le,pe,"h","\u210e"),oe(ie,le,pe,"h","\u210e");let ze="";for(let e=0;e<52;e++){const t=Se.charAt(e);ze=String.fromCharCode(55349,56320+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56372+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56424+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56580+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56684+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56736+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56788+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56840+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56944+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),e<26&&(ze=String.fromCharCode(55349,56632+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,56476+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze))}ze=String.fromCharCode(55349,56668),oe(se,le,pe,"k",ze),oe(ie,le,xe,"k",ze);for(let e=0;e<10;e++){const t=e.toString();ze=String.fromCharCode(55349,57294+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,57314+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,57324+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze),ze=String.fromCharCode(55349,57334+e),oe(se,le,pe,t,ze),oe(ie,le,xe,t,ze)}const Me="\xd0\xde\xfe";for(let e=0;e<3;e++){const t=Me.charAt(e);oe(se,le,pe,t,t),oe(ie,le,xe,t,t)}const Ae=[["mathbf","textbf","Main-Bold"],["mathbf","textbf","Main-Bold"],["mathnormal","textit","Math-Italic"],["mathnormal","textit","Math-Italic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["mathscr","textscr","Script-Regular"],["","",""],["","",""],["","",""],["mathfrak","textfrak","Fraktur-Regular"],["mathfrak","textfrak","Fraktur-Regular"],["mathbb","textbb","AMS-Regular"],["mathbb","textbb","AMS-Regular"],["mathboldfrak","textboldfrak","Fraktur-Regular"],["mathboldfrak","textboldfrak","Fraktur-Regular"],["mathsf","textsf","SansSerif-Regular"],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathitsf","textitsf","SansSerif-Italic"],["mathitsf","textitsf","SansSerif-Italic"],["","",""],["","",""],["mathtt","texttt","Typewriter-Regular"],["mathtt","texttt","Typewriter-Regular"]],Te=[["mathbf","textbf","Main-Bold"],["","",""],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathtt","texttt","Typewriter-Regular"]],Be=function(e,t,r){if(ne[r][e]){const t=ne[r][e].replace;t&&(e=t)}return{value:e,metrics:J(e,t,r)}},qe=function(e,t,r,n,o){const s=Be(e,t,r),i=s.metrics;let l;if(e=s.value,i){let t=i.italic;("text"===r||n&&"mathit"===n.font)&&(t=0),l=new Y(e,i.height,i.depth,t,i.skew,i.width,o)}else"undefined"!=typeof console&&console.warn("No character metrics for '"+e+"' in style '"+t+"' and mode '"+r+"'"),l=new Y(e,0,0,0,0,0,o);if(n){l.maxFontSize=n.sizeMultiplier,n.style.isTight()&&l.classes.push("mtight");const e=n.getColor();e&&(l.style.color=e)}return l},Ce=function(e,t,r,n){return void 0===n&&(n=[]),"boldsymbol"===r.font&&Be(e,"Main-Bold",t).metrics?qe(e,"Main-Bold",t,r,n.concat(["mathbf"])):"\\"===e||"main"===ne[t][e].font?qe(e,"Main-Regular",t,r,n):qe(e,"AMS-Regular",t,r,n.concat(["amsrm"]))},Ie=function(e,t,r){const o=e.mode,s=e.text,i=["mord"],l="math"===o||"text"===o&&t.font,a=l?t.font:t.fontFamily;let c="",h="";if(55349===s.charCodeAt(0)&&([c,h]=((e,t)=>{const r=1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536,o="math"===t?0:1;if(119808<=r&&r<120484){const e=Math.floor((r-119808)/26);return[Ae[e][2],Ae[e][o]]}if(120782<=r&&r<=120831){const e=Math.floor((r-120782)/10);return[Te[e][2],Te[e][o]]}if(120485===r||120486===r)return[Ae[0][2],Ae[0][o]];if(1204860)return qe(s,c,o,t,i.concat(h));if(a){let e,n;if("boldsymbol"===a){const t=function(e,t,r,n,o){return"textord"!==o&&Be(e,"Math-BoldItalic",t).metrics?{fontName:"Math-BoldItalic",fontClass:"boldsymbol"}:{fontName:"Main-Bold",fontClass:"mathbf"}}(s,o,0,0,r);e=t.fontName,n=[t.fontClass]}else l?(e=Ue[a].fontName,n=[a]):(e=Ge(a,t.fontWeight,t.fontShape),n=[a,t.fontWeight,t.fontShape]);if(Be(s,e,o).metrics)return qe(s,e,o,t,i.concat(n));if(we.hasOwnProperty(s)&&"Typewriter"===e.slice(0,10)){const r=[];for(let l=0;l{if(N(e.classes)!==N(t.classes)||e.skew!==t.skew||e.maxFontSize!==t.maxFontSize||0!==e.italic&&e.hasClass("mathnormal"))return!1;if(1===e.classes.length){const t=e.classes[0];if("mbin"===t||"mord"===t)return!1}for(const r of Object.keys(e.style))if(e.style[r]!==t.style[r])return!1;for(const r of Object.keys(t.style))if(e.style[r]!==t.style[r])return!1;return!0},He=e=>{for(let t=0;tt&&(t=s.height),s.depth>r&&(r=s.depth),s.maxFontSize>n&&(n=s.maxFontSize)}e.height=t,e.depth=r,e.maxFontSize=n},Oe=function(e,t,r,n){const o=new V(e,t,r,n);return Ee(o),o},Ne=(e,t,r,n)=>new V(e,t,r,n),De=function(e,t,r){const n=Oe([e],[],t);return n.height=Math.max(r||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=O(n.height),n.maxFontSize=1,n},Le=function(e){const t=new C(e);return Ee(t),t},Pe=function(e,t){return e instanceof C?Oe([],[e],t):e},Fe=function(e,t){const{children:r,depth:n}=function(e){if("individualShift"===e.positionType){const t=e.children,r=[t[0]],n=-t[0].shift-t[0].elem.depth;let o=n;for(let e=1;e{const r=Oe(["mspace"],[],t),n=E(e,t);return r.style.marginRight=O(n),r},Ge=function(e,t,r){let n,o="";switch(e){case"amsrm":o="AMS";break;case"textrm":o="Main";break;case"textsf":o="SansSerif";break;case"texttt":o="Typewriter";break;default:o=e}return n="textbf"===t&&"textit"===r?"BoldItalic":"textbf"===t?"Bold":"textit"===t?"Italic":"Regular",o+"-"+n},Ue={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathsfit:{variant:"sans-serif-italic",fontName:"SansSerif-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},Xe={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},Ye=function(e,t){const[r,n,o]=Xe[e],s=new j(r),i=new W([s],{width:O(n),height:O(o),style:"width:"+O(n),viewBox:"0 0 "+1e3*n+" "+1e3*o,preserveAspectRatio:"xMinYMin"}),l=Ne(["overlay"],[i],t);return l.height=o,l.style.height=O(o),l.style.width=O(n),l},We={number:3,unit:"mu"},je={number:4,unit:"mu"},_e={number:5,unit:"mu"},$e={mord:{mop:We,mbin:je,mrel:_e,minner:We},mop:{mord:We,mop:We,mrel:_e,minner:We},mbin:{mord:je,mop:je,mopen:je,minner:je},mrel:{mord:_e,mop:_e,mopen:_e,minner:_e},mopen:{},mclose:{mop:We,mbin:je,mrel:_e,minner:We},mpunct:{mord:We,mop:We,mrel:_e,mopen:We,mclose:We,mpunct:We,minner:We},minner:{mord:We,mop:We,mbin:je,mrel:_e,mopen:We,mpunct:We,minner:We}},Ze={mord:{mop:We},mop:{mord:We,mop:We},mbin:{},mrel:{},mopen:{},mclose:{mop:We},mpunct:{},minner:{mop:We}},Ke={},Je={},Qe={};function et(e){let{type:t,names:r,props:n,handler:o,htmlBuilder:s,mathmlBuilder:i}=e;const l={type:t,numArgs:n.numArgs,argTypes:n.argTypes,allowedInArgument:!!n.allowedInArgument,allowedInText:!!n.allowedInText,allowedInMath:void 0===n.allowedInMath||n.allowedInMath,numOptionalArgs:n.numOptionalArgs||0,infix:!!n.infix,primitive:!!n.primitive,handler:o};for(let e=0;e{const r=t.classes[0],n=e.classes[0];"mbin"===r&&st.has(n)?t.classes[0]="mord":"mbin"===n&&ot.has(r)&&(e.classes[0]="mord")},{node:i},l,a),ct(o,(e,t)=>{var r,n;const o=ut(t),i=ut(e),l=o&&i?e.hasClass("mtight")?null==(r=Ze[o])?void 0:r[i]:null==(n=$e[o])?void 0:n[i]:null;if(l)return Ve(l,s)},{node:i},l,a),o},ct=function(e,t,r,n,o){n&&e.push(n);let s=0;for(;sr=>{e.splice(t+1,0,r),s++})(s)}n&&e.pop()},ht=function(e){return e instanceof C||e instanceof G||e instanceof V&&e.hasClass("enclosing")?e:null},mt=function(e,t){const r=ht(e);if(r){const e=r.children;if(e.length){if("right"===t)return mt(e[e.length-1],"right");if("left"===t)return mt(e[0],"left")}}return e},ut=function(e,t){if(!e)return null;t&&(e=mt(e,t));const r=e.classes[0];return lt[r]||null},pt=function(e,t){const r=["nulldelimiter"].concat(e.baseSizingClasses());return Oe(t.concat(r))},dt=function(e,t,r){if(!e)return Oe();if(Je[e.type]){let n=Je[e.type](e,t);if(r&&t.size!==r.size){n=Oe(t.sizingClasses(r),[n],t);const e=t.sizeMultiplier/r.sizeMultiplier;n.height*=e,n.depth*=e}return n}throw new n("Got group of unknown type: '"+e.type+"'")};function gt(e,t){const r=Oe(["base"],e,t),n=Oe(["strut"]);return n.style.height=O(r.height+r.depth),r.depth&&(n.style.verticalAlign=O(-r.depth)),r.children.unshift(n),r}function ft(e,t){let r=null;1===e.length&&"tag"===e[0].type&&(r=e[0].tag,e=e[0].body);const n=at(e,t,"root");let o;2===n.length&&n[1].hasClass("tag")&&(o=n.pop());const s=[];let i,l=[];for(let e=0;e0&&(s.push(gt(l,t)),l=[]),s.push(n[e]));l.length>0&&s.push(gt(l,t)),r?(i=gt(at(r,t,!0),t),i.classes=["tag"],s.push(i)):o&&s.push(o);const a=Oe(["katex-html"],s);if(a.setAttribute("aria-hidden","true"),i){const e=i.children[0];e.style.height=O(a.height+a.depth),a.depth&&(e.style.verticalAlign=O(-a.depth))}return a}function bt(e){return new C(e)}class yt{constructor(e,t,r){this.type=e,this.attributes={},this.children=t||[],this.classes=r||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){const e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);this.classes.length>0&&(e.className=N(this.classes));for(let t=0;t0&&(e+=' class ="'+a(N(this.classes))+'"'),e+=">";for(let t=0;t",e}toText(){return this.children.map(e=>e.toText()).join("")}}class xt{constructor(e){this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return a(this.toText())}toText(){return this.text}}class wt{constructor(e){this.width=e,this.character=e>=.05555&&e<=.05556?"\u200a":e>=.1666&&e<=.1667?"\u2009":e>=.2222&&e<=.2223?"\u2005":e>=.2777&&e<=.2778?"\u2005\u200a":e>=-.05556&&e<=-.05555?"\u200a\u2063":e>=-.1667&&e<=-.1666?"\u2009\u2063":e>=-.2223&&e<=-.2222?"\u205f\u2063":e>=-.2778&&e<=-.2777?"\u2005\u2063":null}toNode(){if(this.character)return document.createTextNode(this.character);{const e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",O(this.width)),e}}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}}const vt=new Set(["\\imath","\\jmath"]),kt=new Set(["mrow","mtable"]),St=function(e,t,r){return!ne[t][e]||!ne[t][e].replace||55349===e.charCodeAt(0)||we.hasOwnProperty(e)&&r&&(r.fontFamily&&"tt"===r.fontFamily.slice(4,6)||r.font&&"tt"===r.font.slice(4,6))||(e=ne[t][e].replace),new xt(e)},zt=function(e){return 1===e.length?e[0]:new yt("mrow",e)},Mt=function(e,t){if("texttt"===t.fontFamily)return"monospace";if("textsf"===t.fontFamily)return"textit"===t.fontShape&&"textbf"===t.fontWeight?"sans-serif-bold-italic":"textit"===t.fontShape?"sans-serif-italic":"textbf"===t.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===t.fontShape&&"textbf"===t.fontWeight)return"bold-italic";if("textit"===t.fontShape)return"italic";if("textbf"===t.fontWeight)return"bold";const r=t.font;if(!r||"mathnormal"===r)return null;const n=e.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"textord"===e.type?"bold":"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathsfit"===r)return"sans-serif-italic";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";let o=e.text;if(vt.has(o))return null;if(ne[n][o]){const e=ne[n][o].replace;e&&(o=e)}return J(o,Ue[r].fontName,n)?Ue[r].variant:null};function At(e){if(!e)return!1;if("mi"===e.type&&1===e.children.length){const t=e.children[0];return t instanceof xt&&"."===t.text}if("mo"===e.type&&1===e.children.length&&"true"===e.getAttribute("separator")&&"0em"===e.getAttribute("lspace")&&"0em"===e.getAttribute("rspace")){const t=e.children[0];return t instanceof xt&&","===t.text}return!1}const Tt=function(e,t,r){if(1===e.length){const n=qt(e[0],t);return r&&n instanceof yt&&"mo"===n.type&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}const n=[];let o;for(let r=0;r=1&&("mn"===o.type||At(o))){const e=s.children[0];e instanceof yt&&"mn"===e.type&&(e.children=[...o.children,...e.children],n.pop())}else if("mi"===o.type&&1===o.children.length){const e=o.children[0];if(e instanceof xt&&"\u0338"===e.text&&("mo"===s.type||"mi"===s.type||"mn"===s.type)){const e=s.children[0];e instanceof xt&&e.text.length>0&&(e.text=e.text.slice(0,1)+"\u0338"+e.text.slice(1),n.pop())}}}n.push(s),o=s}return n},Bt=function(e,t,r){return zt(Tt(e,t,r))},qt=function(e,t){if(!e)return new yt("mrow");if(Qe[e.type]){return Qe[e.type](e,t)}throw new n("Got group of unknown type: '"+e.type+"'")};function Ct(e,t,r,n,o){const s=Tt(e,r);let i;i=1===s.length&&s[0]instanceof yt&&kt.has(s[0].type)?s[0]:new yt("mrow",s);const l=new yt("annotation",[new xt(t)]);l.setAttribute("encoding","application/x-tex");const a=new yt("semantics",[i,l]),c=new yt("math",[a]);c.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&c.setAttribute("display","block");return Oe([o?"katex":"katex-mathml"],[c])}const It=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],Rt=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],Ht=function(e,t){return t.size<2?e:It[e-1][t.size-1]};class Et{constructor(e){this.style=e.style,this.color=e.color,this.size=e.size||Et.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=Rt[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){const t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};return Object.assign(t,e),new Et(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:Ht(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:Rt[e-1]})}havingBaseStyle(e){e=e||this.style.text();const t=Ht(Et.BASESIZE,e);return this.size===t&&this.textSize===Et.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){let e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==Et.BASESIZE?["sizing","reset-size"+this.size,"size"+Et.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=function(e){let t;if(t=e>=5?0:e>=3?1:2,!Q[t]){const e=Q[t]={cssEmPerMu:Z.quad[t]/18};for(const r in Z)Z.hasOwnProperty(r)&&(e[r]=Z[r][t])}return Q[t]}(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}}Et.BASESIZE=6;var Ot=Et;const Nt=function(e){return new Ot({style:e.displayMode?S.DISPLAY:S.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},Dt=function(e,t){if(t.displayMode){const r=["katex-display"];t.leqno&&r.push("leqno"),t.fleqn&&r.push("fleqn"),e=Oe(r,[e])}return e},Lt=function(e,t,r){const n=Nt(r);let o;if("mathml"===r.output)return Ct(e,t,n,r.displayMode,!0);if("html"===r.output){const t=ft(e,n);o=Oe(["katex"],[t])}else{const s=Ct(e,t,n,r.displayMode,!1),i=ft(e,n);o=Oe(["katex"],[s,i])}return Dt(o,r)};const Pt={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",underbracket:"\u23b5",overbracket:"\u23b4",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},Ft=function(e){const t=new yt("mo",[new xt(Pt[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},Vt={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overbracket:[["leftbracketover","rightbracketover"],1.6,440],underbracket:[["leftbracketunder","rightbracketunder"],1.6,410],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},Gt=new Set(["widehat","widecheck","widetilde","utilde"]),Ut=function(e,t){const{span:r,minWidth:n,height:o}=function(){let r=4e5;const n=e.label.slice(1);if(Gt.has(n)){const o=e,s="ordgroup"===o.base.type?o.base.body.length:1;let i,l,a;if(s>5)"widehat"===n||"widecheck"===n?(i=420,r=2364,a=.42,l=n+"4"):(i=312,r=2340,a=.34,l="tilde4");else{const e=[1,1,2,2,3,3][s];"widehat"===n||"widecheck"===n?(r=[0,1062,2364,2364,2364][e],i=[0,239,300,360,420][e],a=[0,.24,.3,.3,.36,.42][e],l=n+e):(r=[0,600,1033,2339,2340][e],i=[0,260,286,306,312][e],a=[0,.26,.286,.3,.306,.34][e],l="tilde"+e)}const c=new j(l),h=new W([c],{width:"100%",height:O(a),viewBox:"0 0 "+r+" "+i,preserveAspectRatio:"none"});return{span:Ne([],[h],t),minWidth:0,height:a}}{const e=[],o=Vt[n],[s,i,l]=o,a=l/1e3,c=s.length;let h,m;if(1===c){h=["hide-tail"],m=[o[3]]}else if(2===c)h=["halfarrow-left","halfarrow-right"],m=["xMinYMin","xMaxYMin"];else{if(3!==c)throw new Error("Correct katexImagesData or update code here to support\n "+c+" children.");h=["brace-left","brace-center","brace-right"],m=["xMinYMin","xMidYMin","xMaxYMin"]}for(let n=0;n0&&(r.style.minWidth=O(n)),r};function Xt(e,t){if(!e||e.type!==t)throw new Error("Expected node of type "+t+", but got "+(e?"node of type "+e.type:String(e)));return e}function Yt(e){const t=Wt(e);if(!t)throw new Error("Expected node of symbol group type, but got "+(e?"node of type "+e.type:String(e)));return t}function Wt(e){return e&&("atom"===e.type||te.hasOwnProperty(e.type))?e:null}const jt=e=>{return e instanceof Y?e:((t=e)instanceof V||t instanceof G||t instanceof C)&&1===e.children.length?jt(e.children[0]):void 0;var t},_t=(e,t)=>{let r,n,o;e&&"supsub"===e.type?(n=Xt(e.base,"accent"),r=n.base,e.base=r,o=function(e){if(e instanceof V)return e;throw new Error("Expected span but got "+String(e)+".")}(dt(e,t)),e.base=n):(n=Xt(e,"accent"),r=n.base);const s=dt(r,t.havingCrampedStyle());let i=0;var l,a;n.isShifty&&m(r)&&(i=null!=(l=null==(a=jt(s))?void 0:a.skew)?l:0);const c="\\c"===n.label;let h,u=c?s.height+s.depth:Math.min(s.height,t.fontMetrics().xHeight);if(n.isStretchy)h=Ut(n,t),h=Fe({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:h,wrapperClasses:["svg-align"],wrapperStyle:i>0?{width:"calc(100% - "+O(2*i)+")",marginLeft:O(2*i)}:void 0}]});else{let e,r;"\\vec"===n.label?(e=Ye("vec",t),r=Xe.vec[1]):(e=Ie({type:"textord",mode:n.mode,text:n.label},t,"textord"),e=function(e){if(e instanceof Y)return e;throw new Error("Expected symbolNode but got "+String(e)+".")}(e),e.italic=0,r=e.width,c&&(u+=e.depth)),h=Oe(["accent-body"],[e]);const o="\\textcircled"===n.label;o&&(h.classes.push("accent-full"),u=s.height);let l=i;o||(l-=r/2),h.style.left=O(l),"\\textcircled"===n.label&&(h.style.top=".2em"),h=Fe({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-u},{type:"elem",elem:h}]})}const p=Oe(["mord","accent"],[h],t);return o?(o.children[0]=p,o.height=Math.max(p.height,o.height),o.classes[0]="mord",o):p},$t=(e,t)=>{const r=e.isStretchy?Ft(e.label):new yt("mo",[St(e.label,e.mode)]),n=new yt("mover",[qt(e.base,t),r]);return n.setAttribute("accent","true"),n},Zt=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(e=>"\\"+e).join("|"));et({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(e,t)=>{const r=rt(t[0]),n=!Zt.test(e.funcName),o=!n||"\\widehat"===e.funcName||"\\widetilde"===e.funcName||"\\widecheck"===e.funcName;return{type:"accent",mode:e.parser.mode,label:e.funcName,isStretchy:n,isShifty:o,base:r}},htmlBuilder:_t,mathmlBuilder:$t}),et({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(e,t)=>{const r=t[0];let n=e.parser.mode;return"math"===n&&(e.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+e.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:e.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:_t,mathmlBuilder:$t}),et({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"accentUnder",mode:r.mode,label:n,base:o}},htmlBuilder:(e,t)=>{const r=dt(e.base,t),n=Ut(e,t),o="\\utilde"===e.label?.12:0,s=Fe({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:o},{type:"elem",elem:r}]});return Oe(["mord","accentunder"],[s],t)},mathmlBuilder:(e,t)=>{const r=Ft(e.label),n=new yt("munder",[qt(e.base,t),r]);return n.setAttribute("accentunder","true"),n}});const Kt=e=>{const t=new yt("mpadded",e?[e]:[]);return t.setAttribute("width","+0.6em"),t.setAttribute("lspace","0.3em"),t};function Jt(e,t){const r=at(e.body,t,!0);return Oe([e.mclass],r,t)}function Qt(e,t){let r;const n=Tt(e.body,t);return"minner"===e.mclass?r=new yt("mpadded",n):"mord"===e.mclass?e.isCharacterBox?(r=n[0],r.type="mi"):r=new yt("mi",n):(e.isCharacterBox?(r=n[0],r.type="mo"):r=new yt("mo",n),"mbin"===e.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"===e.mclass||"mclose"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0em"):"minner"===e.mclass&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em")),r}et({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n,funcName:o}=e;return{type:"xArrow",mode:n.mode,label:o,body:t[0],below:r[0]}},htmlBuilder(e,t){const r=t.style;let n=t.havingStyle(r.sup());const o=Pe(dt(e.body,n,t),t),s="\\x"===e.label.slice(0,2)?"x":"cd";let i;o.classes.push(s+"-arrow-pad"),e.below&&(n=t.havingStyle(r.sub()),i=Pe(dt(e.below,n,t),t),i.classes.push(s+"-arrow-pad"));const l=Ut(e,t),a=-t.fontMetrics().axisHeight+.5*l.height;let c,h=-t.fontMetrics().axisHeight-.5*l.height-.111;if((o.depth>.25||"\\xleftequilibrium"===e.label)&&(h-=o.depth),i){const e=-t.fontMetrics().axisHeight+i.height+.5*l.height+.111;c=Fe({positionType:"individualShift",children:[{type:"elem",elem:o,shift:h},{type:"elem",elem:l,shift:a},{type:"elem",elem:i,shift:e}]})}else c=Fe({positionType:"individualShift",children:[{type:"elem",elem:o,shift:h},{type:"elem",elem:l,shift:a}]});return c.children[0].children[0].children[1].classes.push("svg-align"),Oe(["mrel","x-arrow"],[c],t)},mathmlBuilder(e,t){const r=Ft(e.label);let n;if(r.setAttribute("minsize","x"===e.label.charAt(0)?"1.75em":"3.0em"),e.body){const o=Kt(qt(e.body,t));if(e.below){const s=Kt(qt(e.below,t));n=new yt("munderover",[r,s,o])}else n=new yt("mover",[r,o])}else if(e.below){const o=Kt(qt(e.below,t));n=new yt("munder",[r,o])}else n=Kt(),n=new yt("mover",[r,n]);return n}}),et({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"mclass",mode:r.mode,mclass:"m"+n.slice(5),body:nt(o),isCharacterBox:m(o)}},htmlBuilder:Jt,mathmlBuilder:Qt});const er=e=>{const t="ordgroup"===e.type&&e.body.length?e.body[0]:e;return"atom"!==t.type||"bin"!==t.family&&"rel"!==t.family?"mord":"m"+t.family};et({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(e,t){let{parser:r}=e;return{type:"mclass",mode:r.mode,mclass:er(t[0]),body:nt(t[1]),isCharacterBox:m(t[1])}}}),et({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(e,t){let{parser:r,funcName:n}=e;const o=t[1],s=t[0];let i;i="\\stackrel"!==n?er(o):"mrel";const l={type:"op",mode:o.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:nt(o)},a={type:"supsub",mode:s.mode,base:l,sup:"\\underset"===n?null:s,sub:"\\underset"===n?s:null};return{type:"mclass",mode:r.mode,mclass:i,body:[a],isCharacterBox:m(a)}},htmlBuilder:Jt,mathmlBuilder:Qt}),et({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"pmb",mode:r.mode,mclass:er(t[0]),body:nt(t[0])}},htmlBuilder(e,t){const r=at(e.body,t,!0),n=Oe([e.mclass],r,t);return n.style.textShadow="0.02em 0.01em 0.04px",n},mathmlBuilder(e,t){const r=Tt(e.body,t),n=new yt("mstyle",r);return n.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),n}});const tr={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},rr=()=>({type:"styling",body:[],mode:"math",style:"display"}),nr=e=>"textord"===e.type&&"@"===e.text,or=(e,t)=>("mathord"===e.type||"atom"===e.type)&&e.text===t;function sr(e,t,r){const n=tr[e];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{const e={type:"atom",text:n,mode:"math",family:"rel"},o={type:"ordgroup",mode:"math",body:[r.callFunction("\\\\cdleft",[t[0]],[]),r.callFunction("\\Big",[e],[]),r.callFunction("\\\\cdright",[t[1]],[])]};return r.callFunction("\\\\cdparent",[o],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{const e={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[e],[])}default:return{type:"textord",text:" ",mode:"math"}}}et({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"cdlabel",mode:r.mode,side:n.slice(4),label:t[0]}},htmlBuilder(e,t){const r=t.havingStyle(t.style.sup()),n=Pe(dt(e.label,r,t),t);return n.classes.push("cd-label-"+e.side),n.style.bottom=O(.8-n.depth),n.height=0,n.depth=0,n},mathmlBuilder(e,t){let r=new yt("mrow",[qt(e.label,t)]);return r=new yt("mpadded",[r]),r.setAttribute("width","0"),"left"===e.side&&r.setAttribute("lspace","-1width"),r.setAttribute("voffset","0.7em"),r=new yt("mstyle",[r]),r.setAttribute("displaystyle","false"),r.setAttribute("scriptlevel","1"),r}}),et({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(e,t){let{parser:r}=e;return{type:"cdlabelparent",mode:r.mode,fragment:t[0]}},htmlBuilder(e,t){const r=Pe(dt(e.fragment,t),t);return r.classes.push("cd-vert-arrow"),r},mathmlBuilder(e,t){return new yt("mrow",[qt(e.fragment,t)])}}),et({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;const o=Xt(t[0],"ordgroup").body;let s="";for(let e=0;e=1114111)throw new n("\\@char with invalid code point "+s);return l<=65535?i=String.fromCharCode(l):(l-=65536,i=String.fromCharCode(55296+(l>>10),56320+(1023&l))),{type:"textord",mode:r.mode,text:i}}});const ir=(e,t)=>{const r=at(e.body,t.withColor(e.color),!1);return Le(r)},lr=(e,t)=>{const r=Tt(e.body,t.withColor(e.color)),n=new yt("mstyle",r);return n.setAttribute("mathcolor",e.color),n};et({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(e,t){let{parser:r}=e;const n=Xt(t[0],"color-token").color,o=t[1];return{type:"color",mode:r.mode,color:n,body:nt(o)}},htmlBuilder:ir,mathmlBuilder:lr}),et({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(e,t){let{parser:r,breakOnTokenText:n}=e;const o=Xt(t[0],"color-token").color;r.gullet.macros.set("\\current@color",o);const s=r.parseExpression(!0,n);return{type:"color",mode:r.mode,color:o,body:s}},htmlBuilder:ir,mathmlBuilder:lr}),et({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(e,t,r){let{parser:n}=e;const o="["===n.gullet.future().text?n.parseSizeGroup(!0):null,s=!n.settings.displayMode||!n.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:n.mode,newLine:s,size:o&&Xt(o,"size").value}},htmlBuilder(e,t){const r=Oe(["mspace"],[],t);return e.newLine&&(r.classes.push("newline"),e.size&&(r.style.marginTop=O(E(e.size,t)))),r},mathmlBuilder(e,t){const r=new yt("mspace");return e.newLine&&(r.setAttribute("linebreak","newline"),e.size&&r.setAttribute("height",O(E(e.size,t)))),r}});const ar={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},cr=e=>{const t=e.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(t))throw new n("Expected a control sequence",e);return t},hr=(e,t,r,n)=>{let o=e.gullet.macros.get(r.text);null==o&&(r.noexpand=!0,o={tokens:[r],numArgs:0,unexpandable:!e.gullet.isExpandable(r.text)}),e.gullet.macros.set(t,o,n)};et({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(e){let{parser:t,funcName:r}=e;t.consumeSpaces();const o=t.fetch();if(ar[o.text])return"\\global"!==r&&"\\\\globallong"!==r||(o.text=ar[o.text]),Xt(t.parseFunction(),"internal");throw new n("Invalid token after macro prefix",o)}}),et({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e,o=t.gullet.popToken();const s=o.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(s))throw new n("Expected a control sequence",o);let i,l=0;const a=[[]];for(;"{"!==t.gullet.future().text;)if(o=t.gullet.popToken(),"#"===o.text){if("{"===t.gullet.future().text){i=t.gullet.future(),a[l].push("{");break}if(o=t.gullet.popToken(),!/^[1-9]$/.test(o.text))throw new n('Invalid argument number "'+o.text+'"');if(parseInt(o.text)!==l+1)throw new n('Argument number "'+o.text+'" out of order');l++,a.push([])}else{if("EOF"===o.text)throw new n("Expected a macro definition");a[l].push(o.text)}let{tokens:c}=t.gullet.consumeArg();return i&&c.unshift(i),"\\edef"!==r&&"\\xdef"!==r||(c=t.gullet.expandTokens(c),c.reverse()),t.gullet.macros.set(s,{tokens:c,numArgs:l,delimiters:a},r===ar[r]),{type:"internal",mode:t.mode}}}),et({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=cr(t.gullet.popToken());t.gullet.consumeSpaces();const o=(e=>{let t=e.gullet.popToken();return"="===t.text&&(t=e.gullet.popToken()," "===t.text&&(t=e.gullet.popToken())),t})(t);return hr(t,n,o,"\\\\globallet"===r),{type:"internal",mode:t.mode}}}),et({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=cr(t.gullet.popToken()),o=t.gullet.popToken(),s=t.gullet.popToken();return hr(t,n,s,"\\\\globalfuture"===r),t.gullet.pushToken(s),t.gullet.pushToken(o),{type:"internal",mode:t.mode}}});const mr=function(e,t,r){const n=J(ne.math[e]&&ne.math[e].replace||e,t,r);if(!n)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return n},ur=function(e,t,r,n){const o=r.havingBaseStyle(t),s=Oe(n.concat(o.sizingClasses(r)),[e],r),i=o.sizeMultiplier/r.sizeMultiplier;return s.height*=i,s.depth*=i,s.maxFontSize=o.sizeMultiplier,s},pr=function(e,t,r){const n=t.havingBaseStyle(r),o=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=O(o),e.height-=o,e.depth+=o},dr=function(e,t,r,n,o,s){const i=function(e,t,r,n){return qe(e,"Size"+t+"-Regular",r,n)}(e,t,o,n),l=ur(Oe(["delimsizing","size"+t],[i],n),S.TEXT,n,s);return r&&pr(l,n,S.TEXT),l},gr=function(e,t,r){let n;n="Size1-Regular"===t?"delim-size1":"delim-size4";return{type:"elem",elem:Oe(["delimsizinginner",n],[Oe([],[qe(e,t,r)])])}},fr=function(e,t,r){const n=$["Size4-Regular"][e.charCodeAt(0)]?$["Size4-Regular"][e.charCodeAt(0)][4]:$["Size1-Regular"][e.charCodeAt(0)][4],o=new j("inner",function(e,t){switch(e){case"\u239c":return T("M291 0 H417 V"+t+" H291z");case"\u2223":return T("M145 0 H188 V"+t+" H145z");case"\u2225":return T("M145 0 H188 V"+t+" H145z")+T("M367 0 H410 V"+t+" H367z");case"\u239f":return T("M457 0 H583 V"+t+" H457z");case"\u23a2":return T("M319 0 H403 V"+t+" H319z");case"\u23a5":return T("M263 0 H347 V"+t+" H263z");case"\u23aa":return T("M384 0 H504 V"+t+" H384z");case"\u23d0":return T("M312 0 H355 V"+t+" H312z");case"\u2016":return T("M257 0 H300 V"+t+" H257z")+T("M478 0 H521 V"+t+" H478z");default:return""}}(e,Math.round(1e3*t))),s=new W([o],{width:O(n),height:O(t),style:"width:"+O(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),i=Ne([],[s],r);return i.height=t,i.style.height=O(t),i.style.width=O(n),{type:"elem",elem:i}},br={type:"kern",size:-.008},yr=new Set(["|","\\lvert","\\rvert","\\vert"]),xr=new Set(["\\|","\\lVert","\\rVert","\\Vert"]),wr=function(e,t,r,n,o,s){let i,l,a,c,h="",m=0;i=a=c=e,l=null;let u="Size1-Regular";"\\uparrow"===e?a=c="\u23d0":"\\Uparrow"===e?a=c="\u2016":"\\downarrow"===e?i=a="\u23d0":"\\Downarrow"===e?i=a="\u2016":"\\updownarrow"===e?(i="\\uparrow",a="\u23d0",c="\\downarrow"):"\\Updownarrow"===e?(i="\\Uparrow",a="\u2016",c="\\Downarrow"):yr.has(e)?(a="\u2223",h="vert",m=333):xr.has(e)?(a="\u2225",h="doublevert",m=556):"["===e||"\\lbrack"===e?(i="\u23a1",a="\u23a2",c="\u23a3",u="Size4-Regular",h="lbrack",m=667):"]"===e||"\\rbrack"===e?(i="\u23a4",a="\u23a5",c="\u23a6",u="Size4-Regular",h="rbrack",m=667):"\\lfloor"===e||"\u230a"===e?(a=i="\u23a2",c="\u23a3",u="Size4-Regular",h="lfloor",m=667):"\\lceil"===e||"\u2308"===e?(i="\u23a1",a=c="\u23a2",u="Size4-Regular",h="lceil",m=667):"\\rfloor"===e||"\u230b"===e?(a=i="\u23a5",c="\u23a6",u="Size4-Regular",h="rfloor",m=667):"\\rceil"===e||"\u2309"===e?(i="\u23a4",a=c="\u23a5",u="Size4-Regular",h="rceil",m=667):"("===e||"\\lparen"===e?(i="\u239b",a="\u239c",c="\u239d",u="Size4-Regular",h="lparen",m=875):")"===e||"\\rparen"===e?(i="\u239e",a="\u239f",c="\u23a0",u="Size4-Regular",h="rparen",m=875):"\\{"===e||"\\lbrace"===e?(i="\u23a7",l="\u23a8",c="\u23a9",a="\u23aa",u="Size4-Regular"):"\\}"===e||"\\rbrace"===e?(i="\u23ab",l="\u23ac",c="\u23ad",a="\u23aa",u="Size4-Regular"):"\\lgroup"===e||"\u27ee"===e?(i="\u23a7",c="\u23a9",a="\u23aa",u="Size4-Regular"):"\\rgroup"===e||"\u27ef"===e?(i="\u23ab",c="\u23ad",a="\u23aa",u="Size4-Regular"):"\\lmoustache"===e||"\u23b0"===e?(i="\u23a7",c="\u23ad",a="\u23aa",u="Size4-Regular"):"\\rmoustache"!==e&&"\u23b1"!==e||(i="\u23ab",c="\u23a9",a="\u23aa",u="Size4-Regular");const p=mr(i,u,o),d=p.height+p.depth,g=mr(a,u,o),f=g.height+g.depth,b=mr(c,u,o),y=b.height+b.depth;let x=0,w=1;if(null!==l){const e=mr(l,u,o);x=e.height+e.depth,w=2}const v=d+y+x,k=v+Math.max(0,Math.ceil((t-v)/(w*f)))*w*f;let z=n.fontMetrics().axisHeight;r&&(z*=n.sizeMultiplier);const M=k/2-z,A=[];if(h.length>0){const e=k-d-y,t=Math.round(1e3*k),r=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v1759 h347 v-84\nH403z M403 1759 V0 H319 V1759 v"+t+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v1759 H0 v84 H347z\nM347 1759 V0 H263 V1759 v"+t+" v1759 h84z";case"vert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z";case"doublevert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z\nM367 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M410 15 H367 v585 v"+t+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+t+" v1715 h263 v84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+t+" v1799 H0 v-84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v602 h84z\nM403 1759 V0 H319 V1759 v"+t+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v602 h84z\nM347 1759 V0 h-84 V1759 v"+t+" v602 h84z";case"lparen":return"M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1\nc-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349,\n-36,557 l0,"+(t+84)+"c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210,\n949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9\nc0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5,\n-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189\nl0,-"+(t+92)+"c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3,\n-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z";case"rparen":return"M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3,\n63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5\nc11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,"+(t+9)+"\nc-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664\nc-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11\nc0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17\nc242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558\nl0,-"+(t+144)+"c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7,\n-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z";default:throw new Error("Unknown stretchy delimiter.")}}(h,Math.round(1e3*e)),o=new j(h,r),s=O(m/1e3),i=O(t/1e3),l=new W([o],{width:s,height:i,viewBox:"0 0 "+m+" "+t}),a=Ne([],[l],n);a.height=t/1e3,a.style.width=s,a.style.height=i,A.push({type:"elem",elem:a})}else{if(A.push(gr(c,u,o)),A.push(br),null===l){const e=k-d-y+.016;A.push(fr(a,e,n))}else{const e=(k-d-y-x)/2+.016;A.push(fr(a,e,n)),A.push(br),A.push(gr(l,u,o)),A.push(br),A.push(fr(a,e,n))}A.push(br),A.push(gr(i,u,o))}const T=n.havingBaseStyle(S.TEXT),B=Fe({positionType:"bottom",positionData:M,children:A});return ur(Oe(["delimsizing","mult"],[B],T),S.TEXT,n,s)},vr=.08,kr=function(e,t,r,n,o){const s=function(e,t,r){t*=1e3;let n="";switch(e){case"sqrtMain":n=function(e,t){return"M95,"+(622+e+t)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+e/2.075+" -"+e+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+e)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize1":n=function(e,t){return"M263,"+(601+e+t)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+e/2.084+" -"+e+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+e)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize2":n=function(e,t){return"M983 "+(10+e+t)+"\nl"+e/3.13+" -"+e+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+e)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize3":n=function(e,t){return"M424,"+(2398+e+t)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+e/4.223+" -"+e+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+e)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+e)+" "+t+"\nh400000v"+(40+e)+"h-400000z"}(t,B);break;case"sqrtSize4":n=function(e,t){return"M473,"+(2713+e+t)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+e/5.298+" -"+e+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+e)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"}(t,B);break;case"sqrtTall":n=function(e,t,r){return"M702 "+(e+t)+"H400000"+(40+e)+"\nH742v"+(r-54-t-e)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+t+"H400000v"+(40+e)+"H742z"}(t,B,r)}return n}(e,n,r),i=new j(e,s),l=new W([i],{width:"400em",height:O(t),viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return Ne(["hide-tail"],[l],o)},Sr=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"]),zr=new Set(["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"]),Mr=new Set(["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"]),Ar=[0,1.2,1.8,2.4,3],Tr=function(e,t,r,o,s){if("<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),Sr.has(e)||Mr.has(e))return dr(e,t,!1,r,o,s);if(zr.has(e))return wr(e,Ar[t],!1,r,o,s);throw new n("Illegal delimiter: '"+e+"'")},Br=[{type:"small",style:S.SCRIPTSCRIPT},{type:"small",style:S.SCRIPT},{type:"small",style:S.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],qr=[{type:"small",style:S.SCRIPTSCRIPT},{type:"small",style:S.SCRIPT},{type:"small",style:S.TEXT},{type:"stack"}],Cr=[{type:"small",style:S.SCRIPTSCRIPT},{type:"small",style:S.SCRIPT},{type:"small",style:S.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],Ir=function(e){if("small"===e.type)return"Main-Regular";if("large"===e.type)return"Size"+e.size+"-Regular";if("stack"===e.type)return"Size4-Regular";{const t=e.type;throw new Error("Add support for delim type '"+t+"' here.")}},Rr=function(e,t,r,n){for(let o=Math.min(2,3-n.style.size);ot)return s}return r[r.length-1]},Hr=function(e,t,r,n,o,s){let i;"<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),i=Mr.has(e)?Br:Sr.has(e)?Cr:qr;const l=Rr(e,t,i,n);return"small"===l.type?function(e,t,r,n,o,s){const i=qe(e,"Main-Regular",o,n),l=ur(i,t,n,s);return r&&pr(l,n,t),l}(e,l.style,r,n,o,s):"large"===l.type?dr(e,l.size,r,n,o,s):wr(e,t,r,n,o,s)},Er=function(e,t,r,n,o,s){const i=n.fontMetrics().axisHeight*n.sizeMultiplier,l=5/n.fontMetrics().ptPerEm,a=Math.max(t-i,r+i),c=Math.max(a/500*901,2*a-l);return Hr(e,c,!0,n,o,s)},Or={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},Nr=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."]);function Dr(e,t){const r=Wt(e);if(r&&Nr.has(r.text))return r;throw new n(r?"Invalid delimiter '"+r.text+"' after '"+t.funcName+"'":"Invalid delimiter type '"+e.type+"'",e)}function Lr(e){if(!e.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}et({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(e,t)=>{const r=Dr(t[0],e);return{type:"delimsizing",mode:e.parser.mode,size:Or[e.funcName].size,mclass:Or[e.funcName].mclass,delim:r.text}},htmlBuilder:(e,t)=>"."===e.delim?Oe([e.mclass]):Tr(e.delim,e.size,t,e.mode,[e.mclass]),mathmlBuilder:e=>{const t=[];"."!==e.delim&&t.push(St(e.delim,e.mode));const r=new yt("mo",t);"mopen"===e.mclass||"mclose"===e.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r.setAttribute("stretchy","true");const n=O(Ar[e.size]);return r.setAttribute("minsize",n),r.setAttribute("maxsize",n),r}}),et({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=e.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new n("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:e.parser.mode,delim:Dr(t[0],e).text,color:r}}}),et({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=Dr(t[0],e),n=e.parser;++n.leftrightDepth;const o=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);const s=Xt(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:o,left:r.text,right:s.delim,rightColor:s.color}},htmlBuilder:(e,t)=>{Lr(e);const r=at(e.body,t,!0,["mopen","mclose"]);let n,o,s=0,i=0,l=!1;for(let e=0;e{Lr(e);const r=Tt(e.body,t);if("."!==e.left){const t=new yt("mo",[St(e.left,e.mode)]);t.setAttribute("fence","true"),r.unshift(t)}if("."!==e.right){const t=new yt("mo",[St(e.right,e.mode)]);t.setAttribute("fence","true"),e.rightColor&&t.setAttribute("mathcolor",e.rightColor),r.push(t)}return zt(r)}}),et({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=Dr(t[0],e);if(!e.parser.leftrightDepth)throw new n("\\middle without preceding \\left",r);return{type:"middle",mode:e.parser.mode,delim:r.text}},htmlBuilder:(e,t)=>{let r;if("."===e.delim)r=pt(t,[]);else{r=Tr(e.delim,1,t,e.mode,[]);const n={delim:e.delim,options:t};r.isMiddle=n}return r},mathmlBuilder:(e,t)=>{const r="\\vert"===e.delim||"|"===e.delim?St("|","text"):St(e.delim,e.mode),n=new yt("mo",[r]);return n.setAttribute("fence","true"),n.setAttribute("lspace","0.05em"),n.setAttribute("rspace","0.05em"),n}});const Pr=(e,t)=>{const r=Pe(dt(e.body,t),t),n=e.label.slice(1);let o,s=t.sizeMultiplier,i=0;const l=m(e.body);if("sout"===n)o=Oe(["stretchy","sout"]),o.height=t.fontMetrics().defaultRuleThickness/s,i=-.5*t.fontMetrics().xHeight;else if("phase"===n){const e=E({number:.6,unit:"pt"},t),n=E({number:.35,unit:"ex"},t);s/=t.havingBaseSizing().sizeMultiplier;const l=r.height+r.depth+e+n;r.style.paddingLeft=O(l/2+e);const c=Math.floor(1e3*l*s),h="M400000 "+(a=c)+" H0 L"+a/2+" 0 l65 45 L145 "+(a-80)+" H400000z",m=new W([new j("phase",h)],{width:"400em",height:O(c/1e3),viewBox:"0 0 400000 "+c,preserveAspectRatio:"xMinYMin slice"});o=Ne(["hide-tail"],[m],t),o.style.height=O(l),i=r.depth+e+n}else{/cancel/.test(n)?l||r.classes.push("cancel-pad"):"angl"===n?r.classes.push("anglpad"):r.classes.push("boxpad");let s=0,a=0,c=0;/box/.test(n)?(c=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness),s=t.fontMetrics().fboxsep+("colorbox"===n?0:c),a=s):"angl"===n?(c=Math.max(t.fontMetrics().defaultRuleThickness,t.minRuleThickness),s=4*c,a=Math.max(0,.25-r.depth)):(s=l?.2:0,a=s),o=function(e,t,r,n,o){let s;const i=e.height+e.depth+r+n;if(/fbox|color|angl/.test(t)){if(s=Oe(["stretchy",t],[],o),"fbox"===t){const e=o.color&&o.getColor();e&&(s.style.borderColor=e)}}else{const e=[];/^[bx]cancel$/.test(t)&&e.push(new _({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&e.push(new _({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));const r=new W(e,{width:"100%",height:O(i)});s=Ne([],[r],o)}return s.height=i,s.style.height=O(i),s}(r,n,s,a,t),/fbox|boxed|fcolorbox/.test(n)?(o.style.borderStyle="solid",o.style.borderWidth=O(c)):"angl"===n&&.049!==c&&(o.style.borderTopWidth=O(c),o.style.borderRightWidth=O(c)),i=r.depth+a,e.backgroundColor&&(o.style.backgroundColor=e.backgroundColor,e.borderColor&&(o.style.borderColor=e.borderColor))}var a;let c;if(e.backgroundColor)c=Fe({positionType:"individualShift",children:[{type:"elem",elem:o,shift:i},{type:"elem",elem:r,shift:0}]});else{const e=/cancel|phase/.test(n)?["svg-align"]:[];c=Fe({positionType:"individualShift",children:[{type:"elem",elem:r,shift:0},{type:"elem",elem:o,shift:i,wrapperClasses:e}]})}return/cancel/.test(n)&&(c.height=r.height,c.depth=r.depth),/cancel/.test(n)&&!l?Oe(["mord","cancel-lap"],[c],t):Oe(["mord"],[c],t)},Fr=(e,t)=>{let r=0;const n=new yt(e.label.includes("colorbox")?"mpadded":"menclose",[qt(e.body,t)]);switch(e.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\phase":n.setAttribute("notation","phasorangle");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\angl":n.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(r=t.fontMetrics().fboxsep*t.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),"\\fcolorbox"===e.label){const r=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness);n.setAttribute("style","border: "+O(r)+" solid "+e.borderColor)}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return e.backgroundColor&&n.setAttribute("mathbackground",e.backgroundColor),n};et({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=Xt(t[0],"color-token").color,i=t[1];return{type:"enclose",mode:n.mode,label:o,backgroundColor:s,body:i}},htmlBuilder:Pr,mathmlBuilder:Fr}),et({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=Xt(t[0],"color-token").color,i=Xt(t[1],"color-token").color,l=t[2];return{type:"enclose",mode:n.mode,label:o,backgroundColor:i,borderColor:s,body:l}},htmlBuilder:Pr,mathmlBuilder:Fr}),et({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\fbox",body:t[0]}}}),et({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\phase"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}},htmlBuilder:Pr,mathmlBuilder:Fr}),et({type:"enclose",names:["\\sout"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;"math"===r.mode&&r.settings.reportNonstrict("mathVsSout","LaTeX's \\sout works only in text mode");const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}},htmlBuilder:Pr,mathmlBuilder:Fr}),et({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\angl",body:t[0]}}});const Vr={};function Gr(e){let{type:t,names:r,props:n,handler:o,htmlBuilder:s,mathmlBuilder:i}=e;const l={type:t,numArgs:n.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:o};for(let e=0;e{if(!e.parser.settings.displayMode)throw new n("{"+e.envName+"} can be used only in display mode.")},$r=new Set(["gather","gather*"]);function Zr(e){if(!e.includes("ed"))return!e.includes("*")}function Kr(e,t,r){let{hskipBeforeAndAfter:o,addJot:s,cols:i,arraystretch:l,colSeparationType:a,autoTag:c,singleRow:h,emptySingleRow:m,maxNumCols:u,leqno:p}=t;if(e.gullet.beginGroup(),h||e.gullet.macros.set("\\cr","\\\\\\relax"),!l){const t=e.gullet.expandMacroAsText("\\arraystretch");if(null==t)l=1;else if(l=parseFloat(t),!l||l<0)throw new n("Invalid \\arraystretch: "+t)}e.gullet.beginGroup();let d=[];const g=[d],f=[],b=[],y=null!=c?[]:void 0;function x(){c&&e.gullet.macros.set("\\@eqnsw","1",!0)}function w(){y&&(e.gullet.macros.get("\\df@tag")?(y.push(e.subparse([new Wr("\\df@tag")])),e.gullet.macros.set("\\df@tag",void 0,!0)):y.push(Boolean(c)&&"1"===e.gullet.macros.get("\\@eqnsw")))}for(x(),b.push(jr(e));;){const t=e.parseExpression(!1,h?"\\end":"\\\\");e.gullet.endGroup(),e.gullet.beginGroup();let o={type:"ordgroup",mode:e.mode,body:t};r&&(o={type:"styling",mode:e.mode,style:r,body:[o]}),d.push(o);const s=e.fetch().text;if("&"===s){if(u&&d.length===u){if(h||a)throw new n("Too many tab characters: &",e.nextToken);e.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}e.consume()}else{if("\\end"===s){w(),1===d.length&&"styling"===o.type&&1===o.body.length&&"ordgroup"===o.body[0].type&&0===o.body[0].body.length&&(g.length>1||!m)&&g.pop(),b.length0&&(y+=.25),c.push({pos:y,isDashed:e[t]})}for(x(i[0]),r=0;r0&&(u+=b,ce))for(r=0;r=l)continue;var q,C;if(o>0||e.hskipBeforeAndAfter)i=null!=(q=null==(C=c)?void 0:C.pregap)?q:u,0!==i&&(z=Oe(["arraycolsep"],[]),z.style.width=O(i),k.push(z));const p=[];for(r=0;r0){const e=De("hline",t,h),r=De("hdashline",t,h),n=[{type:"elem",elem:H,shift:0}];for(;c.length>0;){const t=c.pop(),o=t.pos-w;t.isDashed?n.push({type:"elem",elem:r,shift:o}):n.push({type:"elem",elem:e,shift:o})}H=Fe({positionType:"individualShift",children:n})}if(0===A.length)return Oe(["mord"],[H],t);{const e=Fe({positionType:"individualShift",children:A}),r=Oe(["tag"],[e],t);return Le([H,r])}},en={c:"center ",l:"left ",r:"right "},tn=function(e,t){const r=[],n=new yt("mtd",[],["mtr-glue"]),o=new yt("mtd",[],["mml-eqn-num"]);for(let s=0;s0){const t=e.cols;let r="",n=!1,o=0,i=t.length;"separator"===t[0].type&&(l+="top ",o=1),"separator"===t[t.length-1].type&&(l+="bottom ",i-=1);for(let e=o;e0?"left ":"",l+=h[h.length-1].length>0?"right ":"";for(let e=1;e0&&h&&(n=1),r[e]={type:"align",align:t,pregap:n,postgap:0}}return i.colSeparationType=h?"align":"alignat",i};Gr({type:"array",names:["array","darray"],props:{numArgs:1},handler(e,t){const r=(Wt(t[0])?[t[0]]:Xt(t[0],"ordgroup").body).map(function(e){const t=Yt(e).text;if("lcr".includes(t))return{type:"align",align:t};if("|"===t)return{type:"separator",separator:"|"};if(":"===t)return{type:"separator",separator:":"};throw new n("Unknown column alignment: "+t,e)}),o={cols:r,hskipBeforeAndAfter:!0,maxNumCols:r.length};return Kr(e.parser,o,Jr(e.envName))},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(e){const t={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[e.envName.replace("*","")];let r="c";const o={hskipBeforeAndAfter:!1,cols:[{type:"align",align:r}]};if("*"===e.envName.charAt(e.envName.length-1)){const t=e.parser;if(t.consumeSpaces(),"["===t.fetch().text){if(t.consume(),t.consumeSpaces(),r=t.fetch().text,!"lcr".includes(r))throw new n("Expected l or c or r",t.nextToken);t.consume(),t.consumeSpaces(),t.expect("]"),t.consume(),o.cols=[{type:"align",align:r}]}}const s=Kr(e.parser,o,Jr(e.envName)),i=Math.max(0,...s.body.map(e=>e.length));return s.cols=new Array(i).fill({type:"align",align:r}),t?{type:"leftright",mode:e.mode,body:[s],left:t[0],right:t[1],rightColor:void 0}:s},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(e){const t=Kr(e.parser,{arraystretch:.5},"script");return t.colSeparationType="small",t},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["subarray"],props:{numArgs:1},handler(e,t){const r=(Wt(t[0])?[t[0]]:Xt(t[0],"ordgroup").body).map(function(e){const t=Yt(e).text;if("lc".includes(t))return{type:"align",align:t};throw new n("Unknown column alignment: "+t,e)});if(r.length>1)throw new n("{subarray} can contain only one column");const o={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5},s=Kr(e.parser,o,"script");if(s.body.length>0&&s.body[0].length>1)throw new n("{subarray} can contain only one column");return s},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=Kr(e.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},Jr(e.envName));return{type:"leftright",mode:e.mode,body:[t],left:e.envName.includes("r")?".":"\\{",right:e.envName.includes("r")?"\\}":".",rightColor:void 0}},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:rn,htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){$r.has(e.envName)&&_r(e);const t={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:Zr(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return Kr(e.parser,t,"display")},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:rn,htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){_r(e);const t={autoTag:Zr(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:e.parser.settings.leqno};return Kr(e.parser,t,"display")},htmlBuilder:Qr,mathmlBuilder:tn}),Gr({type:"array",names:["CD"],props:{numArgs:0},handler(e){return _r(e),function(e){const t=[];for(e.gullet.beginGroup(),e.gullet.macros.set("\\cr","\\\\\\relax"),e.gullet.beginGroup();;){t.push(e.parseExpression(!1,"\\\\")),e.gullet.endGroup(),e.gullet.beginGroup();const r=e.fetch().text;if("&"!==r&&"\\\\"!==r){if("\\end"===r){0===t[t.length-1].length&&t.pop();break}throw new n("Expected \\\\ or \\cr or \\end",e.nextToken)}e.consume()}let r=[];const o=[r];for(let s=0;sAV".includes(o))throw new n('Expected one of "<>AV=|." after @',i[t]);for(let e=0;e<2;e++){let r=!0;for(let l=t+1;l{const r=e.font,n=t.withFont(r);return dt(e.body,n)},sn=(e,t)=>{const r=e.font,n=t.withFont(r);return qt(e.body,n)},ln={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};et({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathsfit","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=rt(t[0]);let s=n;return s in ln&&(s=ln[s]),{type:"font",mode:r.mode,font:s.slice(1),body:o}},htmlBuilder:on,mathmlBuilder:sn}),et({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"mclass",mode:r.mode,mclass:er(n),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:n}],isCharacterBox:m(n)}}}),et({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n,breakOnTokenText:o}=e;const{mode:s}=r,i=r.parseExpression(!0,o);return{type:"font",mode:s,font:"math"+n.slice(1),body:{type:"ordgroup",mode:r.mode,body:i}}},htmlBuilder:on,mathmlBuilder:sn});const an=(e,t)=>{if(!t)return e;return{type:"styling",mode:e.mode,style:t,body:[e]}};et({type:"genfrac",names:["\\cfrac","\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];let i,l=null,a=null;switch(n){case"\\cfrac":case"\\dfrac":case"\\frac":case"\\tfrac":i=!0;break;case"\\\\atopfrac":i=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":i=!1,l="(",a=")";break;case"\\\\bracefrac":i=!1,l="\\{",a="\\}";break;case"\\\\brackfrac":i=!1,l="[",a="]";break;default:throw new Error("Unrecognized genfrac command")}const c="\\cfrac"===n;let h=null;return c||n.startsWith("\\d")?h="display":n.startsWith("\\t")&&(h="text"),an({type:"genfrac",mode:r.mode,numer:o,denom:s,continued:c,hasBarLine:i,leftDelim:l,rightDelim:a,barSize:null},h)},htmlBuilder:(e,t)=>{const r=t.style,n=r.fracNum(),o=r.fracDen();let s;s=t.havingStyle(n);const i=dt(e.numer,s,t);if(e.continued){const e=8.5/t.fontMetrics().ptPerEm,r=3.5/t.fontMetrics().ptPerEm;i.height=i.height0?3*h:7*h,p=t.fontMetrics().denom1):(c>0?(m=t.fontMetrics().num2,u=h):(m=t.fontMetrics().num3,u=3*h),p=t.fontMetrics().denom2),a){const e=t.fontMetrics().axisHeight;m-i.depth-(e+.5*c){const r=new yt("mfrac",[qt(e.numer,t),qt(e.denom,t)]);if(e.hasBarLine){if(e.barSize){const n=E(e.barSize,t);r.setAttribute("linethickness",O(n))}}else r.setAttribute("linethickness","0px");if(null!=e.leftDelim||null!=e.rightDelim){const t=[];if(null!=e.leftDelim){const r=new yt("mo",[new xt(e.leftDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}if(t.push(r),null!=e.rightDelim){const r=new yt("mo",[new xt(e.rightDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}return zt(t)}return r}}),et({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(e){let t,{parser:r,funcName:n,token:o}=e;switch(n){case"\\over":t="\\frac";break;case"\\choose":t="\\binom";break;case"\\atop":t="\\\\atopfrac";break;case"\\brace":t="\\\\bracefrac";break;case"\\brack":t="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:t,token:o}}});const cn=["display","text","script","scriptscript"],hn=function(e){let t=null;return e.length>0&&(t=e,t="."===t?null:t),t};et({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(e,t){let{parser:r}=e;const n=t[4],o=t[5],s=rt(t[0]),i="atom"===s.type&&"open"===s.family?hn(s.text):null,l=rt(t[1]),a="atom"===l.type&&"close"===l.family?hn(l.text):null,c=Xt(t[2],"size");let h,m=null;c.isBlank?h=!0:(m=c.value,h=m.number>0);let u=null,p=t[3];if("ordgroup"===p.type){if(p.body.length>0){const e=Xt(p.body[0],"textord");u=cn[Number(e.text)]}}else p=Xt(p,"textord"),u=cn[Number(p.text)];return an({type:"genfrac",mode:r.mode,numer:n,denom:o,continued:!1,hasBarLine:h,barSize:m,leftDelim:i,rightDelim:a},u)}}),et({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(e,t){let{parser:r,funcName:n,token:o}=e;return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:Xt(t[0],"size").value,token:o}}}),et({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=Xt(t[1],"infix").size;if(!s)throw new Error("\\\\abovefrac expected size, but got "+String(s));const i=t[2],l=s.number>0;return{type:"genfrac",mode:r.mode,numer:o,denom:i,continued:!1,hasBarLine:l,barSize:s,leftDelim:null,rightDelim:null}}});const mn=(e,t)=>{const r=t.style;let n,o;"supsub"===e.type?(n=e.sup?dt(e.sup,t.havingStyle(r.sup()),t):dt(e.sub,t.havingStyle(r.sub()),t),o=Xt(e.base,"horizBrace")):o=Xt(e,"horizBrace");const s=dt(o.base,t.havingBaseStyle(S.DISPLAY)),i=Ut(o,t);let l;if(o.isOver?(l=Fe({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:i}]}),l.children[0].children[0].children[1].classes.push("svg-align")):(l=Fe({positionType:"bottom",positionData:s.depth+.1+i.height,children:[{type:"elem",elem:i},{type:"kern",size:.1},{type:"elem",elem:s}]}),l.children[0].children[0].children[0].classes.push("svg-align")),n){const e=Oe(["minner",o.isOver?"mover":"munder"],[l],t);l=o.isOver?Fe({positionType:"firstBaseline",children:[{type:"elem",elem:e},{type:"kern",size:.2},{type:"elem",elem:n}]}):Fe({positionType:"bottom",positionData:e.depth+.2+n.height+n.depth,children:[{type:"elem",elem:n},{type:"kern",size:.2},{type:"elem",elem:e}]})}return Oe(["minner",o.isOver?"mover":"munder"],[l],t)};et({type:"horizBrace",names:["\\overbrace","\\underbrace","\\overbracket","\\underbracket"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"horizBrace",mode:r.mode,label:n,isOver:n.includes("\\over"),base:t[0]}},htmlBuilder:mn,mathmlBuilder:(e,t)=>{const r=Ft(e.label);return new yt(e.isOver?"mover":"munder",[qt(e.base,t),r])}}),et({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[1],o=Xt(t[0],"url").url;return r.settings.isTrusted({command:"\\href",url:o})?{type:"href",mode:r.mode,href:o,body:nt(n)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:(e,t)=>{const r=at(e.body,t,!1);return function(e,t,r,n){const o=new G(e,t,r,n);return Ee(o),o}(e.href,[],r,t)},mathmlBuilder:(e,t)=>{let r=Bt(e.body,t);return r instanceof yt||(r=new yt("mrow",[r])),r.setAttribute("href",e.href),r}}),et({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=Xt(t[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");const o=[];for(let e=0;e{let{parser:r,funcName:o,token:s}=e;const i=Xt(t[0],"raw").string,l=t[1];let a;r.settings.strict&&r.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");const c={};switch(o){case"\\htmlClass":c.class=i,a={command:"\\htmlClass",class:i};break;case"\\htmlId":c.id=i,a={command:"\\htmlId",id:i};break;case"\\htmlStyle":c.style=i,a={command:"\\htmlStyle",style:i};break;case"\\htmlData":{const e=i.split(",");for(let t=0;t{const r=at(e.body,t,!1),n=["enclosing"];e.attributes.class&&n.push(...e.attributes.class.trim().split(/\s+/));const o=Oe(n,r,t);for(const t in e.attributes)"class"!==t&&e.attributes.hasOwnProperty(t)&&o.setAttribute(t,e.attributes[t]);return o},mathmlBuilder:(e,t)=>Bt(e.body,t)}),et({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInArgument:!0,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"htmlmathml",mode:r.mode,html:nt(t[0]),mathml:nt(t[1])}},htmlBuilder:(e,t)=>{const r=at(e.html,t,!1);return Le(r)},mathmlBuilder:(e,t)=>Bt(e.mathml,t)});const un=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};{const t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new n("Invalid size: '"+e+"' in \\includegraphics");const r={number:+(t[1]+t[2]),unit:t[3]};if(!H(r))throw new n("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r}};et({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:(e,t,r)=>{let{parser:o}=e,s={number:0,unit:"em"},i={number:.9,unit:"em"},l={number:0,unit:"em"},a="";if(r[0]){const e=Xt(r[0],"raw").string.split(",");for(let t=0;t{const r=E(e.height,t);let n=0;e.totalheight.number>0&&(n=E(e.totalheight,t)-r);let o=0;e.width.number>0&&(o=E(e.width,t));const s={height:O(r+n)};o>0&&(s.width=O(o)),n>0&&(s.verticalAlign=O(-n));const i=new U(e.src,e.alt,s);return i.height=r,i.depth=n,i},mathmlBuilder:(e,t)=>{const r=new yt("mglyph",[]);r.setAttribute("alt",e.alt);const n=E(e.height,t);let o=0;if(e.totalheight.number>0&&(o=E(e.totalheight,t)-n,r.setAttribute("valign",O(-o))),r.setAttribute("height",O(n+o)),e.width.number>0){const n=E(e.width,t);r.setAttribute("width",O(n))}return r.setAttribute("src",e.src),r}}),et({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=Xt(t[0],"size");if(r.settings.strict){const e="m"===n[1],t="mu"===o.value.unit;e?(t||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, not "+o.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):t&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:o.value}},htmlBuilder(e,t){return Ve(e.dimension,t)},mathmlBuilder(e,t){const r=E(e.dimension,t);return new wt(r)}}),et({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:o}},htmlBuilder:(e,t)=>{let r;"clap"===e.alignment?(r=Oe([],[dt(e.body,t)]),r=Oe(["inner"],[r],t)):r=Oe(["inner"],[dt(e.body,t)]);const n=Oe(["fix"],[]);let o=Oe([e.alignment],[r,n],t);const s=Oe(["strut"]);return s.style.height=O(o.height+o.depth),o.depth&&(s.style.verticalAlign=O(-o.depth)),o.children.unshift(s),o=Oe(["thinbox"],[o],t),Oe(["mord","vbox"],[o],t)},mathmlBuilder:(e,t)=>{const r=new yt("mpadded",[qt(e.body,t)]);if("rlap"!==e.alignment){const t="llap"===e.alignment?"-1":"-0.5";r.setAttribute("lspace",t+"width")}return r.setAttribute("width","0px"),r}}),et({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){let{funcName:r,parser:n}=e;const o=n.mode;n.switchMode("math");const s="\\("===r?"\\)":"$",i=n.parseExpression(!1,s);return n.expect(s),n.switchMode(o),{type:"styling",mode:n.mode,style:"text",body:i}}}),et({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){throw new n("Mismatched "+e.funcName)}});const pn=(e,t)=>{switch(t.style.size){case S.DISPLAY.size:return e.display;case S.TEXT.size:return e.text;case S.SCRIPT.size:return e.script;case S.SCRIPTSCRIPT.size:return e.scriptscript;default:return e.text}};et({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"mathchoice",mode:r.mode,display:nt(t[0]),text:nt(t[1]),script:nt(t[2]),scriptscript:nt(t[3])}},htmlBuilder:(e,t)=>{const r=pn(e,t),n=at(r,t,!1);return Le(n)},mathmlBuilder:(e,t)=>{const r=pn(e,t);return Bt(r,t)}});const dn=(e,t,r,n,o,s,i)=>{e=Oe([],[e]);const l=r&&m(r);let a,c,h;if(t){const e=dt(t,n.havingStyle(o.sup()),n);c={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-e.depth)}}if(r){const e=dt(r,n.havingStyle(o.sub()),n);a={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-e.height)}}if(c&&a){const t=n.fontMetrics().bigOpSpacing5+a.elem.height+a.elem.depth+a.kern+e.depth+i;h=Fe({positionType:"bottom",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:a.elem,marginLeft:O(-s)},{type:"kern",size:a.kern},{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:O(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]})}else if(a){const t=e.height-i;h=Fe({positionType:"top",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:a.elem,marginLeft:O(-s)},{type:"kern",size:a.kern},{type:"elem",elem:e}]})}else{if(!c)return e;{const t=e.depth+i;h=Fe({positionType:"bottom",positionData:t,children:[{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:O(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]})}}const u=[h];if(a&&0!==s&&!l){const e=Oe(["mspace"],[],n);e.style.marginRight=O(s),u.unshift(e)}return Oe(["mop","op-limits"],u,n)},gn=new Set(["\\smallint"]),fn=(e,t)=>{let r,n,o,s=!1;"supsub"===e.type?(r=e.sup,n=e.sub,o=Xt(e.base,"op"),s=!0):o=Xt(e,"op");const i=t.style;let l,a=!1;if(i.size===S.DISPLAY.size&&o.symbol&&!gn.has(o.name)&&(a=!0),o.symbol){const e=a?"Size2-Regular":"Size1-Regular";let r="";if("\\oiint"!==o.name&&"\\oiiint"!==o.name||(r=o.name.slice(1),o.name="oiint"===r?"\\iint":"\\iiint"),l=qe(o.name,e,"math",t,["mop","op-symbol",a?"large-op":"small-op"]),r.length>0){const e=l.italic,n=Ye(r+"Size"+(a?"2":"1"),t);l=Fe({positionType:"individualShift",children:[{type:"elem",elem:l,shift:0},{type:"elem",elem:n,shift:a?.08:0}]}),o.name="\\"+r,l.classes.unshift("mop"),l.italic=e}}else if(o.body){const e=at(o.body,t,!0);1===e.length&&e[0]instanceof Y?(l=e[0],l.classes[0]="mop"):l=Oe(["mop"],e,t)}else{const e=[];for(let r=1;r{let r;if(e.symbol)r=new yt("mo",[St(e.name,e.mode)]),gn.has(e.name)&&r.setAttribute("largeop","false");else if(e.body)r=new yt("mo",Tt(e.body,t));else{r=new yt("mi",[new xt(e.name.slice(1))]);const t=new yt("mo",[St("\u2061","text")]);r=e.parentIsSupSub?new yt("mrow",[r,t]):bt([r,t])}return r},yn={"\u220f":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22c0":"\\bigwedge","\u22c1":"\\bigvee","\u22c2":"\\bigcap","\u22c3":"\\bigcup","\u2a00":"\\bigodot","\u2a01":"\\bigoplus","\u2a02":"\\bigotimes","\u2a04":"\\biguplus","\u2a06":"\\bigsqcup"};et({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220f","\u2210","\u2211","\u22c0","\u22c1","\u22c2","\u22c3","\u2a00","\u2a01","\u2a02","\u2a04","\u2a06"],props:{numArgs:0},handler:(e,t)=>{let{parser:r,funcName:n}=e,o=n;return 1===o.length&&(o=yn[o]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:o}},htmlBuilder:fn,mathmlBuilder:bn}),et({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:nt(n)}},htmlBuilder:fn,mathmlBuilder:bn});const xn={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};et({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:fn,mathmlBuilder:bn}),et({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:fn,mathmlBuilder:bn}),et({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],props:{numArgs:0,allowedInArgument:!0},handler(e){let{parser:t,funcName:r}=e,n=r;return 1===n.length&&(n=xn[n]),{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:fn,mathmlBuilder:bn});const wn=(e,t)=>{let r,n,o,s,i=!1;if("supsub"===e.type?(r=e.sup,n=e.sub,o=Xt(e.base,"operatorname"),i=!0):o=Xt(e,"operatorname"),o.body.length>0){const e=o.body.map(e=>{const t="text"in e?e.text:void 0;return"string"==typeof t?{type:"textord",mode:e.mode,text:t}:e}),r=at(e,t.withFont("mathrm"),!0);for(let e=0;e{let{parser:r,funcName:n}=e;const o=t[0];return{type:"operatorname",mode:r.mode,body:nt(o),alwaysHandleSupSub:"\\operatornamewithlimits"===n,limits:!1,parentIsSupSub:!1}},htmlBuilder:wn,mathmlBuilder:(e,t)=>{let r=Tt(e.body,t.withFont("mathrm")),n=!0;for(let e=0;ee.toText()).join("");r=[new xt(e)]}const o=new yt("mi",r);o.setAttribute("mathvariant","normal");const s=new yt("mo",[St("\u2061","text")]);return e.parentIsSupSub?new yt("mrow",[o,s]):bt([o,s])}}),Xr("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),tt({type:"ordgroup",htmlBuilder(e,t){return e.semisimple?Le(at(e.body,t,!1)):Oe(["mord"],at(e.body,t,!0),t)},mathmlBuilder(e,t){return Bt(e.body,t,!0)}}),et({type:"overline",names:["\\overline"],props:{numArgs:1},handler(e,t){let{parser:r}=e;const n=t[0];return{type:"overline",mode:r.mode,body:n}},htmlBuilder(e,t){const r=dt(e.body,t.havingCrampedStyle()),n=De("overline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Fe({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*o},{type:"elem",elem:n},{type:"kern",size:o}]});return Oe(["mord","overline"],[s],t)},mathmlBuilder(e,t){const r=new yt("mo",[new xt("\u203e")]);r.setAttribute("stretchy","true");const n=new yt("mover",[qt(e.body,t),r]);return n.setAttribute("accent","true"),n}}),et({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"phantom",mode:r.mode,body:nt(n)}},htmlBuilder:(e,t)=>{const r=at(e.body,t.withPhantom(),!1);return Le(r)},mathmlBuilder:(e,t)=>{const r=Tt(e.body,t);return new yt("mphantom",r)}}),Xr("\\hphantom","\\smash{\\phantom{#1}}"),et({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"vphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{const r=Oe(["inner"],[dt(e.body,t.withPhantom())]),n=Oe(["fix"],[]);return Oe(["mord","rlap"],[r,n],t)},mathmlBuilder:(e,t)=>{const r=Tt(nt(e.body),t),n=new yt("mphantom",r),o=new yt("mpadded",[n]);return o.setAttribute("width","0px"),o}}),et({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;const n=Xt(t[0],"size").value,o=t[1];return{type:"raisebox",mode:r.mode,dy:n,body:o}},htmlBuilder(e,t){const r=dt(e.body,t),n=E(e.dy,t);return Fe({positionType:"shift",positionData:-n,children:[{type:"elem",elem:r}]})},mathmlBuilder(e,t){const r=new yt("mpadded",[qt(e.body,t)]),n=e.dy.number+e.dy.unit;return r.setAttribute("voffset",n),r}}),et({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0,allowedInArgument:!0},handler(e){let{parser:t}=e;return{type:"internal",mode:t.mode}}}),et({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["size","size","size"]},handler(e,t,r){let{parser:n}=e;const o=r[0],s=Xt(t[0],"size"),i=Xt(t[1],"size");return{type:"rule",mode:n.mode,shift:o&&Xt(o,"size").value,width:s.value,height:i.value}},htmlBuilder(e,t){const r=Oe(["mord","rule"],[],t),n=E(e.width,t),o=E(e.height,t),s=e.shift?E(e.shift,t):0;return r.style.borderRightWidth=O(n),r.style.borderTopWidth=O(o),r.style.bottom=O(s),r.width=n,r.height=o+s,r.depth=-s,r.maxFontSize=1.125*o*t.sizeMultiplier,r},mathmlBuilder(e,t){const r=E(e.width,t),n=E(e.height,t),o=e.shift?E(e.shift,t):0,s=t.color&&t.getColor()||"black",i=new yt("mspace");i.setAttribute("mathbackground",s),i.setAttribute("width",O(r)),i.setAttribute("height",O(n));const l=new yt("mpadded",[i]);return o>=0?l.setAttribute("height",O(o)):(l.setAttribute("height",O(o)),l.setAttribute("depth",O(-o))),l.setAttribute("voffset",O(o)),l}});const kn=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];et({type:"sizing",names:kn,props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!1,r);return{type:"sizing",mode:o.mode,size:kn.indexOf(n)+1,body:s}},htmlBuilder:(e,t)=>{const r=t.havingSize(e.size);return vn(e.body,r,t)},mathmlBuilder:(e,t)=>{const r=t.havingSize(e.size),n=Tt(e.body,r),o=new yt("mstyle",n);return o.setAttribute("mathsize",O(r.sizeMultiplier)),o}}),et({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:(e,t,r)=>{let{parser:n}=e,o=!1,s=!1;const i=r[0]&&Xt(r[0],"ordgroup");if(i){let e="";for(let t=0;t{const r=Oe([],[dt(e.body,t)]);if(!e.smashHeight&&!e.smashDepth)return r;if(e.smashHeight&&(r.height=0),e.smashDepth&&(r.depth=0),e.smashHeight&&e.smashDepth)return Oe(["mord","smash"],[r],t);if(r.children)for(let t=0;t{const r=new yt("mpadded",[qt(e.body,t)]);return e.smashHeight&&r.setAttribute("height","0px"),e.smashDepth&&r.setAttribute("depth","0px"),r}}),et({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n}=e;const o=r[0],s=t[0];return{type:"sqrt",mode:n.mode,body:s,index:o}},htmlBuilder(e,t){let r=dt(e.body,t.havingCrampedStyle());0===r.height&&(r.height=t.fontMetrics().xHeight),r=Pe(r,t);const n=t.fontMetrics().defaultRuleThickness;let o=n;t.style.idr.height+r.depth+s&&(s=(s+h-r.height-r.depth)/2);const m=l.height-r.height-s-a;r.style.paddingLeft=O(c);const u=Fe({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+m)},{type:"elem",elem:l},{type:"kern",size:a}]});if(e.index){const r=t.havingStyle(S.SCRIPTSCRIPT),n=dt(e.index,r,t),o=.6*(u.height-u.depth),s=Fe({positionType:"shift",positionData:-o,children:[{type:"elem",elem:n}]}),i=Oe(["root"],[s]);return Oe(["mord","sqrt"],[i,u],t)}return Oe(["mord","sqrt"],[u],t)},mathmlBuilder(e,t){const{body:r,index:n}=e;return n?new yt("mroot",[qt(r,t),qt(n,t)]):new yt("msqrt",[qt(r,t)])}});const Sn={display:S.DISPLAY,text:S.TEXT,script:S.SCRIPT,scriptscript:S.SCRIPTSCRIPT};et({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e,t){let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!0,r),i=n.slice(1,n.length-5);return{type:"styling",mode:o.mode,style:i,body:s}},htmlBuilder(e,t){const r=Sn[e.style],n=t.havingStyle(r).withFont("");return vn(e.body,n,t)},mathmlBuilder(e,t){const r=Sn[e.style],n=t.havingStyle(r),o=Tt(e.body,n),s=new yt("mstyle",o),i={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[e.style];return s.setAttribute("scriptlevel",i[0]),s.setAttribute("displaystyle",i[1]),s}});tt({type:"supsub",htmlBuilder(e,t){const r=function(e,t){const r=e.base;if(r)return"op"===r.type?r.limits&&(t.style.size===S.DISPLAY.size||r.alwaysHandleSupSub)?fn:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(t.style.size===S.DISPLAY.size||r.limits)?wn:null:"accent"===r.type?m(r.base)?_t:null:"horizBrace"===r.type&&!e.sub===r.isOver?mn:null;return null}(e,t);if(r)return r(e,t);const{base:n,sup:o,sub:s}=e,i=dt(n,t);let l,a;const c=t.fontMetrics();let h=0,u=0;const p=n&&m(n);if(o){const e=t.havingStyle(t.style.sup());l=dt(o,e,t),p||(h=i.height-e.fontMetrics().supDrop*e.sizeMultiplier/t.sizeMultiplier)}if(s){const e=t.havingStyle(t.style.sub());a=dt(s,e,t),p||(u=i.depth+e.fontMetrics().subDrop*e.sizeMultiplier/t.sizeMultiplier)}let d;d=t.style===S.DISPLAY?c.sup1:t.style.cramped?c.sup3:c.sup2;const g=t.sizeMultiplier,f=O(.5/c.ptPerEm/g);let b,y=null;if(a){const t=e.base&&"op"===e.base.type&&e.base.name&&("\\oiint"===e.base.name||"\\oiiint"===e.base.name);(i instanceof Y||t)&&(y=O(-i.italic))}if(l&&a){h=Math.max(h,d,l.depth+.25*c.xHeight),u=Math.max(u,c.sub2);const e=4*c.defaultRuleThickness;if(h-l.depth-(a.height-u)0&&(h+=t,u-=t)}b=Fe({positionType:"individualShift",children:[{type:"elem",elem:a,shift:u,marginRight:f,marginLeft:y},{type:"elem",elem:l,shift:-h,marginRight:f}]})}else if(a){u=Math.max(u,c.sub1,a.height-.8*c.xHeight);b=Fe({positionType:"shift",positionData:u,children:[{type:"elem",elem:a,marginLeft:y,marginRight:f}]})}else{if(!l)throw new Error("supsub must have either sup or sub.");h=Math.max(h,d,l.depth+.25*c.xHeight),b=Fe({positionType:"shift",positionData:-h,children:[{type:"elem",elem:l,marginRight:f}]})}const x=ut(i,"right")||"mord";return Oe([x],[i,Oe(["msupsub"],[b])],t)},mathmlBuilder(e,t){let r,n,o=!1;e.base&&"horizBrace"===e.base.type&&(n=!!e.sup,n===e.base.isOver&&(o=!0,r=e.base.isOver)),!e.base||"op"!==e.base.type&&"operatorname"!==e.base.type||(e.base.parentIsSupSub=!0);const s=[qt(e.base,t)];let i;if(e.sub&&s.push(qt(e.sub,t)),e.sup&&s.push(qt(e.sup,t)),o)i=r?"mover":"munder";else if(e.sub)if(e.sup){const r=e.base;i=r&&"op"===r.type&&r.limits&&t.style===S.DISPLAY||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.style===S.DISPLAY||r.limits)?"munderover":"msubsup"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===S.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===S.DISPLAY)?"munder":"msub"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===S.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===S.DISPLAY)?"mover":"msup"}return new yt(i,s)}}),tt({type:"atom",htmlBuilder(e,t){return Ce(e.text,e.mode,t,["m"+e.family])},mathmlBuilder(e,t){const r=new yt("mo",[St(e.text,e.mode)]);if("bin"===e.family){const n=Mt(e,t);"bold-italic"===n&&r.setAttribute("mathvariant",n)}else"punct"===e.family?r.setAttribute("separator","true"):"open"!==e.family&&"close"!==e.family||r.setAttribute("stretchy","false");return r}});const zn={mi:"italic",mn:"normal",mtext:"normal"};tt({type:"mathord",htmlBuilder(e,t){return Ie(e,t,"mathord")},mathmlBuilder(e,t){const r=new yt("mi",[St(e.text,e.mode,t)]),n=Mt(e,t)||"italic";return n!==zn[r.type]&&r.setAttribute("mathvariant",n),r}}),tt({type:"textord",htmlBuilder(e,t){return Ie(e,t,"textord")},mathmlBuilder(e,t){const r=St(e.text,e.mode,t),n=Mt(e,t)||"normal";let o;return o="text"===e.mode?new yt("mtext",[r]):/[0-9]/.test(e.text)?new yt("mn",[r]):"\\prime"===e.text?new yt("mo",[r]):new yt("mi",[r]),n!==zn[o.type]&&o.setAttribute("mathvariant",n),o}});const Mn={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},An={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};tt({type:"spacing",htmlBuilder(e,t){if(An.hasOwnProperty(e.text)){const r=An[e.text].className||"";if("text"===e.mode){const n=Ie(e,t,"textord");return n.classes.push(r),n}return Oe(["mspace",r],[Ce(e.text,e.mode,t)],t)}if(Mn.hasOwnProperty(e.text))return Oe(["mspace",Mn[e.text]],[],t);throw new n('Unknown type of space "'+e.text+'"')},mathmlBuilder(e,t){let r;if(!An.hasOwnProperty(e.text)){if(Mn.hasOwnProperty(e.text))return new yt("mspace");throw new n('Unknown type of space "'+e.text+'"')}return r=new yt("mtext",[new xt("\xa0")]),r}});const Tn=()=>{const e=new yt("mtd",[]);return e.setAttribute("width","50%"),e};tt({type:"tag",mathmlBuilder(e,t){const r=new yt("mtable",[new yt("mtr",[Tn(),new yt("mtd",[Bt(e.body,t)]),Tn(),new yt("mtd",[Bt(e.tag,t)])])]);return r.setAttribute("width","100%"),r}});const Bn={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},qn={"\\textbf":"textbf","\\textmd":"textmd"},Cn={"\\textit":"textit","\\textup":"textup"},In=(e,t)=>{const r=e.font;return r?Bn[r]?t.withTextFontFamily(Bn[r]):qn[r]?t.withTextFontWeight(qn[r]):"\\emph"===r?"textit"===t.fontShape?t.withTextFontShape("textup"):t.withTextFontShape("textit"):t.withTextFontShape(Cn[r]):t};et({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"text",mode:r.mode,body:nt(o),font:n}},htmlBuilder(e,t){const r=In(e,t),n=at(e.body,r,!0);return Oe(["mord","text"],n,r)},mathmlBuilder(e,t){const r=In(e,t);return Bt(e.body,r)}}),et({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"underline",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=dt(e.body,t),n=De("underline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Fe({positionType:"top",positionData:r.height,children:[{type:"kern",size:o},{type:"elem",elem:n},{type:"kern",size:3*o},{type:"elem",elem:r}]});return Oe(["mord","underline"],[s],t)},mathmlBuilder(e,t){const r=new yt("mo",[new xt("\u203e")]);r.setAttribute("stretchy","true");const n=new yt("munder",[qt(e.body,t),r]);return n.setAttribute("accentunder","true"),n}}),et({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"vcenter",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=dt(e.body,t),n=t.fontMetrics().axisHeight,o=.5*(r.height-n-(r.depth+n));return Fe({positionType:"shift",positionData:o,children:[{type:"elem",elem:r}]})},mathmlBuilder(e,t){const r=new yt("mpadded",[qt(e.body,t)],["vcenter"]);return new yt("mrow",[r])}}),et({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(e,t,r){throw new n("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(e,t){const r=Rn(e),n=[],o=t.havingStyle(t.style.text());for(let t=0;te.body.replace(/ /g,e.star?"\u2423":"\xa0");var Hn=Ke;const En="[ \r\n\t]",On="(\\\\[a-zA-Z@]+)"+En+"*",Nn="[\u0300-\u036f]",Dn=new RegExp(Nn+"+$"),Ln="("+En+"+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff]"+Nn+"*|[\ud800-\udbff][\udc00-\udfff]"+Nn+"*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|"+On+"|\\\\[^\ud800-\udfff])";class Pn{constructor(e,t){this.input=e,this.settings=t,this.tokenRegex=new RegExp(Ln,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){const e=this.input,t=this.tokenRegex.lastIndex;if(t===e.length)return new Wr("EOF",new Yr(this,t,t));const r=this.tokenRegex.exec(e);if(null===r||r.index!==t)throw new n("Unexpected character: '"+e[t]+"'",new Wr(e[t],new Yr(this,t,t+1)));const o=r[6]||r[3]||(r[2]?"\\ ":" ");if(14===this.catcodes[o]){const t=e.indexOf("\n",this.tokenRegex.lastIndex);return-1===t?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=t+1,this.lex()}return new Wr(o,new Yr(this,t,this.tokenRegex.lastIndex))}}class Fn{constructor(e,t){void 0===e&&(e={}),void 0===t&&(t={}),this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(0===this.undefStack.length)throw new n("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");const e=this.undefStack.pop();for(const t in e)e.hasOwnProperty(t)&&(null==e[t]?delete this.current[t]:this.current[t]=e[t])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,t,r){if(void 0===r&&(r=!1),r){for(let t=0;t0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{const t=this.undefStack[this.undefStack.length-1];t&&!t.hasOwnProperty(e)&&(t[e]=this.current[e])}null==t?delete this.current[e]:this.current[e]=t}}var Vn=Ur;Xr("\\noexpand",function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}}),Xr("\\expandafter",function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}}),Xr("\\@firstoftwo",function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}}),Xr("\\@secondoftwo",function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}}),Xr("\\@ifnextchar",function(e){const t=e.consumeArgs(3);e.consumeSpaces();const r=e.future();return 1===t[0].length&&t[0][0].text===r.text?{tokens:t[1],numArgs:0}:{tokens:t[2],numArgs:0}}),Xr("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Xr("\\TextOrMath",function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}});const Gn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};Xr("\\char",function(e){let t,r=e.popToken(),o=0;if("'"===r.text)t=8,r=e.popToken();else if('"'===r.text)t=16,r=e.popToken();else if("`"===r.text)if(r=e.popToken(),"\\"===r.text[0])o=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new n("\\char` missing argument");o=r.text.charCodeAt(0)}else t=10;if(t){if(o=Gn[r.text],null==o||o>=t)throw new n("Invalid base-"+t+" digit "+r.text);let s;for(;null!=(s=Gn[e.future().text])&&s{let s=e.consumeArg().tokens;if(1!==s.length)throw new n("\\newcommand's first argument must be a macro name");const i=s[0].text,l=e.isDefined(i);if(l&&!t)throw new n("\\newcommand{"+i+"} attempting to redefine "+i+"; use \\renewcommand");if(!l&&!r)throw new n("\\renewcommand{"+i+"} when command "+i+" does not yet exist; use \\newcommand");let a=0;if(s=e.consumeArg().tokens,1===s.length&&"["===s[0].text){let t="",r=e.expandNextToken();for(;"]"!==r.text&&"EOF"!==r.text;)t+=r.text,r=e.expandNextToken();if(!t.match(/^\s*[0-9]+\s*$/))throw new n("Invalid number of arguments: "+t);a=parseInt(t),s=e.consumeArg().tokens}return l&&o||e.macros.set(i,{tokens:s,numArgs:a}),""};Xr("\\newcommand",e=>Un(e,!1,!0,!1)),Xr("\\renewcommand",e=>Un(e,!0,!1,!1)),Xr("\\providecommand",e=>Un(e,!0,!0,!0)),Xr("\\message",e=>{const t=e.consumeArgs(1)[0];return console.log(t.reverse().map(e=>e.text).join("")),""}),Xr("\\errmessage",e=>{const t=e.consumeArgs(1)[0];return console.error(t.reverse().map(e=>e.text).join("")),""}),Xr("\\show",e=>{const t=e.popToken(),r=t.text;return console.log(t,e.macros.get(r),Hn[r],ne.math[r],ne.text[r]),""}),Xr("\\bgroup","{"),Xr("\\egroup","}"),Xr("~","\\nobreakspace"),Xr("\\lq","`"),Xr("\\rq","'"),Xr("\\aa","\\r a"),Xr("\\AA","\\r A"),Xr("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),Xr("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),Xr("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),Xr("\u212c","\\mathscr{B}"),Xr("\u2130","\\mathscr{E}"),Xr("\u2131","\\mathscr{F}"),Xr("\u210b","\\mathscr{H}"),Xr("\u2110","\\mathscr{I}"),Xr("\u2112","\\mathscr{L}"),Xr("\u2133","\\mathscr{M}"),Xr("\u211b","\\mathscr{R}"),Xr("\u212d","\\mathfrak{C}"),Xr("\u210c","\\mathfrak{H}"),Xr("\u2128","\\mathfrak{Z}"),Xr("\\Bbbk","\\Bbb{k}"),Xr("\\llap","\\mathllap{\\textrm{#1}}"),Xr("\\rlap","\\mathrlap{\\textrm{#1}}"),Xr("\\clap","\\mathclap{\\textrm{#1}}"),Xr("\\mathstrut","\\vphantom{(}"),Xr("\\underbar","\\underline{\\text{#1}}"),Xr("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}\\nobreak}{\\char"338}'),Xr("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),Xr("\\ne","\\neq"),Xr("\u2260","\\neq"),Xr("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),Xr("\u2209","\\notin"),Xr("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),Xr("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),Xr("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),Xr("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),Xr("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),Xr("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),Xr("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),Xr("\u27c2","\\perp"),Xr("\u203c","\\mathclose{!\\mkern-0.8mu!}"),Xr("\u220c","\\notni"),Xr("\u231c","\\ulcorner"),Xr("\u231d","\\urcorner"),Xr("\u231e","\\llcorner"),Xr("\u231f","\\lrcorner"),Xr("\xa9","\\copyright"),Xr("\xae","\\textregistered"),Xr("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}'),Xr("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}'),Xr("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}'),Xr("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}'),Xr("\\vdots","{\\varvdots\\rule{0pt}{15pt}}"),Xr("\u22ee","\\vdots"),Xr("\\varGamma","\\mathit{\\Gamma}"),Xr("\\varDelta","\\mathit{\\Delta}"),Xr("\\varTheta","\\mathit{\\Theta}"),Xr("\\varLambda","\\mathit{\\Lambda}"),Xr("\\varXi","\\mathit{\\Xi}"),Xr("\\varPi","\\mathit{\\Pi}"),Xr("\\varSigma","\\mathit{\\Sigma}"),Xr("\\varUpsilon","\\mathit{\\Upsilon}"),Xr("\\varPhi","\\mathit{\\Phi}"),Xr("\\varPsi","\\mathit{\\Psi}"),Xr("\\varOmega","\\mathit{\\Omega}"),Xr("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),Xr("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),Xr("\\boxed","\\fbox{$\\displaystyle{#1}$}"),Xr("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),Xr("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),Xr("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;"),Xr("\\dddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}"),Xr("\\ddddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}");const Xn={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"},Yn=new Set(["bin","rel"]);Xr("\\dots",function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in Xn?t=Xn[r]:("\\not"===r.slice(0,4)||r in ne.math&&Yn.has(ne.math[r].group))&&(t="\\dotsb"),t});const Wn={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};Xr("\\dotso",function(e){return e.future().text in Wn?"\\ldots\\,":"\\ldots"}),Xr("\\dotsc",function(e){const t=e.future().text;return t in Wn&&","!==t?"\\ldots\\,":"\\ldots"}),Xr("\\cdots",function(e){return e.future().text in Wn?"\\@cdots\\,":"\\@cdots"}),Xr("\\dotsb","\\cdots"),Xr("\\dotsm","\\cdots"),Xr("\\dotsi","\\!\\cdots"),Xr("\\dotsx","\\ldots\\,"),Xr("\\DOTSI","\\relax"),Xr("\\DOTSB","\\relax"),Xr("\\DOTSX","\\relax"),Xr("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Xr("\\,","\\tmspace+{3mu}{.1667em}"),Xr("\\thinspace","\\,"),Xr("\\>","\\mskip{4mu}"),Xr("\\:","\\tmspace+{4mu}{.2222em}"),Xr("\\medspace","\\:"),Xr("\\;","\\tmspace+{5mu}{.2777em}"),Xr("\\thickspace","\\;"),Xr("\\!","\\tmspace-{3mu}{.1667em}"),Xr("\\negthinspace","\\!"),Xr("\\negmedspace","\\tmspace-{4mu}{.2222em}"),Xr("\\negthickspace","\\tmspace-{5mu}{.277em}"),Xr("\\enspace","\\kern.5em "),Xr("\\enskip","\\hskip.5em\\relax"),Xr("\\quad","\\hskip1em\\relax"),Xr("\\qquad","\\hskip2em\\relax"),Xr("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Xr("\\tag@paren","\\tag@literal{({#1})}"),Xr("\\tag@literal",e=>{if(e.macros.get("\\df@tag"))throw new n("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"}),Xr("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),Xr("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Xr("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Xr("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Xr("\\newline","\\\\\\relax"),Xr("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");const jn=O($["Main-Regular"]["T".charCodeAt(0)][1]-.7*$["Main-Regular"]["A".charCodeAt(0)][1]);Xr("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+jn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),Xr("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+jn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),Xr("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Xr("\\@hspace","\\hskip #1\\relax"),Xr("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Xr("\\ordinarycolon",":"),Xr("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),Xr("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),Xr("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),Xr("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),Xr("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),Xr("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),Xr("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),Xr("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),Xr("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),Xr("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),Xr("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),Xr("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),Xr("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),Xr("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),Xr("\u2237","\\dblcolon"),Xr("\u2239","\\eqcolon"),Xr("\u2254","\\coloneqq"),Xr("\u2255","\\eqqcolon"),Xr("\u2a74","\\Coloneqq"),Xr("\\ratio","\\vcentcolon"),Xr("\\coloncolon","\\dblcolon"),Xr("\\colonequals","\\coloneqq"),Xr("\\coloncolonequals","\\Coloneqq"),Xr("\\equalscolon","\\eqqcolon"),Xr("\\equalscoloncolon","\\Eqqcolon"),Xr("\\colonminus","\\coloneq"),Xr("\\coloncolonminus","\\Coloneq"),Xr("\\minuscolon","\\eqcolon"),Xr("\\minuscoloncolon","\\Eqcolon"),Xr("\\coloncolonapprox","\\Colonapprox"),Xr("\\coloncolonsim","\\Colonsim"),Xr("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Xr("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Xr("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Xr("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Xr("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),Xr("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Xr("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Xr("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Xr("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Xr("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}"),Xr("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}"),Xr("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}"),Xr("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}"),Xr("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),Xr("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),Xr("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),Xr("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),Xr("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),Xr("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),Xr("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),Xr("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),Xr("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),Xr("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),Xr("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),Xr("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),Xr("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),Xr("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),Xr("\\imath","\\html@mathml{\\@imath}{\u0131}"),Xr("\\jmath","\\html@mathml{\\@jmath}{\u0237}"),Xr("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),Xr("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),Xr("\u27e6","\\llbracket"),Xr("\u27e7","\\rrbracket"),Xr("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),Xr("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),Xr("\u2983","\\lBrace"),Xr("\u2984","\\rBrace"),Xr("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29b5}}"),Xr("\u29b5","\\minuso"),Xr("\\darr","\\downarrow"),Xr("\\dArr","\\Downarrow"),Xr("\\Darr","\\Downarrow"),Xr("\\lang","\\langle"),Xr("\\rang","\\rangle"),Xr("\\uarr","\\uparrow"),Xr("\\uArr","\\Uparrow"),Xr("\\Uarr","\\Uparrow"),Xr("\\N","\\mathbb{N}"),Xr("\\R","\\mathbb{R}"),Xr("\\Z","\\mathbb{Z}"),Xr("\\alef","\\aleph"),Xr("\\alefsym","\\aleph"),Xr("\\Alpha","\\mathrm{A}"),Xr("\\Beta","\\mathrm{B}"),Xr("\\bull","\\bullet"),Xr("\\Chi","\\mathrm{X}"),Xr("\\clubs","\\clubsuit"),Xr("\\cnums","\\mathbb{C}"),Xr("\\Complex","\\mathbb{C}"),Xr("\\Dagger","\\ddagger"),Xr("\\diamonds","\\diamondsuit"),Xr("\\empty","\\emptyset"),Xr("\\Epsilon","\\mathrm{E}"),Xr("\\Eta","\\mathrm{H}"),Xr("\\exist","\\exists"),Xr("\\harr","\\leftrightarrow"),Xr("\\hArr","\\Leftrightarrow"),Xr("\\Harr","\\Leftrightarrow"),Xr("\\hearts","\\heartsuit"),Xr("\\image","\\Im"),Xr("\\infin","\\infty"),Xr("\\Iota","\\mathrm{I}"),Xr("\\isin","\\in"),Xr("\\Kappa","\\mathrm{K}"),Xr("\\larr","\\leftarrow"),Xr("\\lArr","\\Leftarrow"),Xr("\\Larr","\\Leftarrow"),Xr("\\lrarr","\\leftrightarrow"),Xr("\\lrArr","\\Leftrightarrow"),Xr("\\Lrarr","\\Leftrightarrow"),Xr("\\Mu","\\mathrm{M}"),Xr("\\natnums","\\mathbb{N}"),Xr("\\Nu","\\mathrm{N}"),Xr("\\Omicron","\\mathrm{O}"),Xr("\\plusmn","\\pm"),Xr("\\rarr","\\rightarrow"),Xr("\\rArr","\\Rightarrow"),Xr("\\Rarr","\\Rightarrow"),Xr("\\real","\\Re"),Xr("\\reals","\\mathbb{R}"),Xr("\\Reals","\\mathbb{R}"),Xr("\\Rho","\\mathrm{P}"),Xr("\\sdot","\\cdot"),Xr("\\sect","\\S"),Xr("\\spades","\\spadesuit"),Xr("\\sub","\\subset"),Xr("\\sube","\\subseteq"),Xr("\\supe","\\supseteq"),Xr("\\Tau","\\mathrm{T}"),Xr("\\thetasym","\\vartheta"),Xr("\\weierp","\\wp"),Xr("\\Zeta","\\mathrm{Z}"),Xr("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Xr("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Xr("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),Xr("\\bra","\\mathinner{\\langle{#1}|}"),Xr("\\ket","\\mathinner{|{#1}\\rangle}"),Xr("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Xr("\\Bra","\\left\\langle#1\\right|"),Xr("\\Ket","\\left|#1\\right\\rangle");const _n=e=>t=>{const r=t.consumeArg().tokens,n=t.consumeArg().tokens,o=t.consumeArg().tokens,s=t.consumeArg().tokens,i=t.macros.get("|"),l=t.macros.get("\\|");t.macros.beginGroup();const a=t=>r=>{e&&(r.macros.set("|",i),o.length&&r.macros.set("\\|",l));let s=t;if(!t&&o.length){"|"===r.future().text&&(r.popToken(),s=!0)}return{tokens:s?o:n,numArgs:0}};t.macros.set("|",a(!1)),o.length&&t.macros.set("\\|",a(!0));const c=t.consumeArg().tokens,h=t.expandTokens([...s,...c,...r]);return t.macros.endGroup(),{tokens:h.reverse(),numArgs:0}};Xr("\\bra@ket",_n(!1)),Xr("\\bra@set",_n(!0)),Xr("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),Xr("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),Xr("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),Xr("\\angln","{\\angl n}"),Xr("\\blue","\\textcolor{##6495ed}{#1}"),Xr("\\orange","\\textcolor{##ffa500}{#1}"),Xr("\\pink","\\textcolor{##ff00af}{#1}"),Xr("\\red","\\textcolor{##df0030}{#1}"),Xr("\\green","\\textcolor{##28ae7b}{#1}"),Xr("\\gray","\\textcolor{gray}{#1}"),Xr("\\purple","\\textcolor{##9d38bd}{#1}"),Xr("\\blueA","\\textcolor{##ccfaff}{#1}"),Xr("\\blueB","\\textcolor{##80f6ff}{#1}"),Xr("\\blueC","\\textcolor{##63d9ea}{#1}"),Xr("\\blueD","\\textcolor{##11accd}{#1}"),Xr("\\blueE","\\textcolor{##0c7f99}{#1}"),Xr("\\tealA","\\textcolor{##94fff5}{#1}"),Xr("\\tealB","\\textcolor{##26edd5}{#1}"),Xr("\\tealC","\\textcolor{##01d1c1}{#1}"),Xr("\\tealD","\\textcolor{##01a995}{#1}"),Xr("\\tealE","\\textcolor{##208170}{#1}"),Xr("\\greenA","\\textcolor{##b6ffb0}{#1}"),Xr("\\greenB","\\textcolor{##8af281}{#1}"),Xr("\\greenC","\\textcolor{##74cf70}{#1}"),Xr("\\greenD","\\textcolor{##1fab54}{#1}"),Xr("\\greenE","\\textcolor{##0d923f}{#1}"),Xr("\\goldA","\\textcolor{##ffd0a9}{#1}"),Xr("\\goldB","\\textcolor{##ffbb71}{#1}"),Xr("\\goldC","\\textcolor{##ff9c39}{#1}"),Xr("\\goldD","\\textcolor{##e07d10}{#1}"),Xr("\\goldE","\\textcolor{##a75a05}{#1}"),Xr("\\redA","\\textcolor{##fca9a9}{#1}"),Xr("\\redB","\\textcolor{##ff8482}{#1}"),Xr("\\redC","\\textcolor{##f9685d}{#1}"),Xr("\\redD","\\textcolor{##e84d39}{#1}"),Xr("\\redE","\\textcolor{##bc2612}{#1}"),Xr("\\maroonA","\\textcolor{##ffbde0}{#1}"),Xr("\\maroonB","\\textcolor{##ff92c6}{#1}"),Xr("\\maroonC","\\textcolor{##ed5fa6}{#1}"),Xr("\\maroonD","\\textcolor{##ca337c}{#1}"),Xr("\\maroonE","\\textcolor{##9e034e}{#1}"),Xr("\\purpleA","\\textcolor{##ddd7ff}{#1}"),Xr("\\purpleB","\\textcolor{##c6b9fc}{#1}"),Xr("\\purpleC","\\textcolor{##aa87ff}{#1}"),Xr("\\purpleD","\\textcolor{##7854ab}{#1}"),Xr("\\purpleE","\\textcolor{##543b78}{#1}"),Xr("\\mintA","\\textcolor{##f5f9e8}{#1}"),Xr("\\mintB","\\textcolor{##edf2df}{#1}"),Xr("\\mintC","\\textcolor{##e0e5cc}{#1}"),Xr("\\grayA","\\textcolor{##f6f7f7}{#1}"),Xr("\\grayB","\\textcolor{##f0f1f2}{#1}"),Xr("\\grayC","\\textcolor{##e3e5e6}{#1}"),Xr("\\grayD","\\textcolor{##d6d8da}{#1}"),Xr("\\grayE","\\textcolor{##babec2}{#1}"),Xr("\\grayF","\\textcolor{##888d93}{#1}"),Xr("\\grayG","\\textcolor{##626569}{#1}"),Xr("\\grayH","\\textcolor{##3b3e40}{#1}"),Xr("\\grayI","\\textcolor{##21242c}{#1}"),Xr("\\kaBlue","\\textcolor{##314453}{#1}"),Xr("\\kaGreen","\\textcolor{##71B307}{#1}");const $n={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class Zn{constructor(e,t,r){this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new Fn(Vn,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new Pn(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){let t,r,n;if(e){if(this.consumeSpaces(),"["!==this.future().text)return null;t=this.popToken(),({tokens:n,end:r}=this.consumeArg(["]"]))}else({tokens:n,start:t,end:r}=this.consumeArg());return this.pushToken(new Wr("EOF",r.loc)),this.pushTokens(n),new Wr("",Yr.range(t,r))}consumeSpaces(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}}consumeArg(e){const t=[],r=e&&e.length>0;r||this.consumeSpaces();const o=this.future();let s,i=0,l=0;do{if(s=this.popToken(),t.push(s),"{"===s.text)++i;else if("}"===s.text){if(--i,-1===i)throw new n("Extra }",s)}else if("EOF"===s.text)throw new n("Unexpected end of input in a macro argument, expected '"+(e&&r?e[l]:"}")+"'",s);if(e&&r)if((0===i||1===i&&"{"===e[l])&&s.text===e[l]){if(++l,l===e.length){t.splice(-l,l);break}}else l=0}while(0!==i||r);return"{"===o.text&&"}"===t[t.length-1].text&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:o,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new n("The length of delimiters doesn't match the number of args!");const r=t[0];for(let e=0;ethis.settings.maxExpand)throw new n("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){const t=this.popToken(),r=t.text,o=t.noexpand?null:this._getExpansion(r);if(null==o||e&&o.unexpandable){if(e&&null==o&&"\\"===r[0]&&!this.isDefined(r))throw new n("Undefined control sequence: "+r);return this.pushToken(t),!1}this.countExpansion(1);let s=o.tokens;const i=this.consumeArgs(o.numArgs,o.delimiters);if(o.numArgs){s=s.slice();for(let e=s.length-1;e>=0;--e){let t=s[e];if("#"===t.text){if(0===e)throw new n("Incomplete placeholder at end of macro body",t);if(t=s[--e],"#"===t.text)s.splice(e+1,1);else{if(!/^[1-9]$/.test(t.text))throw new n("Not a valid argument number",t);s.splice(e,2,...i[+t.text-1])}}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(!1===this.expandOnce()){const e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}}expandMacro(e){return this.macros.has(e)?this.expandTokens([new Wr(e)]):void 0}expandTokens(e){const t=[],r=this.stack.length;for(this.pushTokens(e);this.stack.length>r;)if(!1===this.expandOnce(!0)){const e=this.stack.pop();e.treatAsRelax&&(e.noexpand=!1,e.treatAsRelax=!1),t.push(e)}return this.countExpansion(t.length),t}expandMacroAsText(e){const t=this.expandMacro(e);return t?t.map(e=>e.text).join(""):t}_getExpansion(e){const t=this.macros.get(e);if(null==t)return t;if(1===e.length){const t=this.lexer.catcodes[e];if(null!=t&&13!==t)return}const r="function"==typeof t?t(this):t;if("string"==typeof r){let e=0;if(r.includes("#")){const t=r.replace(/##/g,"");for(;t.includes("#"+(e+1));)++e}const t=new Pn(r,this.settings),n=[];let o=t.lex();for(;"EOF"!==o.text;)n.push(o),o=t.lex();n.reverse();return{tokens:n,numArgs:e}}return r}isDefined(e){return this.macros.has(e)||Hn.hasOwnProperty(e)||ne.math.hasOwnProperty(e)||ne.text.hasOwnProperty(e)||$n.hasOwnProperty(e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:Hn.hasOwnProperty(e)&&!Hn[e].primitive}}const Kn=/^[\u208a\u208b\u208c\u208d\u208e\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2090\u2091\u2095\u1d62\u2c7c\u2096\u2097\u2098\u2099\u2092\u209a\u1d63\u209b\u209c\u1d64\u1d65\u2093\u1d66\u1d67\u1d68\u1d69\u1d6a]/,Jn=Object.freeze({"\u208a":"+","\u208b":"-","\u208c":"=","\u208d":"(","\u208e":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1d62":"i","\u2c7c":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209a":"p","\u1d63":"r","\u209b":"s","\u209c":"t","\u1d64":"u","\u1d65":"v","\u2093":"x","\u1d66":"\u03b2","\u1d67":"\u03b3","\u1d68":"\u03c1","\u1d69":"\u03d5","\u1d6a":"\u03c7","\u207a":"+","\u207b":"-","\u207c":"=","\u207d":"(","\u207e":")","\u2070":"0","\xb9":"1","\xb2":"2","\xb3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1d2c":"A","\u1d2e":"B","\u1d30":"D","\u1d31":"E","\u1d33":"G","\u1d34":"H","\u1d35":"I","\u1d36":"J","\u1d37":"K","\u1d38":"L","\u1d39":"M","\u1d3a":"N","\u1d3c":"O","\u1d3e":"P","\u1d3f":"R","\u1d40":"T","\u1d41":"U","\u2c7d":"V","\u1d42":"W","\u1d43":"a","\u1d47":"b","\u1d9c":"c","\u1d48":"d","\u1d49":"e","\u1da0":"f","\u1d4d":"g","\u02b0":"h","\u2071":"i","\u02b2":"j","\u1d4f":"k","\u02e1":"l","\u1d50":"m","\u207f":"n","\u1d52":"o","\u1d56":"p","\u02b3":"r","\u02e2":"s","\u1d57":"t","\u1d58":"u","\u1d5b":"v","\u02b7":"w","\u02e3":"x","\u02b8":"y","\u1dbb":"z","\u1d5d":"\u03b2","\u1d5e":"\u03b3","\u1d5f":"\u03b4","\u1d60":"\u03d5","\u1d61":"\u03c7","\u1dbf":"\u03b8"}),Qn={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"},"\u0327":{text:"\\c"}},eo={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u1e09":"c\u0327\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\xe7":"c\u0327","\u010f":"d\u030c","\u1e0b":"d\u0307","\u1e11":"d\u0327","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u1e1d":"e\u0327\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u0229":"e\u0327","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u0123":"g\u0327","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\u1e29":"h\u0327","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u0137":"k\u0327","\u013a":"l\u0301","\u013e":"l\u030c","\u013c":"l\u0327","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\u0146":"n\u0327","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u0157":"r\u0327","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u015f":"s\u0327","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\u0163":"t\u0327","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u1e08":"C\u0327\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\xc7":"C\u0327","\u010e":"D\u030c","\u1e0a":"D\u0307","\u1e10":"D\u0327","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u1e1c":"E\u0327\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u0228":"E\u0327","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u0122":"G\u0327","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\u1e28":"H\u0327","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0136":"K\u0327","\u0139":"L\u0301","\u013d":"L\u030c","\u013b":"L\u0327","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\u0145":"N\u0327","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u0156":"R\u0327","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u015e":"S\u0327","\u0164":"T\u030c","\u1e6a":"T\u0307","\u0162":"T\u0327","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"};class to{constructor(e,t){this.mode="math",this.gullet=new Zn(e,t,this.mode),this.settings=t,this.leftrightDepth=0,this.nextToken=null}expect(e,t){if(void 0===t&&(t=!0),this.fetch().text!==e)throw new n("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{const e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){const t=this.nextToken;this.consume(),this.gullet.pushToken(new Wr("}")),this.gullet.pushTokens(e);const r=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,r}parseExpression(e,t){const r=[];for(;;){"math"===this.mode&&this.consumeSpaces();const n=this.fetch();if(to.endOfExpression.has(n.text))break;if(t&&n.text===t)break;if(e&&Hn[n.text]&&Hn[n.text].infix)break;const o=this.parseAtom(t);if(!o)break;"internal"!==o.type&&r.push(o)}return"text"===this.mode&&this.formLigatures(r),this.handleInfixNodes(r)}handleInfixNodes(e){let t,r=-1;for(let o=0;o=128))return null;this.settings.strict&&(A(t.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'" ('+t.charCodeAt(0)+")",e)),o={type:"textord",mode:"text",loc:Yr.range(e),text:t}}if(this.consume(),r)for(let t=0;t{var e={56:(e,t,n)=>{"use strict";e.exports=function(e){var t=n.nc;t&&e.setAttribute("nonce",t)}},72:e=>{"use strict";var t=[];function n(e){for(var n=-1,a=0;a{"use strict";e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}},314:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n="",a=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),a&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),a&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n}).join("")},t.i=function(e,n,a,i,r){"string"==typeof e&&(e=[[null,e,void 0]]);var o={};if(a)for(var s=0;s0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=r),n&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=n):u[2]=n),i&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=i):u[4]="".concat(i)),t.push(u))}},t}},415:(e,t,n)=>{"use strict";n.d(t,{A:()=>s});var a=n(601),i=n.n(a),r=n(314),o=n.n(r)()(i());o.push([e.id,'/* PrismJS 1.14.0\nhttp://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */\n/**\n * prism.js theme for Macaulay2\n 2018 P. Zinn-Justin\n */\n\n/*\ncode[class*="language-"],\npre[class*="language-"] {\n\tcolor: black;\n\tbackground: none;\n\ttext-shadow: 0 1px white;\n\tfont-family: Consolas, Monaco, \'Andale Mono\', \'Ubuntu Mono\', monospace;\n\ttext-align: left;\n\twhite-space: pre;\n\tword-spacing: normal;\n\tword-break: normal;\n\tword-wrap: normal;\n\tline-height: 1.5;\n\n\t-moz-tab-size: 4;\n\t-o-tab-size: 4;\n\ttab-size: 4;\n\n\t-webkit-hyphens: none;\n\t-moz-hyphens: none;\n\t-ms-hyphens: none;\n\thyphens: none;\n}\n\npre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,\ncode[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {\n\ttext-shadow: none;\n\tbackground: #b3d4fc;\n}\n\npre[class*="language-"]::selection, pre[class*="language-"] ::selection,\ncode[class*="language-"]::selection, code[class*="language-"] ::selection {\n\ttext-shadow: none;\n\tbackground: #b3d4fc;\n}\n\n@media print {\n\tcode[class*="language-"],\n\tpre[class*="language-"] {\n\t\ttext-shadow: none;\n\t}\n}\n*/\n/* Code blocks */\n/*\npre[class*="language-"] {\n\tpadding: 1em;\n\tmargin: .5em 0;\n\toverflow: auto;\n}\n\n:not(pre) > code[class*="language-"],\npre[class*="language-"] {\n\tbackground: #f5f2f0;\n}\n*/\n/* Inline code */\n/*\n:not(pre) > code[class*="language-"] {\n\tpadding: .1em;\n\tborder-radius: .3em;\n\twhite-space: normal;\n}\n*/\n.token.comment {\n color: #607080;\n}\n\n/*\n.token.punctuation {\n\tcolor: #999;\n}\n\n.namespace {\n\topacity: .7;\n}\n*/\n\n.token.constant {\n /*\tcolor: #008b8b; */\n color: #004060;\n}\n\n.token.net,\n.token.string {\n\tcolor: #8b2252;\n}\n\n/*\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n\tcolor: #9a6e3a;\n\tbackground: hsla(0, 0%, 100%, .5);\n}\n*/\n\n.token.keyword {\n color: #a020f0;\n}\n\n.token.function {\n color: #0000ff;\n}\n.token.class-name {\n /* color: #228b22; */\n color: #1c701c;\n}\n\n/*\n.token.regex,\n.token.important,\n.token.variable {\n\tcolor: #e90;\n}\n\n.token.important,\n.token.bold {\n\tfont-weight: bold;\n}\n.token.italic {\n\tfont-style: italic;\n}\n\n.token.entity {\n\tcursor: help;\n}\n*/\n',""]);const s=o},432:()=>{Prism.languages.m2=Prism.languages.macaulay2={comment:[{pattern:/(^|[^\\])\-\*[\s\S]*?(?:\*\-|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\-\-.*/,lookbehind:!0,greedy:!0}],string:[{pattern:/"(?:\\[\s\S]|(?!")[^\\])*"/,greedy:!0},{pattern:/\/\/\/(\/(?!\/)|(?:\/\/)+(?!\/)|[^\/])*(?:\/\/)+\/(?!\/)/,greedy:!0}],keyword:/\b(?:SPACE|TEST|and|break|breakpoint|catch|continue|do|elapsedTime|elapsedTiming|else|for|from|global|if|in|list|local|new|not|of|or|profile|return|shield|step|symbol|then|threadLocal|threadVariable|throw|time|timing|to|try|when|while|xor)\b/,"class-name":/\b(?:ANCHOR|Adjacent|AffineVariety|Analyzer|AngleBarList|Array|AssociativeExpression|AtomicInt|BLOCKQUOTE|BODY|BOLD|BR|BUTTON|Bag|BasicList|BettiTally|BinaryOperation|Boolean|CC|CDATA|CODE|COMMENT|CacheTable|ChainComplex|ChainComplexMap|CoherentSheaf|Command|CompiledFunction|CompiledFunctionBody|CompiledFunctionClosure|ComplexField|Constant|DD|DIV|DL|DT|Database|Descent|Describe|Dictionary|DirectSum|Divide|DocumentTag|EM|Eliminate|EngineRing|Equation|ExampleItem|Expression|File|FilePosition|FractionField|Function|FunctionApplication|FunctionBody|FunctionClosure|GaloisField|GeneralOrderedMonoid|GlobalDictionary|GradedModule|GradedModuleMap|GroebnerBasis|GroebnerBasisOptions|HEAD|HEADER1|HEADER2|HEADER3|HEADER4|HEADER5|HEADER6|HR|HREF|HTML|HashTable|HeaderType|Holder|Hybrid|Hypertext|HypertextContainer|HypertextParagraph|HypertextVoid|IFRAME|IMG|INDENT|INPUT|ITALIC|Ideal|ImmutableType|IndeterminateNumber|IndexedVariable|IndexedVariableTable|InexactField|InexactFieldFamily|InexactNumber|InfiniteNumber|IntermediateMarkUpType|Iterator|KBD|Keyword|LABEL|LATER|LI|LINK|LITERAL|List|LocalDictionary|LowerBound|MENU|META|Manipulator|MapExpression|MarkUpType|Matrix|MatrixExpression|MethodFunction|MethodFunctionBinary|MethodFunctionSingle|MethodFunctionWithOptions|Minus|Module|Monoid|MonoidElement|MonomialIdeal|MultigradedBettiTally|MutableHashTable|MutableList|MutableMatrix|Net|NetFile|Nothing|Number|NumberedVerticalList|OL|OneExpression|Option|OptionTable|OrderedMonoid|PARA|PRE|Package|Parenthesize|Parser|Partition|PolynomialRing|Power|Product|ProductOrder|Program|ProgramRun|ProjectiveHilbertPolynomial|ProjectiveVariety|Pseudocode|PseudocodeClosure|QQ|QuotientRing|RR|RRi|RealField|Resolution|Ring|RingElement|RingFamily|RingMap|RowExpression|SAMP|SCRIPT|SMALL|SPAN|STRONG|STYLE|SUB|SUBSECTION|SUP|ScriptedFunctor|SelfInitializingType|Sequence|Set|SheafExpression|SheafMap|SheafOfRings|SparseMonomialVectorExpression|SparseVectorExpression|String|Subscript|Sum|SumOfTwists|Superscript|Symbol|SymbolBody|TABLE|TD|TEX|TH|TITLE|TO|TO2|TOH|TR|TT|Table|Tally|Task|TensorProduct|TestInput|Thing|Time|Type|UL|URL|VAR|Variety|Vector|VectorExpression|VerticalList|VirtualTally|VisibleList|WrapperType|ZZ|ZeroExpression)\b/,function:/\b(?:BesselJ|BesselY|Beta|Digamma|EXAMPLE|End|Fano|GCstats|GF|Gamma|Grassmannian|Hom|LLL|LUdecomposition|M2CODE|NNParser|Proj|QQParser|QRDecomposition|SVD|SYNOPSIS|Schubert|Spec|ZZParser|about|abs|accumulate|acos|acosh|acot|acoth|addCancelTask|addDependencyTask|addEndFunction|addHook|addStartTask|adjoint|agm|alarm|all|ambient|analyticSpread|ancestor|ancestors|andP|ann|annihilator|antipode|any|append|applicationDirectory|apply|applyKeys|applyPairs|applyTable|applyValues|apropos|arXiv|ascii|asin|asinh|ass|assert|associatedGradedRing|associatedPrimes|atEndOfFile|atan|atan2|atanh|autoload|baseFilename|baseName|baseRing|basis|beginDocumentation|benchmark|betti|between|binomial|borel|cacheValue|cancelTask|canonicalBundle|capture|ceiling|centerString|chainComplex|changeBase|changeDirectory|char|charAnalyzer|characters|check|checkDegrees|chi|class|clean|clearEcho|code|codim|coefficient|coefficientRing|coefficients|cohomology|coimage|coker|cokernel|collectGarbage|columnAdd|columnMult|columnPermute|columnRankProfile|columnSwap|columnate|combine|commandInterpreter|commonRing|commonest|comodule|compareExchange|complement|complete|components|compose|compositions|compress|concatenate|conductor|cone|conjugate|connectionCount|constParser|content|contract|conwayPolynomial|copy|copyDirectory|copyFile|cos|cosh|cot|cotangentSheaf|coth|cover|coverMap|cpuTime|createTask|csc|csch|currentColumnNumber|currentDirectory|currentPosition|currentRowNumber|currentTime|deadParser|debug|debugError|decompose|deepSplice|default|degree|degreeGroup|degreeLength|degrees|degreesMonoid|degreesRing|delete|demark|denominator|depth|describe|det|determinant|diagonalMatrix|diameter|dictionary|diff|difference|dim|directProduct|directSum|disassemble|discriminant|dismiss|distinguished|divideByVariable|doc|document|drop|dual|eagonNorthcott|echoOff|echoOn|eigenvalues|eigenvectors|eint|elements|eliminate|endPackage|entries|erase|erf|erfc|error|euler|eulers|even|examples|exchange|exec|exp|expectedReesIdeal|expm1|exponents|export|exportFrom|exportMutable|expression|extend|exteriorPower|factor|fileExecutable|fileExists|fileLength|fileMode|fileReadable|fileTime|fileWritable|fillMatrix|findFiles|findHeft|findProgram|findSynonyms|first|firstkey|fittingIdeal|flagLookup|flatten|flattenRing|flip|floor|fold|forceGB|fork|format|formation|frac|fraction|frames|fromDividedPowers|fromDual|functionBody|futureParser|gb|gbRemove|gbSnapshot|gcd|gcdCoefficients|gcdLLL|genera|generateAssertions|generator|generators|genericMatrix|genericSkewMatrix|genericSymmetricMatrix|gens|genus|get|getChangeMatrix|getGlobalSymbol|getIOThreadMode|getNetFile|getNonUnit|getPrimeWithRootOfUnity|getSymbol|getWWW|getc|getenv|globalAssign|globalAssignFunction|globalAssignment|globalReleaseFunction|gradedModule|gradedModuleMap|gramm|graphIdeal|graphRing|groebnerBasis|groupID|hash|hashTable|headlines|heft|height|hermite|hilbertFunction|hilbertPolynomial|hilbertSeries|hold|homogenize|homology|homomorphism|hooks|horizontalJoin|html|httpHeaders|hypertext|icFracP|icFractions|icMap|icPIdeal|ideal|idealSheaf|idealizer|identity|image|imaginaryPart|importFrom|independentSets|index|indices|inducedMap|inducesWellDefinedMap|info|input|insert|installAssignmentMethod|installHilbertFunction|installMethod|installMinprimes|installPackage|installedPackages|instance|instances|integralClosure|integrate|intersect|intersectInP|intersection|interval|inverse|inverseErf|inversePermutation|inverseRegularizedBeta|inverseRegularizedGamma|inverseSystem|irreducibleCharacteristicSeries|irreducibleDecomposition|isANumber|isAffineRing|isBorel|isCanceled|isCommutative|isConstant|isDirectSum|isDirectory|isEmpty|isExact|isField|isFinite|isFinitePrimeField|isFreeModule|isGlobalSymbol|isHomogeneous|isIdeal|isInfinite|isInjective|isInputFile|isIsomorphic|isIsomorphism|isLLL|isLinearType|isListener|isLocallyFree|isMember|isModule|isMonomialIdeal|isMutable|isNormal|isOpen|isOutputFile|isPolynomialRing|isPrimary|isPrime|isPrimitive|isProjective|isPseudoprime|isQuotientModule|isQuotientOf|isQuotientRing|isReady|isReal|isReduction|isRegularFile|isRing|isSkewCommutative|isSmooth|isSorted|isSquareFree|isStandardGradedPolynomialRing|isSubmodule|isSubquotient|isSubset|isSupportedInZeroLocus|isSurjective|isTable|isUnit|isVeryAmple|isWellDefined|isWeylAlgebra|isc|isomorphism|iterator|jacobian|jacobianDual|join|ker|kernel|kernelLLL|kernelOfLocalization|keys|kill|koszul|last|lcm|leadCoefficient|leadComponent|leadMonomial|leadTerm|left|length|letterParser|lift|liftable|limitFiles|limitProcesses|lines|linkFile|listForm|listSymbols|lngamma|load|loadPackage|localDictionaries|localize|locate|log|log1p|lookup|lookupCount|makeDirectory|makeDocumentTag|makePackageIndex|makeS2|map|markedGB|match|mathML|matrix|max|maxPosition|member|memoize|memoizeClear|memoizeValues|merge|mergePairs|method|methodOptions|methods|midpoint|min|minPosition|minPres|mingens|mingle|minimalBetti|minimalPresentation|minimalPrimes|minimalReduction|minimize|minimizeFilename|minors|minprimes|minus|mkdir|mod|module|modulo|monoid|monomialCurveIdeal|monomialIdeal|monomialSubideal|monomials|moveFile|multidegree|multidoc|multigraded|multiplicity|mutable|mutableIdentity|mutableMatrix|nanosleep|needs|needsPackage|net|netList|newClass|newCoordinateSystem|newNetFile|newPackage|newRing|next|nextPrime|nextkey|nonspaceAnalyzer|norm|normalCone|notImplemented|nullParser|nullSpace|nullhomotopy|numColumns|numRows|number|numcols|numerator|numeric|numericInterval|numgens|numrows|odd|oeis|ofClass|on|openDatabase|openDatabaseOut|openFiles|openIn|openInOut|openListener|openOut|openOutAppend|optP|optionalSignParser|options|orP|override|pack|package|packageTemplate|pad|pager|pairs|parallelApply|parent|part|partition|partitions|parts|pdim|peek|permanents|permutations|pfaffian|pfaffians|pivots|plus|poincare|poincareN|polarize|poly|position|positions|power|powermod|precision|preimage|prepend|presentation|pretty|primaryComponent|primaryDecomposition|print|printString|printerr|processID|product|projectiveHilbertPolynomial|promote|protect|prune|pseudoRemainder|pseudocode|pullback|pushForward|pushout|quotient|quotientRemainder|radical|radicalContainment|random|randomKRationalPoint|randomMutableMatrix|randomSubset|rank|rays|read|readDirectory|readPackage|readlink|realPart|realpath|recursionDepth|reduceHilbert|reducedRowEchelonForm|reductionNumber|reesAlgebra|reesAlgebraIdeal|reesIdeal|regSeqInIdeal|regex|regexQuote|registerFinalizer|regularity|regularizedBeta|regularizedGamma|relations|relativizeFilename|remainder|remove|removeDirectory|removeFile|removeLowestDimension|reorganize|replace|res|reshape|resolution|resultant|reverse|right|ring|ringFromFractions|roots|rotate|round|rowAdd|rowMult|rowPermute|rowRankProfile|rowSwap|rsort|run|runHooks|runLengthEncode|runProgram|same|saturate|scan|scanKeys|scanLines|scanPairs|scanValues|schedule|schreyerOrder|searchPath|sec|sech|seeParsing|select|selectInSubring|selectKeys|selectPairs|selectValues|selectVariables|separate|separateRegexp|sequence|serialNumber|set|setEcho|setGroupID|setIOExclusive|setIOSynchronized|setIOUnSynchronized|setRandomSeed|setup|setupEmacs|setupLift|setupPromote|sheaf|sheafHom|sheafMap|show|showHtml|showTex|sign|simpleDocFrob|sin|singularLocus|sinh|size|size2|sleep|smithNormalForm|solve|someTerms|sort|sortColumns|source|span|specialFiber|specialFiberIdeal|splice|splitWWW|sqrt|stack|stacksProject|stacktrace|standardForm|standardPairs|stashValue|status|store|style|sub|sublists|submatrix|submatrixByDegrees|subquotient|subsets|substitute|substring|subtable|sum|super|support|switch|sylvesterMatrix|symbolBody|symlinkDirectory|symlinkFile|symmetricAlgebra|symmetricAlgebraIdeal|symmetricKernel|symmetricPower|synonym|syz|syzygyScheme|table|take|tally|tan|tangentCone|tangentSheaf|tanh|target|taskResult|temporaryFileName|tensor|tensorAssociativity|terminalParser|terms|testHunekeQuestion|tests|tex|texMath|times|toAbsolutePath|toCC|toDividedPowers|toDual|toExternalString|toField|toList|toLower|toRR|toRRi|toSequence|toString|toUpper|top|topCoefficients|topComponents|trace|transpose|trim|truncate|truncateOutput|tutorial|ultimate|unbag|uncurry|undocumented|uniform|uninstallAllPackages|uninstallPackage|union|unique|uniquePermutations|unsequence|unstack|urlEncode|use|userSymbols|utf8|utf8check|utf8substring|validate|value|values|variety|vars|vector|versalEmbedding|wait|wedgeProduct|weightRange|whichGm|width|wikipedia|wrap|youngest|zero|zeta)\b/,constant:/\b(?:A1BrouwerDegrees|AbstractSimplicialComplexes|AbstractToricVarieties|Acknowledgement|AdditionalPaths|AdjointIdeal|AdjunctionForSurfaces|AfterEval|AfterNoPrint|AfterPrint|AInfinity|AlgebraicSplines|Algorithm|Alignment|AllCodimensions|AllMarkovBases|allowableThreads|AnalyzeSheafOnP1|applicationDirectorySuffix|argument|Ascending|AssociativeAlgebras|Authors|AuxiliaryFiles|backtrace|Bareiss|Base|BaseFunction|baseRings|BaseRow|BasisElementLimit|Bayer|BeforePrint|BeginningMacaulay2|Benchmark|BernsteinSato|Bertini|BettiCharacters|BGG|BIBasis|Binary|Binomial|BinomialEdgeIdeals|Binomials|BKZ|blockMatrixForm|Body|BoijSoederberg|Book3264Examples|BooleanGB|Boxes|Brackets|Browse|Bruns|cache|CacheExampleOutput|CallLimit|CannedExample|CatalanConstant|Caveat|CellularResolutions|Center|Certification|ChainComplexExtras|ChainComplexOperations|ChangeMatrix|CharacteristicClasses|CheckDocumentation|Chordal|Citation|cite|Classic|clearAll|clearOutput|close|closeIn|closeOut|ClosestFit|Code|CodimensionLimit|CodingTheory|CoefficientRing|Cofactor|CohenEngine|CohenTopLevel|CohomCalg|CoincidentRootLoci|commandLine|compactMatrixForm|Complement|CompleteIntersection|CompleteIntersectionResolutions|Complexes|ConductorElement|Configuration|ConformalBlocks|ConnectionMatrices|Consequences|Constants|Contributors|ConvexInterface|ConwayPolynomials|copyright|Core|CorrespondenceScrolls|CotangentSchubert|cotangentSurjection|coverageSummary|CpMackeyFunctors|Cremona|currentFileDirectory|currentFileName|currentLayout|currentPackage|Cyclotomic|Date|dd|DebuggingMode|debuggingMode|debugLevel|DecomposableSparseSystems|Decompose|Default|defaultPrecision|Degree|DegreeGroup|DegreeLift|DegreeLimit|DegreeMap|DegreeOrder|DegreeRank|Degrees|Dense|Density|Depth|Descending|Description|DeterminantalRepresentations|DGAlgebras|dictionaryPath|DiffAlg|Dispatch|DivideConquer|DividedPowers|Dmodules|docExample|docTemplate|Down|Dynamic|EagonResolution|EdgeIdeals|edit|EigenSolver|EisenbudHunekeVasconcelos|Elimination|EliminationMatrices|EllipticCurves|EllipticIntegrals|Email|embeddedToAbstract|end|endl|Engine|engineDebugLevel|EngineTests|EnumerationCurves|environment|EquivariantGB|errorDepth|EulerConstant|eulerSequence|Example|ExampleFiles|ExampleSystems|Exclude|exit|Ext|ExteriorExtensions|ExteriorIdeals|ExteriorModules|ExtLongExactSequence|false|FastMinors|FastNonminimal|FGLM|fileDictionaries|fileExitHooks|FileName|FindOne|FiniteFittingIdeals|First|FirstPackage|FlatMonoid|Flexible|flush|FollowLinks|ForeignFunctions|FormalGroupLaws|Format|FourierMotzkin|FourTiTwo|fpLLL|FrobeniusThresholds|FunctionFieldDesingularization|GameTheory|GBDegrees|gbTrace|GenerateAssertions|Generic|GenericInitialIdeal|GeometricDecomposability|gfanInterface|Givens|GKMVarieties|GLex|Global|GlobalAssignHook|globalAssignmentHooks|GlobalHookStore|GlobalReleaseHook|GlobalSectionLimit|Gorenstein|GradedLieAlgebras|GraphicalModels|GraphicalModelsMLE|Graphics|Graphs|GRevLex|GroebnerStrata|GroebnerWalk|GroupLex|GroupRevLex|GTZ|Hadamard|handleInterrupts|HardDegreeLimit|Heading|Headline|Heft|Height|help|Hermite|Hermitian|HH|hh|HigherCIOperators|HighestWeights|Hilbert|HodgeIntegrals|HolonomicSystems|homeDirectory|HomePage|Homogeneous|Homogeneous2|HomologicalAlgebraPackage|HomotopyLieAlgebra|HorizontalSpace|HyperplaneArrangements|id|idealSheafSequence|IgnoreExampleErrors|ii|IncidenceCorrespondenceCohomology|incomparable|Increment|indeterminate|Index|indexComponents|infinity|InfoDirSection|infoHelp|Inhomogeneous|Inputs|InstallPrefix|IntegerProgramming|IntegralClosure|interpreterDepth|Intersection|InvariantRing|InverseMethod|Inverses|InverseSystems|Invertible|InvolutiveBases|Isomorphism|Item|Iterate|Jacobian|Jets|Join|JSON|JSONRPC|K3Carpets|K3Surfaces|Keep|KeepFiles|KeepZeroes|Key|Keywords|Kronecker|KustinMiller|lastMatch|LatticePolytopes|Layout|Left|LengthLimit|Lex|LexIdeals|LieAlgebraRepresentations|Limit|Linear|LinearAlgebra|LinearTruncations|lineNumber|listLocalSymbols|listUserSymbols|LLLBases|loadDepth|LoadDocumentation|loadedFiles|loadedPackages|Local|LocalRings|LongPolynomial|M0nbar|Macaulay2Doc|Maintainer|MakeDocumentation|MakeHTML|MakeInfo|MakePDF|MapleInterface|Markov|MatchingFields|MatrixFactorizations|MatrixSchubert|Matroids|maxAllowableThreads|maxExponent|MaximalRank|MaxReductionCount|MCMApproximations|MergeTeX|minExponent|MinimalGenerators|MinimalMatrix|minimalPresentationMap|minimalPresentationMapInv|MinimalPrimes|Minimize|MinimumVersion|Miura|MixedMultiplicity|ModuleDeformations|MonodromySolver|Monomial|MonomialAlgebras|MonomialIntegerPrograms|MonomialOrbits|MonomialOrder|Monomials|MonomialSize|Msolve|MultigradedBGG|MultigradedImplicitization|MultiGradedRationalMap|MultiplicitySequence|MultiplierIdeals|MultiplierIdealsDim2|MultiprojectiveVarieties|NAGtypes|Name|Nauty|NautyGraphs|NCAlgebra|NCLex|NewFromMethod|newline|NewMethod|NewOfFromMethod|NewOfMethod|nil|Node|NoetherianOperators|NoetherNormalization|NonminimalComplexes|NonPrincipalTestIdeals|NoPrint|Normaliz|NormalToricVarieties|NotANumber|notify|NTL|null|nullaryMethods|NumericalAlgebraicGeometry|NumericalCertification|NumericalImplicitization|NumericalLinearAlgebra|NumericalSchubertCalculus|NumericalSemigroups|NumericSolutions|numTBBThreads|OIGroebnerBases|OldChainComplexes|OldPolyhedra|OldToricVectorBundles|OnlineLookup|OO|oo|ooo|oooo|OpenMath|operatorAttributes|OptionalComponentsPresent|Options|Order|order|Oscillators|OutputDictionary|Outputs|PackageCitations|PackageDictionary|PackageExports|PackageImports|PackageTemplate|PairLimit|PairsRemaining|ParallelF4|ParallelizeByDegree|Parametrization|Parsing|path|PathSignatures|PencilsOfQuadrics|Permanents|Permutations|PHCpack|PhylogeneticTrees|pi|PieriMaps|PlaneCurveLinearSeries|PlaneCurveSingularities|Points|Polyhedra|Polymake|PolyominoIdeals|Posets|Position|PositivityToricBundles|POSIX|Postfix|Pre|Precision|Prefix|prefixDirectory|prefixPath|PrimaryDecomposition|PrimaryTag|PrimitiveElement|Print|printingAccuracy|printingLeadLimit|printingPrecision|printingSeparator|printingTimeLimit|printingTrailLimit|printWidth|Probability|profileSummary|programPaths|Projective|Prune|PruneComplex|pruningMap|PseudomonomialPrimaryDecomposition|Pullback|pullbackMaps|PushForward|pushoutMaps|Python|QthPower|QuadraticIdealExamplesByRoos|Quasidegrees|QuaternaryQuartics|QuillenSuslin|quit|Quotient|Radical|RadicalCodim1|RaiseError|RandomCanonicalCurves|RandomComplexes|RandomCurves|RandomCurvesOverVerySmallFiniteFields|RandomGenus14Curves|RandomIdeals|RandomMonomialIdeals|RandomObjects|RandomPlaneCurves|RandomPoints|RandomSpaceCurves|Range|RationalMaps|RationalPoints|RationalPoints2|ReactionNetworks|RealFP|RealQP|RealQP1|RealRoots|RealRR|RealXD|recursionLimit|Reduce|ReesAlgebra|References|ReflexivePolytopesDB|Regularity|RelativeCanonicalResolution|Reload|RemakeAllDocumentation|RerunExamples|ResidualIntersections|ResLengthThree|ResolutionsOfStanleyReisnerRings|restart|Result|Resultants|returnCode|Reverse|RevLex|RHom|Right|RInterface|rootPath|rootURI|RunDirectory|RunExamples|RunExternalM2|SagbiGbDetection|Saturation|SaturationMap|Schubert2|SchurComplexes|SchurFunctors|SchurRings|SchurVeronese|SCMAlgebras|scriptCommandLine|SCSCP|SectionRing|SeeAlso|SegreClasses|SemidefiniteProgramming|Seminormalization|SeparateExec|Serialization|sheafExt|ShimoyamaYokoyama|showClassStructure|showStructure|showUserStructure|SimpleDoc|SimplicialComplexes|SimplicialDecomposability|SimplicialPosets|SimplifyFractions|SizeLimit|SkewCommutative|SlackIdeals|SLnEquivariantMatrices|SLPexpressions|Sort|SortStrategy|SourceCode|SourceRing|SpaceCurves|SparseResultants|SpechtModule|SpecialFanoFourfolds|SpectralSequences|SRdeformations|Standard|StartWithOneMinor|StatePolytope|StatGraphs|stderr|stdio|StopBeforeComputation|stopIfError|StopIteration|StopWithMinimalGenerators|Strategy|Strict|StronglyStableIdeals|Style|SubalgebraBases|Subnodes|SubringLimit|subscript|Sugarless|SumsOfSquares|SuperLinearAlgebra|superscript|SVDComplexes|SwitchingFields|SymbolicPowers|SymmetricPolynomials|Synopsis|Syzygies|SyzygyLimit|SyzygyMatrix|SyzygyRows|Tableaux|TangentCone|TateOnProducts|TensorComplexes|TerraciniLoci|Test|testExample|TestIdeals|TeXmacs|Text|ThinSincereQuivers|ThreadedGB|Threads|Threshold|Topcom|topLevelMode|Tor|TorAlgebra|Toric|ToricHigherDirectImages|ToricInvariants|ToricTopology|ToricVectorBundles|Torsion|TorsionFree|TotalPairs|Tree|TriangularSets|Triangulations|Tries|Trim|Triplets|Tropical|TropicalToric|true|Truncate|Truncations|TSpreadIdeals|TypicalValue|typicalValues|Undo|Unique|Units|Unmixed|Up|UpdateOnly|UpperTriangular|Usage|UseCachedExampleOutput|UseHilbertFunction|UserMode|UseSyzygies|Valuations|Variable|VariableBaseName|Variables|Varieties|Vasconcelos|VectorFields|VectorGraphics|Verbose|Verbosity|Verify|VersalDeformations|Version|version|VerticalSpace|viewHelp|VirtualResolutions|Visualize|VNumber|WebApp|Weights|WeilDivisors|WeylAlgebra|WeylAlgebras|WeylGroups|WhitneyStratifications|Wrap|XML|yonedaSheafExtension)\b/}},530:()=>{!function(e){var t={pattern:/\\(?:\ |[^a-z@\s]|[a-z@]+\*?)/i},n={equation:{pattern:/(\$\$?)[^\$]+\1/m,alias:"function",inside:t},keyword:t};e.languages.bib={comment:/%.*/,special:{pattern:/(^\s*)@(?:preamble|string|comment(?=\s*[({]))/im,lookbehind:!0,alias:"important"},"class-name":{pattern:/(^\s*)@[a-z]+(?=\s*{)/im,lookbehind:!0},key:{pattern:/([,{]\s*)[^,={}'"\s]+(?=\s*[,}])/im,lookbehind:!0,alias:"regex"},property:{pattern:/([,{(]\s*)[^,={}'"\s]+(?=\s*=)/im,lookbehind:!0},string:{pattern:/([=#{}]\s*)(?:\d+|("|')(?:(?!\2)[^\\]|\\.)*\2|{(?:(?:{(?:(?:{(?:(?:{(?:[^{}])*})|(?:[^{}]))*})|(?:[^{}]))*})|(?:[^{}]))*})/im,lookbehind:!0,greedy:!0,inside:n},constant:{pattern:/([=#]\s*)[^,={}'"\s]+(?=\s*[#,}])/im,lookbehind:!0},symbol:/#/,punctuation:/[=,{}()]/},e.languages.bibtex=e.languages.bib}(Prism)},540:e=>{"use strict";e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},601:e=>{"use strict";e.exports=function(e){return e[1]}},659:e=>{"use strict";var t={};e.exports=function(e,n){var a=function(e){if(void 0===t[e]){var n=document.querySelector(e);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}t[e]=n}return t[e]}(e);if(!a)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");a.appendChild(n)}},825:e=>{"use strict";e.exports=function(e){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var t=e.insertStyleElement(e);return{update:function(n){!function(e,t,n){var a="";n.supports&&(a+="@supports (".concat(n.supports,") {")),n.media&&(a+="@media ".concat(n.media," {"));var i=void 0!==n.layer;i&&(a+="@layer".concat(n.layer.length>0?" ".concat(n.layer):""," {")),a+=n.css,i&&(a+="}"),n.media&&(a+="}"),n.supports&&(a+="}");var r=n.sourceMap;r&&"undefined"!=typeof btoa&&(a+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),t.styleTagTransform(a,e,t.options)}(t,e,n)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},848:(e,t,n)=>{var a=function(e){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,a={},i={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof r?new r(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);F+=k.value.length,k=k.next){var P=k.value;if(t.length>e.length)return;if(!(P instanceof r)){var A,C=1;if(b){if(!(A=o(S,F,e,y))||A.index>=e.length)break;var M=A.index,w=A.index+A[0].length,R=F;for(R+=k.value.length;M>=R;)R+=(k=k.next).value.length;if(F=R-=k.value.length,k.value instanceof r)continue;for(var T=k;T!==t.tail&&(Rd.reach&&(d.reach=D);var O=k.prev;if(L&&(O=c(t,O,L),F+=L.length),u(t,O,C),k=c(t,O,new r(m,f?i.tokenize(E,f):E,v,E)),I&&c(t,k,I),C>1){var B={cause:m+","+g,reach:D};s(e,t,n,k.prev,F,B),d&&B.reach>d.reach&&(d.reach=B.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function c(e,t,n){var a=t.next,i={value:n,prev:t,next:a};return t.next=i,a.prev=i,e.length++,i}function u(e,t,n){for(var a=t.next,i=0;i"+r.content+""},!e.document)return e.addEventListener?(i.disableWorkerMessageHandler||e.addEventListener("message",function(t){var n=JSON.parse(t.data),a=n.language,r=n.code,o=n.immediateClose;e.postMessage(i.highlight(r,i.languages[a],a)),o&&e.close()},!1),i):i;var d=i.util.currentScript();function m(){i.manual||i.highlightAll()}if(d&&(i.filename=d.src,d.hasAttribute("data-manual")&&(i.manual=!0)),!i.manual){var p=document.readyState;"loading"===p||"interactive"===p&&d&&d.defer?document.addEventListener("DOMContentLoaded",m):window.requestAnimationFrame?window.requestAnimationFrame(m):window.setTimeout(m,16)}return i}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=a),void 0!==n.g&&(n.g.Prism=a),a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var i={"included-cdata":{pattern://i,inside:n}};i["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var r={};r[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},a.languages.insertBefore("markup","cdata",r)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),a.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(){if(void 0!==a&&"undefined"!=typeof document){Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector);var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},t="data-src-status",n="loading",i="loaded",r="pre[data-src]:not(["+t+'="'+i+'"]):not(['+t+'="'+n+'"])';a.hooks.add("before-highlightall",function(e){e.selector+=", "+r}),a.hooks.add("before-sanity-check",function(o){var s=o.element;if(s.matches(r)){o.code="",s.setAttribute(t,n);var l=s.appendChild(document.createElement("CODE"));l.textContent="Loading…";var c=s.getAttribute("data-src"),u=o.language;if("none"===u){var d=(/\.(\w+)$/.exec(c)||[,"none"])[1];u=e[d]||d}a.util.setLanguage(l,u),a.util.setLanguage(s,u);var m=a.plugins.autoloader;m&&m.loadLanguages(u),function(e,n,r){var o=new XMLHttpRequest;o.open("GET",e,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?function(e){s.setAttribute(t,i);var n=function(e){var t=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"");if(t){var n=Number(t[1]),a=t[2],i=t[3];return a?i?[n,Number(i)]:[n,void 0]:[n,n]}}(s.getAttribute("data-range"));if(n){var r=e.split(/\r\n?|\n/g),o=n[0],c=null==n[1]?r.length:n[1];o<0&&(o+=r.length),o=Math.max(0,Math.min(o-1,r.length)),c<0&&(c+=r.length),c=Math.max(0,Math.min(c,r.length)),e=r.slice(o,c).join("\n"),s.hasAttribute("data-start")||s.setAttribute("data-start",String(o+1))}l.textContent=e,a.highlightElement(l)}(o.responseText):o.status>=400?r("✖ Error "+o.status+" while fetching file: "+o.statusText):r("✖ Error: File does not exist or is empty"))},o.send(null)}(c,0,function(e){s.setAttribute(t,"failed"),l.textContent=e})}}),a.plugins.fileHighlight={highlight:function(e){for(var t,n=(e||document).querySelectorAll(r),i=0;t=n[i++];)a.highlightElement(t)}};var o=!1;a.fileHighlight=function(){o||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),o=!0),a.plugins.fileHighlight.highlight.apply(this,arguments)}}}()}},t={};function n(a){var i=t[a];if(void 0!==i)return i.exports;var r=t[a]={id:a,exports:{}};return e[a](r,r.exports,n),r.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.nc=void 0,(()=>{"use strict";n(848),n(432),n(530);var e=n(72),t=n.n(e),a=n(825),i=n.n(a),r=n(659),o=n.n(r),s=n(56),l=n.n(s),c=n(540),u=n.n(c),d=n(113),m=n.n(d),p=n(415),g={};g.styleTagTransform=m(),g.setAttributes=l(),g.insert=o().bind(null,"head"),g.domAPI=i(),g.insertStyleElement=u(),t()(p.A,g),p.A&&p.A.locals&&p.A.locals})()})(); \ No newline at end of file +(()=>{var e={432(){Prism.languages.m2=Prism.languages.macaulay2={comment:[{pattern:/(^|[^\\])\-\*[\s\S]*?(?:\*\-|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\-\-.*/,lookbehind:!0,greedy:!0}],string:[{pattern:/"(?:\\[\s\S]|(?!")[^\\])*"/,greedy:!0},{pattern:/\/\/\/(\/(?!\/)|(?:\/\/)+(?!\/)|[^\/])*(?:\/\/)+\/(?!\/)/,greedy:!0}],keyword:/\b(?:SPACE|TEST|and|break|breakpoint|catch|continue|do|elapsedTime|elapsedTiming|else|except|for|from|global|if|in|list|local|new|not|of|or|profile|return|shield|step|symbol|then|threadLocal|threadVariable|throw|time|timing|to|trap|try|when|while|xor)\b/,"class-name":/\b(?:ANCHOR|Adjacent|AffineVariety|Analyzer|AngleBarList|Array|AssociativeExpression|AtomicInt|BLOCKQUOTE|BODY|BOLD|BR|BUTTON|Bag|BasicList|BettiTally|BinaryOperation|Boolean|CC|CCi|CDATA|CODE|COMMENT|CacheTable|CoherentSheaf|Command|CompiledFunction|CompiledFunctionBody|CompiledFunctionClosure|Complex|ComplexField|ComplexMap|Constant|DD|DIV|DL|DT|Database|Descent|Describe|Dictionary|DirectSum|Divide|DocumentTag|EM|Eliminate|EngineRing|Equation|Error|ExampleItem|Expression|File|FilePosition|FractionField|Function|FunctionApplication|FunctionBody|FunctionClosure|GaloisField|GeneralOrderedMonoid|GlobalDictionary|GradedModule|GradedModuleMap|GroebnerBasis|GroebnerBasisOptions|HEAD|HEADER1|HEADER2|HEADER3|HEADER4|HEADER5|HEADER6|HR|HREF|HTML|HashTable|HeaderType|Holder|Hybrid|Hypertext|HypertextContainer|HypertextParagraph|HypertextVoid|IFRAME|IMG|INDENT|INPUT|ITALIC|Ideal|ImmutableType|IndeterminateNumber|IndexedVariable|IndexedVariableTable|InexactField|InexactFieldFamily|InexactNumber|InfiniteNumber|IntermediateMarkUpType|Iterator|KBD|Keyword|LABEL|LATER|LI|LINK|LITERAL|List|LocalDictionary|LowerBound|MENU|META|Manipulator|MapExpression|MarkUpType|Matrix|MatrixExpression|MethodFunction|MethodFunctionBinary|MethodFunctionSingle|MethodFunctionWithOptions|Minus|Module|Monoid|MonoidElement|MonomialIdeal|MultigradedBettiTally|MutableHashTable|MutableList|MutableMatrix|Mutex|Net|NetFile|Nothing|Number|NumberedVerticalList|OL|OneExpression|Option|OptionTable|OrderedMonoid|PARA|PRE|Package|Parenthesize|Parser|Partition|PolynomialRing|Power|Product|ProductOrder|Program|ProgramRun|ProjectiveHilbertPolynomial|ProjectiveVariety|Pseudocode|PseudocodeClosure|QQ|QuotientRing|RR|RRi|RealField|Resolution|Ring|RingElement|RingFamily|RingMap|RowExpression|SAMP|SCRIPT|SMALL|SPAN|STRONG|STYLE|SUB|SUBSECTION|SUP|ScriptedFunctor|SelfInitializingType|Sequence|Set|SheafExpression|SheafMap|SheafOfRings|SparseMonomialVectorExpression|SparseVectorExpression|String|Subscript|Sum|SumOfTwists|Superscript|Symbol|SymbolBody|TABLE|TD|TEX|TH|TITLE|TO|TO2|TOH|TR|TT|Table|Tally|Task|TensorProduct|TestInput|Thing|Time|Type|UL|URL|VAR|Variety|Vector|VectorExpression|VerticalList|VirtualTally|VisibleList|WrapperType|ZZ|ZeroExpression)\b/,function:/\b(?:BesselJ|BesselY|Beta|Digamma|EXAMPLE|End|Fano|GCstats|GF|Gamma|Grassmannian|Hom|LLL|LUdecomposition|M2CODE|NNParser|Proj|QQParser|QRDecomposition|SVD|SYNOPSIS|Schubert|Spec|ZZParser|about|abs|accumulate|acos|acosh|acot|acoth|addCancelTask|addDependencyTask|addEndFunction|addHook|addStartTask|adjoint|agm|alarm|all|ambient|analyticSpread|ancestor|ancestors|andP|ann|annihilator|antipode|any|append|applicationDirectory|apply|applyKeys|applyPairs|applyTable|applyValues|apropos|arXiv|ascii|asin|asinh|ass|assert|associatedGradedRing|associatedPrimes|atEndOfFile|atan|atan2|atanh|augmentationMap|autoload|baseFilename|baseName|baseRing|basis|beginDocumentation|benchmark|betti|between|binomial|borel|cacheValue|cancelTask|canonicalBundle|canonicalMap|canonicalTruncation|capture|ceiling|centerString|changeBase|changeDirectory|char|charAnalyzer|characters|check|checkDegrees|chi|class|clean|clearEcho|code|codim|coefficient|coefficientRing|coefficients|cohomology|coimage|coker|cokernel|collectGarbage|columnAdd|columnMult|columnPermute|columnRankProfile|columnSwap|columnate|combine|commandInterpreter|commonRing|commonest|comodule|compareExchange|complement|complete|complex|component|components|compose|compositions|compress|concatenate|concentration|conductor|cone|conjugate|connectingExtMap|connectingMap|connectingTorMap|connectionCount|constParser|constantStrand|content|contract|conwayPolynomial|copy|copyDirectory|copyFile|cos|cosh|cot|cotangentSheaf|coth|cover|coverMap|cpuTime|createTask|csc|csch|currentColumnNumber|currentDirectory|currentPosition|currentRowNumber|currentTime|cylinder|deadParser|debug|debugError|decompose|deepSplice|default|degree|degreeGroup|degreeLength|degrees|degreesMonoid|degreesRing|delete|demark|denominator|depth|describe|det|determinant|diagonalMatrix|diameter|dictionary|diff|difference|dim|directProduct|directSum|disassemble|discriminant|dismiss|distinguished|divideByVariable|doc|document|drop|dual|eagonNorthcott|eagonNorthcottComplex|echoOff|echoOn|eigenvalues|eigenvectors|eint|elements|eliminate|endPackage|entries|epicResolutionMap|erase|erf|erfc|error|euler|eulers|even|examples|exchange|exec|exp|expectedReesIdeal|expm1|exponents|export|exportFrom|exportMutable|expression|extend|exteriorPower|factor|fileExecutable|fileExists|fileLength|fileMode|fileReadable|fileTime|fileWritable|fillMatrix|findFiles|findHeft|findProgram|findSynonyms|first|firstkey|fittingIdeal|flagLookup|flatten|flattenRing|flip|floor|fold|forceGB|fork|format|formation|frac|fraction|frames|freeResolution|fromDividedPowers|fromDual|functionBody|futureParser|gb|gbRemove|gbSnapshot|gcd|gcdCoefficients|gcdLLL|genera|generateAssertions|generator|generators|genericMatrix|genericSkewMatrix|genericSymmetricMatrix|gens|genus|get|getChangeMatrix|getGlobalSymbol|getIOThreadMode|getNetFile|getNonUnit|getPrimeWithRootOfUnity|getSymbol|getWWW|getc|getenv|globalAssign|globalAssignFunction|globalAssignment|globalReleaseFunction|gradedModule|gramm|graphIdeal|graphRing|groebnerBasis|groupID|hash|hashTable|headlines|heft|height|hermite|hilbertFunction|hilbertPolynomial|hilbertSeries|hold|homogenize|homology|homomorphism|homotopyMap|hooks|horizontalJoin|horseshoeResolution|html|httpHeaders|hypertext|icFracP|icFractions|icMap|icPIdeal|ideal|idealSheaf|idealizer|identity|image|imaginaryPart|importFrom|independentSets|index|indices|inducedMap|inducesWellDefinedMap|info|input|insert|installAssignmentMethod|installHilbertFunction|installMethod|installMinprimes|installPackage|installedPackages|instance|instances|integralClosure|integrate|intersect|intersectInP|intersection|interval|inverse|inverseErf|inversePermutation|inverseRegularizedBeta|inverseRegularizedGamma|inverseSystem|irreducibleCharacteristicSeries|irreducibleDecomposition|isANumber|isAffineRing|isBorel|isCanceled|isCommutative|isComplexMorphism|isConstant|isDirectSum|isDirectory|isEmpty|isExact|isField|isFinite|isFinitePrimeField|isFree|isFreeModule|isGlobalSymbol|isHomogeneous|isIdeal|isInfinite|isInjective|isInputFile|isIsomorphic|isIsomorphism|isLLL|isLinearType|isListener|isLocallyFree|isMember|isModule|isMonomialIdeal|isMutable|isNormal|isNullHomotopic|isNullHomotopyOf|isOpen|isOutputFile|isPolynomialRing|isPrimary|isPrime|isPrimitive|isProjective|isPseudoprime|isQuasiIsomorphism|isQuotientModule|isQuotientOf|isQuotientRing|isReady|isReal|isReduction|isRegularFile|isRing|isScalar|isShortExactSequence|isSkewCommutative|isSmooth|isSorted|isSquareFree|isStandardGradedPolynomialRing|isSubmodule|isSubquotient|isSubset|isSupportedInZeroLocus|isSurjective|isTable|isUnit|isVeryAmple|isWellDefined|isWeylAlgebra|isc|isomorphism|iterator|jacobian|jacobianDual|join|ker|kernel|kernelLLL|kernelOfLocalization|keys|kill|koszul|koszulComplex|last|lcm|leadCoefficient|leadComponent|leadMonomial|leadTerm|left|length|letterParser|lift|liftMapAlongQuasiIsomorphism|liftable|limitFiles|limitProcesses|lines|linkFile|listForm|listSymbols|lngamma|load|loadPackage|localDictionaries|localize|locate|lock|log|log1p|longExactSequence|lookup|lookupCount|lowerLeft|lowerRight|makeDirectory|makeDocumentTag|makePackageIndex|makeS2|map|markedGB|match|mathML|matrix|max|maxPosition|member|memoize|memoizeClear|memoizeValues|merge|mergePairs|method|methodOptions|methods|midpoint|min|minPosition|minPres|mingens|mingle|minimalBetti|minimalPresentation|minimalPrimes|minimalReduction|minimize|minimizeFilename|minors|minprimes|minus|mkdir|mod|module|modulo|monoid|monomialCurveIdeal|monomialIdeal|monomialSubideal|monomials|moveFile|multidegree|multidoc|multigraded|multiplicity|mutable|mutableIdentity|mutableMatrix|naiveTruncation|nanosleep|needs|needsPackage|net|netList|newClass|newCoordinateSystem|newNetFile|newPackage|newRing|next|nextPrime|nextkey|nonspaceAnalyzer|norm|normalCone|notImplemented|nullHomotopy|nullParser|nullSpace|nullhomotopy|numColumns|numRows|number|numcols|numerator|numeric|numericInterval|numgens|numrows|odd|oeis|ofClass|on|openDatabase|openDatabaseOut|openFiles|openIn|openInOut|openListener|openOut|openOutAppend|optP|optionalSignParser|options|orP|override|pack|package|packageTemplate|pad|pager|pairs|parallelApply|parent|parse|part|partition|partitions|parts|pdim|peek|permanents|permutations|pfaffian|pfaffians|pivots|plus|poincare|poincareN|polarize|poly|polylog|position|positions|power|powermod|precision|preimage|prepend|presentation|pretty|primaryComponent|primaryDecomposition|print|printString|printerr|processID|product|projectiveHilbertPolynomial|promote|protect|prune|pruneComplex|pruneDiff|pruneUnit|pseudoRemainder|pseudocode|pullback|pushForward|pushout|quotient|quotientRemainder|radical|radicalContainment|random|randomComplexMap|randomElement|randomKRationalPoint|randomMutableMatrix|randomSubset|rank|rays|read|readDirectory|readPackage|readlink|realPart|realpath|recursionDepth|reduceHilbert|reducedRowEchelonForm|reductionNumber|reesAlgebra|reesAlgebraIdeal|reesIdeal|regSeqInIdeal|regex|regexQuote|registerFinalizer|regularity|regularizedBeta|regularizedGamma|relations|relativizeFilename|remainder|remove|removeDirectory|removeFile|removeLowestDimension|reorganize|replace|res|reshape|resolution|resolutionMap|resultant|reverse|right|ring|ringFromFractions|roots|rotate|round|rowAdd|rowMult|rowPermute|rowRankProfile|rowSwap|rsort|run|runHooks|runLengthEncode|runProgram|same|saturate|scan|scanKeys|scanLines|scanPairs|scanValues|schedule|schreyerOrder|searchPath|sec|sech|seeParsing|select|selectInSubring|selectKeys|selectPairs|selectValues|selectVariables|separate|separateRegexp|sequence|serialNumber|set|setEcho|setGroupID|setIOExclusive|setIOSynchronized|setIOUnSynchronized|setRandomSeed|setup|setupEmacs|setupLift|setupPromote|sheaf|sheafHom|show|showHtml|showTex|shuffle|sign|simpleDocFrob|sin|singularLocus|sinh|size|size2|sleep|smithNormalForm|solve|someTerms|sort|sortColumns|source|span|specialFiber|specialFiberIdeal|splice|splitWWW|sqrt|stack|stacksProject|stacktrace|standardForm|standardPairs|stashValue|status|store|style|sub|sublists|submatrix|submatrixByDegrees|subquotient|subsets|substitute|substring|subtable|sum|super|support|switch|sylvesterMatrix|symbolBody|symlinkDirectory|symlinkFile|symmetricAlgebra|symmetricAlgebraIdeal|symmetricKernel|symmetricPower|synonym|syz|table|take|tally|tan|tangentCone|tangentSheaf|tanh|target|taskResult|temporaryFileName|tensor|tensorAssociativity|tensorCommutativity|terminalParser|terms|testHunekeQuestion|tests|tex|texMath|times|toAbsolutePath|toCC|toCCi|toChainComplex|toDividedPowers|toDual|toExternalString|toField|toList|toLower|toMutableComplex|toRR|toRRi|toSequence|toString|toUpper|top|topCoefficients|topComponents|torSymmetry|trace|transpose|trim|truncate|truncateOutput|tryLock|tutorial|ultimate|unbag|uncurry|undocumented|uniform|uninstallAllPackages|uninstallPackage|union|unique|uniquePermutations|unlock|unsequence|unstack|upperLeft|upperRight|urlEncode|use|userSymbols|utf8|utf8check|utf8substring|validate|value|values|variety|vars|vector|versalEmbedding|wait|wedgeProduct|weightRange|whichGm|width|wikipedia|wrap|yonedaExtension|yonedaMap|yonedaProduct|youngest|zero|zeta)\b/,constant:/\b(?:A1BrouwerDegrees|AbstractSimplicialComplexes|AbstractToricVarieties|Acknowledgement|AdditionalPaths|AdjointIdeal|AdjunctionForSurfaces|AfterEval|AfterNoPrint|AfterPrint|AInfinity|AlgebraicSplines|Algorithm|Alignment|AllCodimensions|AllMarkovBases|allowableThreads|AnalyzeSheafOnP1|applicationDirectorySuffix|argument|Ascending|AssociativeAlgebras|Authors|AuxiliaryFiles|backtrace|Bareiss|Base|BaseFunction|baseRings|BaseRow|BasisElementLimit|Bayer|BeforePrint|BeginningMacaulay2|Benchmark|BernsteinSato|Bertini|BettiCharacters|BGG|BIBasis|Binary|Binomial|BinomialEdgeIdeals|Binomials|BKZ|blockMatrixForm|Body|BoijSoederberg|Book3264Examples|BooleanGB|Boundary|Boxes|Brackets|Browse|Bruns|cache|CacheExampleOutput|CallLimit|CannedExample|CatalanConstant|Caveat|CellularResolutions|Center|Certification|ChainComplexExtras|ChainComplexOperations|ChangeMatrix|CharacteristicClasses|CheckDocumentation|Chordal|Citation|cite|Classic|clearAll|clearOutput|close|closeIn|closeOut|ClosestFit|Code|CodimensionLimit|CodingTheory|CoefficientRing|Cofactor|CohenEngine|CohenTopLevel|CohomCalg|CoincidentRootLoci|commandLine|compactMatrixForm|Complement|CompleteIntersection|CompleteIntersectionResolutions|Complexes|Concentration|ConductorElement|Configuration|ConformalBlocks|ConnectionMatrices|Consequences|Constants|Contributors|ConvexInterface|ConwayPolynomials|copyright|Core|CorrespondenceScrolls|CotangentSchubert|coverageSummary|CpMackeyFunctors|Cremona|currentFileDirectory|currentFileName|currentLayout|currentPackage|Cycle|Cyclotomic|Date|dd|DebuggingMode|debuggingMode|debugLevel|DecomposableSparseSystems|Decompose|Default|defaultPrecision|Degree|DegreeGroup|DegreeLift|DegreeLimit|DegreeMap|DegreeOrder|DegreeRank|Degrees|Dense|Density|Depth|Descending|Description|DeterminantalRepresentations|DGAlgebras|dictionaryPath|DiffAlg|Direction|Dispatch|DivideConquer|DividedPowers|Dmodules|docExample|docTemplate|Down|Dynamic|EagonResolution|EdgeIdeals|edit|EigenSolver|EisenbudHunekeVasconcelos|Elimination|EliminationMatrices|EliminationTemplates|EllipticCurves|EllipticIntegrals|Email|end|endl|Engine|engineDebugLevel|EngineTests|EnumerationCurves|environment|EquivariantGB|errorDepth|EulerConstant|Example|ExampleFiles|ExampleSystems|Exclude|exit|Ext|ExteriorExtensions|ExteriorIdeals|ExteriorModules|false|FastMinors|FastNonminimal|FGLM|fileDictionaries|fileExitHooks|FileName|FindOne|FiniteFittingIdeals|First|FirstPackage|FlatMonoid|Flexible|flush|FollowLinks|ForeignFunctions|FormalGroupLaws|Format|FourierMotzkin|FourTiTwo|fpLLL|FreeToExact|FrobeniusThresholds|FunctionFieldDesingularization|GameTheory|GBDegrees|gbTrace|GenerateAssertions|Generic|GenericInitialIdeal|GeometricDecomposability|gfanInterface|Givens|GKMVarieties|GLex|Global|GlobalAssignHook|globalAssignmentHooks|GlobalHookStore|GlobalReleaseHook|GlobalSectionLimit|Gorenstein|GradedLieAlgebras|GraphicalModels|GraphicalModelsMLE|Graphics|Graphs|GRevLex|GroebnerStrata|GroebnerWalk|GroupLex|GroupRevLex|GTZ|Hadamard|handleInterrupts|HardDegreeLimit|Heading|Headline|Heft|Height|help|Hermite|Hermitian|HH|hh|HigherCIOperators|HighestWeights|Hilbert|HodgeIntegrals|HolonomicSystems|homeDirectory|HomePage|Homogeneous|Homogeneous2|Homogenization|HomologicalAlgebraPackage|HomotopyLieAlgebra|HorizontalSpace|HyperplaneArrangements|id|IgnoreExampleErrors|ii|IncidenceCorrespondenceCohomology|incomparable|Increment|indeterminate|Index|indexComponents|infinity|InfoDirSection|infoHelp|Inhomogeneous|Inputs|InstallPrefix|IntegerProgramming|IntegralClosure|InternalDegree|interpreterDepth|Intersection|InvariantRing|InverseMethod|Inverses|InverseSystems|Invertible|InvolutiveBases|Isomorphism|Item|Iterate|Jacobian|Jets|Join|JSON|JSONRPC|K3Carpets|K3Surfaces|Keep|KeepFiles|KeepZeroes|Key|Keywords|Kronecker|KustinMiller|lastMatch|LatticePolytopes|Layout|Left|LengthLimit|Lex|LexIdeals|LieAlgebraRepresentations|Limit|Linear|LinearAlgebra|LinearTruncations|lineNumber|listLocalSymbols|listUserSymbols|LLLBases|loadDepth|LoadDocumentation|loadedFiles|loadedPackages|Local|LocalRings|LongPolynomial|M0nbar|Macaulay2Doc|MacaulayPosets|Maintainer|MakeDocumentation|MakeHTML|MakeInfo|MakePDF|MapleInterface|Markov|MatchingFields|MatrixFactorizations|MatrixSchubert|Matroids|maxAllowableThreads|maxExponent|MaximalRank|MaxReductionCount|MCMApproximations|MergeTeX|minExponent|MinimalGenerators|MinimalMatrix|minimalPresentationMap|minimalPresentationMapInv|MinimalPrimes|Minimize|minimizingMap|MinimumVersion|Miura|MixedMultiplicity|ModuleDeformations|MonodromySolver|Monomial|MonomialAlgebras|MonomialIntegerPrograms|MonomialOrbits|MonomialOrder|Monomials|MonomialSize|MRDI|Msolve|MultigradedBGG|MultigradedImplicitization|MultiGradedRationalMap|MultiplicitySequence|MultiplierIdeals|MultiplierIdealsDim2|MultiprojectiveVarieties|NAGtypes|Name|Nauty|NautyGraphs|NCAlgebra|NCLex|NewFromMethod|newline|NewMethod|NewOfFromMethod|NewOfMethod|nil|Node|NoetherianOperators|NoetherNormalization|Nonminimal|NonminimalWithGB|NonPrincipalTestIdeals|NoPrint|Normaliz|NormalToricVarieties|NotANumber|notify|NTL|null|nullaryMethods|NumericalAlgebraicGeometry|NumericalCertification|NumericalImplicitization|NumericalLinearAlgebra|NumericalSchubertCalculus|NumericalSemigroups|NumericSolutions|numTBBThreads|OIGroebnerBases|OldChainComplexes|OnlineLookup|OO|oo|ooo|oooo|OpenMath|operatorAttributes|OptionalComponentsPresent|Options|Order|order|Oscillators|OutputDictionary|Outputs|OverField|OverZZ|PackageCitations|PackageDictionary|PackageExports|PackageImports|PackageTemplate|Padic|PairLimit|PairsRemaining|ParallelF4|ParallelizeByDegree|Parametrization|Parsing|path|PathSignatures|PencilsOfQuadrics|Permanents|Permutations|PHCpack|PhylogeneticTrees|pi|PieriMaps|PlaneCurveLinearSeries|PlaneCurveSingularities|Points|Polyhedra|Polymake|PolyominoIdeals|Posets|Position|PositivityToricBundles|POSIX|Postfix|Pre|Precision|Prefix|prefixDirectory|prefixPath|PrimaryDecomposition|PrimaryTag|PrimitiveElement|Print|printingAccuracy|printingLeadLimit|printingPrecision|printingSeparator|printingTimeLimit|printingTrailLimit|printWidth|Probability|profileSummary|programPaths|Projective|Prune|PruningMap|pruningMap|PseudomonomialPrimaryDecomposition|Pullback|pullbackMaps|PushForward|pushoutMaps|Python|QthPower|QuadraticIdealExamplesByRoos|Quasidegrees|QuaternaryQuartics|QuillenSuslin|quit|Quotient|Radical|RadicalCodim1|RaiseError|RandomCanonicalCurves|RandomComplexes|RandomCurves|RandomCurvesOverVerySmallFiniteFields|RandomGenus14Curves|RandomIdeals|RandomMonomialIdeals|RandomObjects|RandomPlaneCurves|RandomPoints|RandomSpaceCurves|Range|RationalMaps|RationalPoints|RationalPoints2|ReactionNetworks|RealFP|RealQP|RealQP1|RealRoots|RealRR|RealXD|recursionLimit|Reduce|ReesAlgebra|References|ReflexivePolytopesDB|Regularity|RelativeCanonicalResolution|Reload|RemakeAllDocumentation|RerunExamples|ResidualIntersections|ResLengthThree|ResolutionsOfStanleyReisnerRings|restart|Result|Resultants|returnCode|Reverse|RevLex|Right|RInterface|rootPath|rootURI|RunDirectory|RunExamples|RunExternalM2|SagbiGbDetection|Saturation|SaturationMap|Schubert2|SchurComplexes|SchurFunctors|SchurRings|SchurVeronese|SCMAlgebras|scriptCommandLine|SCSCP|SectionRing|SeeAlso|SegreClasses|SemidefiniteProgramming|Seminormalization|SeparateExec|Serialization|sheafExt|ShimoyamaYokoyama|showClassStructure|showStructure|showUserStructure|SimpleDoc|SimplicialComplexes|SimplicialDecomposability|SimplicialModules|SimplicialPosets|SimplifyFractions|SizeLimit|SkewCommutative|SlackIdeals|SLnEquivariantMatrices|SLPexpressions|Sort|SortStrategy|SourceCode|SourceRing|SpaceCurves|SparseResultants|SpechtModule|SpecialFanoFourfolds|SpectralSequences|SRdeformations|Standard|StartWithOneMinor|StatePolytope|StatGraphs|stderr|stdio|StopBeforeComputation|stopIfError|StopIteration|StopWithMinimalGenerators|Strategy|Strict|StronglyStableIdeals|Style|SubalgebraBases|Subnodes|SubringLimit|subscript|Sugarless|SumsOfSquares|SuperLinearAlgebra|superscript|SVDComplexes|SwitchingFields|SymbolicPowers|SymmetricPolynomials|Synopsis|Syzygies|SyzygyLimit|SyzygyMatrix|SyzygyRows|Tableaux|TangentCone|TateOnProducts|TensorComplexes|TerraciniLoci|Test|testExample|TestIdeals|TeXmacs|Text|ThinSincereQuivers|ThreadedGB|Threads|Threshold|Topcom|topLevelMode|Tor|TorAlgebra|Toric|ToricHigherDirectImages|ToricInvariants|ToricTopology|ToricVectorBundles|Torsion|TorsionFree|TotalPairs|Tree|TriangularSets|Triangulations|Tries|Trim|Triplets|Tropical|TropicalToric|true|Truncate|Truncations|TSpreadIdeals|TypicalValue|typicalValues|Undo|Unique|Units|UnitTest|Unmixed|Up|UpdateOnly|UpperTriangular|Usage|UseCachedExampleOutput|UseHilbertFunction|UserMode|UseSyzygies|UseTarget|Valuations|Variable|VariableBaseName|Variables|Varieties|Vasconcelos|VectorFields|VectorGraphics|Verbose|Verbosity|Verify|VersalDeformations|Version|version|VerticalSpace|viewHelp|VirtualResolutions|Visualize|VNumber|WebApp|Weights|WeilDivisors|WeylAlgebra|WeylAlgebras|WeylGroups|WhitneyStratifications|WittVectors|Wrap|XML)\b/}},415(e,t,n){"use strict";n.d(t,{A:()=>s});var a=n(601),i=n.n(a),r=n(314),o=n.n(r)()(i());o.push([e.id,'/* PrismJS 1.14.0\nhttp://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */\n/**\n * prism.js theme for Macaulay2\n 2018 P. Zinn-Justin\n */\n\n/*\ncode[class*="language-"],\npre[class*="language-"] {\n\tcolor: black;\n\tbackground: none;\n\ttext-shadow: 0 1px white;\n\tfont-family: Consolas, Monaco, \'Andale Mono\', \'Ubuntu Mono\', monospace;\n\ttext-align: left;\n\twhite-space: pre;\n\tword-spacing: normal;\n\tword-break: normal;\n\tword-wrap: normal;\n\tline-height: 1.5;\n\n\t-moz-tab-size: 4;\n\t-o-tab-size: 4;\n\ttab-size: 4;\n\n\t-webkit-hyphens: none;\n\t-moz-hyphens: none;\n\t-ms-hyphens: none;\n\thyphens: none;\n}\n\npre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,\ncode[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {\n\ttext-shadow: none;\n\tbackground: #b3d4fc;\n}\n\npre[class*="language-"]::selection, pre[class*="language-"] ::selection,\ncode[class*="language-"]::selection, code[class*="language-"] ::selection {\n\ttext-shadow: none;\n\tbackground: #b3d4fc;\n}\n\n@media print {\n\tcode[class*="language-"],\n\tpre[class*="language-"] {\n\t\ttext-shadow: none;\n\t}\n}\n*/\n/* Code blocks */\n/*\npre[class*="language-"] {\n\tpadding: 1em;\n\tmargin: .5em 0;\n\toverflow: auto;\n}\n\n:not(pre) > code[class*="language-"],\npre[class*="language-"] {\n\tbackground: #f5f2f0;\n}\n*/\n/* Inline code */\n/*\n:not(pre) > code[class*="language-"] {\n\tpadding: .1em;\n\tborder-radius: .3em;\n\twhite-space: normal;\n}\n*/\n.token.comment {\n color: #607080;\n}\n\n/*\n.token.punctuation {\n\tcolor: #999;\n}\n\n.namespace {\n\topacity: .7;\n}\n*/\n\n.token.constant {\n /*\tcolor: #008b8b; */\n color: #004060;\n}\n\n.token.net,\n.token.string {\n\tcolor: #8b2252;\n}\n\n/*\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n\tcolor: #9a6e3a;\n\tbackground: hsla(0, 0%, 100%, .5);\n}\n*/\n\n.token.keyword {\n color: #a020f0;\n}\n\n.token.function {\n color: #0000ff;\n}\n.token.class-name {\n /* color: #228b22; */\n color: #1c701c;\n}\n\n/*\n.token.regex,\n.token.important,\n.token.variable {\n\tcolor: #e90;\n}\n\n.token.important,\n.token.bold {\n\tfont-weight: bold;\n}\n.token.italic {\n\tfont-style: italic;\n}\n\n.token.entity {\n\tcursor: help;\n}\n*/\n',""]);const s=o},314(e){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n="",a=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),a&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),a&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n}).join("")},t.i=function(e,n,a,i,r){"string"==typeof e&&(e=[[null,e,void 0]]);var o={};if(a)for(var s=0;s0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=r),n&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=n):u[2]=n),i&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=i):u[4]="".concat(i)),t.push(u))}},t}},601(e){"use strict";e.exports=function(e){return e[1]}},530(){!function(e){var t={pattern:/\\(?:\ |[^a-z@\s]|[a-z@]+\*?)/i},n={equation:{pattern:/(\$\$?)[^\$]+\1/m,alias:"function",inside:t},keyword:t};e.languages.bib={comment:/%.*/,special:{pattern:/(^\s*)@(?:preamble|string|comment(?=\s*[({]))/im,lookbehind:!0,alias:"important"},"class-name":{pattern:/(^\s*)@[a-z]+(?=\s*{)/im,lookbehind:!0},key:{pattern:/([,{]\s*)[^,={}'"\s]+(?=\s*[,}])/im,lookbehind:!0,alias:"regex"},property:{pattern:/([,{(]\s*)[^,={}'"\s]+(?=\s*=)/im,lookbehind:!0},string:{pattern:/([=#{}]\s*)(?:\d+|("|')(?:(?!\2)[^\\]|\\.)*\2|{(?:(?:{(?:(?:{(?:(?:{(?:[^{}])*})|(?:[^{}]))*})|(?:[^{}]))*})|(?:[^{}]))*})/im,lookbehind:!0,greedy:!0,inside:n},constant:{pattern:/([=#]\s*)[^,={}'"\s]+(?=\s*[#,}])/im,lookbehind:!0},symbol:/#/,punctuation:/[=,{}()]/},e.languages.bibtex=e.languages.bib}(Prism)},848(e,t,n){var a=function(e){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,a={},i={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof r?new r(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);F+=S.value.length,S=S.next){var P=S.value;if(t.length>e.length)return;if(!(P instanceof r)){var M,C=1;if(b){if(!(M=o(k,F,e,y))||M.index>=e.length)break;var A=M.index,w=M.index+M[0].length,R=F;for(R+=S.value.length;A>=R;)R+=(S=S.next).value.length;if(F=R-=S.value.length,S.value instanceof r)continue;for(var T=S;T!==t.tail&&(Rd.reach&&(d.reach=D);var O=S.prev;if(L&&(O=c(t,O,L),F+=L.length),u(t,O,C),S=c(t,O,new r(p,f?i.tokenize(E,f):E,v,E)),I&&c(t,S,I),C>1){var B={cause:p+","+g,reach:D};s(e,t,n,S.prev,F,B),d&&B.reach>d.reach&&(d.reach=B.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function c(e,t,n){var a=t.next,i={value:n,prev:t,next:a};return t.next=i,a.prev=i,e.length++,i}function u(e,t,n){for(var a=t.next,i=0;i"+r.content+""},!e.document)return e.addEventListener?(i.disableWorkerMessageHandler||e.addEventListener("message",function(t){var n=JSON.parse(t.data),a=n.language,r=n.code,o=n.immediateClose;e.postMessage(i.highlight(r,i.languages[a],a)),o&&e.close()},!1),i):i;var d=i.util.currentScript();function p(){i.manual||i.highlightAll()}if(d&&(i.filename=d.src,d.hasAttribute("data-manual")&&(i.manual=!0)),!i.manual){var m=document.readyState;"loading"===m||"interactive"===m&&d&&d.defer?document.addEventListener("DOMContentLoaded",p):window.requestAnimationFrame?window.requestAnimationFrame(p):window.setTimeout(p,16)}return i}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=a),void 0!==n.g&&(n.g.Prism=a),a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var i={"included-cdata":{pattern://i,inside:n}};i["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var r={};r[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},a.languages.insertBefore("markup","cdata",r)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),a.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(){if(void 0!==a&&"undefined"!=typeof document){Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector);var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},t="data-src-status",n="loading",i="loaded",r="pre[data-src]:not(["+t+'="'+i+'"]):not(['+t+'="'+n+'"])';a.hooks.add("before-highlightall",function(e){e.selector+=", "+r}),a.hooks.add("before-sanity-check",function(o){var s=o.element;if(s.matches(r)){o.code="",s.setAttribute(t,n);var l=s.appendChild(document.createElement("CODE"));l.textContent="Loading…";var c=s.getAttribute("data-src"),u=o.language;if("none"===u){var d=(/\.(\w+)$/.exec(c)||[,"none"])[1];u=e[d]||d}a.util.setLanguage(l,u),a.util.setLanguage(s,u);var p=a.plugins.autoloader;p&&p.loadLanguages(u),function(e,n,r){var o=new XMLHttpRequest;o.open("GET",e,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?function(e){s.setAttribute(t,i);var n=function(e){var t=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"");if(t){var n=Number(t[1]),a=t[2],i=t[3];return a?i?[n,Number(i)]:[n,void 0]:[n,n]}}(s.getAttribute("data-range"));if(n){var r=e.split(/\r\n?|\n/g),o=n[0],c=null==n[1]?r.length:n[1];o<0&&(o+=r.length),o=Math.max(0,Math.min(o-1,r.length)),c<0&&(c+=r.length),c=Math.max(0,Math.min(c,r.length)),e=r.slice(o,c).join("\n"),s.hasAttribute("data-start")||s.setAttribute("data-start",String(o+1))}l.textContent=e,a.highlightElement(l)}(o.responseText):o.status>=400?r("✖ Error "+o.status+" while fetching file: "+o.statusText):r("✖ Error: File does not exist or is empty"))},o.send(null)}(c,0,function(e){s.setAttribute(t,"failed"),l.textContent=e})}}),a.plugins.fileHighlight={highlight:function(e){for(var t,n=(e||document).querySelectorAll(r),i=0;t=n[i++];)a.highlightElement(t)}};var o=!1;a.fileHighlight=function(){o||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),o=!0),a.plugins.fileHighlight.highlight.apply(this,arguments)}}}()},72(e){"use strict";var t=[];function n(e){for(var n=-1,a=0;a0?" ".concat(n.layer):""," {")),a+=n.css,i&&(a+="}"),n.media&&(a+="}"),n.supports&&(a+="}");var r=n.sourceMap;r&&"undefined"!=typeof btoa&&(a+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),t.styleTagTransform(a,e,t.options)}(t,e,n)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},113(e){"use strict";e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}}},t={};function n(a){var i=t[a];if(void 0!==i)return i.exports;var r=t[a]={id:a,exports:{}};return e[a](r,r.exports,n),r.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.nc=void 0,(()=>{"use strict";n(848),n(432),n(530);var e=n(72),t=n.n(e),a=n(825),i=n.n(a),r=n(659),o=n.n(r),s=n(56),l=n.n(s),c=n(540),u=n.n(c),d=n(113),p=n.n(d),m=n(415),g={};g.styleTagTransform=p(),g.setAttributes=l(),g.insert=o().bind(null,"head"),g.domAPI=i(),g.insertStyleElement=u(),t()(m.A,g),m.A&&m.A.locals&&m.A.locals})()})(); \ No newline at end of file diff --git a/M2/Macaulay2/packages/SubalgebraBases/sagbi-functions.m2 b/M2/Macaulay2/packages/SubalgebraBases/sagbi-functions.m2 index de7b8cdf631..0d04ac2d54f 100644 --- a/M2/Macaulay2/packages/SubalgebraBases/sagbi-functions.m2 +++ b/M2/Macaulay2/packages/SubalgebraBases/sagbi-functions.m2 @@ -553,7 +553,7 @@ subringIntersection(Subring, Subring) := IntersectedSubring => opts -> (S1, S2) -- The intersection of S1 and S2 is generated by -- the elements SB|_Q of SB that do not contain t := p_0 -- If SB is a finite sagbi basis then the intersection computation - -- is verrified to be correct! + -- is verified to be correct! -- In this case, the elements SB|_Q form a sagbi basis -- for the intersection so use forceSB -- Note that, if the intersection of S1 and S2 diff --git a/M2/Macaulay2/packages/TSpreadIdeals.m2 b/M2/Macaulay2/packages/TSpreadIdeals.m2 index 01d903ea104..109c5422a4d 100644 --- a/M2/Macaulay2/packages/TSpreadIdeals.m2 +++ b/M2/Macaulay2/packages/TSpreadIdeals.m2 @@ -7,7 +7,7 @@ Authors => {{Name => "Luca Amata", Email => "lamata@unime.it", HomePage => "http }, Headline => "t-spread ideals of a polynomial ring", Keywords => {"Commutative Algebra"}, -PackageImports => {"OldChainComplexes"}, +PackageImports => {"Complexes"}, DebuggingMode => false ) diff --git a/M2/Macaulay2/packages/Tableaux.m2 b/M2/Macaulay2/packages/Tableaux.m2 index 115b46d1bc6..2c6a8909f9b 100644 --- a/M2/Macaulay2/packages/Tableaux.m2 +++ b/M2/Macaulay2/packages/Tableaux.m2 @@ -1,10 +1,10 @@ newPackage( "Tableaux", - Version => "0.5", - Date => "July 22, 2025", + Version => "0.6", + Date => "January 13, 2026", Authors => { - {Name => "John Graf", Email => "jrgraf@alumni.ncsu.edu", HomePage => "https://j-graf.github.io/"}}, - Headline => "constructing skew tableaux", + {Name => "John Graf", Email => "jrgraf@udel.edu", HomePage => "https://j-graf.github.io/"}}, + Headline => "constructing Young tableaux", Keywords => {"Combinatorics"}, AuxiliaryFiles => true, DebuggingMode => false, @@ -12,37 +12,44 @@ newPackage( --Configuration => {"Convention" => "English", "TexPackage" => "aTableau"} ) -export {"SkewTableau", "skewTableau", - "youngDiagram", "ferrersDiagram", "drawInnerShape", - "skewShape", "standardize", "rowEntries", "columnEntries", "rowRange", "columnRange", - "isWeaklyDecreasing", "isNonnegative", +export {"YoungTableau", "youngTableau", + "youngDiagram", "ferrersDiagram", "drawInnerShape", "verticalNet", "horizontalNet", + "skewShape", "shape", "outerShape", "innerShape", + "standardize", "rowEntries", "columnEntries", "rowRange", "columnRange", + "isSkew", "isWeaklyDecreasing", "isNonnegative", "isStandard", "isSemistandard", "toPosition", "toIndex", "positionList", "applyEntries", "applyPositions", "verticalConcatenate", "shift", "unshift", - "boxContent", "hookLength", + "boxContent", "hookLength", "rowStabilizer", "columnStabilizer", "isCorner", + "readingWord" } -export {"YoungTableau", "youngTableau", - "shape"} +export {"MutableYoungTableau", "mutableYoungTableau"} + +export {"Tabloid", "tabloid", + "representative"} -export {"allSemistandardTableaux", "numSemistandardTableaux"} +export {"allSemistandardTableaux", "numSemistandardTableaux", "randomSemistandardTableau", + "allStandardTableaux", "numStandardTableaux", "randomStandardTableau", + "allTabloids", "numTabloids", "randomTabloid", + "allSubPartitions", "toPartitionChain"} --- Implementation of class SkewTableau +-- Implementation of class YoungTableau -load "Tableaux/SkewTableaux.m2" +load "Tableaux/YoungTableaux.m2" --- Implementation of subclass YoungTableau +-- Implementation of subclass MutableYoungTableau -load "Tableaux/YoungTableaux.m2" +load "Tableaux/MutableYoungTableaux.m2" -- TODO: Implementation of subclass Tabloid --- load "Tableaux/Tabloids.m2" +load "Tableaux/Tabloids.m2" diff --git a/M2/Macaulay2/packages/Tableaux/MutableYoungTableaux.m2 b/M2/Macaulay2/packages/Tableaux/MutableYoungTableaux.m2 new file mode 100644 index 00000000000..805e5f00d9b --- /dev/null +++ b/M2/Macaulay2/packages/Tableaux/MutableYoungTableaux.m2 @@ -0,0 +1,42 @@ + +MutableYoungTableau = new Type of YoungTableau + +mutableYoungTableau = method(TypicalValue => MutableYoungTableau) +mutableYoungTableau (Partition,Partition,List) := (lam,mu,theList) -> ( + (lam',mu') := standardize (lam,mu); + numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); + + if (numBoxesNeeded != #theList) then error "partition sizes do not match with the length of the list"; + if any(theList, theElt -> theElt === null) then error "filling must not contain null entries"; + + new MutableYoungTableau from { + "outerShape" => lam, + "innerShape" => mu, + values => new MutableList from theList + } + ) +mutableYoungTableau (Sequence,List) := (theShape,theList) -> ( + (lam,mu) := theShape; + + mutableYoungTableau(lam,mu,theList) + ) +mutableYoungTableau (Partition,List) := (lam,theList) -> ( + mutableYoungTableau(lam,new Partition from {},theList) + ) +mutableYoungTableau (Partition,Partition) := (lam,mu) -> ( + (lam',mu') := standardize (lam,mu); + numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); + + mutableYoungTableau(lam,mu,toList(numBoxesNeeded:"")) + ) +mutableYoungTableau Partition := lam -> ( + numBoxesNeeded := sum for i from 0 to #lam-1 list abs(lam#i); + + mutableYoungTableau(lam, new Partition from {}, toList(numBoxesNeeded:"")) + ) + +MutableYoungTableau_Sequence = (T,thePosition,newBox) -> ( + (entries T)#(toIndex(T,thePosition)) = newBox; + + newBox + ) diff --git a/M2/Macaulay2/packages/Tableaux/SkewTableaux.m2 b/M2/Macaulay2/packages/Tableaux/SkewTableaux.m2 deleted file mode 100644 index bcf04bbe20e..00000000000 --- a/M2/Macaulay2/packages/Tableaux/SkewTableaux.m2 +++ /dev/null @@ -1,578 +0,0 @@ - - -SkewTableau = new Type of HashTable - -skewTableau = method(TypicalValue => SkewTableau) -skewTableau (Partition,Partition,List) := (lam,mu,theList) -> ( - (lam',mu') := standardize (lam,mu); - numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); - - if (numBoxesNeeded != #theList) then error "partition sizes do not match with the length of the list"; - if any(theList, theElt -> theElt === null) then error "filling must not contain null entries"; - - new SkewTableau from { - "outerShape" => lam, - "innerShape" => mu, - values => theList - } - ) -skewTableau (Sequence,List) := (theShape,theList) -> ( - (lam,mu) := theShape; - - skewTableau(lam,mu,theList) - ) -skewTableau (Partition,List) := (lam,theList) -> ( - skewTableau(lam,new Partition from {},theList) - ) -skewTableau (Partition,Partition) := (lam,mu) -> ( - (lam',mu') := standardize (lam,mu); - numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); - - skewTableau(lam,mu,toList(numBoxesNeeded:"")) - ) -skewTableau Partition := lam -> ( - numBoxesNeeded := sum for i from 0 to #lam-1 list abs(lam#i); - - skewTableau(lam, new Partition from {}, toList(numBoxesNeeded:"")) - ) - -skewShape = method(TypicalValue => Sequence) -skewShape SkewTableau := T -> ( - (T#"outerShape", T#"innerShape") - ) - -truncate (Partition,Partition) := theInput -> ( - (lam,mu) := theInput#1; - - lamNumTrailingZeros := # for i from 1 to #lam list (if lam#-i == 0 then 1 else break); - muNumTrailingZeros := # for i from 1 to #mu list (if mu#-i == 0 then 1 else break); - - lamShortened := (toList lam)_(toList(0..(#lam-1-lamNumTrailingZeros))); - muShortened := (toList mu)_(toList(0..(#mu-1-muNumTrailingZeros))); - - (new Partition from lamShortened, new Partition from muShortened) - ) - -pad (Partition,Partition) := (lam,mu) -> ( - maxLength := max(#lam,#mu); - - lamPadded := toList(lam)|toList((maxLength - #lam):0); - muPadded := toList(mu)|toList((maxLength - #mu):0); - - (new Partition from lamPadded, new Partition from muPadded) - ) - -standardize = method(TypicalValue => Sequence) -standardize (Partition,Partition) := (lam,mu) -> pad truncate (lam,mu) - -alignToZero = method(TypicalValue => SkewTableau) -alignToZero (Partition,Partition) := (lam,mu) -> ( - (lam,mu) = standardize (lam,mu); - minPart := min(toList mu | toList lam); - - lamAligned := for thePart in lam list thePart - minPart; - muAligned := for thePart in mu list thePart - minPart; - - (new Partition from lamAligned, new Partition from muAligned) - ) -alignToZero SkewTableau := T -> ( - (lamAligned,muAligned) := alignToZero standardize skewShape T; - skewTableau(lamAligned, muAligned, entries T) - ) - -hasNegativeRow = method(TypicalValue => Boolean) -hasNegativeRow SkewTableau := T -> ( - (lam,mu) := standardize skewShape T; - - any(0..(#lam-1), i -> mu#i > lam#i) - ) - --- swap parts when mu#i > lam#i -normalizeNegativeRows = method(TypicalValue => SkewTableau) -normalizeNegativeRows SkewTableau := T -> ( - if not hasNegativeRow T then return T; - - (lam,mu) := standardize skewShape T; - - lam' := new Partition from for i from 0 to #lam-1 list max(lam#i,mu#i); - mu' := new Partition from for i from 0 to #lam-1 list min(lam#i,mu#i); - - skewTableau(lam',mu',entries T) - ) - -listNegativeRows = method(TypicalValue => List) -listNegativeRows SkewTableau := T -> ( - (lam,mu) := standardize skewShape T; - - for i from 0 to #lam-1 list (mu#i > lam#i) - ) - --- retrieving rows and columns - -numcols SkewTableau := T -> ( - (lam,mu) := standardize skewShape normalizeNegativeRows T; - max(max toList lam - min toList mu,0) - ) - -numrows SkewTableau := T -> ( - (lam,mu) := standardize skewShape normalizeNegativeRows T; - #lam - ) - -columnRange = method(TypicalValue => Sequence) -columnRange SkewTableau := T -> ( - (lam,mu) := standardize skewShape normalizeNegativeRows T; - (min (toList mu | toList lam)..(max (toList lam | toList mu) - 1)) - ) - -rowRange = method(TypicalValue => Sequence) -rowRange SkewTableau := T -> (0..(numRows T - 1)) - -rowEntries = method(TypicalValue => List) -rowEntries (ZZ,SkewTableau) := (rowIndex,T) -> ( - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - if rowIndex < 0 then ( - rowIndex = #lam + rowIndex; - ); - if rowIndex >= #lam or rowIndex < 0 then error "index out of bounds"; - - numBoxesAbove := sum for i from 0 to rowIndex-1 list (lam#i-mu#i); - - for i from numBoxesAbove to numBoxesAbove + lam#rowIndex - mu#rowIndex - 1 list ((entries T)#i) - ) -rowEntries (SkewTableau,ZZ) := (T,rowIndex) -> rowEntries(rowIndex,T) - -SkewTableau^ZZ := (T,rowIndex) -> ( - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - if rowIndex < 0 then ( - rowIndex = #lam + rowIndex; - ); - if rowIndex >= #lam or rowIndex < 0 then error "index out of bounds"; - - colInds := columnRange T; - - theEntries := rowEntries(rowIndex,T); - - theRowOrderedLeft := for colIndex from min(colInds#0,0) to -1 list ( - if colIndex < mu#rowIndex or colIndex >= lam#rowIndex then null else ( - theEntries#(colIndex - mu#rowIndex) - ) - ); - - theRowOrderedRight := for colIndex from 0 to max(colInds#-1,0) list ( - if colIndex < mu#rowIndex or colIndex >= lam#rowIndex then null else ( - theEntries#(colIndex - mu#rowIndex) - ) - ); - - theRowOrderedRight|theRowOrderedLeft - ) - -SkewTableau_Sequence := (T,thePosition)->( - (rowIndex,colIndex) := thePosition; - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - if rowIndex < 0 or rowIndex >= numRows T then error("index "|toString(rowIndex)|" out of range"); - if colIndex < mu#rowIndex or colIndex >= lam#rowIndex then error "index "|toString(colIndex)|" out of range"; - - T^rowIndex#colIndex - ) - -SkewTableau_ZZ := (T,colIndex) -> ( - colInds := columnRange T; - - if colIndex < min(colInds#0,0) or colIndex > max(colInds#-1,0) then error "index out of bounds"; - - for rowIndex from 0 to numRows T - 1 list T^rowIndex#colIndex - ) - -columnEntries = method(TypicalValue => List) -columnEntries (ZZ,SkewTableau) := (colIndex,T) -> delete(null,T_colIndex) -columnEntries (SkewTableau,ZZ) := (T,colIndex) -> columnEntries(colIndex,T) - -toPosition = method(TypicalValue => Sequence) -toPosition (ZZ,SkewTableau) := (theIndex,T) -> ( - if theIndex < 0 then ( - theIndex = size T + theIndex; - ); - if theIndex < 0 or theIndex >= size T then error "index out of range"; - - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - numBoxesSeen := 0; - for rowIndex from 0 to numRows T - 1 do ( - for colIndex from mu#rowIndex to lam#rowIndex - 1 do ( - if numBoxesSeen == theIndex then return (rowIndex,colIndex); - numBoxesSeen = numBoxesSeen + 1; - ); - ); - ) -toPosition (SkewTableau,ZZ) := (T,theIndex) -> toPosition(theIndex,T) - -toIndex = method(TypicalValue => ZZ) -toIndex (Sequence,SkewTableau) := (thePosition,T) -> ( - (rowIndex,colIndex) := thePosition; - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - if rowIndex < 0 or rowIndex >= #lam or colIndex < mu#rowIndex or colIndex >= lam#rowIndex then error "index out of range"; - - numBoxesSeen := 0; - for theRowIndex from 0 to numRows T - 1 do ( - for theColIndex from mu#theRowIndex to lam#theRowIndex - 1 do ( - if (theRowIndex,theColIndex) == (rowIndex,colIndex) then return numBoxesSeen; - numBoxesSeen = numBoxesSeen + 1; - ); - ); - ) -toIndex (SkewTableau,Sequence) := (T,thePosition) -> toIndex(thePosition,T) - -positionList = method(TypicalValue => List) -positionList SkewTableau := T -> ( - for i from 0 to size T - 1 list toPosition(i,T) - ) - --- drawing - -isDrawnInner := false - -drawInnerShape = method(TypicalValue => Nothing) -drawInnerShape Boolean := theBool -> (isDrawnInner = theBool;) - -net SkewTableau := T -> ( - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - if #lam == 0 or (size T == 0 and not isDrawnInner) then return "∅"; - - isNegativeRow := listNegativeRows T; - - innerShapeString := if isDrawnInner then "â– " else " "; - - (muSmallest, lamLargest) := (min(min toList mu,0), max(max toList lam,0)); - colWidth := if #entries T == 0 then 1 else max for theBox in entries T list #toString(theBox); - colWidth = max {colWidth + 2,3}; - hasNegativeParts := any(toList(lam)|toList(mu), thePart -> thePart < 0); - - boxColumns := for colIndex from muSmallest to lamLargest-1 list ( - currCol := if colIndex >= mu#0 and colIndex < lam#0 then concatenate(colWidth:"─") else concatenate(colWidth:" "); - for rowIndex from 0 to #lam-1 do ( - isBox := colIndex >= mu#rowIndex and colIndex < lam#rowIndex; - isBoxBelow := rowIndex < #lam-1 and colIndex >= mu#(rowIndex+1) and colIndex < lam#(rowIndex+1); - - boxString := if isBox then ( - boxPadding := if isNegativeRow#rowIndex then "â–‘" else " "; - boxEntry := toString((rowEntries(rowIndex,T))#(colIndex-mu#rowIndex)); - if isNegativeRow#rowIndex and #boxEntry == 0 then boxEntry = "â–‘"; - boxPadding|boxEntry|boxPadding - ) else if (colIndex < 0 and colIndex >= lam#rowIndex) or (colIndex >= 0 and colIndex < mu#rowIndex) then ( - " "|concatenate((colWidth-2):innerShapeString)|" " - ) else ( - " " - ); - - belowString := if isBox or isBoxBelow then "─" else " "; - belowString = concatenate(colWidth:belowString); - - currCol = currCol||boxString||belowString; - ); - currCol - ); - - cornerChar := (leftUp,rightUp,leftDown,rightDown) -> ( - cornerHash := new HashTable from { - "0000" => " ", - "0001" => "┌", - "0010" => "â”", - "0011" => "┬", - "0100" => "â””", - "0101" => "├", - "0110" => "┼", - "0111" => "┼", - "1000" => "┘", - "1001" => "┼", - "1010" => "┤", - "1011" => "┼", - "1100" => "â”´", - "1101" => "┼", - "1110" => "┼", - "1111" => "┼" - }; - - theKey := concatenate({leftUp,rightUp,leftDown,rightDown} / (i -> if i then toString(1) else toString(0))); - - cornerHash#theKey - ); - - sepColumns := for colIndex from muSmallest to lamLargest list ( - isBoxLeftUp := false; - isBoxRightUp := false; - isBoxRightDown := colIndex >= mu#0 and colIndex < lam#0; - isBoxLeftDown := colIndex-1 >= mu#0 and colIndex-1 < lam#0; - - currColNet := if colIndex == 0 and hasNegativeParts then ( - "â•‘" - ) else ( - cornerChar(isBoxLeftUp,isBoxRightUp,isBoxLeftDown,isBoxRightDown) - ); - - for rowIndex from 0 to #lam-1 do ( - - isBoxLeft := colIndex-1 >= mu#rowIndex and colIndex-1 < lam#rowIndex; - isBoxRight := colIndex >= mu#rowIndex and colIndex < lam#rowIndex; - - isBoxLeftUp = isBoxLeft; - isBoxRightUp = isBoxRight; - isBoxRightDown = rowIndex < #lam-1 and colIndex >= mu#(rowIndex+1) and colIndex < lam#(rowIndex+1); - isBoxLeftDown = rowIndex < #lam-1 and colIndex-1 >= mu#(rowIndex+1) and colIndex-1 < lam#(rowIndex+1); - - boxString := if colIndex == 0 and hasNegativeParts then ( - "â•‘" - ) else if isBoxRight or isBoxLeft then ( - "│" - ) else ( - " " - ); - - belowString := if colIndex == 0 and hasNegativeParts then ( - "â•‘" - ) else ( - cornerChar(isBoxLeftUp,isBoxRightUp,isBoxLeftDown,isBoxRightDown) - ); - - currColNet = currColNet||boxString||belowString; - ); - - currColNet - ); - - ans := ""; - for theNet in mingle {sepColumns,boxColumns} do ( - ans = ans|theNet; - ); - ans^1 - ) - -youngDiagram = method(TypicalValue => Net) -youngDiagram (Partition,Partition) := (lam,mu) -> net skewTableau(lam,mu) -youngDiagram Partition := lam -> youngDiagram(lam,new Partition from {0}) -youngDiagram SkewTableau := T -> youngDiagram standardize skewShape T - -ferrersDiagram = method(TypicalValue => Net) -ferrersDiagram (Partition,Partition) := (lam,mu) -> ( - boxChar := "â—"; - negBoxChar := "â—‹"; - - T := skewTableau(lam,mu); - (lam,mu) = standardize skewShape normalizeNegativeRows T; - - if #lam == 0 or (size T == 0 and not isDrawnInner) then return "∅"; - - isNegativeRow := listNegativeRows T; - - ans := concatenate for colIndex in columnRange T list ( - isBox := colIndex >= mu#0 and colIndex < lam#0; - - if isBox and isNegativeRow#0 then ( - negBoxChar|" " - ) else if isBox then ( - boxChar|" " - ) else ( - " " - ) - ); - for rowIndex from 1 to #lam-1 do ( - rowString := concatenate for colIndex in columnRange T list ( - isBox := colIndex >= mu#rowIndex and colIndex < lam#rowIndex; - - if isBox and isNegativeRow#rowIndex then ( - negBoxChar|" " - ) else if isBox then ( - boxChar|" " - ) else ( - " " - ) - ); - ans = ans || rowString; - ); - - ans - ) -ferrersDiagram Partition := lam -> ferrersDiagram(lam,new Partition from {0}) -ferrersDiagram SkewTableau := T -> ferrersDiagram standardize skewShape T - --- getting data - -tex SkewTableau := T -> ( - -- \usepackage{atableau} - (lam,mu) := standardize skewShape normalizeNegativeRows T; - - isNegativeRow := listNegativeRows T; - starString := if hasNegativeRow T then ", star style={fill=red!50}" else ""; - - ans := "\\SkewTableau[skew border, skew boxes"|starString|"] "|toString(toList mu); - filling := for i from 0 to #lam-1 list ( - currRow := rowEntries(i,T); - isRed := isNegativeRow#i; - - concatenate for theBox in currRow list ( - boxString := toString theBox; - boxString = if #boxString == 1 then boxString else "{"|boxString|"}"; - if isRed then "*"|boxString else boxString - ) - ); - ans|toString(filling) - ) - -entries SkewTableau := T -> T#values - -size SkewTableau := T -> # entries T - -components SkewTableau := T -> ( - (lamOriginal,muOriginal) := standardize skewShape T; - (lamOriginal,muOriginal) = (toList lamOriginal,toList muOriginal); - - (lam,mu) := standardize skewShape normalizeNegativeRows T; - (lam,mu) = (toList lam,toList mu); - - isNegativeRow := listNegativeRows T; - - isConnectedToNextRow := for i from 0 to numrows T - 2 list ( - if (mu#i <= mu#(i+1) and mu#(i+1) < lam#i and lam#(i+1) > mu#(i+1)) or (mu#(i+1) <= mu#i and mu#i < lam#(i+1) and lam#i > mu#i) then ( - true - ) else ( - false - ) - ); - isConnectedToNextRow = append(isConnectedToNextRow,false); - - currComponentStart := 0; - compRows := for i from 0 to numrows T - 1 list ( - if isConnectedToNextRow#i then continue else ( - compStart := currComponentStart; - currComponentStart = i + 1; - (compStart..i) - ) - ); - - for partIndices in compRows list ( - lam' := new Partition from lamOriginal_(toList partIndices); - mu' := new Partition from muOriginal_(toList partIndices); - (lam',mu') = alignToZero(lam',mu'); - entryList' := flatten for theIndex in partIndices list rowEntries(theIndex,T); - if #entryList' == 0 then continue else skewTableau(lam',mu',entryList') - ) - ) - --- making new tableau - -applyEntries = method(TypicalValue => SkewTableau) -applyEntries (SkewTableau,Function) := (T,f) -> skewTableau(truncate skewShape T, apply(entries T, f)) - -applyPositions = method(TypicalValue => SkewTableau) -applyPositions (SkewTableau,Function) := (T,f) -> skewTableau(truncate skewShape T, apply(positionList T, f)) - -conjugate SkewTableau := T -> ( - if not isWeaklyDecreasing T or not isNonnegative T then error "expected shape to be weakly decreasing and nonnegative"; - (lam,mu) := standardize skewShape T; - - if #lam == 0 then return T; - - lam' := conjugate lam; - mu' := conjugate mu; - entryList' := flatten for colIndex from 0 to lam#0-1 list columnEntries(colIndex,T); - - skewTableau(lam',mu',entryList') - ) - -verticalConcatenate = method(TypicalValue => SkewTableau) -verticalConcatenate List := tabList -> ( - lam := new Partition from flatten for T in tabList list toList((standardize skewShape T)#0); - mu := new Partition from flatten for T in tabList list toList((standardize skewShape T)#1); - entryList := flatten for T in tabList list entries T; - - skewTableau(lam, mu, entryList) - ) - -SkewTableau || SkewTableau := (T1,T2) -> ( - verticalConcatenate {T1,T2} - ) - -SkewTableau ++ SkewTableau := (T1,T2) -> ( - (lam1,mu1) := standardize skewShape T1; - (lam2,mu2) := standardize skewShape T2; - (entryList1, entryList2) := (entries T1, entries T2); - - lastMu1 := mu1#-1; - firstLam2 := lam2#0; - shiftAmount := firstLam2 - lastMu1; - - lam1 = for thePart in lam1 list (thePart + shiftAmount); - mu1 = for thePart in mu1 list (thePart + shiftAmount); - - lam := new Partition from ((toList lam1)|(toList lam2)); - mu := new Partition from ((toList mu1)|(toList mu2)); - entryList := entryList1|entryList2; - - skewTableau(lam, mu, entryList) - ) - -shift = method(TypicalValue => SkewTableau) -shift (SkewTableau,ZZ) := (T,firstRowAmount) -> ( - (lam,mu) := standardize skewShape T; - - lam = for i from 0 to #lam-1 list (lam#i+(i+firstRowAmount)); - mu = for i from 0 to #lam-1 list (mu#i+(i+firstRowAmount)); - - skewTableau(new Partition from lam, new Partition from mu, entries T) - ) -shift SkewTableau := T -> ( - shift(T,0) - ) - -unshift = method(TypicalValue => SkewTableau) -unshift (SkewTableau,ZZ) := (T,firstRowAmount) -> ( - (lam,mu) := standardize skewShape T; - - lam = for i from 0 to #lam-1 list (lam#i-(i+firstRowAmount)); - mu = for i from 0 to #lam-1 list (mu#i-(i+firstRowAmount)); - - skewTableau(new Partition from lam, new Partition from mu, entries T) - ) -unshift SkewTableau := T -> ( - unshift(T,0) - ) - --- bools - -isWeaklyDecreasing = method(TypicalValue => Boolean) -isWeaklyDecreasing Partition := lam -> rsort toList lam == toList lam -isWeaklyDecreasing (Partition,Partition) := (lam,mu) -> isWeaklyDecreasing lam and isWeaklyDecreasing mu -isWeaklyDecreasing SkewTableau := T -> isWeaklyDecreasing skewShape T - -isNonnegative = method(TypicalValue => Boolean) -isNonnegative Partition := lam -> all(toList lam, thePart -> thePart >= 0) -isNonnegative (Partition,Partition) := (lam,mu) -> isNonnegative lam and isNonnegative mu -isNonnegative SkewTableau := T -> isNonnegative standardize skewShape T - -isSkew = method(TypicalValue => Boolean) -isSkew SkewTableau := T -> ( - (lam,mu) := truncate skewShape T; - - #mu != 0 - ) - --- tableau calculations - -boxContent = method(TypicalValue => ZZ) -boxContent (ZZ,ZZ) := (rowIndex,colIndex) -> colIndex - rowIndex - -hookLength = method(TypicalValue => ZZ) -hookLength (Sequence,SkewTableau) := (thePosition,T) -> ( - (rowIndex,colIndex) := thePosition; - (lam,mu) := standardize skewShape T; - lam' := conjugate lam; - - 1 + (lam#rowIndex - colIndex - 1) + (lam'#colIndex-rowIndex - 1) - ) - diff --git a/M2/Macaulay2/packages/Tableaux/Tabloids.m2 b/M2/Macaulay2/packages/Tableaux/Tabloids.m2 new file mode 100644 index 00000000000..28f8db2fe6c --- /dev/null +++ b/M2/Macaulay2/packages/Tableaux/Tabloids.m2 @@ -0,0 +1,50 @@ + +Tabloid = new Type of YoungTableau + +tabloid = method(TypicalValue => Tabloid) +tabloid (Partition,Partition,List) := (lam,mu,theList) -> ( + (lam',mu') := standardize (lam,mu); + numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); + + if sort theList != toList(1..numBoxesNeeded) then error "list must contain distinct entries in 1..n"; + + new Tabloid from { + "outerShape" => lam, + "innerShape" => mu, + values => theList + } + ) +tabloid (Sequence,List) := (theShape,theList) -> ( + (lam,mu) := theShape; + + tabloid(lam,mu,theList) + ) +tabloid (Partition,List) := (lam,theList) -> ( + tabloid(lam,new Partition from {},theList) + ) +tabloid YoungTableau := T -> tabloid(skewShape T, entries T) + +youngTableau Tabloid := T -> youngTableau(skewShape T, entries T) + +toList Tabloid := T -> ( + T = representative T; + + rowPermutations := for rowIndex from 0 to numRows T - 1 list ( + theRow := rowEntries(T,rowIndex); + + permList := for thePerm in permutations (#theRow) list permutation for theNum in thePerm list theNum + 1; + + permutedRowList := for thePerm in permList list thePerm * theRow + ); + + entryLists := rowPermutations#0; + for rowIndex from 1 to numRows T - 1 do entryLists = entryLists ** rowPermutations#rowIndex; + entryLists = for theSeq in entryLists / deepSplice list join sequence theSeq; + + Bag for theList in entryLists list youngTableau(skewShape T, theList) + ) + +representative = method(TypicalValue => Tabloid) +representative Tabloid := T -> youngTableau(skewShape T, flatten for i from 0 to numRows T - 1 list sort rowEntries(T,i)) + +Tabloid == Tabloid := (T1,T2) -> representative T1 == representative T2 diff --git a/M2/Macaulay2/packages/Tableaux/YoungTableaux.m2 b/M2/Macaulay2/packages/Tableaux/YoungTableaux.m2 index aa49b91deef..814612594bb 100644 --- a/M2/Macaulay2/packages/Tableaux/YoungTableaux.m2 +++ b/M2/Macaulay2/packages/Tableaux/YoungTableaux.m2 @@ -1,35 +1,75 @@ -YoungTableau = new Type of SkewTableau +YoungTableau = new Type of HashTable youngTableau = method(TypicalValue => YoungTableau) -youngTableau (Partition,List) := (lam,theList) -> ( - numBoxesNeeded := sum for i from 0 to #lam-1 list abs(lam#i); +youngTableau (Partition,Partition,List) := (lam,mu,theList) -> ( + (lam',mu') := standardize (lam,mu); + numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); if (numBoxesNeeded != #theList) then error "partition sizes do not match with the length of the list"; if any(theList, theElt -> theElt === null) then error "filling must not contain null entries"; - mu := new Partition from {}; - new YoungTableau from { "outerShape" => lam, "innerShape" => mu, values => theList } ) +youngTableau (Sequence,List) := (theShape,theList) -> ( + (lam,mu) := theShape; + + youngTableau(lam,mu,theList) + ) +youngTableau (Partition,List) := (lam,theList) -> ( + youngTableau(lam,new Partition from {},theList) + ) +youngTableau (Partition,Partition) := (lam,mu) -> ( + (lam',mu') := standardize (lam,mu); + numBoxesNeeded := sum for i from 0 to #lam'-1 list abs(lam'#i-mu'#i); + + youngTableau(lam,mu,toList(numBoxesNeeded:"")) + ) youngTableau Partition := lam -> ( numBoxesNeeded := sum for i from 0 to #lam-1 list abs(lam#i); - youngTableau(lam, toList(numBoxesNeeded:"")) + youngTableau(lam, new Partition from {}, toList(numBoxesNeeded:"")) + ) + +-- computing tableau shapes + +skewShape = method(TypicalValue => Sequence) +skewShape YoungTableau := T -> ( + (T#"outerShape", T#"innerShape") + ) + +outerShape = method(TypicalValue => Partition) +outerShape YoungTableau := T -> ( + T#"outerShape" ) -skewTableau YoungTableau := T -> new SkewTableau from T +innerShape = method(TypicalValue => Partition) +innerShape YoungTableau := T -> ( + T#"innerShape" + ) shape = method(TypicalValue => Partition) shape YoungTableau := T -> ( - T#"outerShape" + if isSkew T then error "expected a non-skew tableau"; + + outerShape T + ) + +trim Partition := Partition => o -> lam -> ( + numTrailingZeros := # for i from 1 to #lam list (if lam#-i == 0 then 1 else break); + + lamShortened := (toList lam)_(toList(0..(#lam-1-numTrailingZeros))); + + new Partition from lamShortened ) +trim (Partition,Partition) := (Partition,Partition) => o -> (lam,mu) -> (trim lam, trim mu) +-* truncate Partition := theInput -> ( lam := theInput#1; numTrailingZeros := # for i from 1 to #lam list (if lam#-i == 0 then 1 else break); @@ -38,3 +78,728 @@ truncate Partition := theInput -> ( new Partition from lamShortened ) + +truncate (Partition,Partition) := theInput -> ( + (lam,mu) := theInput#1; + + (truncate lam, truncate mu) + ) +*- + +pad (Partition,Partition) := (lam,mu) -> ( + maxLength := max(#lam,#mu); + + lamPadded := toList(lam)|toList((maxLength - #lam):0); + muPadded := toList(mu)|toList((maxLength - #mu):0); + + (new Partition from lamPadded, new Partition from muPadded) + ) + +standardize = method(TypicalValue => Sequence) +standardize (Partition,Partition) := (lam,mu) -> ( + (lam,mu) = pad trim (lam,mu); + if #lam == 0 then return (new Partition from {0}, new Partition from {0}); + + (lam,mu) + ) + +alignToZero = method(TypicalValue => YoungTableau) +alignToZero (Partition,Partition) := (lam,mu) -> ( + (lam,mu) = standardize (lam,mu); + minPart := min(toList mu | toList lam); + + lamAligned := for thePart in lam list thePart - minPart; + muAligned := for thePart in mu list thePart - minPart; + + (new Partition from lamAligned, new Partition from muAligned) + ) +alignToZero YoungTableau := T -> ( + (lamAligned,muAligned) := alignToZero standardize skewShape T; + youngTableau(lamAligned, muAligned, entries T) + ) + +hasNegativeRow = method(TypicalValue => Boolean) +hasNegativeRow YoungTableau := T -> ( + (lam,mu) := standardize skewShape T; + + any(0..(#lam-1), i -> mu#i > lam#i) + ) + +-- swap parts when mu#i > lam#i +normalizeNegativeRows = method(TypicalValue => YoungTableau) +normalizeNegativeRows YoungTableau := T -> ( + if not hasNegativeRow T then return T; + + (lam,mu) := standardize skewShape T; + + lam' := new Partition from for i from 0 to #lam-1 list max(lam#i,mu#i); + mu' := new Partition from for i from 0 to #lam-1 list min(lam#i,mu#i); + + youngTableau(lam',mu',entries T) + ) + +listNegativeRows = method(TypicalValue => List) +listNegativeRows YoungTableau := T -> ( + (lam,mu) := standardize skewShape T; + + for i from 0 to #lam-1 list (mu#i > lam#i) + ) + +-- retrieving rows and columns + +numcols YoungTableau := T -> ( + (lam,mu) := standardize skewShape normalizeNegativeRows T; + max(max toList lam - min toList mu,0) + ) + +numrows YoungTableau := T -> ( + (lam,mu) := standardize skewShape normalizeNegativeRows T; + #lam + ) + +columnRange = method(TypicalValue => Sequence) +columnRange YoungTableau := T -> ( + (lam,mu) := standardize skewShape normalizeNegativeRows T; + (min (toList mu | toList lam)..(max (toList lam | toList mu) - 1)) + ) + +rowRange = method(TypicalValue => Sequence) +rowRange YoungTableau := T -> (0..(numRows T - 1)) + +rowEntries = method(TypicalValue => List) +rowEntries (ZZ,YoungTableau) := (rowIndex,T) -> ( + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + if rowIndex < 0 then ( + rowIndex = #lam + rowIndex; + ); + if rowIndex >= #lam or rowIndex < 0 then error "index out of bounds"; + + numBoxesAbove := sum for i from 0 to rowIndex-1 list (lam#i-mu#i); + + for i from numBoxesAbove to numBoxesAbove + lam#rowIndex - mu#rowIndex - 1 list ((entries T)#i) + ) +rowEntries (YoungTableau,ZZ) := (T,rowIndex) -> rowEntries(rowIndex,T) + +YoungTableau^ZZ := (T,rowIndex) -> ( + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + if rowIndex < 0 then ( + rowIndex = #lam + rowIndex; + ); + if rowIndex >= #lam or rowIndex < 0 then error "index out of bounds"; + + colInds := columnRange T; + + theEntries := rowEntries(rowIndex,T); + + theRowOrderedLeft := for colIndex from min(colInds#0,0) to -1 list ( + if colIndex < mu#rowIndex or colIndex >= lam#rowIndex then null else ( + theEntries#(colIndex - mu#rowIndex) + ) + ); + + theRowOrderedRight := for colIndex from 0 to max(colInds#-1,0) list ( + if colIndex < mu#rowIndex or colIndex >= lam#rowIndex then null else ( + theEntries#(colIndex - mu#rowIndex) + ) + ); + + theRowOrderedRight|theRowOrderedLeft + ) + +YoungTableau_Sequence := (T,thePosition)->( + (rowIndex,colIndex) := thePosition; + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + if rowIndex < 0 or rowIndex >= numRows T then error("index "|toString(rowIndex)|" out of range"); + if colIndex < mu#rowIndex or colIndex >= lam#rowIndex then error "index "|toString(colIndex)|" out of range"; + + T^rowIndex#colIndex + ) + +YoungTableau_ZZ := (T,colIndex) -> ( + colInds := columnRange T; + + if colIndex < min(colInds#0,0) or colIndex > max(colInds#-1,0) then error "index out of bounds"; + + for rowIndex from 0 to numRows T - 1 list T^rowIndex#colIndex + ) + +columnEntries = method(TypicalValue => List) +columnEntries (ZZ,YoungTableau) := (colIndex,T) -> delete(null,T_colIndex) +columnEntries (YoungTableau,ZZ) := (T,colIndex) -> columnEntries(colIndex,T) + +toPosition = method(TypicalValue => Sequence) +toPosition (ZZ,YoungTableau) := (theIndex,T) -> ( + if theIndex < 0 then ( + theIndex = size T + theIndex; + ); + if theIndex < 0 or theIndex >= size T then error "index out of range"; + + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + numBoxesSeen := 0; + for rowIndex from 0 to numRows T - 1 do ( + for colIndex from mu#rowIndex to lam#rowIndex - 1 do ( + if numBoxesSeen == theIndex then return (rowIndex,colIndex); + numBoxesSeen = numBoxesSeen + 1; + ); + ); + ) +toPosition (YoungTableau,ZZ) := (T,theIndex) -> toPosition(theIndex,T) + +toIndex = method(TypicalValue => ZZ) +toIndex (Sequence,YoungTableau) := (thePosition,T) -> ( + (rowIndex,colIndex) := thePosition; + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + if rowIndex < 0 or rowIndex >= #lam or colIndex < mu#rowIndex or colIndex >= lam#rowIndex then error "index out of range"; + + numBoxesSeen := 0; + for theRowIndex from 0 to numRows T - 1 do ( + for theColIndex from mu#theRowIndex to lam#theRowIndex - 1 do ( + if (theRowIndex,theColIndex) == (rowIndex,colIndex) then return numBoxesSeen; + numBoxesSeen = numBoxesSeen + 1; + ); + ); + ) +toIndex (YoungTableau,Sequence) := (T,thePosition) -> toIndex(thePosition,T) + +positionList = method(TypicalValue => List) +positionList YoungTableau := T -> ( + for i from 0 to size T - 1 list toPosition(i,T) + ) +positionList (YoungTableau,Function) := (T,f) -> ( + (lam,mu) := standardize skewShape T; + + if f === isCorner then ( + if isSkew T then error "Expected non-skew shape"; + return((for rowIndex from 0 to numRows T - 2 list ( + if lam#rowIndex > lam#(rowIndex + 1) then (rowIndex,lam#rowIndex - 1) else continue + ))|{(numRows T - 1,lam#-1 - 1)}); + ); + + select(positionList T,f) + ) + +rowList = method(TypicalValue => List) +rowList YoungTableau := T -> for i from 0 to numRows T - 1 list rowEntries(T,i) + +columnList = method(TypicalValue => List) +columnList YoungTableau := T -> for i from 0 to numColumns T - 1 list columnEntries(T,i) + +-- drawing + +isDrawnInner := false + +drawInnerShape = method(TypicalValue => Boolean) +drawInnerShape Boolean := theBool -> (isDrawnInner = theBool; theBool) + +net YoungTableau := T -> ( + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + if #lam == 0 or (size T == 0 and not isDrawnInner) then return "∅"; + + isNegativeRow := listNegativeRows T; + + innerShapeString := if isDrawnInner then "â– " else " "; + + (muSmallest, lamLargest) := (min(min toList mu,0), max(max toList lam,0)); + --colWidth := if #entries T == 0 then 1 else max for theBox in entries T list #toString(theBox); + colWidth := if #entries T == 0 then 1 else max for theBox in entries T list width net theBox; + colWidth = max {colWidth + 2,3}; + rowHeight := if #entries T == 0 then 1 else max for theBox in entries T list (depth net theBox + height net theBox); + rowHeight = max {rowHeight,1}; + hasNegativeParts := any(toList(lam)|toList(mu), thePart -> thePart < 0); + + boxColumns := for colIndex from muSmallest to lamLargest-1 list ( + currCol := if colIndex >= mu#0 and colIndex < lam#0 then concatenate(colWidth:"─") else concatenate(colWidth:" "); + for rowIndex from 0 to #lam-1 do ( + isBox := colIndex >= mu#rowIndex and colIndex < lam#rowIndex; + isBoxBelow := rowIndex < #lam-1 and colIndex >= mu#(rowIndex+1) and colIndex < lam#(rowIndex+1); + + boxString := if isBox then ( + boxEntry := net((rowEntries(rowIndex,T))#(colIndex-mu#rowIndex)); + + boxPadding := if isNegativeRow#rowIndex then "â–‘" else " "; --w + boxPadding = (verticalNet for i from 1 to rowHeight list boxPadding)^(height boxEntry - 1); + + if isNegativeRow#rowIndex and #boxEntry == 0 then boxEntry = "â–‘"; + boxPadding|boxEntry|boxPadding + + ) else if (colIndex < 0 and colIndex >= lam#rowIndex) or (colIndex >= 0 and colIndex < mu#rowIndex) then ( + aString := " "|concatenate((colWidth-2):innerShapeString)|" "; + verticalNet for i from 1 to rowHeight list aString + + ) else ( + --verticalNet for i from 1 to rowHeight list(" ") + " " + ); + + belowString := if isBox or isBoxBelow then "─" else " "; + belowString = concatenate(colWidth:belowString); + + currCol = currCol||boxString||belowString; + ); + currCol + ); + + cornerChar := (leftUp,rightUp,leftDown,rightDown) -> ( + cornerHash := if instance(T, Tabloid) then ( + new HashTable from { + "0000" => " ", + "0001" => "┌", + "0010" => "â”", + "0011" => "─", + "0100" => "â””", + "0101" => "├", + "0110" => "┼", + "0111" => "â”´", + "1000" => "┘", + "1001" => "┼", + "1010" => "┤", + "1011" => "â”´", + "1100" => "─", + "1101" => "┬", + "1110" => "┬", + "1111" => "─" + } + ) else ( + new HashTable from { + "0000" => " ", + "0001" => "┌", + "0010" => "â”", + "0011" => "┬", + "0100" => "â””", + "0101" => "├", + "0110" => "┼", + "0111" => "┼", + "1000" => "┘", + "1001" => "┼", + "1010" => "┤", + "1011" => "┼", + "1100" => "â”´", + "1101" => "┼", + "1110" => "┼", + "1111" => "┼" + } + ); + + theKey := concatenate({leftUp,rightUp,leftDown,rightDown} / (i -> if i then toString(1) else toString(0))); + + cornerHash#theKey + ); + + sepColumns := for colIndex from muSmallest to lamLargest list ( + isBoxLeftUp := false; + isBoxRightUp := false; + isBoxRightDown := colIndex >= mu#0 and colIndex < lam#0; + isBoxLeftDown := colIndex-1 >= mu#0 and colIndex-1 < lam#0; + + currColNet := if colIndex == 0 and hasNegativeParts then ( + "â•‘" + ) else ( + cornerChar(isBoxLeftUp,isBoxRightUp,isBoxLeftDown,isBoxRightDown) + ); + + for rowIndex from 0 to #lam-1 do ( + + isBoxLeft := colIndex-1 >= mu#rowIndex and colIndex-1 < lam#rowIndex; + isBoxRight := colIndex >= mu#rowIndex and colIndex < lam#rowIndex; + + isBoxLeftUp = isBoxLeft; + isBoxRightUp = isBoxRight; + isBoxRightDown = rowIndex < #lam-1 and colIndex >= mu#(rowIndex+1) and colIndex < lam#(rowIndex+1); + isBoxLeftDown = rowIndex < #lam-1 and colIndex-1 >= mu#(rowIndex+1) and colIndex-1 < lam#(rowIndex+1); + + boxString := if colIndex == 0 and hasNegativeParts then ( + "â•‘" + ) else if (not instance(T, Tabloid) and (isBoxRight or isBoxLeft)) or + (instance(T, Tabloid) and (isBoxRight xor isBoxLeft)) then ( + "│" + ) else ( + " " + ); + boxString = verticalNet for i from 1 to rowHeight list boxString; + + belowString := if colIndex == 0 and hasNegativeParts then ( + "â•‘" + ) else ( + cornerChar(isBoxLeftUp,isBoxRightUp,isBoxLeftDown,isBoxRightDown) + ); + + currColNet = currColNet||boxString||belowString; + ); + + currColNet + ); + + ans := ""; + for theNet in mingle {sepColumns,boxColumns} do ( + ans = ans|theNet; + ); + ans^1 + ) + +youngDiagram = method(TypicalValue => Net) +youngDiagram (Partition,Partition) := (lam,mu) -> net youngTableau(lam,mu) +youngDiagram Partition := lam -> youngDiagram(lam,new Partition from {0}) +youngDiagram YoungTableau := T -> youngDiagram standardize skewShape T + +ferrersDiagram = method(TypicalValue => Net) +ferrersDiagram (Partition,Partition) := (lam,mu) -> ( + boxChar := "â—"; + negBoxChar := "â—‹"; + + T := youngTableau(lam,mu); + (lam,mu) = standardize skewShape normalizeNegativeRows T; + + if #lam == 0 or (size T == 0 and not isDrawnInner) then return "∅"; + + isNegativeRow := listNegativeRows T; + + ans := concatenate for colIndex in columnRange T list ( + isBox := colIndex >= mu#0 and colIndex < lam#0; + + if isBox and isNegativeRow#0 then ( + negBoxChar|" " + ) else if isBox then ( + boxChar|" " + ) else ( + " " + ) + ); + for rowIndex from 1 to #lam-1 do ( + rowString := concatenate for colIndex in columnRange T list ( + isBox := colIndex >= mu#rowIndex and colIndex < lam#rowIndex; + + if isBox and isNegativeRow#rowIndex then ( + negBoxChar|" " + ) else if isBox then ( + boxChar|" " + ) else ( + " " + ) + ); + ans = ans || rowString; + ); + + ans + ) +ferrersDiagram Partition := lam -> ferrersDiagram(lam,new Partition from {0}) +ferrersDiagram YoungTableau := T -> ferrersDiagram standardize skewShape T + +verticalNet = theTuple -> ( + ans := net theTuple#0; + for i from 1 to #theTuple-1 do ( + ans = ans || net theTuple#i; + ); + + ans + ) + +horizontalNet = theTuple -> ( + ans := net theTuple#0; + for i from 1 to #theTuple-1 do ( + ans = ans | net theTuple#i; + ); + + ans + ) + +-- getting data + +tex YoungTableau := T -> ( + -- \usepackage{atableau} + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + isNegativeRow := listNegativeRows T; + starString := if hasNegativeRow T then ", star style={fill=red!50}" else ""; + + ans := if instance(T, Tabloid) then ( + "\\Tabloid[skew="|toString(toList mu)|starString|"]" + ) else ( + "\\SkewTableau[skew border, skew boxes"|starString|"]"|toString(toList mu) + ); + filling := for i from 0 to #lam-1 list ( + currRow := rowEntries(i,T); + isRed := isNegativeRow#i; + + concatenate for theBox in currRow list ( + boxString := toString theBox; + boxString = if #boxString == 1 then boxString else "{"|boxString|"}"; + if isRed then "*"|boxString else boxString + ) + ); + ans|toString(filling) + ) + +entries YoungTableau := T -> T#values + +size YoungTableau := T -> # entries T + +components YoungTableau := T -> ( + (lamOriginal,muOriginal) := standardize skewShape T; + (lamOriginal,muOriginal) = (toList lamOriginal,toList muOriginal); + + (lam,mu) := standardize skewShape normalizeNegativeRows T; + (lam,mu) = (toList lam,toList mu); + + isNegativeRow := listNegativeRows T; + + isConnectedToNextRow := for i from 0 to numrows T - 2 list ( + if (mu#i <= mu#(i+1) and mu#(i+1) < lam#i and lam#(i+1) > mu#(i+1)) or (mu#(i+1) <= mu#i and mu#i < lam#(i+1) and lam#i > mu#i) then ( + true + ) else ( + false + ) + ); + isConnectedToNextRow = append(isConnectedToNextRow,false); + + currComponentStart := 0; + compRows := for i from 0 to numrows T - 1 list ( + if isConnectedToNextRow#i then continue else ( + compStart := currComponentStart; + currComponentStart = i + 1; + (compStart..i) + ) + ); + + for partIndices in compRows list ( + lam' := new Partition from lamOriginal_(toList partIndices); + mu' := new Partition from muOriginal_(toList partIndices); + (lam',mu') = alignToZero(lam',mu'); + entryList' := flatten for theIndex in partIndices list rowEntries(theIndex,T); + if #entryList' == 0 then continue else youngTableau(lam',mu',entryList') + ) + ) + +-- making new tableau + +applyEntries = method(TypicalValue => YoungTableau) +applyEntries (YoungTableau,Function) := (T,f) -> ( + if not instance(T,Tabloid) then ( + youngTableau(trim skewShape T, apply(entries T, f)) + ) else ( + tabloid(trim skewShape T, apply(entries T, f)) + ) + ) + +applyPositions = method(TypicalValue => YoungTableau) +applyPositions (YoungTableau,Function) := (T,f) -> ( + if not instance(T,Tabloid) then ( + youngTableau(trim skewShape T, apply(positionList T, f)) + ) else ( + tabloid(trim skewShape T, apply(positionList T, f)) + ) + ) + +conjugate YoungTableau := T -> ( + if not isWeaklyDecreasing T or not isNonnegative T then error "expected shape to be weakly decreasing and nonnegative"; + (lam,mu) := standardize skewShape T; + + if #lam == 0 then return T; + + lam' := conjugate lam; + mu' := conjugate mu; + entryList' := flatten for colIndex from 0 to lam#0-1 list columnEntries(colIndex,T); + + youngTableau(lam',mu',entryList') + ) + +verticalConcatenate = method(TypicalValue => YoungTableau) +verticalConcatenate List := tabList -> ( + lam := new Partition from flatten for T in tabList list toList((standardize skewShape T)#0); + mu := new Partition from flatten for T in tabList list toList((standardize skewShape T)#1); + entryList := flatten for T in tabList list toList entries T; + + youngTableau(lam, mu, entryList) + ) + +YoungTableau || YoungTableau := (T1,T2) -> ( + verticalConcatenate {T1,T2} + ) + +YoungTableau ++ YoungTableau := (T1,T2) -> ( + (lam1,mu1) := standardize skewShape T1; + (lam2,mu2) := standardize skewShape T2; + (entryList1, entryList2) := (entries T1, entries T2); + + lastMu1 := mu1#-1; + firstLam2 := lam2#0; + shiftAmount := firstLam2 - lastMu1; + + lam1 = for thePart in lam1 list (thePart + shiftAmount); + mu1 = for thePart in mu1 list (thePart + shiftAmount); + + lam := new Partition from ((toList lam1)|(toList lam2)); + mu := new Partition from ((toList mu1)|(toList mu2)); + entryList := entryList1|entryList2; + + youngTableau(lam, mu, entryList) + ) + +shift = method(TypicalValue => YoungTableau) +shift (YoungTableau,ZZ) := (T,firstRowAmount) -> ( + (lam,mu) := standardize skewShape T; + + lam = for i from 0 to #lam-1 list (lam#i+(i+firstRowAmount)); + mu = for i from 0 to #lam-1 list (mu#i+(i+firstRowAmount)); + + youngTableau(new Partition from lam, new Partition from mu, entries T) + ) +shift YoungTableau := T -> ( + shift(T,0) + ) + +unshift = method(TypicalValue => YoungTableau) +unshift (YoungTableau,ZZ) := (T,firstRowAmount) -> ( + (lam,mu) := standardize skewShape T; + + lam = for i from 0 to #lam-1 list (lam#i-(i+firstRowAmount)); + mu = for i from 0 to #lam-1 list (mu#i-(i+firstRowAmount)); + + youngTableau(new Partition from lam, new Partition from mu, entries T) + ) +unshift YoungTableau := T -> ( + unshift(T,0) + ) + +-- bools + +isWeaklyDecreasing = method(TypicalValue => Boolean) +isWeaklyDecreasing Partition := lam -> rsort toList lam == toList lam +isWeaklyDecreasing (Partition,Partition) := (lam,mu) -> isWeaklyDecreasing lam and isWeaklyDecreasing mu +isWeaklyDecreasing YoungTableau := T -> isWeaklyDecreasing skewShape T + +isNonnegative = method(TypicalValue => Boolean) +isNonnegative Partition := lam -> all(toList lam, thePart -> thePart >= 0) +isNonnegative (Partition,Partition) := (lam,mu) -> isNonnegative lam and isNonnegative mu +isNonnegative YoungTableau := T -> isNonnegative standardize skewShape T + +isSkew = method(TypicalValue => Boolean) +isSkew YoungTableau := T -> ( + (lam,mu) := trim skewShape T; + + #mu != 0 + ) + +isNatural = method(TypicalValue => Boolean) +isNatural YoungTableau := T -> sort entries T == toList(1..(size T)) + +isColumnStrict = method(TypicalValue => Boolean) +isColumnStrict YoungTableau := T -> ( + isWeakRows := all(0..(numRows T - 1), i -> isSorted rowEntries(T,i)); + isWeakCols := all(0..(numColumns T - 1), j -> isSorted columnEntries(T,j)); + isUniqueCols := all(0..(numColumns T - 1), j -> max values tally columnEntries(T,j) == 1); + + isWeakRows and isWeakCols and isUniqueCols + ) + +isRowStrict = method(TypicalValue => Boolean) +isRowStrict YoungTableau := T -> ( + isWeakCols := all(0..(numColumns T - 1), j -> isSorted columnEntries(T,j)); + isWeakRows := all(0..(numRows T - 1), i -> isSorted rowEntries(T,i)); + isUniqueRows := all(0..(numRows T - 1), i -> max values tally rowEntries(T,i) == 1); + + isWeakRows and isWeakCols and isUniqueRows + ) + +isSemistandard = method(TypicalValue => Boolean) +isSemistandard YoungTableau := T -> ( + isColumnStrict T and isNonnegative T and isWeaklyDecreasing T + ) + +isStandard = method(TypicalValue => Boolean) +isStandard YoungTableau := T -> ( + isNatural T and isSemistandard T + ) + +isCorner = method(TypicalValue => Boolean) +isCorner (Sequence,Partition) := (thePosition,lam) -> ( + (rowIndex,colIndex) := thePosition; + lam = trim lam; + + if not isWeaklyDecreasing lam then error "expected weakly decreasing shape"; + + if rowIndex == #lam-1 then return(colIndex == lam#rowIndex - 1); + + colIndex == lam#rowIndex - 1 and lam#rowIndex > lam#(rowIndex + 1) + ) +isCorner (Sequence,YoungTableau) := (thePosition,T) -> ( + (rowIndex,colIndex) := thePosition; + (lam,mu) := standardize skewShape normalizeNegativeRows T; + + isNonemptyRow := lam#rowIndex > mu#rowIndex; + + isNonemptyRow and isCorner(thePosition,lam) + ) + +-- tableau calculations + +boxContent = method(TypicalValue => ZZ) +boxContent (ZZ,ZZ) := (rowIndex,colIndex) -> colIndex - rowIndex + +hookLength = method(TypicalValue => ZZ) +hookLength (Sequence,YoungTableau) := (thePosition,T) -> ( + (rowIndex,colIndex) := thePosition; + (lam,mu) := standardize skewShape T; + lam' := conjugate lam; + + 1 + (lam#rowIndex - colIndex - 1) + (lam'#colIndex-rowIndex - 1) + ) +hookLength Partition := lam -> ( + if not isWeaklyDecreasing lam or not isNonnegative lam then error "expected shape to be weakly decreasing and nonnegative"; + + T := youngTableau lam; + + (size T)! // product apply(positionList T,thePosition -> hookLength(thePosition,T)) + ) + +rowStabilizer = method(TypicalValue => List) +rowStabilizer YoungTableau := T -> ( + if not isNatural T then error "expected tableau entries in 1..n"; + if not isWeaklyDecreasing T or not isNonnegative T then error "expected shape to be weakly decreasing and nonnegative"; + + n := size T; + + permutedRows := for theRow in rowList T list permutations theRow; + + extendedPerms := for permList in permutedRows list ( + theRow := permList#0; + + for thePerm in permList list ( + extendedPerm := new MutableList from toList(1..n); + + for i from 0 to #thePerm-1 do ( + extendedPerm#(theRow#i-1) = thePerm#i; + ); + + permutation toList extendedPerm + ) + ); + + ans := extendedPerms#0; + for i from 1 to #extendedPerms-1 do ans = (ans ** extendedPerms#i) / splice; + for theSeq in ans list extend(product toList theSeq,n) + ) + +columnStabilizer = method(TypicalValue => List) +columnStabilizer YoungTableau := T -> rowStabilizer conjugate T + +readingWord = method(TypicalValue => List) +readingWord YoungTableau := T -> ( + flatten for colIndex in columnRange T list reverse columnEntries(T,colIndex) + ) + +-- operators + +YoungTableau == YoungTableau := (T1,T2) -> ( + (lam1,mu1) := standardize skewShape T1; + (lam2,mu2) := standardize skewShape T2; + + toList lam1 == toList lam2 and toList mu1 == toList mu2 and entries T1 == entries T2 + ) + +Permutation * YoungTableau := (p,T) -> applyEntries(T, theBox -> (if theBox > #p then theBox else p#(theBox-1))) diff --git a/M2/Macaulay2/packages/Tableaux/algorithms.m2 b/M2/Macaulay2/packages/Tableaux/algorithms.m2 index 0765cb61859..4c3c83f9091 100644 --- a/M2/Macaulay2/packages/Tableaux/algorithms.m2 +++ b/M2/Macaulay2/packages/Tableaux/algorithms.m2 @@ -1,8 +1,16 @@ -maxSSYT = method(TypicalValue => SkewTableau) +cartesianProductBags = theBags -> ( + ans := toList theBags#0; + for i from 1 to #theBags-1 do ( + ans = ans ** toList theBags#i; + ); + Bag (ans / deepSplice) + ) + +maxSSYT = method(TypicalValue => YoungTableau) maxSSYT (Partition,Partition) := (lam,mu) -> ( - tempT := skewTableau(lam,mu); + tempT := youngTableau(lam,mu); (lam,mu) = standardize skewShape tempT; entryList := for entryIndex from 0 to sum toList lam - sum toList mu - 1 list ( @@ -12,12 +20,12 @@ maxSSYT (Partition,Partition) := (lam,mu) -> ( #(delete(null,theColAbove)) ); - skewTableau(lam,mu,entryList) + youngTableau(lam,mu,entryList) ) -minSSYT = method(TypicalValue => SkewTableau) +minSSYT = method(TypicalValue => YoungTableau) minSSYT (Partition,Partition,ZZ) := (lam,mu,maxEntry) -> ( - tempT := skewTableau(lam,mu); + tempT := youngTableau(lam,mu); (lam,mu) = standardize skewShape tempT; entryList := for entryIndex from 0 to sum toList lam - sum toList mu - 1 list ( @@ -27,11 +35,11 @@ minSSYT (Partition,Partition,ZZ) := (lam,mu,maxEntry) -> ( maxEntry - #(delete(null,theColBelow)) ); - skewTableau(lam,mu,entryList) + youngTableau(lam,mu,entryList) ) -addOneSSYT = method(TypicalValue => SkewTableau) -addOneSSYT (SkewTableau,Sequence,Partition,Partition) := (T,thePosition,lam,mu) -> ( +addOneSSYT = method(TypicalValue => YoungTableau) +addOneSSYT (YoungTableau,Sequence,Partition,Partition) := (T,thePosition,lam,mu) -> ( (rowIndex,colIndex) := thePosition; entryList := new MutableList from entries T; @@ -64,10 +72,10 @@ addOneSSYT (SkewTableau,Sequence,Partition,Partition) := (T,thePosition,lam,mu) ); ); - skewTableau(lam,mu,toList entryList) + youngTableau(lam,mu,toList entryList) ) -allSemistandardTableaux = method(TypicalValue => List) +allSemistandardTableaux = method(TypicalValue => Bag) allSemistandardTableaux (Partition,Partition,ZZ) := (lam,mu,maxEntry) -> ( (lam,mu) = standardize (lam,mu); (lamList,muList) := (toList lam, toList mu); @@ -76,9 +84,9 @@ allSemistandardTableaux (Partition,Partition,ZZ) := (lam,mu,maxEntry) -> ( if any(0..(#lam-1), i -> mu#i > lam#i) then return Bag {}; - T := skewTableau(lam,mu); + T := youngTableau(lam,mu); - if #lam == 0 then return Bag {skewTableau(new Partition from {})}; + if #lam == 0 then return Bag {youngTableau(new Partition from {})}; if any(columnRange T,i -> #columnEntries(i,T) > maxEntry) then return Bag {}; maxT := maxSSYT(lam,mu); @@ -118,6 +126,9 @@ allSemistandardTableaux Partition := lam -> ( allSemistandardTableaux(lam,mu,maxEntry) ) +allSemistandardTableaux List := shapeList -> ( + cartesianProductBags for theShape in shapeList list allSemistandardTableaux theShape + ) numSemistandardTableaux = method(TypicalValue => ZZ) numSemistandardTableaux (Partition,ZZ) := (lam,n) -> ( @@ -126,7 +137,7 @@ numSemistandardTableaux (Partition,ZZ) := (lam,n) -> ( numAppendedZeros := n - #lam; lam = new Partition from (toList(lam)|toList(numAppendedZeros:0)); - T := skewTableau(lam); + T := youngTableau(lam); theProd := product flatten for rowIndex from 0 to #lam-1 list ( for colIndex from 0 to lam#rowIndex-1 list ( @@ -142,4 +153,177 @@ numSemistandardTableaux (Partition,ZZ) := (lam,n) -> ( theProd//theDiv ) -numSemistandardTableaux Partition := lam -> numSemistandardTableaux(truncate lam,# truncate lam) +numSemistandardTableaux Partition := lam -> numSemistandardTableaux(trim lam,# trim lam) + +randomSemistandardTableau = method(TypicalValue => YoungTableau) +randomSemistandardTableau (Partition,Partition,ZZ) := (lam,mu,maxEntry) -> ( + maxT := maxSSYT(lam,mu); + minT := minSSYT(lam,mu,maxEntry); + + T := mutableYoungTableau(lam,mu,entries maxT); + + for thePosition in reverse positionList T do ( + (rowIndex,colIndex) := thePosition; + + hasBoxRight := colIndex < lam#rowIndex - 1; + hasBoxBelow := rowIndex < #lam-1 and colIndex < lam#(rowIndex+1); + entryRight := if hasBoxRight then T_(rowIndex,colIndex+1) else infinity; + entryBelow := if hasBoxBelow then T_(rowIndex+1,colIndex) else infinity; + + minEntry := maxT_thePosition; + maxEntry := min(minT_thePosition,entryRight,entryBelow - 1); + --possibleEntries := toList(minEntry .. maxEntry); + possibleEntries := join toSequence for i from minEntry to maxEntry list toList((2^(i-1)):i); + + T_thePosition = randomElement possibleEntries; + ); + + youngTableau(lam,mu,new List from entries T) + ) +randomSemistandardTableau (Partition,ZZ) := (lam,maxEntry) -> randomSemistandardTableau(lam,new Partition from {},maxEntry) +randomSemistandardTableau (Partition,Partition) := (lam,mu) -> randomSemistandardTableau(lam,mu,#lam) +randomSemistandardTableau Partition := lam -> randomSemistandardTableau(lam,new Partition from {},#lam) + +numStandardTableaux = method(TypicalValue => ZZ) +numStandardTableaux Partition := lam -> hookLength lam + +allStandardTableaux = method(TypicalValue => Bag) +allStandardTableaux (Partition) := lam -> ( + lam = trim lam; + n := sum toList lam; + T := youngTableau lam; + + recurse := (indexList,mu) -> ( + if # mu == 0 then ( + entryList := for i from 0 to #indexList - 1 list (n - position(indexList, theIndex -> theIndex == i)); + return youngTableau(lam,entryList); + ); + + cornerList := positionList(youngTableau mu,isCorner); + + flatten for thePosition in cornerList list ( + (rowIndex,colIndex) := thePosition; + muNew := trim new Partition from for i from 0 to #mu-1 list ( + if i == rowIndex then mu#i-1 else mu#i + ); + + recurse(indexList | {toIndex(thePosition,T)}, muNew) + ) + ); + + Bag recurse({},lam) + ) +-* +allStandardTableaux List := shapeList -> ( + cartesianProductBags for theShape in shapeList list allStandardTableaux theShape + ) +*- + +randomStandardTableau = method(TypicalValue => YoungTableau) +randomStandardTableau Partition := lam -> ( + lam = trim lam; + n := sum toList lam; + T := youngTableau lam; + + mu := lam; + indexList := for i from 1 to n list ( + cornerList := positionList(youngTableau mu,isCorner); + randomIndex := random(#cornerList); + thePosition := cornerList#randomIndex; + + (rowIndex,colIndex) := thePosition; + mu = trim new Partition from for i from 0 to #mu-1 list ( + if i == rowIndex then mu#i-1 else mu#i + ); + + toIndex(thePosition,T) + ); + + entryList := for i from 0 to #indexList - 1 list (n - position(indexList, theIndex -> theIndex == i)); + return youngTableau(lam,entryList); + ) + +numTabloids = method(TypicalValue => ZZ) +numTabloids (Partition,Partition) := (lam,mu) -> ( + if hasNegativeRow youngTableau(lam,mu) then error "error: expected lam >= mu"; + + (lam,mu) = standardize (lam,mu); + n := sum for i from 0 to #lam-1 list lam#i - mu#i; + + nNext := n; + product for i from 0 to #lam-1 list ( + n = nNext; + nNext = n - abs(lam#i-mu#i); + binomial(n,abs(lam#i-mu#i)) + ) + ) +numTabloids Partition := lam -> numTabloids(lam,new Partition from {}) + +allTabloids = method(TypicalValue => Bag) +allTabloids (Partition,Partition) := (lam,mu) -> ( + if hasNegativeRow youngTableau(lam,mu) then error "error: expected lam >= mu"; + + (lam,mu) = standardize (lam,mu); + n := sum for i from 0 to #lam-1 list lam#i - mu#i; + + recurse := (currentEntries, remainingEntries, rowIndex) -> ( + if rowIndex == #lam then return tabloid(lam,mu,currentEntries); + + flatten for theRow in subsets(remainingEntries, lam#rowIndex - mu#rowIndex) list ( + recurse(currentEntries | theRow, sort toList(set remainingEntries - set theRow), rowIndex + 1) + ) + ); + + Bag recurse({}, toList(1..n), 0) + ) +allTabloids Partition := lam -> allTabloids(lam,new Partition from {}) + +randomTabloid = method(TypicalValue => Tabloid) +randomTabloid (Partition,Partition) := (lam,mu) -> tabloid(lam,mu,shuffle toList(1..(size youngTableau(lam,mu)))) +randomTabloid Partition := lam -> randomTabloid(lam,new Partition from {}) + +allSubPartitions = method(TypicalValue => Bag) +allSubPartitions (Partition,Partition) := (lam,mu) -> ( + if not isWeaklyDecreasing(lam,mu) or not isNonnegative(lam,mu) then error "expected weakly decreasing, nonnegative partitions"; + + (lam,mu) = standardize(lam,mu); + T := youngTableau(lam,mu); + + if hasNegativeRow T then return Bag {}; + + ans := new MutableList from {toList mu}; + for rowIndex in rowRange T do ( + for thePartition in ans do ( + for j from 1 to lam#rowIndex-mu#rowIndex do ( + if rowIndex > 0 and thePartition#rowIndex + j > thePartition#(rowIndex-1) then continue; + ans#(#ans) = (((toList(rowIndex:0))|{j}|(toList((#lam-rowIndex-1):0))) + thePartition); + ) + ); + + ); + + + Bag for theList in ans list trim new Partition from theList + ) +allSubPartitions Partition := lam -> allSubPartitions(lam,new Partition from {}) + +toPartitionChain = method(TypicalValue => Sequence) +toPartitionChain YoungTableau := T -> ( + (lam,mu) := standardize skewShape T; + + minEntry := min entries T; + maxEntry := max entries T; + + lamPrev := mu; + ans := for i from minEntry to maxEntry list ( + lamNew := new Partition from for rowIndex in rowRange T list ( + lamPrev#rowIndex + number(rowEntries(T,rowIndex), theBox -> theBox == i) + ); + + lamPrev = lamNew; + lamNew + ); + + --toSequence apply({mu}|ans, trim) + toSequence({mu}|ans) + ) diff --git a/M2/Macaulay2/packages/Tableaux/documentation.m2 b/M2/Macaulay2/packages/Tableaux/documentation.m2 index aab47784e40..323b7cf6ff1 100644 --- a/M2/Macaulay2/packages/Tableaux/documentation.m2 +++ b/M2/Macaulay2/packages/Tableaux/documentation.m2 @@ -7,19 +7,20 @@ Node a package for computing with Young tableaux Description Text - This package provides the classes @TO SkewTableau@ and @TO YoungTableau@. + This package provides the classes @TO YoungTableau@, @TO MutableYoungTableau@, and @TO Tabloid@. These classes + can be used to construct (skew) tableaux, and entries in the boxes may be any class. See the constructor's + page @TO youngTableau@ for some basics of this class. /// doc /// Key - SkewTableau - (net, SkewTableau) + MutableYoungTableau Headline - a type of HashTable representing a skew Young tableau + a type of HashTable representing a Young tableau, with mutable entries Description Text - An object of type SkewTableau is a hash table containing two shapes of type @TO Partition@, - and a list of box entries. The entries may have any type, except for @TO null@ objects. + An object of type MutableYoungTableau is a hash table containing two shapes of type @TO Partition@, + and a mutable list of box entries. The entries may have any type, except for @TO null@ objects. The inner and outer shapes, $\lambda$ and $\mu$ respectively, can be any sequence of integers. In particular, it accepts negative parts, compositions, rows where $\lambda_i < \mu_i$, and compositions where @@ -28,1146 +29,1972 @@ Description lam = new Partition from {4,3,2} mu = new Partition from {3,1} entryList = {1,2,3,3,9} - T = skewTableau(lam,mu,entryList) + T = mutableYoungTableau(lam,mu,entryList) + T_(1,1) = 8 + T SeeAlso - skewTableau - YoungTableau + mutableYoungTableau +/// + +doc /// +Key + Tabloid +Headline + a type of HashTable representing a Young tabloid +Description + Text + An object of type Tabloid is a subclass of type @TO YoungTableau@. A tabloid represents an equivalence class + of Young tableaux, with entries in $\{1,\ldots,n\}$. Two tabloids are equal if they have the same shape, + and each corresponding row contains the same boxes, up to some permutation. Hence, rows are drawn without + dividing boxes. + Example + lam = new Partition from {4,3,2} + mu = new Partition from {3,1} + T = tabloid(lam,mu,{1,2,5,3,4}) + T' = tabloid(lam,mu,{1,5,2,3,4}) + T == T' +SeeAlso + tabloid /// doc /// Key YoungTableau + (net, YoungTableau) Headline - a type of HashTable representing a (nonskew) Young tableau + a type of HashTable representing a Young tableau Description Text - This is a subclass of @TO SkewTableau@. Each object of YoungTableau is essentially just a skew tableau with inner - shape $\mu=0$. + An object of type YoungTableau is a hash table containing two shapes of type @TO Partition@, + and a list of box entries. The entries may have any type, except for @TO null@ objects. + + The inner and outer shapes, $\lambda$ and $\mu$ respectively, can be any sequence of integers. In particular, + it accepts negative parts, compositions, rows where $\lambda_i < \mu_i$, and compositions where + $\ell(\lambda)\neq\ell(\mu)$. Example lam = new Partition from {4,3,2} - entryList = toList(1..(sum toList lam)) - T = youngTableau(lam,entryList) + mu = new Partition from {3,1} + entryList = {1,2,3,3,9} + T = youngTableau(lam,mu,entryList) SeeAlso youngTableau - SkewTableau /// doc /// Key - skewTableau - (skewTableau, Partition, Partition, List) - (skewTableau, Sequence, List) - (skewTableau, Partition, List) - (skewTableau, Partition, Partition) - (skewTableau, Partition) - (skewTableau, YoungTableau) - drawInnerShape - (drawInnerShape, Boolean) + allSemistandardTableaux + (allSemistandardTableaux, Partition, Partition, ZZ) + (allSemistandardTableaux, Partition, ZZ) + (allSemistandardTableaux, Partition, Partition) + (allSemistandardTableaux, Partition) + (allSemistandardTableaux, List) Headline - constructor for type SkewTableau + list all semistandard Young tableaux of a given shape Usage - skewTableau(lam, mu, entryList) - skewTableau((lam,mu),entryList) + allSemistandardTableaux(lam,mu,N) + allSemistandardTableaux L Inputs lam:Partition - the outer shape $\lambda$. + the outer shape, $\lambda$. mu:Partition - the inner shape $\mu$. If not given, then it is assumed to be the $0$ partition. - entryList:List - the filling of the boxes. If not given, then box entries are assumed to be the empty string "". + the inner shape, $\mu$. + N:ZZ + the maximum entry. + L:List + a list of tuples ($\lambda^{(i)}$,$\mu^{(i)}$,$N^{(i)}$). Outputs - T:SkewTableau - a skew Young tableau of shape $\lambda/\mu$ with the given filling. -Consequences - Item - The list of entries has length equal to $\sum_{i=1}^{\ell(\lambda)}|\lambda_i-\mu_i|$. E.g., - if $\lambda=(2)$ and $\mu=(5)$, then the entry list must have length $3$. - Item - None of the entries are null. + b:Bag + bagged list of all SSYT of given shape and entries in 1..N. If N is not provided, it is assumed + to be the length of the shape. Description - Example - lam = new Partition from {4,3,2} - mu = new Partition from {3,1} - entryList = {1,2,3,3,9} - T = skewTableau(lam,mu,entryList) - Text - We may construct tableaux with any compositions. - Example - skewTableau(new Partition from {3,5,1}, new Partition from {0,1}, {7,"&",4,2,"g","u",6,0}) - Text - The shapes may have negative parts. In this case, a vertical line is drawn by @TO2{(net, SkewTableau),"net"}@ - to indicate that negative parts are to the left. - Example - skewTableau(new Partition from {3,-3,-1}, new Partition from {-2,-4,-1}) Text - If any $\lambda_i<\mu_i$, then the boxes in that row are drawn shaded. + The resulting bag can be thought of as the set $\mathrm{SSYT}(\lambda,\mu,N)$ of semistandard + Young tableaux of shape $\lambda/\mu$ and entries in $1,2,\ldots,N$. Example - skewTableau(new Partition from {-2,-4,2}, new Partition from {1,-1,-1}, {1,2,3,4,5,6,7,8,9}) + theBag = allSemistandardTableaux(new Partition from {5,4,2}, new Partition from {3,1}) + for i from 0 to 4 do print theBag#i Text - We may cast an object of type @TO YoungTableau@ to type SkewTableau. + When the inner partition is $0$, the function @TO numSemistandardTableaux@ computes the number of SSYT via + the hook-content formula. Example - T' = youngTableau(new Partition from {3,1,1}) - skewTableau T' + # allSemistandardTableaux(new Partition from {6,6,5,1,1}) + numSemistandardTableaux(new Partition from {6,6,5,1,1}) Text - The inner shape may be drawn by calling drawInnerShape. + A convenient way to compute the Cartesian product + $\mathrm{SSYT}(\lambda^{(1)},\mu^{(1)},N^{(1)})\times\mathrm{SSYT}(\lambda^{(2)},\mu^{(2)},N^{(2)})\times\cdots$, + is to use a list of the inputs + $L=\{(\lambda^{(1)},\mu^{(1)},N^{(1)}),(\lambda^{(2)},\mu^{(2)},N^{(2)}),\ldots\}$. In this case, + the output is a bagged list of tuples of tableaux. Example - T'' = skewTableau(new Partition from {5,4,-1}, new Partition from {2,4,-3}) - drawInnerShape true -* no-capture-flag *- - T'' - drawInnerShape false - T'' + lam1 = new Partition from {3,1} + mu1 = new Partition from {2} + lam2 = new Partition from {1,1} + theProduct = allSemistandardTableaux {(lam1,mu1), (lam2,3)} + for i from 0 to 5 do print theProduct#i SeeAlso - SkewTableau - YoungTableau - youngTableau + numSemistandardTableaux + allStandardTableaux + allTabloids /// doc /// Key - youngTableau - (youngTableau, Partition, List) - (youngTableau, Partition) + allStandardTableaux + (allStandardTableaux, Partition) Headline - constructor for type YoungTableau + list all standard Young tableaux of a given shape Usage - youngTableau(lam, entryList) + allSemistandardTableaux(lam) Inputs lam:Partition - the shape $\lambda$. - entryList:List - the filling of the boxes. If not given, then box entries are assumed to be the empty string "". + the shape, $\lambda$. Outputs - T:YoungTableau - a (nonskew) Young tableau of shape $\lambda$ with the given filling. -Consequences - Item - The list of entries has length equal to $\sum_{i=1}^{\ell(\lambda)}|\lambda_i|$. E.g., - if $\lambda=(-2)$, then the entry list must have length $2$. - Item - None of the entries are @TO null@. + b:Bag + bagged list of all SYT of given shape and entries in $\{1,\ldots,N\}$, where $N$ is the size of $\lambda$. Description Example - lam = new Partition from {4,3,2} - entryList = {1,2,3,4,5,6,7,8,9} - T = youngTableau(lam,entryList) - Text - We may construct tableaux with any compositions. - Example - youngTableau(new Partition from {3,5,1}, {7,"&",4,2,"g","u",6,0,-1}) - Text - The shape may have negative parts. In this case, a vertical line is drawn by @TO2{(net, SkewTableau),"net"}@ - to indicate that negative parts are to the left. If any $\lambda_i<0$, then the boxes in that row are drawn shaded. - Example - youngTableau(new Partition from {-2,-4,2}) + theBag = allStandardTableaux(new Partition from {4,3,1}) + for i from 0 to 4 do print theBag#i Text - We may cast an object of type YoungTableau to type @TO SkewTableau@. + The function @TO numStandardTableaux@ computes the number of SYT. Example - T' = youngTableau(new Partition from {3,1,1}) - skewTableau T' + # theBag + numStandardTableaux(new Partition from {4,3,1}) SeeAlso - SkewTableau - YoungTableau - skewTableau + numStandardTableaux + allSemistandardTableaux + allTabloids /// doc /// Key - youngDiagram - (youngDiagram, Partition, Partition) - (youngDiagram, Partition) - (youngDiagram, SkewTableau) + allSubPartitions + (allSubPartitions, Partition, Partition) + (allSubPartitions, Partition) Headline - a net of the Young diagram + list all partitions whose Young diagrams are contained in another Usage - youngDiagram(lam, mu) - youngDiagram T + allSubPartitions(lam,mu) Inputs lam:Partition - the outer shape $\lambda$. + the outer shape, $\lambda$. mu:Partition - the inner shape $\mu$. If not given, if is assumed to be the $0$ partition. - T:SkewTableau - a skew tableau. + the inner shape, $\mu$. If not provided, then it is the $0$ partition. Outputs - n:Net - a representation of the Young diagram of shape $\lambda/\mu$. + b:Bag + bagged list of all partitions $\nu$ such that $\mu\subseteq\nu\subseteq\lambda$. Description Example - T = skewTableau(new Partition from {4,3,1}, new Partition from {2,1}, {1,2,3,4,5}) - youngDiagram T + theBag = allSubPartitions(new Partition from {2,2,1},new Partition from {1}) + for nu in theBag do print youngDiagram nu SeeAlso - skewTableau - youngTableau - ferrersDiagram + allSemistandardTableaux + allStandardTableaux + allTabloids /// doc /// Key - ferrersDiagram - (ferrersDiagram, Partition, Partition) - (ferrersDiagram, Partition) - (ferrersDiagram, SkewTableau) + allTabloids + (allTabloids, Partition, Partition) + (allTabloids, Partition) Headline - a net of the Ferrers diagram + list all tabloids of a given shape Usage - ferrersDiagram(lam, mu) - ferrersDiagram T + allSemistandardTableaux(lam,mu) Inputs lam:Partition - the outer shape $\lambda$. + the outer shape, $\lambda$. mu:Partition - the inner shape $\mu$. If not given, if is assumed to be the $0$ partition. - T:SkewTableau - a skew tableau. + the inner shape, $\mu$. Outputs - n:Net - a representation of the Ferrers diagram of shape $\lambda/\mu$. + b:Bag + bagged list of all tabloids of given shape and entries in $\{1,\ldots,N\}$, where $N$ is the size of + the tabloid. Description Example - T = skewTableau(new Partition from {4,3,1}, new Partition from {2,1}, {1,2,3,4,5}) - ferrersDiagram T + theBag = allTabloids(new Partition from {4,3,1}) + for i from 0 to 4 do print theBag#i + Text + The function @TO numTabloids@ computes the number of tabloids. + Example + # theBag + numTabloids(new Partition from {4,3,1}) SeeAlso - skewTableau - youngTableau - youngDiagram + numTabloids + allSemistandardTableaux + allStandardTableaux /// doc /// Key - (tex, SkewTableau) + applyEntries + (applyEntries, YoungTableau, Function) Headline - LaTeX output for a skew tableau + apply a function to all entries in the tableau Usage - tex T + applyEntries(T,f) Inputs - T:SkewTableau - a skew tableau. + T:YoungTableau + a tableau. + f:Function + acts on the entries of T. Outputs - s:String - the LaTeX code for reproducting the given skew tableau. + T':YoungTableau + a tableau with the same shape as T, and entries n -> f n. Description - Text - The LaTeX code uses commands from the LaTeX package @HREF("https://github.com/AndrewMathas/aTableau","aTableau")@. Example - T = skewTableau(new Partition from {4,3,1}, new Partition from {2,1}, {1,2,3,4,5}) - tex T + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..8)) + applyEntries(T, theBox -> theBox^2) SeeAlso - skewTableau - youngTableau - youngDiagram - ferrersDiagram + applyPositions /// doc /// Key - skewShape - (skewShape, SkewTableau) - (truncate, Partition, Partition) - (pad, Partition, Partition) - standardize - (standardize, Partition, Partition) + applyPositions + (applyPositions, YoungTableau, Function) Headline - the shape of a skew tableau + apply a function to all positions of boxes in a tableau Usage - (lam,mu) = skewShape T + applyPositions(T,f) Inputs - T:SkewTableau - a skew tableau. + T:YoungTableau + a skew Tableau. + f:Function + acts on the entries of T. Outputs - lam:Partition - the outer shape, $\lambda$, of T. - mu:Partition - the inner shape, $\mu$, of T. + T':YoungTableau + a tableau with the same shape as T, and entries (i,j) -> f (i,j). Description - Text - This returns the original shape used to construct the tableau. - Example - T = skewTableau(new Partition from {4,3,1,0}, new Partition from {2,1}, {1,2,3,4,5}) - skewShape T - Text - It may be convenient to remove trailing $0$'s from the partitions by using truncate on a sequence of two partitions. - Example - truncate skewShape T - Text - It may also be useful to append $0$s to make the partitions have the same number of parts. Example - (lam',mu') = pad skewShape T - rowLengths' = for i from 0 to #lam'-1 list(lam'#i-mu'#i) - Text - You may also 'standardize' a skew shape to return a pair of partitions of equal length, without extra $0$'s. - I.e., one of the resulting partitions will end with a nonzero part, and the other will be padded with $0$'s. - Example - (lam'',mu'') = standardize skewShape T - rowLengths = for i from 0 to #lam''-1 list(lam''#i-mu''#i) + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..8)) + applyPositions(T, thePosition -> thePosition) + applyPositions(T, thePosition -> thePosition#1 - thePosition#0) + applyPositions(T, thePosition -> (T_thePosition)^2) SeeAlso - shape + applyEntries /// - doc /// Key - shape - (shape, YoungTableau) - (truncate, Partition) + boxContent + (boxContent, ZZ, ZZ) Headline - the shape of a Young tableau + compute the content of a box of a tableau Usage - lam = shape T + boxContent(i,j) Inputs - T:SkewTableau - a Young tableau. + i:ZZ + the row index of a box. + j:ZZ + the column index of a box. Outputs - lam:Partition - the shape, $\lambda$, of T. + n:ZZ + the hook length of box (i,j). Description Text - This returns the original shape used to construct the tableau. - Example - T = youngTableau(new Partition from {4,3,1,0}) - shape T - Text - It may be convenient to remove trailing $0$'s from the partitions by using truncate on the partition. + The content of box (i,j) is defined as $j-i$. Example - truncate shape T -SeeAlso - skewShape + T = youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) + boxContent(1,1) + applyPositions(T, thePosition -> boxContent thePosition) /// - doc /// Key - (entries, SkewTableau) + columnEntries + (columnEntries, YoungTableau, ZZ) + (columnEntries, ZZ, YoungTableau) Headline - the filling of a skew tableau + get the entries in a column Usage - entries T + columnEntries(T,j) + columnEntries(j,T) Inputs - T:SkewTableau - a skew tableau. + T:YoungTableau + a tableau. + j:ZZ + the index of a column. Outputs l:List - the filling of T. + the entries of column $j$ of T. Description Text - This returns the filling as a list. + This returns the entries of a column without extra @TO null@ entries, as opposed to T_i. Example - T = skewTableau(new Partition from {4,3,1,0},toList(1..8)) - entries T + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(1..9)) + columnEntries(T,0) + T_0 SeeAlso - (size, SkewTableau) + (symbol _, YoungTableau, ZZ) /// doc /// Key - (size, SkewTableau) + columnRange + (columnRange, YoungTableau) Headline - the number of boxes in a skew tableau + the range of column indices of a tableau Usage - size T + columnRange T Inputs - T:SkewTableau - a skew Tableau. + T:YoungTableau + a tableau. Outputs - n:ZZ - the number of boxes in T. + seq:Sequence + the range of column indices. Description + Text + Although it is straightforward that the row indices are 0..(numRows T - 1), the analogous sequence does not + work for columns since columns may have negative indices. Hence, columnRange provides an easy way to iterate + over column indices. The lowest number is the smallest part of the tableau's shape, and the largest number + is the largest part of its shape. Example - T = skewTableau(new Partition from {4,3,1,0}) - size T + T = youngTableau(new Partition from {8,7,6,6,1}, new Partition from {4,4,3,2,-2}, toList(1..17)) + columnRange T + for j in columnRange T do print columnEntries(T,j) SeeAlso - (entries, SkewTableau) + (symbol _, YoungTableau, ZZ) + columnEntries + rowRange + positionList /// doc /// Key - (numRows, SkewTableau) + columnStabilizer + (columnStabilizer, YoungTableau) Headline - the number of rows in a tableau + the column stabilizer of a tableau Usage - numRows T + columnStabilizer T Inputs - T:SkewTableau + T:YoungTableau a tableau. Outputs - n:ZZ - the number of rows in T. + l:List + the permutations in the column stabilizer. Description Text - The number of rows is the same as the length of its shapes, excluding trailing $0$'s. + A permutation acts on a tableau by permuting its entries. The column stabilizer of a tableau is the group + of permutations that leave boxes in their original columns. Example - T = skewTableau(new Partition from {4,3,1,0}, new Partition from {1,1,3,2,0}) - numRows T - (lam,mu) = standardize skewShape T - #lam + T = youngTableau(new Partition from {2,2,1}, {1,4,2,5,3}) + columnStabilizer T SeeAlso - (numColumns, SkewTableau) + rowStabilizer /// doc /// Key - (numColumns, SkewTableau) + toPartitionChain + (toPartitionChain, YoungTableau) Headline - the number of columns in a tableau + decompose a tableau into a chain of partitions Usage - numColumns T + toPartitionChain T Inputs - T:SkewTableau + T:YoungTableau a tableau. Outputs - n:ZZ - the number of columns in T. + s:Sequence + a sequence of partitions. Description Text - The number of rows is the same as largest part in its shape, minus the smallest part in the shape. + A (semistandard) Young tableau of shape $\lambda/\mu$ determines (and is determined by) a sequence of + partitions $(\lambda^{(0)},\ldots,\lambda^{(r)})$, where + $\mu= \lambda^{(0)}\subseteq\lambda^{(1)}\subseteq\cdots\subseteq\lambda^{(r)}=\lambda$. Example - drawInnerShape true -* no-capture-flag *- - T = skewTableau(new Partition from {4,3,1}, new Partition from {1,1,1}) - numColumns T + lam = new Partition from {6,6,5,3,2,2,2,1} + mu = new Partition from {3,1} + entryList = {1,1,7,1,3,4,4,8,1,2,4,7,7,2,3,5,3,4,4,5,5,6,8} + T = youngTableau(lam,mu,entryList) + theDecomp = toPartitionChain T Text - Note that, if a shape contains negative parts, then columns may have negative indices. + This chain has the property that each $\lambda^{(i)}/\lambda^{(i-1)}$ is a horizontal strip. Example - T' = skewTableau(new Partition from {4,3,1}, new Partition from {-1,1,1}) - numColumns T' -SeeAlso - (numRows, SkewTableau) - columnRange + drawInnerShape true -* no-capture-flag *- + for i from 1 to #theDecomp-1 do ( + print(toString(toSequence trim theDecomp#i)|"/"|toString(toSequence trim theDecomp#(i-1))|":"); + print youngTableau(theDecomp#i,theDecomp#(i-1)); + ) /// doc /// Key - (symbol ^, SkewTableau, ZZ) + drawInnerShape + (drawInnerShape, Boolean) Headline - get the entries in a row. + option to draw the inner shape of a tableau Usage - T^i + drawInnerShape b Inputs - T:SkewTableau - a tableau. - i:ZZ - the index of a row. + b:Boolean + whether to draw the shape or not. Outputs - l:List - the entries in the $i$th row of T. + b:Boolean + the inputted value. +Consequences + Item + If b is true, then all subsequent calls to net will draw the inner shape. If b is false, then the inner + shape is not drawn. Description - Text - Note that the resulting list may contain @TO null@ entries in places where a box is not present. This means that - the notations (T^i)#j, (T_j)#i, and T_(i,j) are all equivalent for valid positions (i,j). However, the notation - T_(i,j) will raise an error for nonvalid positions, whereas the other two will return @TO null@. - Example - T = skewTableau(new Partition from {4,3,1,0}, new Partition from {1,1}, {1,2,3,4,5,6}) - for i from 0 to numRows T - 1 do print T^i - (T^0)#1 - (T_1)#0 - T_(0,1) - Text - If T has negative rows, then the entries at negative column coordinates are to the right in the output. - Example - T' = skewTableau(new Partition from {4,3,1,0}, new Partition from {1,-1}, {1,2,3,4,5,6,7,8}) - for i from 0 to numRows T' - 1 do print T'^i - T'^1#-1 - T'_(1,-1) - Text - To get the row entries exactly as they appear in the tableau and without extra null's, use rowEntries. Example - T'' = skewTableau(new Partition from {3,4}, new Partition from {-2}, {1,2,3,4,5,6,7,8,9}) - T''^0 - rowEntries(T'',0) + T = youngTableau(new Partition from {5,4,1}, new Partition from {2,1,1}) + drawInnerShape true -* no-capture-flag *- + T + drawInnerShape false -* no-capture-flag *- + T SeeAlso - (numRows, SkewTableau) - rowEntries - (symbol _, SkewTableau, Sequence) - (symbol _, SkewTableau, ZZ) + youngTableau /// doc /// Key - (symbol _, SkewTableau, ZZ) + ferrersDiagram + (ferrersDiagram, Partition, Partition) + (ferrersDiagram, Partition) + (ferrersDiagram, YoungTableau) Headline - get the entries in a column. + a net of the Ferrers diagram Usage - T_j + ferrersDiagram(lam, mu) + ferrersDiagram T Inputs - T:SkewTableau - a tableau. - j:ZZ - the index of a column. + lam:Partition + the outer shape $\lambda$. + mu:Partition + the inner shape $\mu$. If not given, if is assumed to be the $0$ partition. + T:YoungTableau + a skew tableau. Outputs - l:List - the entries in the $j$th column of T. + n:Net + a representation of the Ferrers diagram of shape $\lambda/\mu$. Description - Text - Note that the resulting list may contain @TO null@ entries in places where a box is not present. This means that - the notations (T^i)#j, (T_j)#i, and T_(i,j) are all equivalent for valid positions (i,j). However, the notation - T_(i,j) will raise an error for nonvalid positions, whereas the other two will return @TO null@. - Example - T = skewTableau(new Partition from {4,3,1,0}, new Partition from {1,1}, {1,2,3,4,5,6}) - for i in columnRange T do print T_i - (T^0)#1 - (T_1)#0 - T_(0,1) - Text - To get the column entries without extra null's, use columnEntries. Example - T'' = skewTableau(new Partition from {3,1}, new Partition from {-2,0,-1}, {1,2,3,4,5,6,7}) - T''_-1 - columnEntries(T'',-1) + T = youngTableau(new Partition from {4,3,1}, new Partition from {2,1}, {1,2,3,4,5}) + ferrersDiagram T SeeAlso - (numColumns, SkewTableau) - columnEntries - (symbol _, SkewTableau, Sequence) - (symbol ^, SkewTableau, ZZ) + youngTableau + youngDiagram /// doc /// Key - (symbol _, SkewTableau, Sequence) + hookLength + (hookLength, Sequence, YoungTableau) + (hookLength, Partition) Headline - get the entry at a specific position. + compute the hook length of a box of a tableau Usage - T_seq - T_(i,j) + n = hookLength(thePosition,T) + m = hookLength lam Inputs - T:SkewTableau + thePosition:Sequence + a pair (i,j) of integers. + T:YoungTableau a tableau. - seq:Sequence - a pair (i,j) of integers in ZZ. + lam:Partition + a partition. Outputs - theBox:Thing - the entry of T in position (i,j). + n:ZZ + the hook length of box (i,j) in T. + m:ZZ + the hook length formula for lam. Description Text - The notations (T^i)#j, (T_j)#i, and T_(i,j) are all equivalent for valid positions (i,j). However, the notation - T_(i,j) will raise an error for nonvalid positions, whereas the other two will return @TO null@. + The hook length of box (i,j) is defined as the number of boxes directly below, and directly to the right, + of a box, including the box itself. Example - T = skewTableau(new Partition from {4,3,1,0}, new Partition from {1,1}, {1,2,3,4,5,6}) - (T^0)#1 - (T_1)#0 - T_(0,1) + T = youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) + hookLength((1,1),T) + applyPositions(T, thePosition -> hookLength(thePosition,T)) + Text + The hook length formula computes the number of standard Young tableau of shape $\lambda$. + Example + lam = new Partition from {3,2,2} + hookLength lam SeeAlso - (symbol _, SkewTableau, ZZ) - (symbol ^, SkewTableau, ZZ) + numStandardTableaux + allStandardTableaux /// doc /// Key - columnRange - (columnRange, SkewTableau) + horizontalNet Headline - the range of column indices of a tableau. + multiple nets concatenated horizontally Usage - columnRange T + horizontalNet L Inputs - T:SkewTableau - a tableau. + L:List + a list or sequence. Outputs - seq:Sequence - the range of column indices. + n:Net + a net of objects in L, stacked horizontally and justified at the top. Description Text - Although it is straightforward that the row indices are 0..(numRows T - 1), the analogous sequence does not - work for columns since columns may have negative indices. Hence, columnRange provides an easy way to iterate - over column indices. The lowest number is the smallest part of the tableau's shape, and the largest number - is the largest part of its shape. + If L is a list of objects $A_1,A_2,\ldots,A_n$, then this concatenates their nets horizontally. Example - T = skewTableau(new Partition from {8,7,6,6,1}, new Partition from {4,4,3,2,-2}, toList(1..17)) - columnRange T - for j in columnRange T do print columnEntries(T,j) + T1 = youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) + T2 = youngTableau(new Partition from {3,1}, new Partition from {1}, {"a","b","c"}) + horizontalNet {T1, T2} SeeAlso - (symbol _, SkewTableau, ZZ) - columnEntries - rowRange - positionList + verticalNet /// doc /// Key - rowRange - (rowRange, SkewTableau) + innerShape + (innerShape, YoungTableau) Headline - the range of row indices of a tableau. + the inner shape of a (skew) Young tableau Usage - rowRange T + mu = innerShape T Inputs - T:SkewTableau - a tableau. + T:YoungTableau + a Young tableau of shape $\lambda/\mu$. Outputs - seq:Sequence - the range of row indices. + mu:Partition + the inner shape, $\mu$, of T. Description Text - Returns the sequence 0..(numRows T - 1). + If the tableau has (non-skew) shape $\lambda$, then this returns the empty partition. Example - T = skewTableau(new Partition from {5,4,2,1,0,0}, new Partition from {2,2,2}, toList(1..6)) - rowRange T - for i in rowRange T do print rowEntries(T,i) + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {3,1}) + innerShape T SeeAlso - (symbol ^, SkewTableau, ZZ) - rowEntries - columnRange - positionList + outerShape + shape + skewShape /// doc /// Key - rowEntries - (rowEntries, SkewTableau, ZZ) - (rowEntries, ZZ, SkewTableau) + isCorner + (isCorner, Sequence, Partition) + (isCorner, Sequence, YoungTableau) Headline - get the entries in a row. + checks if a box is a corner of a tableau Usage - rowEntries(T,i) - rowEntries(i,T) + isCorner((rowIndex,colIndex), T) Inputs - T:SkewTableau - a tableau. - i:ZZ - the index of a row. + rowIndex:ZZ + the row index of a box. + colIndex:ZZ + the column index of a box. + T:YoungTableau + a Young tableau. Outputs - l:List - the entries of row $i$ of T. + b:Boolean + returns true if the box is a corner, and false otherwise. Description Text - This returns the entries exactly as they appear in the row without extra @TO null@ entries, as opposed to T^i. + The corners of a tableau are the boxes that are both the last box in a row, and the last box in a column. Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(1..9)) - rowEntries(T,0) - T^0 -SeeAlso - (symbol ^, SkewTableau, ZZ) + T = youngTableau(new Partition from {7,7,6,5,3,2,2,2,1}, new Partition from {3,1}) + isCorner((1,3),T) + applyPositions(T,thePosition -> isCorner(thePosition,T)) /// doc /// Key - columnEntries - (columnEntries, SkewTableau, ZZ) - (columnEntries, ZZ, SkewTableau) + isSemistandard + (isSemistandard, YoungTableau) Headline - get the entries in a column. + checks if a Young tableau is semistandard Usage - columnEntries(T,j) - columnEntries(j,T) + isSemistandard T Inputs - T:SkewTableau - a tableau. - j:ZZ - the index of a column. + T:YoungTableau + a Young tableau. Outputs - l:List - the entries of column $j$ of T. + b:Boolean + returns true if the tableau is semistandard, and false otherwise. Description Text - This returns the entries of a column without extra @TO null@ entries, as opposed to T_i. + A semistandard Young tableau (SSYT) has entries in $\N=\{1,2,3,\ldots\}$ where the rows are weakly increasing, + and columns are strictly increasing. Here, we also require $\lambda$ and $\mu$ to have nonnegative parts. Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(1..9)) - columnEntries(T,0) - T_0 + T = youngTableau(new Partition from {5,3,2}, new Partition from {3,1}, {1,3,2,2,2,3}) + isSemistandard T SeeAlso - (symbol _, SkewTableau, ZZ) + isStandard /// doc /// Key - toPosition - (toPosition, SkewTableau, ZZ) - (toPosition, ZZ, SkewTableau) + isSkew + (isSkew, YoungTableau) Headline - get the position of a box, given its index. + checks if a Young tableau is skew Usage - toPosition(T,k) - toPosition(k,T) + isSkew T Inputs - T:SkewTableau - a tableau. - k:ZZ - the index of a box. + T:YoungTableau + a Young tableau of shape $\lambda/\mu$. Outputs - l:Sequence - the position (i,j) of the box in T. + b:Boolean + returns true if the inner shape $\mu$ is not $0$, and true if $\mu=0$. Description - Text - The boxes in entries T fill the tableau from top to bottom, and left to right. Hence, an index in entries T - corresponds to a position (i,j). - Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) - (entries T)#5 - toPosition(T,5) - Text - We may replace the filling in each box with its index to see the order. - Example - skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..(size T - 1))) - Text - We may also replace the filling in each box with the corresponding position. Example - entryList = for i from 0 to size T - 1 list toPosition(T,i) - skewTableau(new Partition from {6,3,2}, new Partition from {2}, entryList) -SeeAlso - toIndex - positionList + T = youngTableau(new Partition from {5,3,2}, new Partition from {3,1}, {1,3,2,2,2,3}) + isSkew T /// - doc /// Key - toIndex - (toIndex, SkewTableau, Sequence) - (toIndex, Sequence, SkewTableau) + isStandard + (isStandard, YoungTableau) Headline - get the index of a box, given its position. + checks if a Young tableau is standard Usage - toIndex(T,seq) - toIndex(T,(i,j)) - toIndex(seq,T) - toIndex((i,j),T) + isStandard T Inputs - T:SkewTableau - a tableau. - seq:Sequence - the position (i,j) of a box. + T:YoungTableau + a Young tableau. Outputs - k:ZZ - the index k of the box in T. + b:Boolean + returns true if the tableau is standard, and false otherwise. Description Text - The boxes in entries T fill the tableau from top to bottom, and left to right. Hence, a position (i,j) - corresponds to an index k in entries T - Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) - thePosition = (1,2) - T_thePosition - theIndex = toIndex(T,thePosition) - (entries T)#theIndex - Text - We may replace the filling in each box with its index to see the order. - Example - skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..(size T - 1))) - Text - We may also replace the filling in each box with the corresponding position. + A standard Young tableau (SYT) has entries in $\N=\{1,2,3,\ldots,N\}$ where $N$ is the number of boxes, and + the rows are columns are strictly increasing. Here, we also require $\lambda$ to have + nonnegative parts, and inner shape $\mu=0$. Example - entryList = for i from 0 to size T - 1 list toPosition(T,i) - skewTableau(new Partition from {6,3,2}, new Partition from {2}, entryList) + T = youngTableau(new Partition from {5,3,1}, toList(1..9)) + isStandard T SeeAlso - toPosition - positionList + isSemistandard /// doc /// Key - positionList - (positionList, SkewTableau) + isNonnegative + (isNonnegative, YoungTableau) + (isNonnegative, Partition, Partition) + (isNonnegative, Partition) Headline - get the positions of all the boxes in a tableau + check if shapes are nonnegative Usage - positionList T + isNonnegative T + isNonnegative (lam,mu) + isNonnegative lam Inputs - T:SkewTableau + T:YoungTableau a tableau. + lam:Partition + the outer shape, $\lambda$. + mu:Partition + the inner shape, $\mu$. Outputs - l:List - the positions (i,j) of boxes in T. + b:Boolean + returns true if both shapes are nonnegative, and false otherwise. Description - Text - This method is useful for iterating over all positions in a tableau Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) - theList = positionList T - T' = skewTableau(new Partition from {6,3,2}, new Partition from {2}, theList) -SeeAlso - toPosition - toIndex + isNonnegative youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) + isNonnegative new Partition from {5,3,-1,0} /// doc /// Key - applyEntries - (applyEntries, SkewTableau, Function) + isWeaklyDecreasing + (isWeaklyDecreasing, YoungTableau) + (isWeaklyDecreasing, Partition, Partition) + (isWeaklyDecreasing, Partition) Headline - apply a function to all entries in the tableau + check if shapes are weakly decreasing Usage - applyEntries(T,f) + isWeaklyDecreasing T + isWeaklyDecreasing (lam,mu) + isWeaklyDecreasing lam Inputs - T:SkewTableau + T:YoungTableau a tableau. - f:Function - acts on the entries of T. + lam:Partition + the outer shape, $\lambda$. + mu:Partition + the inner shape, $\mu$. Outputs - T':SkewTableau - a tableau with the same shape as T, and entries n -> f n. + b:Boolean + returns true if both shapes have weakly decreasing parts, and false otherwise. Description Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..8)) - applyEntries(T, theBox -> theBox^2) -SeeAlso - applyPositions + isWeaklyDecreasing youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) + isWeaklyDecreasing new Partition from {5,3,-1,0} /// doc /// Key - applyPositions - (applyPositions, SkewTableau, Function) + mutableYoungTableau + (mutableYoungTableau, Partition, Partition, List) + (mutableYoungTableau, Sequence, List) + (mutableYoungTableau, Partition, List) + (mutableYoungTableau, Partition, Partition) + (mutableYoungTableau, Partition) Headline - apply a function to all positions of boxes in a tableau + constructor for type MutableYoungTableau Usage - applyPositions(T,f) + mutableYoungTableau(lam, mu, entryList) + mutableYoungTableau((lam,mu),entryList) Inputs - T:SkewTableau - a skew Tableau. - f:Function - acts on the entries of T. + lam:Partition + the outer shape $\lambda$. + mu:Partition + the inner shape $\mu$. If not given, then it is assumed to be the $0$ partition. + entryList:List + the filling of the boxes. If not given, then box entries are assumed to be the empty string "". Outputs - T':SkewTableau - a tableau with the same shape as T, and entries (i,j) -> f (i,j). + T:MutableYoungTableau + a skew Young tableau of shape $\lambda/\mu$ with the given (mutable) filling. +Consequences + Item + The list of entries has length equal to $\sum_{i=1}^{\ell(\lambda)}|\lambda_i-\mu_i|$. E.g., + if $\lambda=(2)$ and $\mu=(5)$, then the entry list must have length $3$. + Item + None of the entries are null. Description + Text + This is the same as type @TO YoungTableau@, except that the entries may be changed. Example - T = skewTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..8)) - applyPositions(T, thePosition -> thePosition) - applyPositions(T, thePosition -> thePosition#1 - thePosition#0) - applyPositions(T, thePosition -> (T_thePosition)^2) + T = mutableYoungTableau(new Partition from {4,2,1}, new Partition from {1}, toList(1..6)) + T_(0,2) = 9 + T SeeAlso - applyEntries + ((symbol _, symbol =), MutableYoungTableau, Sequence) + youngTableau /// doc /// Key - (components, SkewTableau) + ((symbol _, symbol =), MutableYoungTableau, Sequence) Headline - get the connected components of a tableau + change an entry of a mutable Young tableau Usage - components T + T_thePosition = k Inputs - T:SkewTableau - a tableau. + T:MutableYoungTableau + a mutable tableau. + thePosition:Sequence + a pair (i,j) of integers. + k:Thing + the new entry in position (i,j). Outputs - l:List - a list of all connected components of T. + T:MutableYoungTableau + a mutable tableau with entry k in position (i,j). Description Example - T = skewTableau(new Partition from {6,2,2}, new Partition from {2}, toList(0..7)) - components T -SeeAlso - (symbol ++, SkewTableau, SkewTableau) + T = mutableYoungTableau(new Partition from {4,2,1}, new Partition from {1}, toList(1..6)) + T_(0,2) = 9 + T /// doc /// Key - (symbol ++, SkewTableau, SkewTableau) + numSemistandardTableaux + (numSemistandardTableaux, Partition, ZZ) + (numSemistandardTableaux, Partition) Headline - direct sum of tableaux + compute the number of semistandard Young tableaux of a given shape Usage - T1 ++ T2 + numSemistandardTableaux(lam,N) Inputs - T1:SkewTableau - a tableau. - T2:SkewTableau - a tableau. + lam:Partition + the shape, $\lambda$. + N:ZZ + the maximum entry. Outputs - S:SkewTableau - a direct sum of T1 and T2. + n:ZZ + the number of SSYT of the given shape, and entries in 1..N. If N is not provided, it is assumed + to be the length of the shape. Description Text - The tableaux are combined into the disconnected components (not sharing an edge) of a single tableau. + A bagged list of the tableaux can be created with @TO allSemistandardTableaux@. Example - T1 = skewTableau(new Partition from {5}, new Partition from {2}) - T2 = skewTableau(new Partition from {3,2}, new Partition from {1}, {1,2,3,4}) - T1 ++ T2 - T2 ++ T1 + # allSemistandardTableaux(new Partition from {6,6,5,1,1}) + numSemistandardTableaux(new Partition from {6,6,5,1,1}) SeeAlso - (components, SkewTableau) - (symbol ||, SkewTableau, SkewTableau) + allSemistandardTableaux + numStandardTableaux + numTabloids /// doc /// Key - (symbol ||, SkewTableau, SkewTableau) + numStandardTableaux + (numStandardTableaux, Partition) Headline - vertical concatenation of two tableaux + compute the number of standard Young tableaux of a given shape Usage - T1 || T2 + numStandardTableaux lam Inputs - T1:SkewTableau - a tableau. - T2:SkewTableau - a tableau. + lam:Partition + the shape, $\lambda$. Outputs - S:SkewTableau - a single tableau with T1 directly above T2. + n:ZZ + the number of SYT of the given shape, and entries in $\{1,\ldots,N\}$, where $N$ is the number of boxes. Description + Text + A bagged list of the tableaux can be created with @TO allStandardTableaux@. Example - T1 = skewTableau(new Partition from {5}, new Partition from {2}) - T2 = skewTableau(new Partition from {3,2}, new Partition from {1}, {1,2,3,4}) - T1 || T2 - T2 || T1 + # allStandardTableaux(new Partition from {4,2,1}) + numStandardTableaux(new Partition from {4,2,1}) SeeAlso - (symbol ++, SkewTableau, SkewTableau) - verticalConcatenate + allStandardTableaux + numSemistandardTableaux + numTabloids /// doc /// Key - verticalConcatenate - (verticalConcatenate, List) + numTabloids + (numTabloids, Partition, Partition) + (numTabloids, Partition) Headline - vertical concatenation of a list of tableaux + compute the number of tabloids of a given shape Usage - verticalConcatenate l + numTabloids(lam,mu) Inputs - l:List - a list of tableaux. + lam:Partition + the outer shape $\lambda$. + mu:Partition + the inner shape $\mu$. If not given, then it is assumed to be the $0$ partition. Outputs - S:SkewTableau - a single tableau with combining those in the list. + n:ZZ + the number of tabloids of the given shape, and entries in $\{1,\ldots,N\}$, where $N$ is the number of boxes. Description + Text + A bagged list of the tableaux can be created with @TO allTabloids@. Example - T1 = skewTableau(new Partition from {5}, new Partition from {2}) - T2 = skewTableau(new Partition from {3,2}, new Partition from {1}, {1,2,3,4}) - T3 = skewTableau(new Partition from {5,-3}) - verticalConcatenate {T1,T2,T3} + # allTabloids(new Partition from {4,2,1}) + numTabloids(new Partition from {4,2,1}) SeeAlso - (symbol ||, SkewTableau, SkewTableau) + allTabloids + numSemistandardTableaux + numStandardTableaux /// doc /// Key - shift - (shift, SkewTableau) - (shift, SkewTableau, ZZ) + outerShape + (outerShape, YoungTableau) Headline - shift a tableau + the outer shape of a (skew) Young tableau Usage - shift T - shift(T,n) + mu = outerShape T Inputs - T:SkewTableau - a tableau. - n:ZZ - an additional amount to shift. + T:YoungTableau + a Young tableau of shape $\lambda/\mu$. Outputs - S:SkewTableau - each row i of T has been shifted by i + n to the right (where n=0 if not provided). + lam:Partition + the outer shape, $\lambda, of T. Description Example - T = skewTableau(new Partition from {6,6,5,3,1}) - shift T - shift(T,2) + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {3,1}) + outerShape T SeeAlso - unshift + innerShape + shape + skewShape /// doc /// Key - unshift - (unshift, SkewTableau) - (unshift, SkewTableau, ZZ) + positionList + (positionList, YoungTableau) + (positionList, YoungTableau, Function) Headline - unshift a tableau + get the positions of all the boxes in a tableau Usage - unshift T - unshift(T,n) + positionList T + positionList(T,f) Inputs - T:SkewTableau - a skew tableau. - n:ZZ - an additional amount to shift. + T:YoungTableau + a tableau. + f:Function + a function that acts on the positions, and returns either true or false. Outputs - S:SkewTableau - each row i of T has been shifted by i + n to the left (where n=0 if not provided). + l:List + the positions (i,j) of boxes in T. If a function f is provided, then only the positions returning true + are in the list. Description + Text + This method is useful for iterating over all positions in a tableau. Example - T = skewTableau(new Partition from {6,6,5,3,1}) - T' = shift T - unshift T' + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) + theList = positionList T + T' = youngTableau(new Partition from {6,3,2}, new Partition from {2}, theList) + Text + Including a function returns only the positions that return true. For example, the following code returns + all positions of corners in the tableau, using function @TO isCorner@. + Example + positionList(T, thePosition -> isCorner(thePosition,T)) SeeAlso - shift + toPosition + toIndex /// doc /// Key - (conjugate, SkewTableau) + randomSemistandardTableau + (randomSemistandardTableau, Partition, Partition, ZZ) + (randomSemistandardTableau, Partition, ZZ) + (randomSemistandardTableau, Partition, Partition) + (randomSemistandardTableau, Partition) Headline - conjugate a tableau + get a random semistandard Young Tableau Usage - conjugate T + randomSemistandardTableau(lam,mu,N) Inputs - T:SkewTableau - a tableau. + lam:Partition + the outer shape, $\lambda$, of T. + mu:Partition + the inner shape, $\mu$, of T. + N:ZZ + the maximum entry. Outputs - S:SkewTableau - the rows and columns have been switched. + T:YoungTableau + a random semistandard Young tableau with entries in $\{1,\ldots,N\}$. If $N$ is not provided, then it is + assumed to be the length of $\lambda$. If $\mu$ is not provided, then it is assumed to be $0$. Description Text - This method conjugates the tableau only if the shape has weakly decreasing parts, and no negative parts. - Otherwise, it will raise an error. + Note that this does not select a tableau uniformly randomly. Example - T = skewTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}, toList(1..17)) - conjugate T + lam = new Partition from {5,4,3,3,1} + mu = new Partition from {3,1,1} + randomSemistandardTableau(lam,mu) + randomSemistandardTableau lam +SeeAlso + randomStandardTableau + randomTabloid + allSemistandardTableaux /// doc /// Key - hookLength - (hookLength, Sequence, SkewTableau) + randomStandardTableau + (randomStandardTableau, Partition) Headline - compute the hook length of a box of a tableau + get a random standard Young Tableau Usage - hookLength(thePosition,T) + randomStandardTableau(lam,mu,N) Inputs - thePosition:Sequence - a pair (i,j) of integers. - T:SkewTableau - a tableau. + lam:Partition + the outer shape, $\lambda$, of T. + mu:Partition + the inner shape, $\mu$, of T. Outputs - n:ZZ - the hook length of box (i,j) in T. + T:YoungTableau + a random standard Young tableau of shape $\lambda$. Description Text - The hook length of box (i,j) is defined as the number of boxes directly below, and directly to the right, - of a box, including the box itself. + Note that this does not select a tableau uniformly randomly. Example - T = skewTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) - hookLength((1,1),T) - applyPositions(T, thePosition -> hookLength(thePosition,T)) + lam = new Partition from {5,4,3,3,1} + randomStandardTableau lam +SeeAlso + randomSemistandardTableau + randomTabloid + allStandardTableaux /// doc /// Key - boxContent - (boxContent, ZZ, ZZ) + randomTabloid + (randomTabloid, Partition, Partition) + (randomTabloid, Partition) Headline - compute the content of a box of a tableau + get a random tabloid Usage - boxContent(i,j) + randomTabloid(lam,mu) Inputs - i:ZZ - the row index of a box. - j:ZZ - the column index of a box. + lam:Partition + the outer shape, $\lambda$, of T. + mu:Partition + the inner shape, $\mu$, of T. Outputs - n:ZZ - the hook length of box (i,j). + T:Tabloid + a random tabloid with entries in $\{1,\ldots,N\}$, where $N$ is the length of $\lambda$. + If $\mu$ is not provided, then it is assumed to be $0$. Description Text - The content of box (i,j) is defined as $j-i$. Example - T = skewTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) - boxContent(1,1) - applyPositions(T, thePosition -> boxContent thePosition) + lam = new Partition from {5,4,3,3,1} + mu = new Partition from {3,1,1} + randomTabloid(lam,mu) + randomTabloid lam +SeeAlso + randomSemistandardTableau + randomStandardTableau + allTabloids /// doc /// Key - isWeaklyDecreasing - (isWeaklyDecreasing, SkewTableau) - (isWeaklyDecreasing, Partition, Partition) - (isWeaklyDecreasing, Partition) + readingWord + (readingWord, YoungTableau) Headline - check if shapes are weakly decreasing + get the reading word of a tableau Usage - isWeaklyDecreasing T - isWeaklyDecreasing (lam,mu) - isWeaklyDecreasing lam + readingWord T Inputs - T:SkewTableau + T:YoungTableau a tableau. - lam:Partition - the outer shape, $\lambda$. - mu:Partition - the inner shape, $\mu$. Outputs - b:Boolean - returns true if both shapes have weakly decreasing parts, and false otherwise. + l:List + the reading word of T. Description + Text + The reading word is the list of entries going up the columns, from left to right. Example - isWeaklyDecreasing skewTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) - isWeaklyDecreasing new Partition from {5,3,-1,0} + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) + readingWord T /// doc /// Key - isNonnegative - (isNonnegative, SkewTableau) - (isNonnegative, Partition, Partition) - (isNonnegative, Partition) + representative + (representative, Tabloid) Headline - check if shapes are nonnegative + get a canonical representative of a tabloid Usage - isNonnegative T - isNonnegative (lam,mu) - isNonnegative lam + representative T Inputs - T:SkewTableau - a tableau. - lam:Partition - the outer shape, $\lambda$. - mu:Partition - the inner shape, $\mu$. + T:Tabloid + a tabloid. Outputs - b:Boolean - returns true if both shapes are nonnegative, and false otherwise. + T':YoungTableau + a representative of T. Description + Text + A tabloid is an equivalence class of Young Tableaux. We can obtain a canonical representative by sorting the + boxes in each row. Example - isNonnegative skewTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) - isNonnegative new Partition from {5,3,-1,0} + T = tabloid(new Partition from {6,3,2}, new Partition from {2}, {9,5,1,2,4,3,7,8,6}) + representative T +SeeAlso + Tabloid /// doc /// Key - allSemistandardTableaux - (allSemistandardTableaux, Partition, Partition, ZZ) - (allSemistandardTableaux, Partition, ZZ) - (allSemistandardTableaux, Partition, Partition) - (allSemistandardTableaux, Partition) + rowEntries + (rowEntries, YoungTableau, ZZ) + (rowEntries, ZZ, YoungTableau) Headline - list all semistandard Young tableaux of a given shape + get the entries in a row Usage - allSemistandardTableaux(lam,mu,N) + rowEntries(T,i) + rowEntries(i,T) Inputs - lam:Partition - the outer shape, $\lambda$. - mu:Partition - the inner shape, $\mu$. - N:ZZ - the maximum entry. + T:YoungTableau + a tableau. + i:ZZ + the index of a row. Outputs - b:Bag - bagged list of all SSYT of given shape and entries in 1..N. If N is not provided, it is assumed - to be the length of the shape. + l:List + the entries of row $i$ of T. +Description + Text + This returns the entries exactly as they appear in the row without extra @TO null@ entries, as opposed to T^i. + Example + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(1..9)) + rowEntries(T,0) + T^0 +SeeAlso + (symbol ^, YoungTableau, ZZ) +/// + + +doc /// +Key + rowRange + (rowRange, YoungTableau) +Headline + the range of row indices of a tableau +Usage + rowRange T +Inputs + T:YoungTableau + a tableau. +Outputs + seq:Sequence + the range of row indices. +Description + Text + Returns the sequence 0..(numRows T - 1). + Example + T = youngTableau(new Partition from {5,4,2,1,0,0}, new Partition from {2,2,2}, toList(1..6)) + rowRange T + for i in rowRange T do print rowEntries(T,i) +SeeAlso + (symbol ^, YoungTableau, ZZ) + rowEntries + columnRange + positionList +/// + +doc /// +Key + rowStabilizer + (rowStabilizer, YoungTableau) +Headline + the row stabilizer of a tableau +Usage + rowStabilizer T +Inputs + T:YoungTableau + a tableau. +Outputs + l:List + the permutations in the row stabilizer. +Description + Text + A permutation acts on a tableau by permuting its entries. The row stabilizer of a tableau is the group + of permutations that leave boxes in their original rows. + Example + T = youngTableau(new Partition from {2,2,1}, {1,4,2,5,3}) + rowStabilizer T +SeeAlso + columnStabilizer +/// + +doc /// +Key + shape + (shape, YoungTableau) + (trim, Partition) +Headline + the shape of a Young tableau +Usage + lam = shape T +Inputs + T:YoungTableau + a Young tableau. +Outputs + lam:Partition + the shape, $\lambda$, of T. +Description + Text + This returns the original shape used to construct the tableau. + Example + T = youngTableau(new Partition from {4,3,1,0}) + shape T + Text + It may be convenient to remove trailing $0$'s from the partitions by using trim on the partition. + Example + trim shape T +SeeAlso + skewShape +/// + +doc /// +Key + shift + (shift, YoungTableau) + (shift, YoungTableau, ZZ) +Headline + shift a tableau +Usage + shift T + shift(T,n) +Inputs + T:YoungTableau + a tableau. + n:ZZ + an additional amount to shift. +Outputs + S:YoungTableau + each row i of T has been shifted by i + n to the right (where n=0 if not provided). +Description + Example + T = youngTableau(new Partition from {6,6,5,3,1}) + shift T + shift(T,2) +SeeAlso + unshift +/// + +doc /// +Key + skewShape + (skewShape, YoungTableau) + (trim, Partition, Partition) + (pad, Partition, Partition) +Headline + the shape of a skew tableau +Usage + (lam,mu) = skewShape T +Inputs + T:YoungTableau + a skew tableau. +Outputs + lam:Partition + the outer shape, $\lambda$, of T. + mu:Partition + the inner shape, $\mu$, of T. +Description + Text + This returns the original shape used to construct the tableau. + Example + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {2,1}, {1,2,3,4,5}) + skewShape T + Text + It may be convenient to remove trailing $0$'s from the partitions by using trim on a sequence of two partitions. + Example + trim skewShape T + Text + It may also be useful to append $0$s to make the partitions have the same number of parts. + Example + (lam',mu') = pad skewShape T + rowLengths' = for i from 0 to #lam'-1 list(lam'#i-mu'#i) + Text + You may also 'standardize' a skew shape to return a pair of partitions of equal length, without extra $0$'s. + I.e., one of the resulting partitions will end with a nonzero part, and the other will be padded with $0$'s. + Example + (lam'',mu'') = standardize skewShape T + rowLengths = for i from 0 to #lam''-1 list(lam''#i-mu''#i) +SeeAlso + shape + standardize +/// + +doc /// +Key + standardize + (standardize, Partition, Partition) +Headline + standardize the lengths of a pair of partitions +Usage + (lam',mu') = standardize (lam,mu) +Inputs + lam:Partition + a partition. + mu:Partition + a partition. +Outputs + lam':Partition + a partition obtained from lam, with trailing zeros added or removed. + mu':Partition + a partition obtained from mu, with trailing zeros added or removed. +Consequences + Item + Partitions lam' and mu' have the same number of parts, possibly with trailing 0s. +Description + Text + A tableau T of shape $\lambda/\mu$ may be constructed with $\ell(\lambda)\neq\ell(\mu)$. It is common to + iterate over the rows of T, and it is convenient to append trailing 0s to the shorter partition. This method + effectively removes all trailing 0s from both shapes, then appends 0s to the shorter shape until they + have the same length. + Example + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {2,1}) + skewShape T + standardize skewShape T +SeeAlso + skewShape + shape +/// + +doc /// +Key + tabloid + (tabloid, Partition, Partition, List) + (tabloid, Sequence, List) + (tabloid, Partition, List) + (tabloid, YoungTableau) +Headline + constructor for type Tabloid +Usage + tabloid(lam, mu, entryList) + tabloid((lam,mu),entryList) +Inputs + lam:Partition + the outer shape $\lambda$. + mu:Partition + the inner shape $\mu$. If not given, then it is assumed to be the $0$ partition. + entryList:List + the filling of the boxes. +Outputs + T:Tabloid + a tabloid of shape $\lambda/\mu$ with the given filling. +Consequences + Item + The list of entries has length equal to $\sum_{i=1}^{\ell(\lambda)}|\lambda_i-\mu_i|$. E.g., + if $\lambda=(2)$ and $\mu=(5)$, then the entry list must have length $3$. + Item + The entries are $\{1,\ldots,N\}$, where $N$ is the number of boxes. +Description + Example + lam = new Partition from {4,3,2} + mu = new Partition from {3,1} + entryList = {1,4,2,3,5} + T = tabloid(lam,mu,entryList) +SeeAlso + Tabloid + YoungTableau +/// + +doc /// +Key + toIndex + (toIndex, YoungTableau, Sequence) + (toIndex, Sequence, YoungTableau) +Headline + get the index of a box, given its position +Usage + toIndex(T,seq) + toIndex(T,(i,j)) + toIndex(seq,T) + toIndex((i,j),T) +Inputs + T:YoungTableau + a tableau. + seq:Sequence + the position (i,j) of a box. +Outputs + k:ZZ + the index k of the box in T. +Description + Text + The boxes in entries T fill the tableau from top to bottom, and left to right. Hence, a position (i,j) + corresponds to an index k in entries T + Example + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) + thePosition = (1,2) + T_thePosition + theIndex = toIndex(T,thePosition) + (entries T)#theIndex + Text + We may replace the filling in each box with its index to see the order. + Example + youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..(size T - 1))) + Text + We may also replace the filling in each box with the corresponding position. + Example + entryList = for i from 0 to size T - 1 list toPosition(T,i) + youngTableau(new Partition from {6,3,2}, new Partition from {2}, entryList) +SeeAlso + toPosition + positionList +/// + +doc /// +Key + toPosition + (toPosition, YoungTableau, ZZ) + (toPosition, ZZ, YoungTableau) +Headline + get the position of a box, given its index +Usage + toPosition(T,k) + toPosition(k,T) +Inputs + T:YoungTableau + a tableau. + k:ZZ + the index of a box. +Outputs + l:Sequence + the position (i,j) of the box in T. +Description + Text + The boxes in entries T fill the tableau from top to bottom, and left to right. Hence, an index in entries T + corresponds to a position (i,j). + Example + T = youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(10..18)) + (entries T)#5 + toPosition(T,5) + Text + We may replace the filling in each box with its index to see the order. + Example + youngTableau(new Partition from {6,3,2}, new Partition from {2}, toList(0..(size T - 1))) + Text + We may also replace the filling in each box with the corresponding position. + Example + entryList = for i from 0 to size T - 1 list toPosition(T,i) + youngTableau(new Partition from {6,3,2}, new Partition from {2}, entryList) +SeeAlso + toIndex + positionList +/// + +doc /// +Key + unshift + (unshift, YoungTableau) + (unshift, YoungTableau, ZZ) +Headline + unshift a tableau +Usage + unshift T + unshift(T,n) +Inputs + T:YoungTableau + a skew tableau. + n:ZZ + an additional amount to shift. +Outputs + S:YoungTableau + each row i of T has been shifted by i + n to the left (where n=0 if not provided). +Description + Example + T = youngTableau(new Partition from {6,6,5,3,1}) + T' = shift T + unshift T' +SeeAlso + shift +/// + +doc /// +Key + verticalConcatenate + (verticalConcatenate, List) +Headline + vertical concatenation of a list of tableaux +Usage + verticalConcatenate l +Inputs + l:List + a list of tableaux. +Outputs + S:YoungTableau + a single tableau with combining those in the list. +Description + Example + T1 = youngTableau(new Partition from {5}, new Partition from {2}) + T2 = youngTableau(new Partition from {3,2}, new Partition from {1}, {1,2,3,4}) + T3 = youngTableau(new Partition from {5,-3}) + verticalConcatenate {T1,T2,T3} +SeeAlso + (symbol ||, YoungTableau, YoungTableau) +/// + +doc /// +Key + verticalNet +Headline + multiple nets concatenated vertically +Usage + verticalNet L +Inputs + L:List + a list or sequence. +Outputs + n:Net + a net of objects in L, stacked vertically and justified on the left. +Description + Text + If L is a list of objects $A_1,A_2,\ldots,A_n$, then this concatenates their nets vertically. Note that + this concatenates their nets into a single net, whereas @TO verticalConcatenate@ concatenates the + tableaux themselves into a single tableau. + Example + T1 = youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}) + T2 = youngTableau(new Partition from {3,1}, new Partition from {1}, {"a","b","c"}) + verticalNet {T1, T2} +SeeAlso + horizontalNet +/// + +doc /// +Key + youngDiagram + (youngDiagram, Partition, Partition) + (youngDiagram, Partition) + (youngDiagram, YoungTableau) +Headline + a net of the Young diagram +Usage + youngDiagram(lam, mu) + youngDiagram T +Inputs + lam:Partition + the outer shape $\lambda$. + mu:Partition + the inner shape $\mu$. If not given, if is assumed to be the $0$ partition. + T:YoungTableau + a skew tableau. +Outputs + n:Net + a representation of the Young diagram of shape $\lambda/\mu$. +Description + Example + T = youngTableau(new Partition from {4,3,1}, new Partition from {2,1}, {1,2,3,4,5}) + youngDiagram T +SeeAlso + youngTableau + ferrersDiagram +/// + +doc /// +Key + youngTableau + (youngTableau, Partition, Partition, List) + (youngTableau, Sequence, List) + (youngTableau, Partition, List) + (youngTableau, Partition, Partition) + (youngTableau, Partition) + (youngTableau, Tabloid) +Headline + constructor for type YoungTableau +Usage + youngTableau(lam, mu, entryList) + youngTableau((lam,mu),entryList) + youngTableau(lam, entryList) +Inputs + lam:Partition + the outer shape $\lambda$. + mu:Partition + the inner shape $\mu$. If not given, then it is assumed to be the $0$ partition. + entryList:List + the filling of the boxes. If not given, then box entries are assumed to be the empty string "". +Outputs + T:YoungTableau + a skew Young tableau of shape $\lambda/\mu$ with the given filling. +Consequences + Item + The list of entries has length equal to $\sum_{i=1}^{\ell(\lambda)}|\lambda_i-\mu_i|$. E.g., + if $\lambda=(2)$ and $\mu=(5)$, then the entry list must have length $3$. + Item + None of the entries are null. +Description + Example + lam = new Partition from {4,3,2} + mu = new Partition from {3,1} + entryList = {1,2,3,3,9} + T = youngTableau(lam,mu,entryList) + Text + We may construct tableaux with any compositions. + Example + youngTableau(new Partition from {3,5,1}, new Partition from {0,1}, {7,"&",4,2,"g","u",6,0}) + Text + The shapes may have negative parts. In this case, a vertical line is drawn by @TO2{(net, YoungTableau),"net"}@ + to indicate that negative parts are to the left. + Example + youngTableau(new Partition from {3,-3,-1}, new Partition from {-2,-4,-1}) + Text + If any $\lambda_i<\mu_i$, then the boxes in that row are drawn shaded. + Example + youngTableau(new Partition from {-2,-4,2}, new Partition from {1,-1,-1}, {1,2,3,4,5,6,7,8,9}) + Text + The inner shape may be drawn by calling drawInnerShape. + Example + T'' = youngTableau(new Partition from {5,4,-1}, new Partition from {2,4,-3}) + drawInnerShape true -* no-capture-flag *- + T'' + drawInnerShape false -* no-capture-flag *- + T'' + Text + The filling may be of any class (besides @TO null@ objects), including other tableaux! + Example + youngTableau(lam,mu,for i from 1 to size T list randomTabloid lam) +SeeAlso + YoungTableau +/// + +doc /// +Key + (components, YoungTableau) +Headline + get the connected components of a tableau +Usage + components T +Inputs + T:YoungTableau + a tableau. +Outputs + l:List + a list of all connected components of T. +Description + Example + T = youngTableau(new Partition from {6,2,2}, new Partition from {2}, toList(0..7)) + components T +SeeAlso + (symbol ++, YoungTableau, YoungTableau) +/// + +doc /// +Key + (conjugate, YoungTableau) +Headline + conjugate a tableau +Usage + conjugate T +Inputs + T:YoungTableau + a tableau. +Outputs + S:YoungTableau + the rows and columns have been switched. +Description + Text + This method conjugates the tableau only if the shape has weakly decreasing parts, and no negative parts. + Otherwise, it will raise an error. + Example + T = youngTableau(new Partition from {6,6,5,3,1}, new Partition from {2,1,1}, toList(1..17)) + conjugate T +/// + +doc /// +Key + (entries, YoungTableau) +Headline + the filling of a tableau +Usage + entries T +Inputs + T:YoungTableau + a Young tableau. +Outputs + l:List + the filling of T. +Description + Text + This returns the filling as a list. + Example + T = youngTableau(new Partition from {4,3,1,0},toList(1..8)) + entries T +SeeAlso + (size, YoungTableau) +/// + +doc /// +Key + (numColumns, YoungTableau) +Headline + the number of columns in a tableau +Usage + numColumns T +Inputs + T:YoungTableau + a tableau. +Outputs + n:ZZ + the number of columns in T. Description + Text + The number of rows is the same as largest part in its shape, minus the smallest part in the shape. Example - theBag = allSemistandardTableaux(new Partition from {5,4,2}, new Partition from {3,1}) - for i from 0 to 4 do print theBag#i + drawInnerShape true -* no-capture-flag *- + T = youngTableau(new Partition from {4,3,1}, new Partition from {1,1,1}) + numColumns T Text - When the inner partition is $0$, the function @TO numSemistandardTableaux@ computes the number of SSYT via - the hook-content formula. + Note that, if a shape contains negative parts, then columns may have negative indices. Example - # allSemistandardTableaux(new Partition from {6,6,5,1,1}) - numSemistandardTableaux(new Partition from {6,6,5,1,1}) + T' = youngTableau(new Partition from {4,3,1}, new Partition from {-1,1,1}) + numColumns T' SeeAlso - allSemistandardTableaux + (numRows, YoungTableau) + columnRange /// doc /// Key - numSemistandardTableaux - (numSemistandardTableaux, Partition, ZZ) - (numSemistandardTableaux, Partition) + (numRows, YoungTableau) Headline - compute the number of semistandard Young tableaux of a given shape + the number of rows in a tableau Usage - numSemistandardTableaux(lam,N) + numRows T Inputs - lam:Partition - the shape, $\lambda$. - N:ZZ - the maximum entry. + T:YoungTableau + a tableau. Outputs n:ZZ - the number of SSYT of the given shape, and entries in 1..N. If N is not provided, it is assumed - to be the length of the shape. + the number of rows in T. Description Text - A bagged list of the tableaux can be created with @TO allSemistandardTableaux@. + The number of rows is the same as the length of its shapes, excluding trailing $0$'s. Example - # allSemistandardTableaux(new Partition from {6,6,5,1,1}) - numSemistandardTableaux(new Partition from {6,6,5,1,1}) + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {1,1,3,2,0}) + numRows T + (lam,mu) = standardize skewShape T + #lam SeeAlso - allSemistandardTableaux + (numColumns, YoungTableau) +/// + +doc /// +Key + (size, YoungTableau) +Headline + the number of boxes in a tableau +Usage + size T +Inputs + T:YoungTableau + a skew Tableau. +Outputs + n:ZZ + the number of boxes in T. +Description + Example + T = youngTableau(new Partition from {4,3,1,0}) + size T +SeeAlso + (entries, YoungTableau) +/// + +doc /// +Key + (symbol ==, Tabloid, Tabloid) +Headline + check if two tabloids are equal +Usage + T1 == T2 +Inputs + T1:Tabloid + a tabloid. + T2:Tabloid + a tabloid. +Outputs + b:Boolean + whether the tabloids are equal or not. +Description + Text + Two tabloids are equal if they have the same representative. That is, if their corresponding rows contain the + same numbers. + Example + T1 = tabloid(new Partition from {5,3,1}, new Partition from {2}, {7,2,4,1,3,6,5}) + T2 = tabloid(new Partition from {5,3,1}, new Partition from {2}, {2,7,4,3,6,1,5}) + T1 == T2 +/// + +doc /// +Key + (tex, YoungTableau) +Headline + LaTeX output for a tableau +Usage + tex T +Inputs + T:YoungTableau + a tableau. +Outputs + s:String + the LaTeX code for reproducting the given tableau. +Description + Text + The LaTeX code uses commands from the LaTeX package @HREF("https://github.com/AndrewMathas/aTableau","aTableau")@. + Example + T = youngTableau(new Partition from {4,3,1}, new Partition from {2,1}, {1,2,3,4,5}) + tex T + T' = tabloid T + tex T' +SeeAlso + youngTableau + youngDiagram + ferrersDiagram +/// + +doc /// +Key + (toList, Tabloid) +Headline + list representation of a tabloid +Usage + toList T +Inputs + T:Tabloid + a tabloid. +Outputs + l:Bag + a bagged list of all Young tableaux in the equivalence class T. +Description + Example + T = tabloid(new Partition from {3,1}, {1,2,3,4}) + listT = toList T + peek listT +/// + +doc /// +Key + (symbol ++, YoungTableau, YoungTableau) +Headline + direct sum of tableaux +Usage + T1 ++ T2 +Inputs + T1:YoungTableau + a tableau. + T2:YoungTableau + a tableau. +Outputs + S:YoungTableau + a direct sum of T1 and T2. +Description + Text + The tableaux are combined into the disconnected components (not sharing an edge) of a single tableau. + Example + T1 = youngTableau(new Partition from {5}, new Partition from {2}) + T2 = youngTableau(new Partition from {3,2}, new Partition from {1}, {1,2,3,4}) + T1 ++ T2 + T2 ++ T1 +SeeAlso + (components, YoungTableau) + (symbol ||, YoungTableau, YoungTableau) +/// + +doc /// +Key + (symbol ==, YoungTableau, YoungTableau) +Headline + check if two Young tableaux are equal +Usage + T1 == T2 +Inputs + T1:YoungTableau + a tableau. + T2:YoungTableau + a tableau. +Outputs + b:Boolean + whether the tableaux are equal or not. +Description + Text + Two tableaux are equal if they have the same shape (ignoring trailing 0s), and the same filling. + Example + T1 = youngTableau(new Partition from {5,3,1}, new Partition from {2}, {7,2,4,1,3,6,5}) + T2 = youngTableau(new Partition from {5,3,1}, new Partition from {2}, {2,7,4,3,6,1,5}) + T1 == T2 +/// + +doc /// +Key + (symbol ^, YoungTableau, ZZ) +Headline + get the entries in a row +Usage + T^i +Inputs + T:YoungTableau + a tableau. + i:ZZ + the index of a row. +Outputs + l:List + the entries in the $i$th row of T. +Description + Text + Note that the resulting list may contain @TO null@ entries in places where a box is not present. This means that + the notations (T^i)#j, (T_j)#i, and T_(i,j) are all equivalent for valid positions (i,j). However, the notation + T_(i,j) will raise an error for nonvalid positions, whereas the other two will return @TO null@. + Example + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {1,1}, {1,2,3,4,5,6}) + for i from 0 to numRows T - 1 do print T^i + (T^0)#1 + (T_1)#0 + T_(0,1) + Text + If T has negative rows, then the entries at negative column coordinates are to the right in the output. + Example + T' = youngTableau(new Partition from {4,3,1,0}, new Partition from {1,-1}, {1,2,3,4,5,6,7,8}) + for i from 0 to numRows T' - 1 do print T'^i + T'^1#-1 + T'_(1,-1) + Text + To get the row entries exactly as they appear in the tableau and without extra null's, use rowEntries. + Example + T'' = youngTableau(new Partition from {3,4}, new Partition from {-2}, {1,2,3,4,5,6,7,8,9}) + T''^0 + rowEntries(T'',0) +SeeAlso + (numRows, YoungTableau) + rowEntries + (symbol _, YoungTableau, Sequence) + (symbol _, YoungTableau, ZZ) +/// + +doc /// +Key + (symbol _, YoungTableau, Sequence) +Headline + get the entry at a specific position +Usage + T_seq + T_(i,j) +Inputs + T:YoungTableau + a tableau. + seq:Sequence + a pair (i,j) of integers in ZZ. +Outputs + theBox:Thing + the entry of T in position (i,j). +Description + Text + The notations (T^i)#j, (T_j)#i, and T_(i,j) are all equivalent for valid positions (i,j). However, the notation + T_(i,j) will raise an error for nonvalid positions, whereas the other two will return @TO null@. + Example + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {1,1}, {1,2,3,4,5,6}) + (T^0)#1 + (T_1)#0 + T_(0,1) +SeeAlso + (symbol _, YoungTableau, ZZ) + (symbol ^, YoungTableau, ZZ) +/// + +doc /// +Key + (symbol _, YoungTableau, ZZ) +Headline + get the entries in a column +Usage + T_j +Inputs + T:YoungTableau + a tableau. + j:ZZ + the index of a column. +Outputs + l:List + the entries in the $j$th column of T. +Description + Text + Note that the resulting list may contain @TO null@ entries in places where a box is not present. This means that + the notations (T^i)#j, (T_j)#i, and T_(i,j) are all equivalent for valid positions (i,j). However, the notation + T_(i,j) will raise an error for nonvalid positions, whereas the other two will return @TO null@. + Example + T = youngTableau(new Partition from {4,3,1,0}, new Partition from {1,1}, {1,2,3,4,5,6}) + for i in columnRange T do print T_i + (T^0)#1 + (T_1)#0 + T_(0,1) + Text + To get the column entries without extra null's, use columnEntries. + Example + T'' = youngTableau(new Partition from {3,1}, new Partition from {-2,0,-1}, {1,2,3,4,5,6,7}) + T''_-1 + columnEntries(T'',-1) +SeeAlso + (numColumns, YoungTableau) + columnEntries + (symbol _, YoungTableau, Sequence) + (symbol ^, YoungTableau, ZZ) +/// + +doc /// +Key + (symbol ||, YoungTableau, YoungTableau) +Headline + vertical concatenation of two tableaux +Usage + T1 || T2 +Inputs + T1:YoungTableau + a tableau. + T2:YoungTableau + a tableau. +Outputs + S:YoungTableau + a single tableau with T1 directly above T2. +Description + Example + T1 = youngTableau(new Partition from {5}, new Partition from {2}) + T2 = youngTableau(new Partition from {3,2}, new Partition from {1}, {1,2,3,4}) + T1 || T2 + T2 || T1 +SeeAlso + verticalConcatenate + (symbol ++, YoungTableau, YoungTableau) /// diff --git a/M2/Macaulay2/packages/Tableaux/tests.m2 b/M2/Macaulay2/packages/Tableaux/tests.m2 index 9a07592cbdd..10355522eef 100644 --- a/M2/Macaulay2/packages/Tableaux/tests.m2 +++ b/M2/Macaulay2/packages/Tableaux/tests.m2 @@ -1,13 +1,17 @@ -TEST /// -- skewTableau, size T, skewShape, truncate skewShape, pad skewShape, standardize +TEST /// -- youngTableau, size T, skewShape, trim skewShape, pad skewShape, standardize + -- shape, innerShape, outerShape lam = new Partition from {4,1,0,-3,0,2,0,0} mu = new Partition from {1,0,0,2,-2,-1,-5,-1,0,0} entryList = toList(1..20) - T = skewTableau(lam,mu,entryList) + T = youngTableau(lam,mu,entryList) assert (toList T#"outerShape" == toList lam and toList T#"innerShape" == toList mu and T#values == entryList) + assert(toList T#"outerShape" == toList outerShape T) + assert(toList T#"innerShape" == toList innerShape T) + assert(toList shape youngTableau lam == toList lam) assert (size T == 20) (lam',mu') = skewShape T @@ -16,7 +20,7 @@ TEST /// -- skewTableau, size T, skewShape, truncate skewShape, pad skewShape, s (lam'',mu'') = pad (lam',mu') assert (toList lam'' == {4,1,0,-3,0,2,0,0,0,0} and toList mu'' == {1,0,0,2,-2,-1,-5,-1,0,0}) - (lam'',mu'') = truncate (lam',mu') + (lam'',mu'') = trim (lam',mu') assert (toList lam'' == {4,1,0,-3,0,2} and toList mu'' == {1,0,0,2,-2,-1,-5,-1}) (lam'',mu'') = standardize (lam',mu') @@ -24,15 +28,32 @@ TEST /// -- skewTableau, size T, skewShape, truncate skewShape, pad skewShape, s /// +TEST /// -- mutableYoungTableau, tabloid + -- verticalConcatenate + + lam = new Partition from {3,3,1} + mu = new Partition from {1} + entryList = {4, 5, 3, 2, 1, 6} + + assert(entries representative tabloid(lam,mu,entryList) == {4, 5, 1, 2, 3, 6}) + + T = mutableYoungTableau(lam,mu,entryList) + T_(1,0) = 9 + + assert(toList entries T == {4, 5, 9, 2, 1, 6}) + assert(entries verticalConcatenate {T,T} == (toList entries T | toList entries T)) + +/// + TEST /// -- numRows, numColumns, rowRange, columnRange, positionList, toIndex, toPosition -- T^i, T_j, T_(i,j), rowEntries, columnEntries lam = new Partition from {4,1,0,-3,0,2,0,0} mu = new Partition from {1,0,0,2,-2,-1,-5,-1,0,0} entryList = toList(1..20) - T = skewTableau(lam,mu,entryList) + T = youngTableau(lam,mu,entryList) - assert(numRows T == max(# truncate lam, # truncate mu)) + assert(numRows T == max(# trim lam, # trim mu)) assert(numColumns T == max(toList lam | toList mu) - min(toList lam | toList mu)) assert(rowRange T == (0..(numRows T - 1))) assert(columnRange T == (min(toList lam | toList mu)..(max(toList lam | toList mu)-1))) @@ -52,45 +73,90 @@ TEST /// -- numRows, numColumns, rowRange, columnRange, positionList, toIndex, t /// +TEST /// -- applyEntries, applyPositions + -- boxContent, hookLength + + lam = new Partition from {6,5,5,4,2,1} + mu = new Partition from {3,2,2} + entryList = toList(1..(sum toList lam - sum toList mu)) + T = youngTableau(lam,mu,entryList) + + assert(entries applyEntries(T, theBox -> theBox^2) == apply(entries T, theBox -> theBox^2)) + assert(entries applyPositions(T, thePosition -> thePosition) == positionList T) + + assert(boxContent(3,1) == -2) + assert(apply(positionList T, thePosition -> hookLength(thePosition,T)) == {6,4,1,5,4,2,4,3,1,6,4,2,1,3,1,1}) + +/// + +TEST /// -- rowStabilizer, columnStabilizer, readingWord + + lam = new Partition from {2,2,1} + entryList = {1,4,2,5,3} + T = youngTableau(lam, entryList) + + assert(# columnStabilizer T == 12) + assert(# rowStabilizer T == 4) + + lam' = new Partition from {3,2} + entryList' = {1,3,4,2,5} + T' = youngTableau(lam', entryList') + + assert(readingWord T' == {2, 1, 5, 3, 4}) + +/// + TEST /// -- youngTableau -- isWeaklyDecreasing, isNonnegative + -- randomSemistandardTableau, randomStandardTableau, randomTabloid + -- isCorner, isSemistandard, isStandard, isSkew + -- representative + -- shift, unshift lam = new Partition from {4,1,0,-3,0,2,0,0} mu = new Partition from {1,0,0,2,-2,-1,-5,-1,0,0} entryList = toList(1..20) - T = skewTableau(lam,mu,entryList) + T = youngTableau(lam,mu,entryList) lam' = new Partition from {6,5,3,1} T' = youngTableau(lam') + assert(isSkew T) + assert(isWeaklyDecreasing T == false) assert(isWeaklyDecreasing T' == true) assert(isNonnegative T == false) assert(isNonnegative T' == true) - -/// -TEST /// -- applyEntries, applyPositions - -- boxContent, hookLength + assert(isSemistandard randomSemistandardTableau lam') + assert(isStandard randomStandardTableau lam') - lam = new Partition from {6,5,5,4,2,1} - mu = new Partition from {3,2,2} - entryList = toList(1..(sum toList lam - sum toList mu)) - T = skewTableau(lam,mu,entryList) + correctCorners = {false, false, false, false, false, true, false, false, false, false, true, false, false, true, true} + assert(entries applyPositions(T',thePosition -> isCorner(thePosition,T')) == correctCorners) - assert(entries applyEntries(T, theBox -> theBox^2) == apply(entries T, theBox -> theBox^2)) - assert(entries applyPositions(T, thePosition -> thePosition) == positionList T) + assert(instance(representative randomTabloid lam',YoungTableau)) - assert(boxContent(3,1) == -2) - assert(apply(positionList T, thePosition -> hookLength(thePosition,T)) == {6,4,1,5,4,2,4,3,1,6,4,2,1,3,1,1}) + assert(T' == unshift shift T') /// TEST /// -- allSemistandardTableaux, numSemistandardTableaux + -- allStandardTableaux, numStandardTableaux + -- allTalboids, numTabloids + -- allSubPartitions lam = new Partition from {6,5,4,2,1} assert(numSemistandardTableaux(lam, #lam) == # allSemistandardTableaux(lam, #lam)) + lam = new Partition from {5,4,2,1} + + assert(numStandardTableaux(lam) == # allStandardTableaux(lam)) + + lam = new Partition from {5,3,2} + + assert(numTabloids(lam) == # allTabloids(lam)) + + assert(# allSubPartitions(new Partition from {4,3,1}, new Partition from {2,1}) == 16) /// diff --git a/M2/Macaulay2/packages/TateOnProducts.m2 b/M2/Macaulay2/packages/TateOnProducts.m2 index 420a507dbf4..ad0f22c893a 100644 --- a/M2/Macaulay2/packages/TateOnProducts.m2 +++ b/M2/Macaulay2/packages/TateOnProducts.m2 @@ -1,7 +1,24 @@ +-- line 2782 + 3, +-- use file /var/folders/wb/8v4mm0j52pq9pf5gkr8f23z40000gr/T/M2-17803-0/ +-- from running installPackage. 1 example failing. + +-- need to replace 'new Complex' and nearby code with modules = ..., maps = ...... +-- Did the following: ChainComplex -> Complex, chainComplex -> complex +-- Some things were not changed: e.g. isChainComplex +-- Removed chainComplexMap. +-- TODO: res -> freeResolution DONE +-- eliminate new ChainComplex, +-- remove trailing zeros, etc. ?? +-- double check on projections and inclusion maps. We need the right indices. +-- some flotsam: functions that we might have implemented. Remove these or take them. +-- We also changed: LinearTruncations, VirtualResolutions, but have not tested those. +-- Currently we are at 3 examples failing. + -* restart uninstallPackage"TateOnProducts" restart +installPackage "TateOnProducts" installPackage("TateOnProducts")--,FileName=>schreyer/Dropbox/SVDComplexes/from-git/TateOnProducts.m2) loadPackage("TateOnProducts",Reload=>true) viewHelp "TateOnProducts" @@ -21,8 +38,8 @@ newPackage( { Name => "Yeongrak Kim", Email => "kim@math.uni-sb.de", HomePage => "http://sites.google.com/view/yeongrak/"} }, Keywords => {"Commutative Algebra"}, - PackageImports => { "Truncations" }, - PackageExports => { "Isomorphism", "SVDComplexes", "OldChainComplexes" }, + PackageImports => {"Truncations"}, + PackageExports => {"Isomorphism", "Complexes", "SVDComplexes"}, DebuggingMode => false ) @@ -84,7 +101,7 @@ export { -- "isMinimalChainComplex", -- "resolutionOfChainComplex", -- "chainComplexMap", - "InitialDegree", +-- "InitialDegree", "isQuism", "isAction" -- Check @@ -197,7 +214,7 @@ multigradedRegularity Module := List => M -> ( coarseMultigradedRegularity = method(Options => {Strategy =>"MinimalResolution"}) --*coarseMultigradedRegularity ChainComplex := o-> F -> ( +-*coarseMultigradedRegularity Complex := o-> F -> ( --we assume F starts in homological degree 0. el := length F; r := degreeLength ring F; @@ -239,7 +256,7 @@ LL (ZZ,List) := (d,n) -> ( select(L1, ell -> all(#n, i-> ell_i<= (1+n_i))); ) -coarseMultigradedRegularity ChainComplex := o-> F -> ( +coarseMultigradedRegularity Complex := o-> F -> ( --we assume F starts in homological degree 0. t := degreeLength ring F; range := toList(min F..max F-1); @@ -253,35 +270,52 @@ coarseMultigradedRegularity ChainComplex := o-> F -> ( coarseMultigradedRegularity Module := o-> M-> ( t := degreeLength ring M; - if o.Strategy == "MinimalResolution" then F := res M else - if o.Strategy == "FastNonminimal" then ( - S := ring M; - S' := coefficientRing S[gens S]; - m := presentation M; - Tm := target m; - Tm':= S'^(degrees Tm/sum); - M' := coker map(Tm',,sub(presentation M, S')); - assert(isHomogeneous M'); - F' := res(M', FastNonminimal=>true); - F = allGradings(F',Tm, S)); + if o.Strategy == "MinimalResolution" then + F := freeResolution M + else if o.Strategy == "FastNonminimal" then ( + S := ring M; + S' := coefficientRing S[gens S]; + m := presentation M; + Tm := target m; + Tm':= S'^(degrees Tm/sum); + M' := coker map(Tm',,sub(presentation M, S')); + assert(isHomogeneous M'); + F' := freeResolution(M', Strategy => Nonminimal); + F = allGradings(F',Tm, S) + ); coarseMultigradedRegularity(F, Strategy => o.Strategy) ) allGradings=method() -allGradings (ChainComplex,Module, Ring) := (fJ,F0,Sall) -> ( - fJall := new ChainComplex; - fJall.Ring = Sall; - fJall_0 = F0; - for i from 1 to length fJ do ( - m := map(fJall_(i-1),,sub(fJ.dd_i,Sall)); - fJall_i = source m; - fJall.dd_i=m); - chainComplex apply(length fJ,i->fJall.dd_(i+1)) +-- Older version, to be removed: +-- allGradings (Complex,Module, Ring) := (fJ,F0,Sall) -> ( +-- fJall := new ChainComplex; +-- fJall.Ring = Sall; +-- fJall_0 = F0; +-- for i from 1 to length fJ do ( +-- m := map(fJall_(i-1),,sub(fJ.dd_i,Sall)); +-- fJall_i = source m; +-- fJall.dd_i=m); +-- complex apply(length fJ,i->fJall.dd_(i+1)) +-- ) +allGradings (Complex, Module, Ring) := Complex => (fJ,F0,Sall) -> ( + -- fJ is a complex over a singly graded ring + S := ring F0; + if Sall =!= S then error "we expected module over the given ring"; + modules := new MutableHashTable; + maps := new MutableHashTable; + (lo,hi) := concentration fJ; + modules#lo = F0; + for i from lo+1 to hi do ( + maps#i = map(modules#(i-1),,sub(fJ.dd_i,S)); + modules#i = source maps#i; + ); + if lo === hi then + complex F0 + else + complex maps ) - - - productOfProjectiveSpaces = method(Options=> {CoefficientField=>ZZ/32003, Variables=>{getSymbol "x", getSymbol "e"}, @@ -325,9 +359,9 @@ M = coker gens pfaffians(4, m1-transpose m1) R = coarseMultigradedRegularity M R ={4,4} netList apply(1+ length G, i-> tally degrees G_i) -betti (G =res M) +betti (G =freeResolution M) R = {} %{1,4} also works. -betti (G = res truncate(R, M)) +betti (G = freeResolution truncate(R, M)) netList apply(1+ length G, i-> tally degrees G_i) regularity G @@ -393,7 +427,7 @@ subMatrix=(m,d,e) -> ( upperCorner=method() -- needs update if we work with negative grading on exterior algebra. -upperCorner(ChainComplex,List) := (F,deg) ->( +upperCorner(Complex,List) := (F,deg) ->( E:=ring F; degsE:= unique degrees E; n:=apply(degsE,dege->#select(degrees E,d->d==dege)-1); @@ -412,7 +446,7 @@ upperCorner(ChainComplex,List) := (F,deg) ->( lowerCorner=method() -lowerCorner(ChainComplex,List) := (F,deg) ->( +lowerCorner(Complex,List) := (F,deg) ->( E:=ring F; degsE:= unique degrees E; n:=apply(degsE,dege->#select(degrees E,d->d==dege)-1); @@ -431,7 +465,7 @@ lowerCorner(ChainComplex,List) := (F,deg) ->( -* corner=method() -corner(ChainComplex,List) := (F,deg) ->( +corner(Complex,List) := (F,deg) ->( E:=ring F; degsE:= unique degrees E; assert( @@ -446,7 +480,7 @@ corner(ChainComplex,List) := (F,deg) ->( transpose (transpose F.dd_k_L1)_L2 ) -corner(ChainComplex,ZZ,List) := (F,k,deg) ->( +corner(Complex,ZZ,List) := (F,k,deg) ->( --Frank: I do not understand why we want this function, most likely for a bit more flexibility E:=ring F; degsE:= unique degrees E; @@ -472,7 +506,7 @@ corner(ChainComplex,ZZ,List) := (F,k,deg) ->( cohomologyMatrix=method() -cohomologyMatrix(ChainComplex,List,List) := (F,da,db) -> ( +cohomologyMatrix(Complex,List,List) := (F,da,db) -> ( --Under the assumption that T is part of a Tate resolution of a sheaf F on a product of --two projective space P^{n_1} x P^{n_2}, the function returns a matrix of cohomology polynomials --$$\sum_{i=0}^{|n|} \, dim H^i(\mathbb P^{n_1}\times \mathbb P^{n_2},\mathcal F(c_1,c_2)) * h^i \in \, \mathbb Z[h,k]$$ @@ -489,7 +523,7 @@ cohomologyMatrix(ChainComplex,List,List) := (F,da,db) -> ( --(b_1,b_2) sits in the north-east corner, the one corresponding to (a_1,a_2) in the south-west --corner. E:= ring F; - if degreeLength E != 2 then error "cohomologyMatrix works only with a product of two projective spaces"; + if not #unique degrees E==2 then error "works only for two factors"; L:=flatten apply(toList(min F..max F), k-> apply(degrees F_k, deg-> sum deg-k)); @@ -503,7 +537,7 @@ cohomologyMatrix(ChainComplex,List,List) := (F,da,db) -> ( C ) cohomologyMatrix(Module, List, List) := (M, low, high) -> ( - if degreeLength ring M != 2 then error "cohomologyMatrix works only with a product of two projective spaces"; + if degreeLength ring M != 2 then error"this version works only with a product of two projective spaces."; if #low !=2 or #high !=2 then error"expected degree lists of length 2"; if not all(#low, i-> low_i<=high_i) then error"low should be less than high"; C := tateResolution(M, low, high); @@ -527,12 +561,12 @@ eulerPolynomialTable HashTable := H ->( ) eulerPolynomialTable(Module, List, List) := (M,low,high) -> eulerPolynomialTable cohomologyHashTable(M,low,high) -eulerPolynomialTable(ChainComplex, List, List) := (T,low,high) -> +eulerPolynomialTable(Complex, List, List) := (T,low,high) -> eulerPolynomialTable cohomologyHashTable(T,low,high) cohomologyHashTable=method() -cohomologyHashTable(ChainComplex,List,List) := (F,low,high) -> ( +cohomologyHashTable(Complex,List,List) := (F,low,high) -> ( --Under the assumption that T is part of a Tate resolution of a sheaf F on a product of --projective spaces P^{n_1} x ... x P^{n_t}, the function returns a hashTable --In case T corresponds to an object in the derived category D^b(P^{n_1}x P^{n_2}), then @@ -562,7 +596,7 @@ cohomologyHashTable(Module, List, List) := (M, low, high) -> ( tallyDegrees=method() -tallyDegrees(ChainComplex) := C -> ( +tallyDegrees(Complex) := C -> ( apply(min C..max C,k->tally degrees C_k)) @@ -584,23 +618,27 @@ boxDegrees(Ring) := E -> ( beilinsonWindow=method() -beilinsonWindow ChainComplex := (C)-> ( +beilinsonWindow Complex := (C)-> ( tD := tateData ring C; (S,E) := tD.Rings; - (minC, maxC, mapsC) := toSequence chainComplexData C; + (minC, maxC) := concentration C; windows := for i from minC to maxC list ( degs := degrees C_i; positions(degs, a -> inBeilinsonWindow(a,E)) ); - maps := for i from minC + 1 to maxC list ( - submatrix(C.dd_i, windows_(i-minC-1), windows_(i-minC)) + maps := hashTable for i from minC + 1 to maxC list ( + rowPositions := windows_(i-minC-1); + colPositions := windows_(i-minC); + if #rowPositions === 0 then continue + else + i => submatrix(C.dd_i, rowPositions, colPositions) ); - removeZeroTrailingTerms chainComplexFromData{minC, maxC, maps} + + complex maps ) - isChainComplex=method() -isChainComplex(ChainComplex) := W -> ( +isChainComplex(Complex) := W -> ( lengthW:= max W- min W; #select(min W+1..max W-1,i->( if (source W.dd_i==0 or W.dd_(i+1)==0) then true else W.dd_i*W.dd_(i+1)==0)) ==lengthW-1) @@ -635,13 +673,13 @@ truncateInE(List,Module):= (d,M) -> ( cornerComplex=method() -cornerComplex(ChainComplex,List) := (C,c) ->( +cornerComplex(Complex,List) := (C,c) ->( d:=c-toList(#c:1); cornerComplex1(C,d) ) cornerComplex1=method() -cornerComplex1(ChainComplex,List) := (C,c) -> ( +cornerComplex1(Complex,List) := (C,c) -> ( -- added this line to make the function work for the zero complex if C==0 then return C; -- @@ -651,10 +689,11 @@ cornerComplex1(ChainComplex,List) := (C,c) -> ( Cge := firstQuadrantComplex1(C'[-#t+1],c); Cle := lastQuadrantComplex1(C',c); -- <<(betti Cge, betti Cle) < ( - d:=e+#t; A=Cge.dd_(d); - B= Cle.dd_(d); AB = cornerMap(C',c,d); + Ccorner:= complex apply(max C- min C - #t-1, e-> ( + d := e+#t; + A := Cge.dd_(d); + B := Cle.dd_(d); + AB := cornerMap(C',c,d); -- print((betti A,betti AB,betti B)); (A|AB)||(map(target B, source A,0)|B)) ); @@ -668,7 +707,7 @@ cornerComplex(Module, List, List) := (M,low, high) ->( hi := apply(#regs, i->max(regs_i, high_i+1)); --hi N := presentation truncate(hi, M)**S^{hi};-- betti N Q := symExt(N,E); --betti Q - (res (coker Q,LengthLimit=>(sum hi-sum low)))**E^{hi}[sum hi] + (freeResolution (coker Q,LengthLimit=>(sum hi-sum low)))**E^{hi}[sum hi] ) *- @@ -683,7 +722,7 @@ tateResolution(Module, List, List) := (M,low, high) ->( N := presentation truncate(hi, M)**S^{hi};-- betti N Q := symExt(N,E); --betti Q - (res (coker Q,LengthLimit=>(sum hi-sum low)))**E^{hi}[sum hi-#regs+2] + ((freeResolution (coker Q,LengthLimit=>(sum hi-sum low)))**E^{hi})[sum hi-#regs+2] ) tateResolution(Matrix, List, List) := (A, low, high) -> ( @@ -702,15 +741,15 @@ tateResolution(Matrix, List, List) := (A, low, high) -> ( QM := symExt(linearPresentationM,E); QN := symExt(linearPresentationN,E); - CM := (res (coker QM,LengthLimit=>(sum hi-sum low))); - CN := (res (coker QN,LengthLimit=>(sum hi-sum low))); + CM := (freeResolution (coker QM,LengthLimit=>(sum hi-sum low))); + CN := (freeResolution (coker QN,LengthLimit=>(sum hi-sum low))); - extPos := extend(CN[1], CM[1], sub(matrix truncatedMapMtoN, E)); + extPos := extend(CN[1], CM[1], map(CN_1, CM_1, sub(matrix truncatedMapMtoN, E))); extNeg := dual extend(dual CM[-1], dual CN[-1], transpose sub(matrix truncatedMapMtoN,E)); -- shift does not change the sign of map of complexes - extAll := map(CN, CM, i->(if i<1 then extNeg#(i-1) else extPos#(i-1))); + extAll := map(CN, CM, i->(if i<1 then extNeg_(i-1) else extPos_(i-1))); - extAll**E^{hi}[sum hi-#regM+2] + (extAll**E^{hi})[sum hi-#regM+2] ) /// @@ -809,12 +848,12 @@ quadrantMap1(Matrix,List) := (M,c) -> ( return ((M^goodRows)_goodColumns)) firstQuadrantComplex=method() -firstQuadrantComplex(ChainComplex,List) := (C,c) -> ( +firstQuadrantComplex(Complex,List) := (C,c) -> ( -- c index of the lower corner of the first quadrant firstQuadrantComplex1(C,c-toList(#c:1)) ) firstQuadrantComplex1=method() -firstQuadrantComplex1(ChainComplex,List) := (C,c) -> ( +firstQuadrantComplex1(Complex,List) := (C,c) -> ( -- c index of upper corner of the complementary last quadrant -- added this line to make the function work for the zero complex if C==0 then return C; @@ -824,8 +863,8 @@ firstQuadrantComplex1(ChainComplex,List) := (C,c) -> ( -- I:= numFactors ring C; -- sign change for c (1x) --now replace each map in C' with the corresponding "quadrantMap" --- Cge:=chainComplex apply(max C'-1,d -> quadrantMap(C'.dd_(d+1),-c,{},{})); - Cge:=chainComplex apply(max C'-1,d -> quadrantMap1(C'.dd_(d+1),-c)); +-- Cge:=complex apply(max C'-1,d -> quadrantMap(C'.dd_(d+1),-c,{},{})); + Cge:=complex apply(max C'-1,d -> quadrantMap1(C'.dd_(d+1),-c)); return Cge[-s]) /// restart @@ -835,8 +874,8 @@ uninstallPackage "TateOnProducts" restart installPackage "TateOnProducts" (S,E) = productOfProjectiveSpaces {1,1}; - T1= (dual res( trim (ideal vars E)^2,LengthLimit=>8))[1]; - T=trivialHomologicalTruncation(T2=res(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7],-5,6); + T1= (dual freeResolution( trim (ideal vars E)^2,LengthLimit=>8))[1]; + T=trivialHomologicalTruncation(T2=freeResolution(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7],-5,6); betti T cohomologyMatrix(T,-{4,4},{3,2}) fqT=firstQuadrantComplex(T,-{2,1}); @@ -855,13 +894,13 @@ viewHelp TateOnProducts lastQuadrantComplex=method() -lastQuadrantComplex(ChainComplex,List) := (C,c) -> ( +lastQuadrantComplex(Complex,List) := (C,c) -> ( -- c index of the lower corner of the complementary first quadrant lastQuadrantComplex1(C,c-toList(#c:1))) lastQuadrantComplex1=method() -lastQuadrantComplex1(ChainComplex,List) := (C,c) -> ( +lastQuadrantComplex1(Complex,List) := (C,c) -> ( -- c index of the upper corner of the last quadrant -- added this line to make the function work for the zero complex if C==0 then return C; @@ -870,11 +909,11 @@ lastQuadrantComplex1(ChainComplex,List) := (C,c) -> ( C':=C[s]; I:= numFactors ring C; --sign chain for c (1x) - Cge:=chainComplex apply(max C'-1,d -> quadrantMap(C'.dd_(d+1),-c,I,I)); + Cge:=complex apply(max C'-1,d -> quadrantMap(C'.dd_(d+1),-c,I,I)); return Cge[-s]) cornerMap=method() -cornerMap(ChainComplex,List,ZZ) := (C,c,d) -> ( +cornerMap(Complex,List,ZZ) := (C,c,d) -> ( -- added this line to make the function work for the zero complex if C==0 then return C; -- @@ -891,11 +930,11 @@ cornerMap(ChainComplex,List,ZZ) := (C,c,d) -> ( regionComplex=method() -regionComplex(ChainComplex,List,Sequence) := (T,c,IJK) -> ( +regionComplex(Complex,List,Sequence) := (T,c,IJK) -> ( T1:=trivialHomologicalTruncation(T,nonzeroMin T, nonzeroMax T); T2:=T1[min T1]; Ls:=apply(toList(min T2..max T2),k->goodColumns(T2_k,c,IJK)); - rT:=chainComplex apply(min T2+1..max T2,k-> ((T2.dd_k))^(Ls_(k-1))_(Ls_(k))); + rT:=complex apply(toList(min T2+1..max T2),k-> ((T2.dd_k))^(Ls_(k-1))_(Ls_(k))); rT[-min T1]) goodColumns=method() @@ -916,39 +955,39 @@ goodDegree(List,List,Sequence) := (d,c,IJK) -> ( strand=method() -strand(ChainComplex,List,List) := (T,c,I) -> ( +strand(Complex,List,List) := (T,c,I) -> ( regionComplex(T,c,({},I,{}))) -------------------------------------------------- --- formal ChainComplex manipulations -- +-- formal Complex manipulations -- -------------------------------------------------- -chainComplexData = C->( - minC := min C; - maxC := max C; - C':=C[minC]; - {minC, maxC, apply(toList(1..maxC-minC), i-> (C').dd_i)} -) - -chainComplexFromData = method() -chainComplexFromData List := L ->( - --format of L is desired min, desired max, list of - --shifted maps - C := chainComplex L_2; - assert( min C == 0); - C[-L_0]) - -chainComplexFromData(ZZ, List) := (minC,L) ->( - --minC will become the min of the output complex - C := chainComplex L; - assert( min C ==0); - C[-minC]) +-- chainComplexData = C->( +-- minC := min C; +-- maxC := max C; +-- C':=C[minC]; +-- {minC, maxC, apply(toList(1..maxC-minC), i-> (C').dd_i)} +-- ) + +-- chainComplexFromData = method() +-- chainComplexFromData List := L ->( +-- --format of L is desired min, desired max, list of +-- --shifted maps +-- C := complex L_2; +-- assert( min C == 0); +-- C[-L_0]) + +-- chainComplexFromData(ZZ, List) := (minC,L) ->( +-- --minC will become the min of the output complex +-- C := complex L; +-- assert( min C ==0); +-- C[-minC]) trivialHomologicalTruncation=method() -trivialHomologicalTruncation(ChainComplex,ZZ,ZZ) := (C,d,e) -> ( +trivialHomologicalTruncation(Complex,ZZ,ZZ) := (C,d,e) -> ( F := C; -- given a chain complex -- ... <- C_{k-1} <- C_{k} <- C_{k+1} <- ... @@ -958,41 +997,58 @@ trivialHomologicalTruncation(ChainComplex,ZZ,ZZ) := (C,d,e) -> ( while min F > d do (F =prependZeroMap F); while max F < e do (F=appendZeroMap F); G := F[d]; - if d==e then (G= prependZeroMap chainComplex map(G_0,(ring G)^0,0)) else ( - G=prependZeroMap appendZeroMap chainComplex apply(toList(1..e-d),k->G.dd_k)); + if d==e then (G= prependZeroMap complex G_0) else ( + G=prependZeroMap appendZeroMap complex apply(toList(1..e-d),k->G.dd_k)); G[-d]) /// E=ZZ/101[e_0,e_1,SkewCommutative=>true] -F=res ideal vars E +F=freeResolution(ideal vars E, LengthLimit => 3) betti F -C=dual res (coker transpose F.dd_3,LengthLimit=>8)[-3] +C=dual freeResolution (coker transpose F.dd_3,LengthLimit=>8)[-3] betti C C1=trivialHomologicalTruncation(C,-2,2) trivialHomologicalTruncation(C1,-3,3) /// - +-- XXXX Fix these prependZeroMap= method() -prependZeroMap ChainComplex := C->( - L := chainComplexData(C[-1]); - minC := L_0; - newd := map((ring C)^0, target L_2_0, 0); - (chainComplexFromData(minC-1,prepend(newd,L_2)))[1] +-- prependZeroMap Complex := C->( +-- L := chainComplexData(C[-1]); +-- minC := L_0; +-- newd := map((ring C)^0, target L_2_0, 0); +-- (chainComplexFromData(minC-1,prepend(newd,L_2)))[1] +-- ) + +prependZeroMap Complex := C -> ( + (lo, hi) := concentration C; + if lo === hi then complex({(ring C)^0, C_lo}, Base => lo-1) + else complex hashTable for i from lo to hi list i => ( + if i === lo then map((ring C)^0, C_lo, 0) + else dd^C_i + ) ) appendZeroMap= method() -appendZeroMap ChainComplex := C->( - L := chainComplexData(C); - minC := L_0; - newd := map(source last L_2,(ring C)^0, 0); - chainComplexFromData(minC,append(L_2,newd)) - ) +-- appendZeroMap Complex := C->( +-- L := chainComplexData(C); +-- minC := L_0; +-- newd := map(source last L_2,(ring C)^0, 0); +-- chainComplexFromData(minC,append(L_2,newd)) +-- ) +appendZeroMap Complex := C -> ( + (lo, hi) := concentration C; + if lo === hi then complex({C_lo, (ring C)^0}, Base => lo) + else complex hashTable for i from lo+1 to hi+1 list i => ( + if i === hi+1 then map(C_hi, (ring C)^0, 0) + else dd^C_i + ) + ) nonzeroMin = method() -nonzeroMin(ChainComplex) := C -> ( +nonzeroMin(Complex) := C -> ( --assert( not C==0); if C==0 then return min C; m:= min C; @@ -1001,7 +1057,7 @@ nonzeroMin(ChainComplex) := C -> ( nonzeroMax = method() -nonzeroMax(ChainComplex) := C -> ( +nonzeroMax(Complex) := C -> ( --assert( not C==0); if C==0 then return max C; m:= max C; @@ -1010,42 +1066,47 @@ nonzeroMax(ChainComplex) := C -> ( /// symbol tt R=ZZ[tt] -C=chainComplex {matrix{{R_0}}} +C=complex {matrix{{R_0}}} C1=appendZeroMap prependZeroMap C nonzeroMax C1,max C1 nonzeroMin C1, min C1 /// removeZeroTrailingTerms = method() -removeZeroTrailingTerms(ChainComplex) := W -> ( - E := ring W; +removeZeroTrailingTerms(Complex) := W -> ( +-- E := ring W; mi := nonzeroMin W; ma := nonzeroMax W; - W' := W[mi]; - if mi==ma then (return (chainComplex({map(E^0,W'_0,0),map(W'_0,E^0,0)}))[-mi+1]) else - (chainComplex apply(toList(1..ma-mi),i->W'.dd_i))[-mi] +-- W' := W[mi]; + if mi==ma then ( + complex(W_mi, Base => mi) +-- return (complex({map(E^0,W'_0,0),map(W'_0,E^0,0)}))[-mi+1]) + ) + else complex hashTable for i from mi+1 to ma list ( + i => W.dd_i) +-- (complex apply(toList(1..ma-mi),i->W'.dd_i))[-mi] ) /// R=ZZ[tt] -C=chainComplex {matrix{{R_0}}} +C=complex {matrix{{R_0}}} C1=appendZeroMap prependZeroMap C removeZeroTrailingTerms C1 /// extendFromMiddle = method() -extendFromMiddle (ChainComplex, ChainComplex, Matrix, ZZ) := (F1, F2, f, i) ->( - --f is a map to F1_i from F2_0. Output is a ChainComplexMap to F1 from F2e, +extendFromMiddle (Complex, Complex, Matrix, ZZ) := (F1, F2, f, i) ->( + --f is a map to F1_i from F2_0. Output is a ComplexMap to F1 from F2e, --where F2e is a chain complex obtained from F2 by prepending zeros. - --CAVEAT the process of making a new ChainComplex seems to destroy + --CAVEAT the process of making a new Complex seems to destroy --the direct sum information in the source and target modules! S:= ring F1; ind := toList(min F1.. max F1); F1List := apply (ind, i->F1.dd_i); - F1i := chainComplex F1List_{i+1..max F1}; + F1i := complex F1List_{i+1..max F1}; fi := extend(F1i,F2,f); - F2e := chainComplex( + F2e := complex( apply(ind, j-> if j( if j< i then map(F1_j, F2e_j,0) else fi_(j-i)) ) -chainComplexMap=method( - Options => {InitialDegree => -infinity} -) -chainComplexMap(ChainComplex,ChainComplex,List):= o -> (D,C,maps) -> ( - --- the code commented out should also work, and is in some sense - --- more desirable as it uses map in the code. However, something squirly - --- happens in the map code. - --- startDeg := min C; - --- if (o.InitialDegree != -infinity) then startDeg = o.InitialDegree; - --- definingSet := set (startDeg..startDeg + length maps - 1); - --- map(D,C,i -> (if member(i, definingSet) then maps_(i - startDeg) else 0)) - startDeg := min C; - if (o.InitialDegree != -infinity) then startDeg = o.InitialDegree; - F := new ChainComplexMap; - F.degree = 0; - F.source = C; - F.target = D; - index1 := startDeg; - scan(maps, x -> (F#index1 = x; index1 = index1 + 1;)); - F -) +-- Commented out MS+GS +-- chainComplexMap=method( +-- Options => {InitialDegree => -infinity} +-- ) +-- chainComplexMap(Complex,Complex,List):= o -> (D,C,maps) -> ( +-- --- the code commented out should also work, and is in some sense +-- --- more desirable as it uses map in the code. However, something squirly +-- --- happens in the map code. +-- --- startDeg := min C; +-- --- if (o.InitialDegree != -infinity) then startDeg = o.InitialDegree; +-- --- definingSet := set (startDeg..startDeg + length maps - 1); +-- --- map(D,C,i -> (if member(i, definingSet) then maps_(i - startDeg) else 0)) +-- startDeg := min C; +-- if (o.InitialDegree != -infinity) then startDeg = o.InitialDegree; +-- F := new ChainComplexMap; -- XXX Fix +-- F.degree = 0; +-- F.source = C; +-- F.target = D; +-- index1 := startDeg; +-- scan(maps, x -> (F#index1 = x; index1 = index1 + 1;)); +-- F +-- ) resolutionOfChainComplex = method(Options=>{LengthLimit => infinity}) -resolutionOfChainComplex ChainComplex := o -> C -> ( +resolutionOfChainComplex Complex := o -> C -> ( -- computes a (generally non-minimal) resolution of a complex by the method - -- of iterated mapping cones, and returns the ChainComplexMap from this to C. + -- of iterated mapping cones, and returns the ComplexMap from this to C. -- If -- C: 0 -> Cn ->...->Cm ->0 -- is a chain complex, and Gi is a resolution of @@ -1098,7 +1160,7 @@ resolutionOfChainComplex ChainComplex := o -> C -> ( n := numgens ring C; lengthLimit := max(n+len, len+o.LengthLimit); ind := toList(minC..maxC); - reslist := apply(ind, i-> res(C_i, LengthLimit => lengthLimit-(i-minC))); + reslist := apply(ind, i-> freeResolution(C_i, LengthLimit => lengthLimit-(i-minC))); mats := apply(ind, i-> matrix C.dd_i); --mats_i is the map from the free cover of C_i to --the free cover of C_(i-1) @@ -1127,10 +1189,10 @@ resolutionOfChainComplex ChainComplex := o -> C -> ( -- Cres.cache.comparisonMap = compMap[-minC]; -- Cres -- compMap[-minC] - chainComplexMap(C,F[-minC],comp) + map(C,F[-minC],comp) -- XXX check shifts ) -minimize ChainComplex := E ->( +minimize Complex := E ->( --To simplify the notation consider the complex C = E[min E] that --is shifted so that the first nonzero module is C_0. --The algorithm: @@ -1139,7 +1201,7 @@ minimize ChainComplex := E ->( -- the map rho is not a chain complex map, but the image of --(rho | d*rho): C ++ C[1] --> C is a subcomplex and --the minimization of C is the complex C/image(rho|d*rho). - --The script returns the ChainComplexMap from the minimization to C. + --The script returns the ComplexMap from the minimization to C. complete E; C:= E[min E]; -- now min C == 0. M := max C; @@ -1182,7 +1244,7 @@ isMinimalChainComplex = C -> ( minimize = method ( Options => {Check => false} ) -minimize ChainComplex := o -> E ->( +minimize Complex := o -> E ->( --To simplify the notation consider the complex C = E[nonZeroMin E] that --is shifted so that the first nonzer module is C_0. --The algorithm: @@ -1219,12 +1281,12 @@ minimize ChainComplex := o -> E ->( ) *- --isExact=method() -isExact(ChainComplex) := {} >> o -> (C) -> ( +isExact(Complex):= {} >> opts -> (C) -> ( if (all((min C,max C), i -> (prune HH_i(C) == 0))) then true else false ) isQuism=method() -isQuism(ChainComplexMap):=(phi) -> ( +isQuism(ComplexMap):=(phi) -> ( isExact(cone phi) ) @@ -1234,7 +1296,7 @@ restart loadPackage "TateOnProducts" S=ZZ/101[x_0..x_2] m=random(S^{1,0},S^{0,-1}) -C=chainComplex{m} +C=complex{m} target minimize C *- @@ -1246,7 +1308,7 @@ inWindow = method() inWindow(List,List) := (D,n) -> #D == #select(#D, i->(0<=D_i and D_i<=n_i)) -inWindow(ChainComplex) := W -> ( +inWindow(Complex) := W -> ( (t,v,n,varsList,irrList) := ringData ring W; L:=flatten apply(toList(nonzeroMin W.. nonzeroMax W),d-> degrees W_d); #select(L, D-> not inWindow(D,n))==0) @@ -1369,32 +1431,36 @@ pushAboveWindow List := List => L ->( append(L',B) ) -pushAboveWindow ChainComplex := ChainComplex => C -> ( +pushAboveWindow Complex := Complex => C -> ( --makes the chain complex into a list of matrices, --does pushAboveWindow to that, --and makes it back into a chain complex. --That is: Takes a chain complex and adds all the syzygies of maps in the complex that --are outside the Beilinson window. - C':=appendZeroMap appendZeroMap prependZeroMap C; - L := chainComplexData C'; - M := pushAboveWindow L_2; - chainComplexFromData(min C', M) - ) + (minC, maxC) := concentration C; + L2 := for i from minC to maxC+2 list C.dd_i; + M := pushAboveWindow L2; + complex(M, Base => minC-1) + -- C':=appendZeroMap appendZeroMap prependZeroMap C; + -- L := chainComplexData C'; + -- M := pushAboveWindow L_2; + -- chainComplexFromData(min C', M) + ); TEST /// debug TateOnProducts n={1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 4))[1]; a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2 = T1 ** complex(E^{a})[sum a]; W=beilinsonWindow T2 T3 = pushAboveWindow W assert(beilinsonWindow T3 == W) /// tateExtension=method() -tateExtension(ChainComplex) := W -> ( +tateExtension(Complex) := W -> ( -- input W : a Beilinson representative of an object in D^b(PP) -- output : an Tate extension in a bounded range -- compute the TateExtension in a sloppy way: the Beilinson window of the extension is only @@ -1409,14 +1475,14 @@ tateExtension(ChainComplex) := W -> ( --betti W1,betti TW1 --Bbounds given for the length of the resolution have to be discussed --They should come out of the proof of the theorem !! - TW1e := res(coker TW1.dd_(ma),LengthLimit=>(3*sum v))[-ma]; + TW1e := freeResolution(coker TW1.dd_(ma),LengthLimit=>(3*sum v))[-ma]; --betti TW1e --changed a sign here --TW1c := cornerComplex(TW1e,2*v); TW1c := cornerComplex(TW1e,-2*v); -- replace with upper quad cplx --cohomologyMatrix(TW1c, -4*v,4*v) --betti TWc - TW2 := dual res(coker transpose TW1c.dd_(ma+sum v), + TW2 := dual freeResolution(coker transpose TW1c.dd_(ma+sum v), LengthLimit =>(ma+3*sum v -mi))[-ma-sum v+1]; --cohomologyMatrix(TW2, -4*v,4*v) --betti TW2 @@ -1426,11 +1492,11 @@ tateExtension(ChainComplex) := W -> ( -* --this code is not used continueComplex = method(Options => options res) -continueComplex ChainComplex := o->C ->( +continueComplex Complex := o->C ->( ma := nonzeroMax C; - C' := res(image (C.dd_ma),LengthLimit => o.LengthLimit)[-ma]; + C' := freeResolution(image (C.dd_ma),LengthLimit => o.LengthLimit)[-ma]; ma' := nonzeroMax C'; - D := new ChainComplex; + D := new Complex; D.ring = ring C; apply(toList(min C..ma'),i->( D_i = if i<= ma then C_i else C'_i)); @@ -1444,7 +1510,7 @@ debug TateOnProducts n={1,1}; (S,E) = productOfProjectiveSpaces n; p = 2 -C = res(coker vars E,LengthLimit =>p)[2] +C = freeResolution(coker vars E,LengthLimit =>p)[2] C1 = continueComplex(C, LengthLimit =>2) assert((C1.dd)^2 == 0) /// @@ -1897,9 +1963,10 @@ beilinson Matrix := Matrix => opts -> o -> ( ) ) -beilinson ChainComplex := opts -> (BT) -> ( +beilinson Complex := opts -> (BT) -> ( -- BT should be a complex over E = exterior algebra - data := chainComplexData BT; + (minBT, maxBT) := concentration BT; + -- data := chainComplexData BT; if opts.BundleType === MapsBetweenFreeBundles then ( -- Do we also need a sign convention for them? tD := tateData ring BT; @@ -1909,12 +1976,13 @@ beilinson ChainComplex := opts -> (BT) -> ( --Csrc:=quotientPresentationComplex(BT); Ctar:=beilinson(BT, BundleType=>FreeBundle); - mapList:=data#2/(m->(beilinson(m,opts))); + mapList := for i from minBT+1 to maxBT list beilinson(BT.dd_i, opts); + -- mapList:=data#2/(m->(beilinson(m,opts))); tempList:={}; apply(mapList,i->(tempList=tempList|{i_1})); tempList=tempList|{(mapList_(#mapList-1))_0}; - f:= m->tempList_(m-data#0); + f:= m->tempList_(m-minBT); map(Ctar,Csrc,f) -- Sometimes Ctar, Csrc may have different lengths (at boundary multidegrees). @@ -1922,25 +1990,33 @@ beilinson ChainComplex := opts -> (BT) -> ( -- and the default map is 0 unless we assign. ) else ( - removeZeroTrailingTerms chainComplexFromData{data#0, data#1,data#2/(m -> (beilinson(m, opts)))} - ) + if minBT === maxBT then + return complex(target beilinson(BT.dd_(minBT+1), opts), + Base => minBT); + C := complex hashTable for i from minBT+1 to maxBT list ( + m := beilinson(BT.dd_i, opts); + if source m == 0 then continue + else i => m + ); + removeZeroTrailingTerms C + ) ) -- quotientPresentationComplex = method() -- This method extracts the maps on twisted free modules of multidegree {-1,...,-1}. -- The beilinson complex in 'QuotientBundle' option is a quotient of this free complex. -- maybe it is not a natural answer; take 'contractionSequence' might be better. --* quotientPresentationComplex ChainComplex := (BT) -> ( +-* quotientPresentationComplex Complex := (BT) -> ( BTQuot := beilinson (BT, BundleType=>QuotientBundle); -- already zero trailing terms are removed tmin:=(min BTQuot); tmax:=(max BTQuot); BTshifted:=BTQuot[tmin]; - C:=chainComplex(apply(1..tmax-tmin, i->map(target presentation target BTshifted.dd_(i), target presentation source BTshifted.dd_(i),BTshifted.dd_(i)))); + C:=complex(apply(toList(1..tmax-tmin), i->map(target presentation target BTshifted.dd_(i), target presentation source BTshifted.dd_(i),BTshifted.dd_(i)))); C[-tmin] ) *- --*embeddingToFree ChainComplex := (BT) -> ( +-*embeddingToFree Complex := (BT) -> ( -- return a list of embedding maps from a BT complex to a free complex data := chainComplexData BT; BTquotient := chainComplexFromData{data#0, data#1,data#2/(m -> (beilinson(m, BundleType=>QuotientBundle)))}; @@ -2009,29 +2085,42 @@ contractionFunctor Matrix := Matrix => m -> ( map(tar,src,mats) ) -contractionFunctor ChainComplex := (C) -> ( +contractionFunctor Complex := (C) -> ( -- C should be a complex over E = exterior algebra - data := chainComplexData contractionWindow C; - removeZeroTrailingTerms chainComplexFromData{data#0, data#1,data#2/(m -> contractionFunctor m)} + C' := contractionWindow C; + (minC', maxC') := concentration C'; + complex hashTable for i from minC' + 1 to maxC' list ( + m := contractionFunctor C'.dd_i; + if source m == 0 then continue + else i => m + ) + -- data := chainComplexData contractionWindow C; + -- removeZeroTrailingTerms chainComplexFromData{data#0, data#1,data#2/(m -> contractionFunctor m)} ) contractionWindow=method() -contractionWindow ChainComplex := (C)-> ( +contractionWindow Complex := (C)-> ( tD := tateData ring C; (S,E) := tD.Rings; - (minC, maxC, mapsC) := toSequence chainComplexData C; + (minC, maxC) := concentration C; + -- (minC, maxC, mapsC) := toSequence chainComplexData C; windows := for i from minC to maxC list ( degs := degrees C_i; positions(degs, a -> inContractionWindow(a,E)) ); - maps := for i from minC + 1 to maxC list ( - submatrix(C.dd_i, windows_(i-minC-1), windows_(i-minC)) - ); - removeZeroTrailingTerms chainComplexFromData{minC, maxC, maps} + complex hashTable for i from minC + 1 to maxC list ( + m := submatrix(C.dd_i, windows_(i-minC-1), windows_(i-minC)); + if source m == 0 then continue + else i => m + ) + -- maps := for i from minC + 1 to maxC list ( + -- submatrix(C.dd_i, windows_(i-minC-1), windows_(i-minC)) + -- ); + -- removeZeroTrailingTerms chainComplexFromData{minC, maxC, maps} ) contractionSequence = method() -contractionSequence ChainComplex := (T) -> ( +contractionSequence Complex := (T) -> ( -- given a complex of E-modules, forms two -- exact sequences, -- C: beilinson(T, BundleType=>QuotientBundle) @@ -2061,8 +2150,8 @@ n={1} n = {2,1} (S,E) = productOfProjectiveSpaces n -needsPackage "ChainComplexExtras" -C=chainComplex (matrix {{E_0}}) +--needsPackage "ChainComplexExtras" +C=complex {matrix {{E_0}}} beilinson(C) (beilinson(C, BundleType=>DummyQuotientBundle)).dd_1 (beilinson(C, BundleType=>FreeBundle)).dd_1 @@ -2074,9 +2163,9 @@ beilinson(C) m = map(E^{{0,-1}}, E^{{-2,-1}}, {{e_(0,1)*e_(0,2)}}) contractionFunctor m - T1 = (dual res trim (ideal vars E)^2 [1]); + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 4)[1]); cohomologyMatrix(T1,-3*n,3*n) - T2 = res(coker lowerCorner(T1, {2,2}), LengthLimit=>14)[4] + T2 = freeResolution(coker lowerCorner(T1, {2,2}), LengthLimit=>14)[4] cohomologyMatrix(T2,-3*n,3*n) betti contractionWindow (T2**E^{{0,0}}) betti beilinsonWindow (T2**E^{{-1,-1}}) @@ -2185,7 +2274,7 @@ cornerCohomologyTablesOfUa(List) := n-> ( Us:=flatten apply(n_0+1,a0->apply(n_1+1,a1->( a={a0,a1}; U=E^{ -a}; - W=(chainComplex {map(E^0,U,0),map(U,E^0,0)})[1]; + W=(complex {map(E^0,U,0),map(U,E^0,0)})[1]; {a,W}) )); Ts:=apply(Us,aW->( @@ -2214,7 +2303,7 @@ cornerCohomologyTablesOfUa(List,List) :=(n,a)-> ( (S,E) := productOfProjectiveSpaces n; U:=0;W:=0;T:=0;cTa:=0;cTb:=0;cTb1:=0; U=E^{ -a}; - W=(chainComplex {map(E^0,U,0),map(U,E^0,0)})[1]; + W=(complex {map(E^0,U,0),map(U,E^0,0)})[1]; T=tateExtension W; T=trivialHomologicalTruncation(T,-2*sum n,2*sum n); cTa=cornerComplex(T,-a); @@ -2286,84 +2375,88 @@ betti m /// bgg = method(Options =>{LengthLimit => null}) -bgg Module := o -> P -> ( +bgg Module := Complex => o -> P -> ( (S,E) := (tateData ring P)#Rings; - D:= null; Ds:= null; - freeModulesDegs:=null; - tar:=null;sour:=null; - utar:=0;usour:=null; - a:=0;a':=0;u:=null; - if ring P === E then( - D = (degrees basis P)_1; - Ds = sort apply(D, d->(sum d,d)); - minP := min(Ds/first); - maxP := max(Ds/first); - if o.LengthLimit =!= null then maxP=min(maxP,minP+1+o.LengthLimit); - freeModuleDegs := hashTable apply(toList(minP..maxP), i-> - (-i=>select(Ds,d-> d_0 == i)/last) + D := null; + Ds := null; + freeModuleDegs := null; + tar := null; + sour := null; + utar := 0; + usour := null; + a := 0; + a':= 0; + u := null; + modules := new MutableHashTable; + maps := null; + if ring P === E then ( + D = (degrees basis P)_1; + Ds = sort apply(D, d->(sum d,d)); + minP := min(Ds/first); + maxP := max(Ds/first); + if o.LengthLimit =!= null then + maxP = min(maxP,minP+1+o.LengthLimit); + freeModuleDegs = hashTable apply(toList(minP..maxP), + i -> (-i => select(Ds, d-> d_0 == i)/last) ); - LP := new ChainComplex; - LP.ring = S; ---define the modules as direct sums, with one degree per summand - scan(toList(minP..maxP), i-> - LP#(-i) = directSum apply(unique freeModuleDegs#(-i), d -> - S^(select(freeModuleDegs#(-i), k-> d ==k)))); ---define the maps - u = L->unique degrees L; - scan(toList(min LP+1..max LP), k->( - tar = LP_(k-1); - sour = LP_k; - utar = u tar; - usour = u sour; - LP.dd#k = sum(#utar, i-> - sum(#usour, j->( - a' = -utar_i; - a = -usour_j; - map(tar,sour, - tar_[i]*multMap(P,a',a)*(sour^[j]) - ) - ))) - ) - ); - return LP); - if ring P === S then ( - if o.LengthLimit === null then LengLim := 1+numgens S else - LengLim = o.LengthLimit; + --define the modules as direct sums, with one degree per summand + for i from minP to maxP do + modules#(-i) = directSum apply(unique freeModuleDegs#(-i), + d -> S^(select(freeModuleDegs#(-i), k -> d == k)) + ); + if minP === maxP then return complex(modules#(-minP), Base => -minP); + maps = hashTable for k from -maxP+1 to -minP list k => ( + tar = modules#(k-1); + sour = modules#k; + utar = unique degrees tar; + usour = unique degrees sour; + sum(#utar, i-> + sum(#usour, j->( + a' = -utar_i; + a = -usour_j; + map(tar,sour, + tar_[i]*multMap(P,a',a)*(sour^[j]) + ) + ))) + ); + return complex maps + ); + if ring P =!= S then + error "expected ring created with 'productOfProjectiveSpaces'"; + + -- at this point ring P is S. + LengLim := if o.LengthLimit === null + then 1+numgens S + else o.LengthLimit; M := P/((ideal vars S)^(LengLim+1)); D = (degrees basis M)_1; Ds = sort apply(D, d->(sum d,d)); minM := min(Ds/first); maxM := max(Ds/first); --- (maxM - minM) - freeModuleDegs = hashTable apply(toList(minM..maxM), i-> - (-i=>select(Ds,d-> d_0 == i)/last) - ); - RM := new ChainComplex; - RM.ring = E; ---define the modules as direct sums, with one degree per summand - scan(toList(minM..maxM), i-> - RM#(-i) = directSum apply(unique freeModuleDegs#(-i), d -> - E^(select(freeModuleDegs#(-i), k-> d ==k)))); ---define the maps - u = L->unique degrees L; - scan(toList(min RM+1..max RM), k->( - tar = RM_(k-1); - sour = RM_k; - utar = u tar; - usour = u sour; - RM.dd#k = sum(#utar, i-> - sum(#usour, j->( - a' = -utar_i; - a = -usour_j; - map(tar,sour, - tar_[i]*multMapE(M,a',a)*(sour^[j]) - ) - ))) - ) - ); - return RM)) - - + freeModuleDegs = hashTable apply(toList(minM..maxM), + i -> (-i => select(Ds, d-> d_0 == i)/last) + ); + for i from minM to maxM do + modules#(-i) = directSum apply(unique freeModuleDegs#(-i), + d -> E^(select(freeModuleDegs#(-i), k -> d == k)) + ); + if minM === maxM then return complex(modules#(-minM), Base => -minM); + maps = hashTable for k from -maxM+1 to -minM list k => ( + tar = modules#(k-1); + sour = modules#k; + utar = unique degrees tar; + usour = unique degrees sour; + sum(#utar, i-> + sum(#usour, j->( + a' = -utar_i; + a = -usour_j; + map(tar,sour, + tar_[i]*multMapE(M,a',a)*(sour^[j]) + ) + ))) + ); + complex maps + ) /// restart @@ -2386,9 +2479,8 @@ ring (sour^[j]) isDegreeZeroSurjection := method() isDegreeZeroSurjection(Module,Module) := (A,B)->( --tests a random degree 0 map to see whether its a surjection - z := degree 0_A; - H := Hom(A, B, DegreeLimit => z); - B0 := basis(z, H); + H := Hom(A,B); + B0 := basis(0,H); -- this seems to be total degree 0 in case of degreeLength>1 f := homomorphism(B0*random(source B0, (ring B0)^1)); coker f == 0) @@ -2495,11 +2587,14 @@ directImageComplex(Module,List) := opts -> (M,I) -> ( --print cohomologyMatrix(sT,low,high); sTW := removeZeroTrailingTerms beilinsonWindow sT; --print betti sTW; - mi := min sTW; ma:=max sTW; - W1 := new ChainComplex; - W1.ring = E1; - apply(toList(mi..ma),i-> W1_i = E1^(-apply(degrees sTW_i,d->d_I))); - apply(toList(mi+1..ma),i->W1.dd_i = map(W1_(i-1),W1_i,phi(sTW.dd_i))); + mi := min sTW; + ma := max sTW; + modules := hashTable for i from mi to ma list + i => E1^(-apply(degrees sTW_i,d->d_I)); + maps := hashTable for i from mi+1 to ma list + i => map(modules#(i-1),modules#i,phi(sTW.dd_i)); + W1 := if mi === ma then complex modules#mi + else complex maps; beilinson (W1, opts) ) @@ -2632,7 +2727,7 @@ directImageAction(Ideal, Module) := (I,M)->( T:=tateResolution(Mgraph,-{n+2,d+2},{n+2,d+2}); sT:=removeZeroTrailingTerms strand(T,toList(2:0),{0}); mi := min sT; ma:=max sT; - W := new ChainComplex; + W := new Complex; W.ring = E1; apply(toList(mi..ma),i-> W_i = E1^(-apply(degrees sT_i,d->d_{1}))); apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,signChange projOnEs sT.dd_i)); @@ -2662,7 +2757,7 @@ directImageAction(Ideal, Module) := (I,M)->( sT10 := removeZeroTrailingTerms strand(T10,toList(2:0),{0}); mi = min sT10; ma=max sT10; - W10 := new ChainComplex; + W10 := new Complex; W10.ring = E1; apply(toList(mi..ma),i-> W10_i = E1^(-apply(degrees sT10_i,d->d_{1}))); apply(toList(mi+1..ma),i->W10.dd_i = map(W10_(i-1),W10_i,projOnEs(sT10.dd_i))); @@ -2687,7 +2782,7 @@ directImageAction(Ideal, Module) := (I,M)->( *- inverseQIsoFromTate=method() -inverseQIsoFromTate(ChainComplex) := (W)->( +inverseQIsoFromTate(Complex) := (W)->( -- Input: W, a Tate resolution on a single projective space -- W should be big enough so that the corresponding S-complex is exact @@ -2716,25 +2811,53 @@ inverseQIsoFromTate(ChainComplex) := (W)->( R := ring B'; kk := coefficientRing R; B'' := beilinson (W**E^{1}, BundleType=>DummyQuotientBundle) ** R^{1}; --- assert(B'' == coker phi); + assert(B'' == coker phi); psi := map(B'', B, id_B); -- computing pseudoinverse - mapsB := for i from (min B)+1 to max B list sub(B.dd_i,kk); -- reduction to the vector space maps - tempB := chainComplexFromData{min B, max B, mapsB}; + tempB := complex hashTable for i from min B + 1 to max B list i => sub(B.dd_i, kk); + pinvtempB := pseudoInverse tempB; + assert(arePseudoInverses(tempB, pinvtempB)); Binv:= (pseudoInverse tempB)**R; -- takes too much time in several cases --- assert(arePseudoInverses(tempB, Binv)); --- splittingMapsFromB:= for i from min B to max B list psi_(i+1)*Binv.dd_(-i); --- mapFromB:=map(B''[1],B,i->splittingMapsFromB_(i-min B)); - - splittingMapsFromB':= for i from min B to max B list psi_(i+1)*Binv.dd_(-i)*phi_i; - qIs:= map(B''[1],B',i->splittingMapsFromB'_(i-min B)); - --- (B',B,B'',mapFromB,qIs) + qIs := map(B''[1], B', i -> psi_(i+1)*Binv.dd_(-i)*map(target phi_i, B'_i, phi_i)); + assert(isQuism qIs); qIs ) +/// +-- to be removed: testing maps in above qIs +i = 0 +B'_0 +source(psi_(i+1)*Binv.dd_(-i)*phi_i) +target psi_1 +source psi_1 +target Binv.dd_(0) +source Binv.dd_(0) +target phi_0 +source phi_0 +(B''[1])_0 +target(psi_(i+1)*Binv.dd_(-i)*phi_i) + +i = -1 +assert(target(psi_(i+1)) === (B''[1])_i) +assert(target(Binv.dd_(-i)) === source psi_(i+1)) +assert(source(Binv.dd_(-i)) === target phi_i) +assert(source phi_i === B'_i) -- FAILS + +i = 0 +assert(target(psi_(i+1)) === (B''[1])_i) +assert(target(Binv.dd_(-i)) === source psi_(i+1)) +assert(source(Binv.dd_(-i)) === target phi_i) +assert(source phi_i === B'_i) -- fails + +i = 1 +assert(target(psi_(i+1)) === (B''[1])_i) +assert(target(Binv.dd_(-i)) === source psi_(i+1)) +assert(source(Binv.dd_(-i)) === target phi_i) +assert(source phi_i === B'_i) +/// + -- tateOfDirectImage=method() @@ -2802,12 +2925,14 @@ tateOfDirectImage(Ideal,Module,Matrix) := (I,M,phi) -> ( sT:=removeZeroTrailingTerms strand(T,toList(2:0),{0}); -- sTW := removeZeroTrailingTerms beilinsonWindow sT; --print betti sTW; - mi := min sT; ma:=max sT; - W1 := new ChainComplex; - W1.ring = E1; - apply(toList(mi..ma),i-> W1_i = E1^(-apply(degrees sT_i,d->d_1))); - apply(toList(mi+1..ma),i->W1.dd_i = map(W1_(i-1),W1_i,psi(sT.dd_i))); - + mi := min sT; + ma := max sT; + modules := hashTable for i from mi to ma list + i => E1^(-apply(degrees sT_i,d->d_1)); + maps := hashTable for i from mi+1 to ma list + i => map(modules#(i-1),modules#i,psi(sT.dd_i)); + W1 := if mi === ma then complex modules#mi + else complex maps; IY:=saturate intersect apply(rank target phi, i->ker map(R, S1, phi^{i})); (IY,W1) @@ -2822,7 +2947,7 @@ J=minors(2,m) dim J, degree J s=2, d=-2 N=symmetricPower(s, coker m)**R^{d} -betti res N +betti freeResolution N ann N == J phi=transpose m RphiN=time directImageComplex(J,N,phi); @@ -2844,7 +2969,7 @@ N=symmetricPower(s, coker m)**R^{d} RphiN=time directImageComplex(J,N,phi); netList apply(toList(min RphiN.. max RphiN),i-> - {-i, saturate annihilator HH^(-i) RphiN,betti res HH^(-i) RphiN}) + {-i, saturate annihilator HH^(-i) RphiN,betti freeResolution HH^(-i) RphiN}) R0=prune HH^0 RphiN dim R0, degree R0 @@ -2863,7 +2988,7 @@ dual dual R0 /// actionOnDirectImage=method() -actionOnDirectImage(Ideal,ChainComplex) := (I,T) -> ( +actionOnDirectImage(Ideal,Complex) := (I,T) -> ( -- Input : T, the Tate resolution of a sheaf, or a complex on a projective variety Y in P^n of dim. d -- I, the ideal of Y (annihilating the sheaves appear in HH beilinson T) -- Output : retTable, a Hashtable whose keys are the cohomology indices i where R^i survives; @@ -2903,11 +3028,19 @@ actionOnDirectImage(Ideal,ChainComplex) := (I,T) -> ( --------- -- construct a complex representing R(pi \circ f) - mi := min T; ma:=max T; - W := new ChainComplex; - W.ring = E1; - apply(toList(mi..ma),i-> W_i = E1^(-apply(degrees T_i,d->d_{0}))); - apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE T.dd_i)); + -- mi := min T; ma:=max T; + -- W := new Complex; + -- W.ring = E1; + -- apply(toList(mi..ma),i-> W_i = E1^(-apply(degrees T_i,d->d_{0}))); + -- apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE T.dd_i)); + + (mi, ma) := concentration T; + modulesW := hashTable for i from mi to ma list + i => E1^(-apply(degrees T_i, d -> d_{0})); + mapsW := hashTable for i from mi+1 to ma list + i => map(modulesW#(i-1), modulesW#i, projOnE T.dd_i); + W := if mi === ma then complex {modulesW#mi} + else complex mapsW; BW := beilinson (W, BundleType=>DummyQuotientBundle); regW:=max apply(min HH BW..max HH BW, i->regularity HH_i BW); @@ -2916,7 +3049,7 @@ actionOnDirectImage(Ideal,ChainComplex) := (I,T) -> ( -- Need to extract linear submatrices of T (w.r.t. its degree) r:=max(n+1, regW); - Tlin := (dual res (coker transpose (T[ma]).dd_0, LengthLimit=>ma+r+1))[-ma]; + Tlin := (dual freeResolution (coker transpose (T[ma]).dd_0, LengthLimit=>ma+r+1))[-ma]; -- need : separate every strand of Tlin; by reading off the linear submatrices -- CAUTION : Tlin might be slightly different from T (choice of bases issue) @@ -2924,18 +3057,34 @@ actionOnDirectImage(Ideal,ChainComplex) := (I,T) -> ( linSubmatrix:=i->submatrixByDegrees(Tlin.dd_(-r), -r-1+i,-r+i); -- linear submatrix corresponding to R^i actionOnEachStrand:=linSub->( - sT := res (coker ((-1)^(r+1)*linSub), LengthLimit=>ma+r+1)[r+1]; + sT := freeResolution (coker ((-1)^(r+1)*linSub), LengthLimit=>ma+r+1)[r+1]; sT' := (sT**E^{-1})[-1]; - sW:= new ChainComplex; - sW.ring = E1; - apply(toList(min sT..max sT),i->sW_i=E1^(-apply(degrees sT_i,d->d_{0}))); - apply(toList(min sT+1..max sT),i->sW.dd_i=map(sW_(i-1),sW_i,projOnE sT.dd_i)); - - sW' := new ChainComplex; - sW'.ring = E1; - apply(toList(min sT'..max sT'),i->sW'_i=E1^(-apply(degrees sT'_i,d->d_{0}))); - apply(toList(min sT'+1..max sT'),i->sW'.dd_i=map(sW'_(i-1),sW'_i,projOnE sT'.dd_i)); + -- sW:= new Complex; + -- sW.ring = E1; + -- apply(toList(min sT..max sT),i->sW_i=E1^(-apply(degrees sT_i,d->d_{0}))); + -- apply(toList(min sT+1..max sT),i->sW.dd_i=map(sW_(i-1),sW_i,projOnE sT.dd_i)); + + (minsT, maxsT) := concentration sT; + sWmodules := hashTable for i from minsT to maxsT list + i => E1^(-apply(degrees sT_i, d -> d_{0})); + sWmaps := hashTable for i from minsT+1 to maxsT list + i => map(sWmodules#(i-1), sWmodules#i, projOnE sT.dd_i); + sW := if minsT === maxsT then complex {sWmodules#minsT} + else complex sWmaps; + + -- sW' := new Complex; + -- sW'.ring = E1; + -- apply(toList(min sT'..max sT'),i->sW'_i=E1^(-apply(degrees sT'_i,d->d_{0}))); + -- apply(toList(min sT'+1..max sT'),i->sW'.dd_i=map(sW'_(i-1),sW'_i,projOnE sT'.dd_i)); + + (minsT', maxsT') := concentration sT'; + sW'modules := hashTable for i from minsT' to maxsT' list + i => E1^(-apply(degrees sT'_i, d -> d_{0})); + sW'maps := hashTable for i from minsT'+1 to maxsT' list + i => map(sW'modules#(i-1), sW'modules#i, projOnE sT'.dd_i); + sW' := if minsT' === maxsT' then complex {sW'modules#minsT'} + else complex sW'maps; multMapOnW := j->( contrMap:=map((sT'[-r])_0, (sT[-r])_0, contract(E_j,sT.dd_(-r))); @@ -3057,13 +3206,13 @@ actionOnDirectImage(Ideal, Module) := (I,M)->( -- construct a projected complex of E1-modules mi := min C; ma:=max C; - W := new ChainComplex; + W := new Complex; W.ring = E1; apply(toList(mi..ma),i-> W_i = E1^(-apply(degrees C_i,d->d_{0}))); apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE C.dd_i)); mi = min C'; ma=max C'; - W' := new ChainComplex; + W' := new Complex; W'.ring = E1; apply(toList(mi..ma),i-> W'_i = E1^(-apply(degrees C'_i,d->d_{0}))); apply(toList(mi+1..ma),i->W'.dd_i = map(W'_(i-1),W'_i,projOnE C'.dd_i)); @@ -3219,7 +3368,7 @@ m=matrix{{x_0,x_3,x_2},{x_3,x_2,x_1}} I=minors(2,m) -- M=symmetricPower(2,coker m) ** R^{1} M=(R^1/I)**R^{2} -- O(2) of a twisted cubic -betti res M +betti freeResolution M RM=time directImageComplex(I,M,matrix id_R); time prune HH RM @@ -3240,7 +3389,7 @@ R=kk[x_0..x_3] m=matrix{{x_0,x_3,x_2},{x_3,x_2,x_1}} I=minors(2,m) M=directSum((R^1/I)**R^{1},(R^1/I)**R^{-3}) -betti res M +betti freeResolution M time betti (RM=directImageComplex(I,M,matrix id_R)); -- 11.94 seconds time prune HH RM @@ -3363,7 +3512,7 @@ dim I, degree I, genera I --M=symmetricPower(2,coker m) -- Ulrich M=(R^1/I)**R^{1} --M=(R^1/I)**R^{2} -- too high computational cost -betti res M +betti freeResolution M AA=time actionOnDirectImage(I,M); keys AA @@ -3420,29 +3569,38 @@ betti (L=prune pushForward(phi,LCC)) -- Take a truncation so that the resolution becomes linear Ltr = (truncate ({2,2},L))**S^{{2,2}}; -betti res Ltr +betti freeResolution Ltr Q=symExt(presentation Ltr, E); -T=time (res (coker Q,LengthLimit=>12))**E^{{2,2}}[4]; +T=time ((freeResolution (coker Q,LengthLimit=>12))**E^{{2,2}})[4]; cohomologyMatrix (T, -{5,5},{3,3}) sT=strand(T,{0,0},{0}); -- strand associated to Rpi_{*}L, where pi:C \times C \to C is the 2nd projection cohomologyMatrix (sT, -{5,5},{3,3}) betti sT -sTFull=new ChainComplex; -sTFull.ring = ring sT +-- sTFull=new Complex; +-- sTFull.ring = ring sT ma=7; -sTFull=(dual res (coker transpose (sT[ma]).dd_0, LengthLimit=>2*ma))[-ma]; +sTFull=(dual freeResolution (coker transpose (sT[ma]).dd_0, LengthLimit=>2*ma))[-ma]; betti sTFull -- a Tate resolution for Rpi_{*}L, consisted of two strands -- note that the boundary maps of sTFull are not exactly same as the maps of sT (basis choice issue) (S',E')=productOfProjectiveSpaces({2},CoefficientField=>kk); projOnE=map(E', E, toList(3:0)|(gens E')); -mi=min sTFull; ma=max sTFull; -W=new ChainComplex; -W.ring = E'; -apply(toList(mi..ma),i-> W_i = E'^(-apply(degrees sTFull_i,d->d_{1}))); -apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE sTFull.dd_i)); +--mi=min sTFull; ma=max sTFull; + +(mi, ma) = concentration sTFull; +Wmodules = hashTable for i from mi to ma list + i => E'^(-apply(degrees sTFull_i, d -> d_{1})); +Wmaps = hashTable for i from mi+1 to ma list + i => map(Wmodules#(i-1), Wmodules#i, projOnE sTFull.dd_i); +W = if mi === ma then complex {Wmodules#mi} + else complex Wmaps; + +-- W=new Complex; +-- W.ring = E'; +-- apply(toList(mi..ma),i-> W_i = E'^(-apply(degrees sTFull_i,d->d_{1}))); +-- apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE sTFull.dd_i)); betti W @@ -3465,11 +3623,11 @@ R0piL = prune HH^0 beilinson W R1piL = prune HH^1 beilinson W degree R0piL -betti res R0piL +betti freeResolution R0piL primaryDecomposition ann R0piL degree R1piL -betti res R1piL +betti freeResolution R1piL primaryDecomposition ann R1piL R0=prune HH^0 source AA#0#0 @@ -3519,31 +3677,42 @@ betti (L=prune (I1*(prune pushForward(phi,temp)))) -- O(D-P*C) --betti (L=prune pushForward(phi,temp)) Ltr = (truncate ({2,2},L))**S^{{2,2}}; -betti res Ltr +betti freeResolution Ltr Q=symExt(presentation Ltr, E); -T=(res (coker Q,LengthLimit=>14))**E^{{2,2}}[4]; +T=((freeResolution (coker Q,LengthLimit=>14))**E^{{2,2}})[4]; cohomologyMatrix (T, -{5,5},{5,5}) sT=strand(T,{0,0},{0}); -- strand associated to Rpi_{*}L, where pi:C \times C \to C is the 2nd projection cohomologyMatrix (sT, -{5,5},{5,5}) betti sT -sTFull=new ChainComplex; -sTFull.ring = ring sT +-- sTFull=new Complex; +-- sTFull.ring = ring sT ma=7; -sTFull=(dual res (coker transpose (sT[ma]).dd_0, LengthLimit=>2*ma))[-ma]; +sTFull=(dual freeResolution (coker transpose (sT[ma]).dd_0, LengthLimit=>2*ma))[-ma]; betti sTFull -- a Tate resolution for Rpi_{*}L, consisted of two strands -- note that the boundary maps of sTFull are not exactly same as the maps of sT (basis choice issue) (S',E')=productOfProjectiveSpaces({2},CoefficientField=>kk); projOnE=map(E', E, toList(3:0)|(gens E')); -mi=min sTFull; ma=max sTFull; -W=new ChainComplex; -W.ring = E'; -apply(toList(mi..ma),i-> W_i = E'^(-apply(degrees sTFull_i,d->d_{1}))); -apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE sTFull.dd_i)); + +-- mi=min sTFull; ma=max sTFull; +-- W=new Complex; +-- W.ring = E'; +-- apply(toList(mi..ma),i-> W_i = E'^(-apply(degrees sTFull_i,d->d_{1}))); +-- apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE sTFull.dd_i)); + +(mi, ma) = concentration sTFull; +Wmodules = hashTable for i from mi to ma list + i => E'^(-apply(degrees sTFull_i, d -> d_{1})); +Wmaps = hashTable for i from mi+1 to ma list + i => map(Wmodules#(i-1), Wmodules#i, projOnE sTFull.dd_i); +W = if mi === ma then complex {Wmodules#mi} + else complex Wmaps; + betti W + IC=ideal(S'_0^3+S'_1^3+S'_2^3); AA=actionOnDirectImage(W,IC); keys AA -- Only R^1 survives @@ -3694,7 +3863,7 @@ composedFunctions = () -> ( cohomologyMatrix(beilinsonWindow T,low, high) B = beilinson T d={2,2} - T1=T**E^{d}[sum d] + T1=(T**E^{d})[sum d] cohomologyMatrix(beilinsonWindow T1,low,high) B1 =beilinson T1 decompose annihilator HH^1 B1 @@ -3707,7 +3876,7 @@ composedFunctions = () -> ( -- Another shift: --Example d={-1,-2} - T2=T**E^{d}[sum d] + T2=(T**E^{d})[sum d] cohomologyMatrix(beilinsonWindow T2,low,high) cohomologyMatrix(T,low,high) B2 =beilinson T2 @@ -3730,7 +3899,7 @@ high=3*n, low=-high -- the example 4.1 from the paper S.?TateData E.?TateData -T1=(dual res(coker gens trim (ideal vars E)^2,LengthLimit=>11))[1] +T1=(dual freeResolution(coker gens trim (ideal vars E)^2,LengthLimit=>11))[1] (ring T1).?TateData @@ -3739,7 +3908,7 @@ cohomologyMatrix(T1,low,high) c={4,4} betti(uc= upperCorner(T1,c)) ring uc -T=res(coker uc,LengthLimit=>12)[sum c] +T=freeResolution(coker uc,LengthLimit=>12)[sum c] betti T ring T cohomologyMatrix(T,2*low,2*high) @@ -3951,7 +4120,7 @@ doc /// betti P LP=bgg P M = (HH^0 LP)**S^{-n} - betti res M + betti freeResolution M T = tateResolution(M,low,high) cohomologyMatrix(T,low,high) Text @@ -4077,7 +4246,7 @@ doc /// cohomologyMatrix(beilinsonWindow T,low, high) B = beilinson T d={2,2} - T1=T**E^{d}[sum d] + T1=(T**E^{d})[sum d] cohomologyMatrix(beilinsonWindow T1,low,high) B1 =beilinson T1 decompose annihilator HH^1 B1 @@ -4090,7 +4259,7 @@ doc /// Another shift: Example d={-1,-2} - T2=T**E^{d}[sum d] + T2=(T**E^{d})[sum d] cohomologyMatrix(beilinsonWindow T2,low,high) cohomologyMatrix(T,low,high) B2 =beilinson T2 @@ -4114,7 +4283,7 @@ doc /// Key coarseMultigradedRegularity (coarseMultigradedRegularity, Module) - (coarseMultigradedRegularity, ChainComplex) + (coarseMultigradedRegularity, Complex) [coarseMultigradedRegularity, Strategy] Headline A truncation that has linear resolution @@ -4123,7 +4292,7 @@ doc /// Inputs M:Module multi-graded module over a multi-graded polynomial ring - M:ChainComplex + M:Complex multi-graded module over a multi-graded polynomial ring Strategy => String Outputs @@ -4137,8 +4306,8 @@ doc /// I = ideal(x_(0,0)^2,x_(1,0)^3,x_(2,0)^4) R = coarseMultigradedRegularity(S^1/I) N = truncate(R,S^1/I); - betti res N - netList toList tallyDegrees res N + betti freeResolution N + netList toList tallyDegrees freeResolution N Text See the proof of Proposition 2.7 in @ HREF("https://arxiv.org/abs/1411.5724","Tate Resolutions on Products of Projective Spaces")@. @@ -4198,12 +4367,6 @@ doc /// peek S.TateData /// -doc /// - Key - InitialDegree - Headline - Option for chainComplexMap -/// doc /// Key CoefficientField @@ -4323,13 +4486,13 @@ doc /// doc /// Key upperCorner - (upperCorner,ChainComplex,List) + (upperCorner,Complex,List) Headline compute the upper corner Usage m=upperCorner(F,d) Inputs - F: ChainComplex + F: Complex over the exterior algebra d: List a (multi)-degree @@ -4349,14 +4512,14 @@ doc /// n={1,2}; (S,E) = productOfProjectiveSpaces n - F=dual res((ker transpose vars E)**E^{{ 2,3}},LengthLimit=>4) + F=dual freeResolution((ker transpose vars E)**E^{{ 2,3}},LengthLimit=>4) cohomologyMatrix(F,-{3,3},{4,4}) betti F tallyDegrees F deg={2,1} m=upperCorner(F,deg); tally degrees target m, tally degrees source m - Fm=(res(coker m,LengthLimit=>4))[sum deg+1] + Fm=(freeResolution(coker m,LengthLimit=>4))[sum deg+1] betti Fm cohomologyMatrix(Fm,-{3,3},{4,4}) /// @@ -4366,13 +4529,13 @@ doc /// doc /// Key lowerCorner - (lowerCorner,ChainComplex,List) + (lowerCorner,Complex,List) Headline compute the lower corner Usage m=lowerCorner(F,d) Inputs - F: ChainComplex + F: Complex over the exterior algebra d: List a (multi)-degree @@ -4387,13 +4550,13 @@ doc /// Example n={1,2}; (S,E) = productOfProjectiveSpaces n - F=dual res((ker transpose vars E)**E^{{ 2,3}},LengthLimit=>4) + F=dual freeResolution((ker transpose vars E)**E^{{ 2,3}},LengthLimit=>4) betti F tallyDegrees F deg={2,1} m=lowerCorner(F,deg); tally degrees target m, tally degrees source m - Fm=(res(coker m,LengthLimit=>7))[sum deg] + Fm=(freeResolution(coker m,LengthLimit=>7))[sum deg] betti Fm cohomologyMatrix(Fm,-{3,3},{4,4}) /// @@ -4407,14 +4570,14 @@ doc /// Key cohomologyMatrix (cohomologyMatrix, Module, List, List) - (cohomologyMatrix,ChainComplex,List,List) + (cohomologyMatrix,Complex,List,List) Headline cohomology groups of a sheaf on P^{n_1}xP^{n_2}, or of (part) of a Tate resolution Usage H=cohomologyMatrix(M,low,high) H=cohomologyMatrix(T,low,high) Inputs - T: ChainComplex + T: Complex free complex over the exterior algebra M: Module graded module representing a sheaf on a product of projective spaces @@ -4479,7 +4642,7 @@ doc /// Key cohomologyHashTable (cohomologyHashTable,Module,List,List) - (cohomologyHashTable,ChainComplex,List,List) + (cohomologyHashTable,Complex,List,List) Headline cohomology groups of a sheaf on a product of projective spaces, or of (part) of a Tate resolution Usage @@ -4488,7 +4651,7 @@ doc /// Inputs M: Module graded module representing a sheaf on a product of projective spaces - T: ChainComplex + T: Complex free complex over the exterior algebra low: List high: List @@ -4556,7 +4719,7 @@ doc /// Key eulerPolynomialTable (eulerPolynomialTable,Module,List,List) - (eulerPolynomialTable,ChainComplex,List,List) + (eulerPolynomialTable,Complex,List,List) (eulerPolynomialTable,HashTable) Headline cohomology groups of a sheaf on a product of projective spaces, or of (part) of a Tate resolution @@ -4568,7 +4731,7 @@ doc /// Inputs M: Module graded module representing a sheaf on a product of projective spaces - T: ChainComplex + T: Complex free complex over the exterior algebra H': HashTable output of cohomologyHashTable low: List @@ -4636,13 +4799,13 @@ doc /// doc /// Key tallyDegrees - (tallyDegrees,ChainComplex) + (tallyDegrees,Complex) Headline collect the degrees of the generators of the terms in a free complex Usage tallyDegrees C Inputs - C: ChainComplex + C: Complex a complex of graded free modules Outputs : Sequence @@ -4652,7 +4815,7 @@ doc /// Returns for each free module C_d in the complex the result of @ TO tally @ @ TO degrees @ C_d Example S=ZZ/101[x_0..x_1,y_0,z_0,Degrees=>{2:{2,0,0},1:{0,1,0},{0,0,1}}] - C =res ideal vars S + C =freeResolution ideal vars S betti C tallyDegrees C /// @@ -4660,18 +4823,18 @@ doc /// doc /// Key trivialHomologicalTruncation - (trivialHomologicalTruncation,ChainComplex,ZZ,ZZ) + (trivialHomologicalTruncation,Complex,ZZ,ZZ) Headline return the trivial truncation of a chain complex Usage - trivialHomologicalTruncation(ChainComplex,d,e) + trivialHomologicalTruncation(Complex,d,e) Inputs - C: ChainComplex + C: Complex d: ZZ e: ZZ homological indices Outputs - : ChainComplex + : Complex Description Text Given a chain complex @@ -4682,8 +4845,9 @@ doc /// 0 <- C_d <- C_{d+1} <- ... < C_e <- 0 Example - E=ZZ/101[e_0,e_1,SkewCommutative=>true];F=res ideal vars E; - C=dual res (coker transpose F.dd_3,LengthLimit=>8)[-3] + E=ZZ/101[e_0,e_1,SkewCommutative=>true]; + F=freeResolution(ideal vars E, LengthLimit => 5); + C=dual freeResolution (coker transpose F.dd_3,LengthLimit=>8)[-3] C1=trivialHomologicalTruncation(C,-2,2) C2=trivialHomologicalTruncation(C1,-3,3) C3=trivialHomologicalTruncation(C2,2,2) @@ -4696,14 +4860,14 @@ doc /// doc /// Key nonzeroMax - (nonzeroMax,ChainComplex) + (nonzeroMax,Complex) Headline - computes the homological position of the last non-zero module in a ChainComplex + computes the homological position of the last non-zero module in a Complex Usage nonzeroMin C nonzeroMax C Inputs - C: ChainComplex + C: Complex Outputs : ZZ Description @@ -4714,7 +4878,7 @@ doc /// the largest positions of a non-zero module. Example S=ZZ/101[x,y]/ideal(x*y) - C=chainComplex(matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2})[1] + C=complex{matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2}}[1] C'=prependZeroMap appendZeroMap C min C', nonzeroMin C' max C', nonzeroMax C' @@ -4726,13 +4890,13 @@ doc /// doc /// Key nonzeroMin - (nonzeroMin,ChainComplex) + (nonzeroMin,Complex) Headline - computes the homological position of the first non-zero module in a ChainComplex + computes the homological position of the first non-zero module in a Complex Usage nonzeroMin C Inputs - C: ChainComplex + C: Complex Outputs : ZZ Description @@ -4743,8 +4907,8 @@ doc /// the smallest positions of a non-zero module. Example S=ZZ/101[x,y]/ideal(x*y) - C=chainComplex(matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2})[1] - isChainComplex C + C=complex{matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2}}[1] + isComplex C C'=prependZeroMap appendZeroMap C min C', nonzeroMin C' max C', nonzeroMax C' @@ -4753,21 +4917,21 @@ doc /// doc /// Key appendZeroMap - (appendZeroMap,ChainComplex) + (appendZeroMap,Complex) Headline append a zero map to chain complex Usage appendZeroMap C Inputs - C: ChainComplex + C: Complex Outputs - : ChainComplex + : Complex Description Text Add a zero map after the last differential in a chain complex. Example S=ZZ/101[x,y]/ideal(x*y) - C=chainComplex(matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2})[1] + C=complex{matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2}}[1] appendZeroMap C prependZeroMap C /// @@ -4775,21 +4939,21 @@ doc /// doc /// Key prependZeroMap - (prependZeroMap,ChainComplex) + (prependZeroMap,Complex) Headline prepend a zero map to chain complex Usage prependZeroMap C Inputs - C: ChainComplex + C: Complex Outputs - : ChainComplex + : Complex Description Text Add a zero map before the first differential in a chain complex. Example S=ZZ/101[x,y]/ideal(x*y) - C=chainComplex(matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2})[1] + C=complex{matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2}}[1] prependZeroMap C appendZeroMap C /// @@ -4797,27 +4961,27 @@ doc /// doc /// Key removeZeroTrailingTerms - (removeZeroTrailingTerms,ChainComplex) + (removeZeroTrailingTerms,Complex) Headline remove trailing zero terms of a chain complex Usage removeZeroTrailingTerms C Inputs - C: ChainComplex + C: Complex Outputs - : ChainComplex + : Complex Description Text Remove trailing zero terms in a complex Example S=ZZ/101[x,y]/ideal(x*y) - C=prependZeroMap appendZeroMap chainComplex(matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2})[1] + C=prependZeroMap appendZeroMap complex{matrix{{x}},matrix{{y}}**S^{ -1},matrix{{x}}**S^{ -2}}[1] removeZeroTrailingTerms C Text If C has only one nonzero term, then the functions returns two zero maps. Example S=ZZ - C=prependZeroMap chainComplex( map(S^0,S^1,0))[3] + C=prependZeroMap complex{map(S^0,S^1,0)}[3] removeZeroTrailingTerms C /// @@ -4840,21 +5004,21 @@ doc /// checks that all differentials compose to zero. Example S=ZZ/101[x,y] - C=res ideal vars S, C'=chainComplex(matrix{{x}},matrix{{y}}) + C=freeResolution ideal vars S, C'=complex{matrix{{x}},matrix{{y}}} isChainComplex C, isChainComplex C' Text The buildin function @ TO dual @ - for chainComplexes over the exterior algebra + for complexes over the exterior algebra does not return a complex, because the dual of a left module is a right module. Example kk=ZZ/101;n=4; E=kk[e_0..e_n,SkewCommutative =>true] m=map(E^{0,1},,matrix{{ e_0,e_1*e_2},{e_3*e_4,e_0*e_1*e_4}}) - fm=res coker m + fm=freeResolution(coker m, LengthLimit => 5) isChainComplex fm dualfm = dual fm isChainComplex dualfm - f2=res( coker dualfm.dd_(-5),LengthLimit=> 6)[6] + f2=freeResolution( coker dualfm.dd_(-5),LengthLimit=> 6)[6] betti f2 betti dual fm /// @@ -4863,16 +5027,16 @@ doc /// doc /// Key minimize - (minimize, ChainComplex) + (minimize, Complex) Headline - minimal quotient complex of a free ChainComplex + minimal quotient complex of a free Complex Usage m = minimize F Inputs - F:ChainComplex + F:Complex chain complex of free modules Outputs - m:ChainComplexMap + m:ComplexMap quasi-isomorphism F -> F', where F' is a minimal free complex Description Text @@ -4888,11 +5052,11 @@ doc /// the map rho is not a chain complex map, but the image of (rho | d*rho): C ++ C[1] --> C is a subcomplex and the minimization of C is the complex C/image(rho|d*rho). - The script returns the ChainComplexMap from the minimization to C. + The script returns the ComplexMap from the minimization to C. Example S=ZZ/101[x,y]; m= map(S^{0,1},S^{0,-1}, matrix{{1,x},{y,x^2}}) - C=chainComplex{m} + C=complex{m} Cmin=target minimize C betti C, betti Cmin m, Cmin.dd_1 @@ -4912,8 +5076,8 @@ doc /// Q = apply(len, i-> random(target difs0_i, target difs0_i))| {random(source difs0_(len-1), source difs0_(len-1))}; difs1 = apply(len, i-> Q_i*difs0_i*Q_(i+1)^(-1)); - E = chainComplex difs1 - isMinimalChainComplex E + E = complex difs1 + isMinimalComplex E Text Now we minimize the result. The free summand we added to the end maps to zero, and thus is part of the minimization. @@ -4923,7 +5087,7 @@ doc /// E[1] == source m E' = target m isChainComplex E' - isMinimalChainComplex E' + isMinimalComplex E' /// *- @@ -4935,20 +5099,20 @@ doc /// doc /// Key regionComplex - (regionComplex,ChainComplex,List,Sequence) + (regionComplex,Complex,List,Sequence) Headline region complex Usage regionComplex(T,c,IJK) Inputs - T: ChainComplex + T: Complex over the exterior algebra c: List a (multi) degree IJK: Sequence a sequence (I,J,K) of disjoint subsets of \{0..t-1\} Outputs - : ChainComplex + : Complex a region complex of T Description Text @@ -4959,8 +5123,9 @@ doc /// Example n={1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; - a=-{2,2};T2=T1**E^{a}[sum a]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 5))[1]; + a=-{2,2}; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2,cohomologyMatrix(W,-2*n,2*n) T=tateExtension W; cohomologyMatrix(T,-{3,3},{3,3}) @@ -4991,20 +5156,20 @@ doc /// doc /// Key strand - (strand,ChainComplex,List,List) + (strand,Complex,List,List) Headline take the strand Usage strand(T,c,I) Inputs - T: ChainComplex + T: Complex over the exterior algebra c: List a (multi) degree I: List a sublist of \{0..t-1\} , where t denotes the number of factors Outputs - : ChainComplex + : Complex the I-strand of T through c Description Text @@ -5016,8 +5181,9 @@ doc /// Example n={1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; - a=-{2,2};T2=T1**E^{a}[sum a]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 5))[1]; + a=-{2,2}; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2,cohomologyMatrix(W,-2*n,2*n) T=tateExtension W; low = -{2,2};high = {2,2}; @@ -5043,26 +5209,26 @@ doc /// doc /// Key firstQuadrantComplex - (firstQuadrantComplex,ChainComplex,List) + (firstQuadrantComplex,Complex,List) Headline form the first quadrant complex Usage firstQuadrantComplex(T,c) Inputs - T: ChainComplex + T: Complex a (part of a) Tate resolution on a product of t projective spaces c: List cohomological degree of the lower corner of the first complex Outputs - : ChainComplex + : Complex Description Text Form the first quadrant complex with corner c of a (part of a) Tate resolution T as defined in @ HREF("http://arxiv.org/abs/1411.5724","Tate Resolutions on Products of Projective Spaces") @. Example (S,E) = productOfProjectiveSpaces {1,1}; - T1= (dual res( trim (ideal vars E)^2,LengthLimit=>8))[1]; - T=trivialHomologicalTruncation(T2=res(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7],-5,6); + T1= (dual freeResolution( trim (ideal vars E)^2,LengthLimit=>8))[1]; + T=trivialHomologicalTruncation(T2=freeResolution(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7],-5,6); betti T cohomologyMatrix(T,-{4,4},{3,2}) fqT=firstQuadrantComplex(T,-{2,1}); @@ -5086,26 +5252,26 @@ doc /// doc /// Key lastQuadrantComplex - (lastQuadrantComplex,ChainComplex,List) + (lastQuadrantComplex,Complex,List) Headline form the last quadrant complex Usage lastQuadrantComplex(T,c) Inputs - T: ChainComplex + T: Complex a (part of a) Tate resolution on a product of t projective spaces c: List cohomological degree of the lower corner of the complementary first quadrant complex Outputs - : ChainComplex + : Complex Description Text Form the last quadrant complex with corner c of a (part of a) Tate resolution T as defined in @ HREF("http://arxiv.org/abs/1411.5724","Tate Resolutions on Products of Projective Spaces") @. Example (S,E) = productOfProjectiveSpaces {1,1}; - T1= (dual res( trim (ideal vars E)^2,LengthLimit=>8))[1]; - T=trivialHomologicalTruncation(T2=res(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7],-5,6); + T1= (dual freeResolution( trim (ideal vars E)^2,LengthLimit=>8))[1]; + T=trivialHomologicalTruncation(T2=freeResolution(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7],-5,6); betti T cohomologyMatrix(T,-{4,4},{3,2}) fqT=firstQuadrantComplex(T,-{2,1}); @@ -5146,9 +5312,9 @@ doc /// A: Matrix a homomorphism of multi-graded modules from M to N Outputs - T : ChainComplex + T : Complex a bounded free complex over the exterior algebra - phi : ChainComplexMap + phi : ComplexMap an induced map from T(M) to T(N) over the exterior algebra Description Text @@ -5205,7 +5371,7 @@ doc /// doc /// Key cornerComplex - (cornerComplex,ChainComplex,List) + (cornerComplex,Complex,List) (cornerComplex,Module,List,List,List) Headline form the corner complex @@ -5213,7 +5379,7 @@ doc /// C = cornerComplex(T,c) C = cornerComplex(M,c,low,high) Inputs - T: ChainComplex + T: Complex a (part of a) Tate resolution on a product of t projective spaces c: List cohomological degree of upper corner of the last quadrant complex which is part of the corner complex @@ -5224,7 +5390,7 @@ doc /// high:List a multidegree Outputs - C : ChainComplex + C : Complex The corner complex Description Text @@ -5243,8 +5409,8 @@ doc /// Example (S,E) = productOfProjectiveSpaces{1,1} low = {-4,-4};high = {3,2}; - T1= (dual res( trim (ideal vars E)^2,LengthLimit=>8))[1]; - T2=res(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7]; + T1= (dual freeResolution( trim (ideal vars E)^2,LengthLimit=>8))[1]; + T2=freeResolution(coker upperCorner(T1,{4,3}),LengthLimit=>13)[7]; Text Finally, we can define T, the sufficient part of the Tate resolution: @@ -5339,16 +5505,16 @@ doc /// doc /// Key beilinsonWindow - (beilinsonWindow,ChainComplex) + (beilinsonWindow,Complex) Headline extract the subquotient complex which contributes to the Beilinson window Usage W=beilinsonWindow T Inputs - T: ChainComplex + T: Complex a (part of a) Tate resolution on a product of t projective spaces Outputs - W: ChainComplex + W: Complex Description Text Extract the terms which under the U-functor defined in @@ -5358,13 +5524,13 @@ doc /// Example n={1,1}; (S,E) = productOfProjectiveSpaces n; - W=(chainComplex {map(E^0,E^1,0),map(E^1,E^0,0)})[1] + W=(complex {map(E^0,E^1,0),map(E^1,E^0,0)})[1] time T=tateExtension W; cohomologyMatrix(T,-{3,3},{3,3}) W=beilinsonWindow T cohomologyMatrix(W,-{2,2},{2,2}) a={2,-3} - W2=beilinsonWindow (T**E^{a}[sum a]) + W2=beilinsonWindow ((T**E^{a})[sum a]) cohomologyMatrix(W2,-{2,2},{2,2}) cohomologyMatrix(tateExtension W2,-{2,2},{2,2}) SeeAlso @@ -5375,16 +5541,16 @@ doc /// doc /// Key tateExtension - (tateExtension,ChainComplex) + (tateExtension,Complex) Headline extend the terms in the Beilinson window to a part of a corner complex of the corresponding Tate resolution Usage T=tateExtension W Inputs - W: ChainComplex + W: Complex terms in the Beilinson window of a Tate resolution Outputs - T: ChainComplex + T: Complex a corner complex of the corresponding Tate resolution Description Text @@ -5398,9 +5564,9 @@ doc /// Example n={1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 5))[1]; a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2 cohomologyMatrix(W,-2*n,2*n) T=tateExtension W @@ -5420,7 +5586,7 @@ doc /// doc /// Key pushAboveWindow - (pushAboveWindow,ChainComplex) + (pushAboveWindow,Complex) (pushAboveWindow,List) (pushAboveWindow,Matrix) (pushAboveWindow,Matrix,Matrix) @@ -5431,10 +5597,10 @@ doc /// Usage T=pushAboveWindow W Inputs - W: ChainComplex + W: Complex terms in the Beilinson window of a Tate resolution Outputs - T: ChainComplex + T: Complex a non-minimal version of the quadrant complex ?!? qT_{\le 0} of the Tate resolution T=T(W) ? Description Text @@ -5449,10 +5615,10 @@ doc /// Example n={1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 5))[1]; isChainComplex T1 a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2 cohomologyMatrix(W,-2*n,2*n) T=tateExtension W; @@ -5474,7 +5640,7 @@ doc /// beilinson (beilinson,Module) (beilinson,Matrix) - (beilinson,ChainComplex) + (beilinson,Complex) [beilinson,BundleType] Headline apply the beilinson functor @@ -5487,7 +5653,7 @@ doc /// a free module over the exterior algebra E psi: Matrix a map between free modules over E - T: ChainComplex + T: Complex a complex of free modules over E BundleType => Symbol the possible values are described in BundleType @@ -5496,7 +5662,7 @@ doc /// a module over the symmetric algebra S phi: Matrix a map between S-modules - C: ChainComplex + C: Complex a chain complex of S-modules Description Text @@ -5520,7 +5686,7 @@ doc /// psi=random(E^{{-1,0}}, E^{{-2,-1}}) phi=beilinson psi beilinson(E^{{-1,0}}) - T = chainComplex(psi) + T = complex{psi} C = beilinson T betti T SeeAlso @@ -5788,9 +5954,9 @@ doc /// BundleType => Symbol the possible values are described in BundleType Outputs - RpiM: ChainComplex + RpiM: Complex a chain complex of modules over a symmetric algebra - RphiN: ChainComplex + RphiN: Complex a chain complex of modules over the coordinate ring of P^m Description Text @@ -5859,7 +6025,7 @@ doc /// dim J, degree J s=2,d=-2 N=symmetricPower(s,coker m)**R^{d}; - betti res N + betti freeResolution N annihilator N == J phi= transpose m RphiN = directImageComplex(J,N,phi) @@ -5873,7 +6039,7 @@ doc /// RphiN = directImageComplex(J,N,phi) T=ring RphiN netList apply(toList(min RphiN.. max RphiN),i-> - {-i, saturate annihilator HH^(-i) RphiN,betti res HH^(-i) RphiN}) + {-i, saturate annihilator HH^(-i) RphiN,betti freeResolution HH^(-i) RphiN}) R0=prune HH^0 RphiN dim R0, degree R0 betti (sR0Dual = syz transpose presentation R0) @@ -5898,7 +6064,7 @@ doc /// actionOnDirectImage (actionOnDirectImage,Ideal,Module) (actionOnDirectImage,Ideal,Module,Matrix) - (actionOnDirectImage,Ideal,ChainComplex) + (actionOnDirectImage,Ideal,Complex) Headline recover the module structure via a Noether normalization Usage @@ -5917,7 +6083,7 @@ doc /// phi: Matrix a k\times (m+1) matrix of homogeneous polynomials on P^n which define a morphism or rational map phi:X\to P^m, i.e., the 2\times 2 minors of phi vanish on X - T: ChainComplex + T: Complex a (long enough) part of the Tate resolution of some complex of sheaves on Y Outputs retTable: HashTable @@ -6000,25 +6166,25 @@ doc /// phi = map(SX,S,vars SX); betti (L=prune pushForward(phi,LX)) Ltr = (truncate ({2,2},L))**S^{{2,2}}; - betti res Ltr + betti freeResolution Ltr Text We read off (a finite subquotient of) the Tate resolution of Rf_{*}L as follows. Example Q=symExt(presentation Ltr, E); - T=(res (coker Q,LengthLimit=>12))**E^{{2,2}}[4]; + T=((freeResolution (coker Q,LengthLimit=>12))**E^{{2,2}})[4]; cohomologyMatrix (T, -{5,5},{3,3}) sT=strand(T,{0,0},{0}); - sTFull=new ChainComplex; - sTFull.ring = ring sT; ma=6; - sTFull=(dual res (coker transpose (sT[ma]).dd_0, LengthLimit=>2*ma))[-ma]; + sTFull=(dual freeResolution (coker transpose (sT[ma]).dd_0, LengthLimit=>2*ma))[-ma]; (S',E')=productOfProjectiveSpaces({2},CoefficientField=>kk); projOnE=map(E', E, toList(3:0)|(gens E')); - mi=min sTFull; ma=max sTFull; - W=new ChainComplex; W.ring = E'; - apply(toList(mi..ma),i-> W_i = E'^(-apply(degrees sTFull_i,d->d_{1}))); - apply(toList(mi+1..ma),i->W.dd_i = map(W_(i-1),W_i,projOnE sTFull.dd_i)); + (mi, ma) = concentration sTFull; + Wmodules = hashTable for i from mi to ma list + i => E'^(-apply(degrees sTFull_i, d -> d_{1})); + Wmaps = hashTable for i from mi+1 to ma list + i => map(Wmodules#(i-1), Wmodules#i, projOnE sTFull.dd_i); + W = if mi === ma then complex {Wmodules#mi} else complex Wmaps; betti W Text One can check that W has two strands (corresponding to R^0f_{*}L and R^1f_{*}L, respectively). @@ -6051,12 +6217,12 @@ doc /// apply(keys retTable, i->isAction(J,prunedActionList(i))) M0=source (prunedActionList(0))_0 - (rank M0, degree M0, betti res M0) + (rank M0, degree M0, betti freeResolution M0) isIsomorphicStrict(truncate(regularity M0, M0), truncate(regularity M0, dual dual M0)) dual dual M0 M1=source (prunedActionList(1))_0 - (rank M1, degree M1, betti res M1) + (rank M1, degree M1, betti freeResolution M1) Text Note that the sheafification of M0 (=R^0(\pi \cdot f)_{*}L) is a rank 4 vector bundle O \oplus O(-1) \oplus O(-2) \oplus O(-3) on P^1, and the sheafification of M1 (= R^1(\pi \cdot f)_{*} L) is a torsion sheaf on P^1 supported on the double point at [1:0]. @@ -6162,20 +6328,20 @@ doc /// -* doc /// Key - resolutionOfChainComplex - (resolutionOfChainComplex, ChainComplex) - [resolutionOfChainComplex,LengthLimit] + resolutionOfComplex + (resolutionOfComplex, Complex) + [resolutionOfComplex,LengthLimit] Headline free resolution of a chain complex Usage - F = resolutionOfChainComplex C + F = resolutionOfComplex C Inputs - C:ChainComplex + C:Complex Outputs - F:ChainComplex + F:Complex Description Text - Given a chain complex C, the routine returns a surjective ChainComplexMap p:F->C from a free + Given a chain complex C, the routine returns a surjective ComplexMap p:F->C from a free complex. The complex F is constructed from minimal free resolutions of the terms of C by the method of iterated mapping cones. @@ -6203,10 +6369,10 @@ doc /// S = kk[a,b,c] R = S/ideal"ab2,a2c3" f = map(R,S,vars R) - C = res(R^1/(ideal vars R))**(R^1/(ideal vars R)^5); + C = freeResolution(R^1/(ideal vars R))**(R^1/(ideal vars R)^5); mods = for i from 0 to max C list pushForward(f, C_i); - C = chainComplex for i from min C+1 to max C list map(mods_(i-1),mods_i,substitute(matrix C.dd_i,S)); - time m = resolutionOfChainComplex C; + C = complex for i from min C+1 to max C list map(mods_(i-1),mods_i,substitute(matrix C.dd_i,S)); + time m = resolutionOfComplex C; betti source m betti target minimize source m SeeAlso @@ -6216,8 +6382,8 @@ doc /// *- document { - Key => {isQuism, (isQuism,ChainComplexMap)}, - Headline => "Test to see if the ChainComplexMap is a quasiisomorphism.", + Key => {isQuism, (isQuism,ComplexMap)}, + Headline => "Test to see if the ComplexMap is a quasiisomorphism.", Usage => "isQuism(phi)", Inputs => { "phi" => {}, @@ -6230,49 +6396,24 @@ document { "in one degree, so isQuism could return bad information in that case.", EXAMPLE { "R = ZZ/101[a,b,c]", - "kRes = res coker vars R", - "multBya = extend(kRes,kRes,matrix{{a}})", + "kRes = freeResolution coker vars R", + "multBya = extend(kRes,kRes ** R^{-1},matrix{{a}})", "isQuism(multBya)", "F = extend(kRes,kRes,matrix{{1_R}})", "isQuism(F)", } } --* -document { - Key => {chainComplexMap, (chainComplexMap,ChainComplex,ChainComplex,List), - [chainComplexMap,InitialDegree]}, - Headline => "Defines a ChainComplexMap via a list of matrices.", - Usage => "chainComplexMap(D,C,mapList)", - Inputs => { - "D" => ChainComplex => {"target of ChainComplexMap"}, - "C" => ChainComplex => {"source of ChainComplexMap"}, - "mapList" => List => {"list of maps defining the new ChainComplexMap"}, - }, - Outputs => { - ChainComplexMap => {"The desired ChainComplexMap."}, - }, - EXAMPLE { - "R = ZZ/101[a,b,c]", - "kRes = res coker vars R", - "multBya = extend(kRes,kRes,matrix{{a}})", - "mapList = apply((min kRes..max kRes), i -> multBya_i)", - "multBya2 = chainComplexMap(kRes,kRes,toList mapList)", - "multBya2 == multBya", - } - } -*- - -* doc /// Key - isMinimalChainComplex + isMinimalComplex Headline tests for minimality Usage - b = isMinimalChainComplex C + b = isMinimalComplex C Inputs - C:ChainComplex + C:Complex chain complex of free modules Outputs b:Boolean @@ -6330,9 +6471,9 @@ doc /// M: Module module over an symmetric algebra S Outputs - LP:ChainComplex + LP:Complex over a symmetric algebra - RM:ChainComplex + RM:Complex over a exterior algebra Description Text @@ -6356,7 +6497,7 @@ doc /// LP = bgg P netList apply(toList(min LP..max LP), i-> decompose ann HH_i LP) M = prune HH_0 LP - betti res M + betti freeResolution M high = {3,3} cohomologyMatrix(M, -high, high) Example @@ -6654,7 +6795,7 @@ correspondence of positions in the cohomology Matrix and the tally" {0,x_(1,1)}, {0,x_(1,2)}} mE = symExt(m,E) - betti(T = res coker mE) + betti(T = freeResolution(coker mE, LengthLimit => 6)) TD = tallyDegrees T; CD = cohomologyHashTable(T, -{2,2},{1,1}); assert((TD_0)#{-1,0} == CD#{{1,0},-1}) @@ -6675,7 +6816,7 @@ TEST /// debug needsPackage "TateOnProducts" n={1,2} (S,E) = productOfProjectiveSpaces n -F=dual (res((ker transpose vars E)**E^{{ 2,3}},LengthLimit=>10)) +F=dual (freeResolution((ker transpose vars E)**E^{{ 2,3}},LengthLimit=>10)) cohomologyMatrix(F,-2*n,2*n) tallyDegrees F @@ -6683,7 +6824,7 @@ deg = {2,1} m = upperCorner(F,deg) betti m tally degrees source m, tally degrees target m -Fm=(res(coker m,LengthLimit=>10))[sum deg] +Fm=(freeResolution(coker m,LengthLimit=>10))[sum deg] betti Fm betti F cohomologyMatrix(Fm,deg-{5,5},deg+{1,1}) @@ -6697,11 +6838,11 @@ debug needsPackage "TateOnProducts" n={1,1} (S,E) = productOfProjectiveSpaces n -time fB=dual res(coker random(E^7,E^{13:{ -1,0},11:{0,-1}}),LengthLimit=>10); +time fB=dual freeResolution(coker random(E^7,E^{13:{ -1,0},11:{0,-1}}),LengthLimit=>10); cohomologyMatrix(fB,-{1,1},{5,5}) deg={3,3} m= upperCorner(fB,deg); -f= res( coker m,LengthLimit=> 10)[6] +f= freeResolution( coker m,LengthLimit=> 10)[6] tallyDegrees f cohomologyMatrix(f,-{3,3},{5,5}) C= cornerComplex(f,{1,1}); @@ -6729,34 +6870,24 @@ assert(pH == apply(pH', p -> (p_0,sub(p_1,CR)))) TEST /// n={4} (S,E) = productOfProjectiveSpaces n -C=res ideal vars E -C1=C**E^{{ +1}}[0] +C=freeResolution(ideal vars E, LengthLimit => 5) +C1=(C**E^{{ +1}})[0] W=beilinsonWindow C1 scan(min W+1 ..max W,k->assert(W.dd_k==C1.dd_k)) /// -TEST /// -debug needsPackage "TateOnProducts" -S=ZZ[x,y]/ideal(x*y) -C=(chainComplex(matrix{{x}},matrix{{y^2}},matrix{{x^2}}))[3] -L=chainComplexData C -C'=chainComplexFromData L -assert(C'== C) -/// - - /// -- Frank: I removed this test many because I do not understand it --restart --loadPackage( "TateOnProducts", Reload=>true) n={2,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2 [1]); + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 6) [1]); -- cohomologyMatrix(T1,-3*n,3*n) -- beilinson beilinsonWindow T1 --beilinson T1 -- beilinson(T1, BundleType=>QuotientBundle) - T2 = res(coker lowerCorner(T1, {2,2}), LengthLimit=>10)[4]; + T2 = freeResolution(coker lowerCorner(T1, {2,2}), LengthLimit=>10)[4]; -- cohomologyMatrix(T2,-3*n,3*n) -- BW2 = beilinsonWindow T2 -- cohomologyMatrix(BW2, -5*n,5*n) @@ -6824,7 +6955,7 @@ assert( BW4.dd^2 == 0) m1 = (presentation tM) ** S^{tdeg}; corner1 = symExt(m1,E); betti corner1 - T5 = ((res(coker corner1, LengthLimit => 10)) ** E^{tdeg})[sum tdeg] + T5 = ((freeResolution(coker corner1, LengthLimit => 10)) ** E^{tdeg})[sum tdeg] cohomologyMatrix(oo, -5*n,5*n) BW5 = beilinsonWindow T5 betti BW5 @@ -6855,7 +6986,7 @@ assert( BW4.dd^2 == 0) m1 = (presentation tM) ** S^{tdeg}; betti m1 corner1 = symExt(m1,E); - T = ((res(coker corner1, LengthLimit => 4)) ** E^{tdeg})[sum tdeg] + T = ((freeResolution(coker corner1, LengthLimit => 4)) ** E^{tdeg})[sum tdeg] betti T -- cohomologyMatrix(T, -5*n,5*n) T1 = T ** E^{{-3,0}}[-3]; @@ -7012,10 +7143,10 @@ restart needsPackage "TateOnProducts" n={1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal (e_(0,1)*e_(1,1)))[1]); + T1 = (dual freeResolution(trim (ideal (e_(0,1)*e_(1,1))), LengthLimit => 5)[1]); cohomologyMatrix(T1,-3*n,3*n) a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; cohomologyMatrix(T2,-3*n,3*n) W=removeZeroTrailingTerms beilinsonWindow T2,cohomologyMatrix(W,-2*n,2*n) T = tateExtension W @@ -7033,8 +7164,8 @@ restart Hs = prune HH UF; ann Hs_0 ann Hs_1 - Wt = chainComplex {W.dd_2} - Wt = chainComplex {W.dd_1} + Wt = complex {W.dd_2} + Wt = complex {W.dd_1} UF = beilinson Wt /// @@ -7046,9 +7177,9 @@ restart n={2,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 6))[1]; a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; cohomologyMatrix(T2,-3*n,3*n) W=removeZeroTrailingTerms beilinsonWindow T2,cohomologyMatrix(W,-2*n,2*n) T = tateExtension W @@ -7057,8 +7188,8 @@ restart Hs = prune HH UF; ann Hs_0 ann Hs_1 - Wt = chainComplex {W.dd_2} - Wt = chainComplex {W.dd_1} + Wt = complex {W.dd_2} + Wt = complex {W.dd_1} UF = beilinson Wt /// @@ -7071,6 +7202,7 @@ end-- restart uninstallPackage"TateOnProducts" +restart installPackage"TateOnProducts" --loadPackage("TateOnProducts",Reload=>true) viewHelp TateOnProducts @@ -7087,10 +7219,10 @@ needsPackage "TateOnProducts" m=map(E^{0,1},,matrix{{ e_0,e_1*e_2+e_3*e_4},{e_3*e_4-e_1*e_2,e_0*e_1*e_4}}) isHomogeneous m dual m - fm=res coker m + fm=freeResolution(coker m, LengthLimit => 6) betti fm dualfm = dual fm - f2=res( coker dualfm.dd_(-1),LengthLimit=> 5)[2] + f2=freeResolution( coker dualfm.dd_(-1),LengthLimit=> 5)[2] betti f2 betti dual fm @@ -7101,11 +7233,11 @@ needsPackage "TateOnProducts" isHomogeneous m dual m m1 = syz transpose syz transpose m - fm=res (coker m, LengthLimit =>10) - fm1=res (coker m, LengthLimit =>10) + fm=freeResolution (coker m, LengthLimit =>10) + fm1=freeResolution (coker m, LengthLimit =>10) betti fm dualfm = dual fm - f2=res( coker dualfm.dd_(-1),LengthLimit=> 10)[2] + f2=freeResolution( coker dualfm.dd_(-1),LengthLimit=> 10)[2] f2.dd_0 betti f2 betti dual fm @@ -7116,17 +7248,17 @@ n={1,2} (S,E) = productOfProjectiveSpaces n a={0,1} Ua=E^{ -a} -W=chainComplex(map(E^0,Ua,0),map(Ua,E^0,0))[1] +W=complex{map(E^0,Ua,0),map(Ua,E^0,0)}[1] time T=tateExtension(W) betti (qT=firstQuadrantComplex(T,{0,0})) cohomologyMatrix(qT,-n,2*n),cohomologyMatrix(T,-2*n,2*n) ------------- --- viewHelp res seems that a some point either Dan or Mike thought about installing res(ChainComplex) +-- viewHelp res seems that a some point either Dan or Mike thought about installing res(Complex) methods res S=ZZ/101[x,y,z]/ideal(x*y) M0=((S^1/ideal y)**S^{2}), M1=S^1, M2=S^{ -1} -C=chainComplex({map(M0,M1,matrix{{x^2}}),map(M1,M2,matrix{{y}})}) +C=complex({map(M0,M1,matrix{{x^2}}),map(M1,M2,matrix{{y}})}) isHomogeneous C -------------- @@ -7141,7 +7273,7 @@ ff = select(gens E, v -> degree v =={0,1}) up = random(E^{5:{1,0}}, E^3) right = random(E^{3:{0,1}}, E^3) tot = up||right -T1 = res(coker tot, LengthLimit => 12); +T1 = freeResolution(coker tot, LengthLimit => 12); high = {6,6} low = -high cohomologyMatrix(T1,low,high) @@ -7149,7 +7281,7 @@ T2=cornerComplex(T1, -{5,5}) --why the numbering of T2? betti T2 phi = transpose T2.dd_9; -T = dual res(image phi, LengthLimit=>15)**E^{{3,4}} +T = dual freeResolution(image phi, LengthLimit=>15)**E^{{3,4}} high = high+{3,4} low = low+{3,4} cohomologyMatrix(T,low, high) @@ -7160,7 +7292,7 @@ p = map(E1,E,matrix{{0,0}}|vars E1) sT' = p sT isHomogeneous sT' betti sT' -tar = (sT'_(-15)); s = chainComplex for i from min sT'+1 to max sT'-1 list( +tar = (sT'_(-15)); s = complex for i from min sT'+1 to max sT'-1 list( phi = map(tar,,sT'.dd_(i+1)); tar = source phi; phi); @@ -7235,24 +7367,24 @@ loadPackage "TateOnProducts" --I believe the time is all taken up with the resolution n={1,2}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E))[1]; + T1 = (dual freeResolution(trim (ideal vars E), LengthLimit => 6))[1]; a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2 time T=tateExtension W; -- 2 sec n={2,2}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E))[1]; + T1 = (dual freeResolution(trim (ideal vars E), LengthLimit => 7))[1]; a=-{2,2}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2 time T=tateExtension W; -- 84 seconds n={1,1,1}; (S,E) = productOfProjectiveSpaces n; - T1 = (dual res trim (ideal vars E)^2)[1]; + T1 = (dual freeResolution(trim (ideal vars E)^2, LengthLimit => 7))[1]; a=-{2,2,3}; - T2=T1**E^{a}[sum a]; + T2=(T1**E^{a})[sum a]; W=beilinsonWindow T2 time T=tateExtension W; -- still computing 10 minutes later... diff --git a/M2/Macaulay2/packages/TensorComplexes.m2 b/M2/Macaulay2/packages/TensorComplexes.m2 index 900e59b2a3f..f03109d969b 100644 --- a/M2/Macaulay2/packages/TensorComplexes.m2 +++ b/M2/Macaulay2/packages/TensorComplexes.m2 @@ -52,7 +52,7 @@ newPackage( Email => "dumitru.stamate@fmi.unibuc.ro"}}, Headline => "multilinear algebra with labeled bases", Keywords => {"Commutative Algebra"}, - PackageExports => {"OldChainComplexes"}, + PackageExports => {"Complexes"}, DebuggingMode => false ) @@ -620,7 +620,7 @@ pureResTC1 (List,Ring) := LabeledModuleMap =>(d,kk)->( pureResTC=method() -pureResTC (List,Ring):=ChainComplex => (d,kk)->( +pureResTC (List,Ring):=Complex => (d,kk)->( res coker matrix pureResTC1(d,kk) ) @@ -638,7 +638,7 @@ pureResES1 (List,Ring) := LabeledModuleMap => o -> (d,kk)->( ) pureResES=method() -pureResES (List,Ring):=ChainComplex => (d,kk)->( +pureResES (List,Ring):=Complex => (d,kk)->( res coker matrix pureResES1(d,kk) ) @@ -761,7 +761,7 @@ doc /// g = tensorComplex1(f,{0,0}); transpose g betti res coker g - betti eagonNorthcott matrix entries matrix f + betti eagonNorthcottComplex matrix entries matrix f Text The following example is taken from the introduction to BEKS. @@ -1145,7 +1145,7 @@ doc /// d: List kk: Ring Outputs - : ChainComplex + : Complex Description Text Given a degree sequence $d$, this function returns the pure resolution of @@ -1207,7 +1207,7 @@ doc /// d: List kk: Ring Outputs - : ChainComplex + : Complex Description Text Given a degree sequence $d$, this function returns a balanced tensor complex diff --git a/M2/Macaulay2/packages/TerraciniLoci.m2 b/M2/Macaulay2/packages/TerraciniLoci.m2 index bbb9d377927..ea02c6b8998 100644 --- a/M2/Macaulay2/packages/TerraciniLoci.m2 +++ b/M2/Macaulay2/packages/TerraciniLoci.m2 @@ -29,22 +29,25 @@ newPackage("TerraciniLoci", Headline => "Terracini loci of projective varieties", - Version => "0.4", - Date => "November 10, 2025", + Version => "0.5", + Date => "February 10, 2026", Authors => { { Name => "Francesco Galuppi", - Email => "galuppi@mimuw.edu.pl"}, + Email => "f.galuppi@uw.edu.pl", + HomePage => "https://www.mimuw.edu.pl/~galuppi/"}, { Name => "Pierpaola Santarsiero", - Email => "pierpaola.santarsiero@unibo.it"}, + Email => "p.santarsiero@staff.univpm.it", + HomePage => "https://pierpaolasantarsiero.wixsite.com/pierpaola"}, { Name => "Doug Torrance", Email => "dtorrance@piedmont.edu", HomePage => "https://webwork.piedmont.edu/~dtorrance"}, { Name => "Ettore Teixeira Turatti", - Email => "ettore.t.turatti@uit.no"}}, + Email => "e.teixeira-turatti@uw.edu.pl", + HomePage => "https://turattiettore.wixsite.com/ettoreturatti"}}, HomePage => "https://github.com/d-torrance/terracini-loci", Keywords => {"Projective Algebraic Geometry"}, PackageImports => { @@ -58,6 +61,11 @@ newPackage("TerraciniLoci", -* +0.5 (2026-12-10, M2 1.26.05) +* update citation information +* add author webpages +* update author email addresses + 0.4 (2025-11-10, M2 1.25.11) * update GPL 2 text (FSF no longer has a physical address) @@ -135,16 +143,22 @@ doc /// This package exports one method, @TO terraciniLocus@, for computing the ideals of these varieties. Citation - @article{Galuppi_2025, - title={Geometry of First Nonempty Terracini Loci}, - ISSN={1793-6683}, - url={http://dx.doi.org/10.1142/S0219199725500531}, - DOI={10.1142/s0219199725500531}, - journal={Communications in Contemporary Mathematics}, - publisher={World Scientific Pub Co Pte Ltd}, - author={Galuppi, Francesco and Santarsiero, Pierpaola and Torrance, Douglas A. and Turatti, Ettore Teixeira}, - year={2025}, - month=apr } + @article {MR5026390, + AUTHOR = {Galuppi, Francesco and Santarsiero, Pierpaola and Torrance, + Douglas A. and Turatti, Ettore Teixeira}, + TITLE = {Geometry of first nonempty {T}erracini loci}, + JOURNAL = {Commun. Contemp. Math.}, + FJOURNAL = {Communications in Contemporary Mathematics}, + VOLUME = {28}, + YEAR = {2026}, + NUMBER = {4}, + PAGES = {Paper No. 2550053}, + ISSN = {0219-1997,1793-6683}, + MRCLASS = {14J45 (14Q15 15A69)}, + MRNUMBER = {5026390}, + DOI = {10.1142/S0219199725500531}, + URL = {https://doi.org/10.1142/S0219199725500531}, + } /// doc /// diff --git a/M2/Macaulay2/packages/TestIdeals.m2 b/M2/Macaulay2/packages/TestIdeals.m2 index 4d2596823ed..ccb79be782d 100644 --- a/M2/Macaulay2/packages/TestIdeals.m2 +++ b/M2/Macaulay2/packages/TestIdeals.m2 @@ -80,7 +80,7 @@ Headline => "singularities in positive characteristic", Keywords => {"Commutative Algebra"}, AuxiliaryFiles=>true, PackageExports=>{"Depth"}, -PackageImports => {"MinimalPrimes", "OldChainComplexes"}, +PackageImports => {"MinimalPrimes", "Complexes"}, Certification => { "journal name" => "The Journal of Software for Algebra and Geometry", "journal URI" => "https://msp.org/jsag/", diff --git a/M2/Macaulay2/packages/ThinSincereQuivers.m2 b/M2/Macaulay2/packages/ThinSincereQuivers.m2 index cd6d789f7a4..40f09f3eb4c 100644 --- a/M2/Macaulay2/packages/ThinSincereQuivers.m2 +++ b/M2/Macaulay2/packages/ThinSincereQuivers.m2 @@ -13,7 +13,19 @@ newPackage( } }, PackageImports => {"Graphs", "Polyhedra", "LatticePolytopes"}, - Keywords => {"Toric Geometry"} + Keywords => {"Toric Geometry"}, + Certification => { + "journal name" => "Journal of Software for Algebra and Geometry", + "journal URI" => "https://msp.org/jsag/", + "article title" => "Quivers and moduli of their thin sincere representations in Macaulay2", + "acceptance date" => "2025-08-20", + "published article URI" => "https://msp.org/jsag/2025/15-1/p08.xhtml", + "published article DOI" => "10.2140/jsag.2025.15.93", + "published code URI" => "https://msp.org/jsag/2025/15-1/jsag-v15-n1-x08-ThinSincereQuivers.m2", + "version at publication" => "0.1", + "volume number" => "15", + "volume URI" => "https://msp.org/jsag/2025/15-1/" + } ) export { -- Methods/Functions @@ -3250,7 +3262,7 @@ multidoc /// This is an optional argument that can be positive integer values, which and which is used in quiver constructor methods when the optional argument for flow is set to random {\tt Flow => "Random"}. The {\tt Height} argument sets the maximum value for - the integer-valued random numhber generator so that values in the flow are chosen + the integer-valued random number generator so that values in the flow are chosen from the interval {\tt [0, Height)}. Example -- create a toric quiver from a matrix with random flow with values in the interval [0, 10) diff --git a/M2/Macaulay2/packages/ThreadedGB.m2 b/M2/Macaulay2/packages/ThreadedGB.m2 index dc9dcfa86fe..6a98b5709fe 100644 --- a/M2/Macaulay2/packages/ThreadedGB.m2 +++ b/M2/Macaulay2/packages/ThreadedGB.m2 @@ -320,10 +320,10 @@ doc /// Key ThreadedGB Headline - a package for distributed computation of Gr\"obner bases + a package for distributed computation of Gröbner bases Description Text - The complexity of Gr\"obner computations has inspired many improvements to Buchberger's + The complexity of Gröbner computations has inspired many improvements to Buchberger's algorithm over the years. While this package does not propose an improvement to the way the algorithm operates mathematically, it offers a way to distribute the algorithm among threads that run in parallel. @@ -334,17 +334,17 @@ doc /// that are added to the basis during a run of Buchberger. How are these affected by the structure of the input system? What do they say about the complexity of the computation itself (and not only the complexity of the basis)? These are questions at the heart of what we are aiming to discover, and the output of the - threaded Gr\"obner bases method @TO tgb@ returns this information in form of a {\bf lineage table}. + threaded Gröbner bases method @TO tgb@ returns this information in form of a {\bf lineage table}. Example QQ[x_1,x_0,x_3,x_5,x_4,x_2,MonomialOrder=>Lex] rnc = minors(2, matrix{{x_0..x_4},{x_1..x_5}}) allowableThreads = 4 g = tgb(rnc) Text - The lineage table is a hash table, whose values are Gr\"obner basis elements, and whose keys are the {\it lineages}. + The lineage table is a hash table, whose values are Gröbner basis elements, and whose keys are the {\it lineages}. {\bf Definition.} A lineage of a polynomial is a natural number, or an ordered pair of lineages, tracing - its history in the given Gr\"obner basis computation. + its history in the given Gröbner basis computation. Lineages that are natural numbers are assigned to the original input polynomials. In the example above, the 10 minors have lineages $0,\dots,9$. @@ -367,19 +367,19 @@ doc /// minimize g gRed = reduce g Text - To get the Gr\"obner basis in standard M2 matrix format, simply call the following: + To get the Gröbner basis in standard M2 matrix format, simply call the following: Example matrix gRed Text {\bf Nuts and Bolts} The main function, @TO tgb@, uses @TO Task@s to distribute the reduction of S-polynomials using a - a current version of the Groenber basis. + a current version of the Gröbner basis. It can reduce and minimize upon request or print out task scheduling information as it creates new tasks. The interesting part of the output may be the lineages of the basis polynomials, - in addition to the Gr\"obner basis itself. - Here is an example where the Gr\"obner basis is trivial. + in addition to the Gröbner basis itself. + Here is an example where the Gröbner basis is trivial. Example QQ[a..d] I=ideal( -c^3+a^2+b*d, a*b*c-1,a*b*c) @@ -400,7 +400,7 @@ doc /// (tgb, List) (tgb, Ideal) Headline - threaded Gr\"obner bases + threaded Gröbner bases Usage tgb(List) tgb(Ideal) @@ -410,11 +410,11 @@ doc /// I : Ideal Outputs : LineageTable - a hashtable whose values are a Gr\"obner basis for the ideal {\tt I} or the ideal generated by {\tt L}, + a hashtable whose values are a Gröbner basis for the ideal {\tt I} or the ideal generated by {\tt L}, and keys are the lineages of the corresponding elements. Description Text - Threaded Gr\"obner basis uses @TO Task@s to compute a Gr\"obner basis of {\tt I} or {\tt ideal L} + Threaded Gröbner basis uses @TO Task@s to compute a Gröbner basis of {\tt I} or {\tt ideal L} using $n$ threads. Example R = ZZ/101[x,y,z, MonomialOrder=>Lex]; @@ -458,7 +458,7 @@ doc /// reduce Caveat Due to threads running in parallel, it can happen that there are redundant elements in the final - Gr\"obner basis. However these can be easily removed using @TO (minimize, LineageTable)@, for example. + Gröbner basis. However these can be easily removed using @TO (minimize, LineageTable)@, for example. Also, {\tt allowableThreads} needs to be set to an integer larger than 1, prior to calling @TO tgb@. Otherwise, errors may occur. It may be a good idea to reset allowableThreads to 1 after the threaded computations are done. @@ -467,14 +467,14 @@ doc /// Key LineageTable Headline - a hash table of Gr\"obner basis polynomials and their lineages + a hash table of Gröbner basis polynomials and their lineages Description Text - A lineage table is a hashtable whose values are a Gr\"obner basis for the ideal {\tt I} or the ideal generated by {\tt L}, + A lineage table is a hashtable whose values are a Gröbner basis for the ideal {\tt I} or the ideal generated by {\tt L}, and keys are the lineages of the corresponding elements. A lineage of a polynomial is a natural number, or an ordered pair of lineages, tracing - its history in the given Gr\"obner basis computation. + its history in the given Gröbner basis computation. Lineages that are natural numbers are assigned to the original input polynomials. SeeAlso tgb @@ -483,7 +483,7 @@ doc /// Key (minimize, LineageTable) Headline - turn a Gr\"obner basis computed using threaded Gr\"obner bases into a minimal one + turn a Gröbner basis computed using threaded Gröbner bases into a minimal one Usage minimize(LineageTable) Inputs @@ -495,8 +495,8 @@ doc /// Description Text Scans values of a hash table H and retains only those whose initial terms are minimal generators - of the ideal generated by the leading terms of the values of H. If the values of H constitute a Gr\"obner basis of the ideal they generate, - this method returns a minimal Gr\"obner basis. + of the ideal generated by the leading terms of the values of H. If the values of H constitute a Gröbner basis of the ideal they generate, + this method returns a minimal Gröbner basis. Example R = ZZ/101[a,b,c]; allowableThreads= 2; @@ -511,7 +511,7 @@ doc /// reduce (reduce, LineageTable) Headline - produce a reduced Gr\"obner basis from one computed by threaded Gr\"obner bases + produce a reduced Gröbner basis from one computed by threaded Gröbner bases Usage reduce LineageTable Inputs @@ -524,8 +524,8 @@ doc /// Text Minimalizes first, then replaces each of the values of a hash table H by its remainder on the division by the remaining values H. - If values H constitute a Gr\"obner basis of the ideal they generate, - this method returns a reduced Gr\"obner basis. + If values H constitute a Gröbner basis of the ideal they generate, + this method returns a reduced Gröbner basis. Example R = ZZ/101[a,b,c]; allowableThreads= 2; @@ -550,7 +550,7 @@ doc /// of non-null values of H Description Text - This simple function just returns the Gr\"obner basis computed with threaded Gr\"obner basis function @TO tgb@ + This simple function just returns the Gröbner basis computed with threaded Gröbner basis function @TO tgb@ in the expected Macaulay2 format, so that further computation are one step easier to set up. Example R = ZZ/101[a,b,c]; @@ -570,7 +570,7 @@ doc /// Use {\tt Verbose=>True} as an argument in the function @TO tgb@ for additional output, including information about each new generated thread (i.e., each new S-polynomial reduction - computation) as well as each new Gr\"obner basis element added to the current basis. + computation) as well as each new Gröbner basis element added to the current basis. Lineages are reported as well. Example S = QQ[x,y,z,w]; @@ -582,14 +582,14 @@ doc /// Minimal [tgb, Minimal] Headline - Option to specify whether the end Gr\"obner basis should be a minimal Gr\"obner basis + Option to specify whether the end Gröbner basis should be a minimal Gröbner basis Usage tgb(...,Minimal=>Boolean) Description Text Use {\tt Minimal=>True} as an argument in the function @TO tgb@ - for ensure the resulting Gr\"obner basis is minimized. - Lineages of non-minimal Gr\"obner basis elements that were added to the basis during the + for ensure the resulting Gröbner basis is minimized. + Lineages of non-minimal Gröbner basis elements that were added to the basis during the distributed computation are saved, with the corresponding entry in the table being null. Example S = ZZ/101[a,b,c]; diff --git a/M2/Macaulay2/packages/TorAlgebra.m2 b/M2/Macaulay2/packages/TorAlgebra.m2 index 5479ccb53e1..48bf5fccd30 100644 --- a/M2/Macaulay2/packages/TorAlgebra.m2 +++ b/M2/Macaulay2/packages/TorAlgebra.m2 @@ -11,7 +11,7 @@ newPackage ( "TorAlgebra", }, Headline => "classification of local rings based on multiplication in homology", Keywords => {"Homological Algebra"}, - PackageImports => { "OldChainComplexes", "LocalRings" }, + PackageImports => { "Complexes", "LocalRings" }, Certification => { -- this package was certified under its old name, "CodepthThree" "journal name" => "The Journal of Software for Algebra and Geometry", "journal URI" => "https://msp.org/jsag/", @@ -364,10 +364,10 @@ toralgdata = R -> ( ) else ( if isHomogeneous ideal R then ( - data := computeBass1 (Q, R, I, e, chainComplex(L.dd_1,L.dd_2), tries) + data := computeBass1 (Q, R, I, e, complex{L.dd_1,L.dd_2}, tries) ) else ( - data = computeBass2 (Q, R, I, e, chainComplex(L.dd_1,L.dd_2), tries); + data = computeBass2 (Q, R, I, e, complex{L.dd_1,L.dd_2}, tries); ); mu := data#"bass"; c':= data#"codepth"; @@ -400,10 +400,10 @@ toralgdata = R -> ( ) else ( if isHomogeneous ideal R then ( - data = computeBass1 (Q, R, I, e, chainComplex(L.dd_1,L.dd_2), tries) + data = computeBass1 (Q, R, I, e, complex{L.dd_1,L.dd_2}, tries) ) else ( - data = computeBass2 (Q, R, I, e, chainComplex(L.dd_1,L.dd_2), tries); + data = computeBass2 (Q, R, I, e, complex{L.dd_1,L.dd_2}, tries); ); mu = data#"bass"; c'= data#"codepth"; @@ -436,10 +436,10 @@ toralgdata = R -> ( ) else ( if isHomogeneous ideal R then ( - data = computeBass1 (Q, R, I, e, chainComplex(L.dd_1,L.dd_2,L.dd_3), tries) + data = computeBass1 (Q, R, I, e, complex{L.dd_1,L.dd_2,L.dd_3}, tries) ) else ( - data = computeBass2 (Q, R, I, e, chainComplex(L.dd_1,L.dd_2,L.dd_3), tries); + data = computeBass2 (Q, R, I, e, complex{L.dd_1,L.dd_2,L.dd_3}, tries); ); mu = data#"bass"; c'= data#"codepth"; diff --git a/M2/Macaulay2/packages/ToricHigherDirectImages.m2 b/M2/Macaulay2/packages/ToricHigherDirectImages.m2 index 8d1a2625d05..63b0a987f74 100644 --- a/M2/Macaulay2/packages/ToricHigherDirectImages.m2 +++ b/M2/Macaulay2/packages/ToricHigherDirectImages.m2 @@ -1,6 +1,6 @@ --- -*- coding: utf-8 -*- ------------------------------------------------------------------------------ --- Copyright 2025 Sasha Zotine +-- Copyright 2026 Sasha Zotine -- -- This program is free software: you can redistribute it and/or modify it -- under the terms of the GNU General Public License as published by the Free @@ -17,8 +17,8 @@ ------------------------------------------------------------------------------ newPackage( "ToricHigherDirectImages", - Version => "1.1", - Date => "2025 June", + Version => "1.1", + Date => "2026 April", Authors => { {Name => "Sasha Zotine", Email => "zotinea@mcmaster.ca", @@ -34,12 +34,14 @@ newPackage( export { -- types -- methods - "frobeniusDirectImage", + "frobeniusPushforward", + "frobeniusDirectImages" => "frobeniusPushforward", "nefContraction", "nefRayContractions", "allNefContractions", "computeEigencharacters", - "HDI" + "HDI", + "higherDirectImages" => "HDI" } importFrom(NormalToricVarieties, {"outerNormals", "rawHHOO"}) @@ -55,29 +57,29 @@ importFrom(Core, {"sortBy", "raw", "rawHilbertBasis"}) -- M - a module over a multigraded polynomial ring. -- OUTPUT: the module (F_p)_* M, i.e. the pushforward of M under the p-th toric -- Frobenius map. -frobeniusDirectImage = method(); -frobeniusDirectImage (ZZ, Module) := Module => (p, M) -> ( +frobeniusPushforward = method() +frobeniusPushforward (ZZ, Module) := Module => (p, M) -> ( if M == 0 then return M; -- If it isn't free - if not isFreeModule M then coker frobeniusDirectImage_p presentation M else ( - S := ring M; - X := variety S; - A := matrix rays X; + if not isFreeModule M then coker frobeniusPushforward_p presentation M else ( + S := ring M; + X := variety S; + A := matrix rays X; -- It will split as a direct sum of line bundles, indexed by characters -- in M/pM. This is a primitive generating set of that, the unit hypercube. - pts := toList (toList (dim X:{0})..toList (dim X:{p-1})); + pts := toList (toList (dim X:{0})..toList (dim X:{p-1})); -- Pick representative Cartier divisors for each summand. - D := (inverse fromCDivToPic X) * transpose matrix degrees M; + D := (inverse fromCDivToPic X) * transpose matrix degrees M; -- This round-down formula computes the bundles appearing in the pushforward. - B := flatten apply(numcols D, d -> apply(pts, pt -> entries transpose ( - fromCDivToPic X * matrix (entries (A * (matrix pt) - D_{d}) // p) - ) - ) + B := flatten apply(numcols D, d -> apply(pts, pt -> entries transpose ( + fromCDivToPic X * matrix (entries (A * (matrix pt) - D_{d}) // p) + ) + ) ); - dSum := directSum apply(B, b -> S^b); + dSum := directSum apply(B, b -> S^b); -- We should remember our choice of Cartier divisors. - dSum.cache.frobeniusDirectImage = D; - dSum + dSum.cache.frobeniusPushforward = D; + dSum ) ) @@ -86,7 +88,7 @@ frobeniusDirectImage (ZZ, Module) := Module => (p, M) -> ( -- INPUT: p - an integer -- f - a map between multigraded modules f : M -> N. -- OUTPUT: the map between modules (F_p)_* f : (F_p)_* M -> (F_p)_* N. -frobeniusDirectImage (ZZ, Matrix) := (p, f) -> ( +frobeniusPushforward (ZZ, Matrix) := (p, f) -> ( S := ring f; -- easy way to access the variety which these modules are over. X := variety S; @@ -94,14 +96,14 @@ frobeniusDirectImage (ZZ, Matrix) := (p, f) -> ( pts := toList (toList (dim X:{0})..toList (dim X:{p-1})); -- our map will be a block matrix blocksize := #pts; - src := frobeniusDirectImage_p source f; - tgt := frobeniusDirectImage_p target f; + src := frobeniusPushforward_p source f; + tgt := frobeniusPushforward_p target f; A := matrix rays X; -- this is the main reason to keep track of the divisors. -- we need them to make sure that the map is sending terms to the correct place. -- e.g. the trivial summand should map to the trivial summand. - Dsrc := src.cache.frobeniusDirectImage; - Dtgt := tgt.cache.frobeniusDirectImage; + Dsrc := src.cache.frobeniusPushforward; + Dtgt := tgt.cache.frobeniusPushforward; -- to make the direct image of the map matrix table(numrows f, numcols f, (r,c) -> ( -- we want to convert the entries of the input map @@ -111,23 +113,23 @@ frobeniusDirectImage (ZZ, Matrix) := (p, f) -> ( blocktgt := directSum (components tgt)_{r*blocksize..(r+1)*blocksize-1}; if f0 == 0 then 0_S else ( -- we'll multiply by the coefficient at the end. - c0 := (coefficients f0)_1_0_0; + c0 := (coefficients f0)_1_0_0; -- the important data of the map comes from the exponents -- of the monomials defining f. - d0 := transpose matrix exponents f0; + d0 := transpose matrix exponents f0; M := matrix table(blocksize, blocksize, (mr, mc) -> ( -- the exponents + Dsrc tell us which summand we should be mapping to. - tgtindex := (entries solve(A, A * (matrix pts_mc) + d0 - Dsrc_{c} + Dtgt_{r})) % p; + tgtindex := (entries solve(A, A * (matrix pts_mc) + d0 - Dsrc_{c} + Dtgt_{r})) % p; -- if there's no such summand, then the map is zero. - if pts_mr != tgtindex then 0 else ( + if pts_mr != tgtindex then 0 else ( -- otherwise, we figure out what the degree of the source and target terms -- are, and the monomial map between them is the difference of their degrees. srcexp := matrix (entries (A * (matrix pts_mc) - Dsrc_{c}) // p); - tgtexp := matrix (entries (A * (matrix pts_mc) - Dsrc_{c} + d0) // p); - exponent := flatten entries (tgtexp - srcexp); - c0 * S_exponent - ) - ) + tgtexp := matrix (entries (A * (matrix pts_mc) - Dsrc_{c} + d0) // p); + exponent := flatten entries (tgtexp - srcexp); + c0 * S_exponent + ) + ) ); map(blocktgt,blocksrc,M) ) @@ -141,11 +143,9 @@ frobeniusDirectImage (ZZ, Matrix) := (p, f) -> ( -- OUTPUT: the pushforward of the complex C, by applying the functor to each differential. -- NOTE: since the Frobenius is a finite map, the pushforward is an exact functor, i.e. -- there are no higher direct images. -frobeniusDirectImage (ZZ, Complex) := Complex => (p, K) -> ( - complex ( - if length K == 0 then map(frobeniusDirectImage_p K_0, 0, 0) - else apply(length K, i -> frobeniusDirectImage(p, K.dd_(i+1))) - ) +frobeniusPushforward (ZZ, Complex) := Complex => (p, K) -> complex ( + if length K == 0 then map(frobeniusPushforward_p K_0, 0, 0) + else apply(length K, i -> frobeniusPushforward(p, K.dd_(i+1))) ) -- Internal method. Modification of isRelativeInterior in NormalToricVarieties. @@ -156,12 +156,12 @@ isContainedInCone (NormalToricVariety, List, Matrix) := Boolean => (X, sigma, v) H0 := transpose H#0; H1 := transpose H#1; all (flatten entries (H0 * v), i -> i <= 0) and - all (flatten entries(H1 * v), i -> i === 0) + all (flatten entries(H1 * v), i -> i === 0) ) -- Internal method. Computes the preimages of all codimension-k cones via a toric map. currently -- only used for k = 0, but some old code used k = 1 as well for gluing complexes. -conePreimages = method(); +conePreimages = method() conePreimages (ToricMap, ZZ) := (phi, k) -> ( X := source phi; n := dim X; @@ -201,7 +201,7 @@ conePreimages (ToricMap, ZZ) := (phi, k) -> ( -- Internal method. this method computes the exponent vectors for the generators of the affine semigroup -- corresponding to w. if w is not maximal, then we only return the non-invertible generators. -affineSemigroupGenerators = method(); +affineSemigroupGenerators = method() affineSemigroupGenerators (NormalToricVariety, List) := Matrix => (X, w) -> ( if w == {} then return matrix toList (dim X:{0}); if #max X == 1 then return id_(ZZ^(#rays X)); @@ -212,8 +212,8 @@ affineSemigroupGenerators (NormalToricVariety, List) := Matrix => (X, w) -> ( result := solve(M,-N); matrix for i to numgens R - 1 list ( for j to numcols result - 1 list ( - if member(i,wC) then result_j_(position(wC, k-> k == i)) - else if i == w_j then 1 else 0 + if member(i,wC) then result_j_(position(wC, k-> k == i)) + else if i == w_j then 1 else 0 ) ) ) @@ -230,14 +230,12 @@ generatingLatticePoints Polyhedron := List => P -> ( -- INPUT: X - a normal toric variety -- v - a vector presumed to be in the nef cone of X. -- OUTPUT: the toric morphism (contraction) f : X -> Y corresponding to the --- face of Nef(X) which v lies in the interior of. +-- face of Nef(X) which v lies in the interior of. -- WARNING: Y may not be a smooth toric variety, in which case we can't use the HDI --- method for computing higher direct images. -nefContraction = method(); -nefContraction (NormalToricVariety, Vector) := ToricMap => (X, v) -> ( - nefContraction(X, entries v)) -nefContraction (NormalToricVariety, Matrix) := ToricMap => (X, v) -> ( - nefContraction(X, flatten entries v)) +-- method for computing higher direct images. +nefContraction = method() +nefContraction (NormalToricVariety, Vector) := ToricMap => (X, v) -> nefContraction(X, entries v) +nefContraction (NormalToricVariety, Matrix) := ToricMap => (X, v) -> nefContraction(X, flatten entries v) nefContraction (NormalToricVariety, List) := ToricMap => (X,v) -> ( R := ring X; nefgens := nefGenerators X; @@ -250,7 +248,7 @@ nefContraction (NormalToricVariety, List) := ToricMap => (X,v) -> ( count := sum apply(mons_i, j -> if j == 0 then 1 else 0); if count == #mons_0 then quotrays = append(quotrays, (Xrays)_i); count - ); + ); proj := if quotrays != {} then ( quot := prune coker transpose matrix quotrays; inverse quot.cache.pruningMap @@ -268,7 +266,7 @@ nefContraction (NormalToricVariety, List) := ToricMap => (X,v) -> ( M := (matrix mons)^rayinds; Yrays := entries ((matrix Xrays)^rayinds * transpose matrix proj); Ymax := for i to numcols M - 1 list ( - maxcone := for j to numrows M - 1 list ( + maxcone := for j to numrows M - 1 list ( if M_i_j == 0 then j else continue ); if #maxcone >= numrows proj then maxcone else continue @@ -280,7 +278,7 @@ nefContraction (NormalToricVariety, List) := ToricMap => (X,v) -> ( -- applies nefConeContraction to all of the rays of the nef cone for convenience. -- INPUT: X - a normal toric variety. -- OUTPUT: all contractions corresponding to the rays of the nef cone of X. -nefRayContractions = method(); +nefRayContractions = method() nefRayContractions NormalToricVariety := List => X -> ( apply(entries transpose nefGenerators X, v -> nefContraction(X,v)) ) @@ -289,25 +287,25 @@ nefRayContractions NormalToricVariety := List => X -> ( -- INPUT: X - a normal toric variety. -- OUTPUT: all contractions corresponding to the all faces of the nef cone of X. -- WARNING: exponential in the number of rays of the nef cone. -allNefContractions = method(); +allNefContractions = method() allNefContractions NormalToricVariety := List => X -> ( nefgens := nefGenerators X; if nefgens == 0 then {} else ( - enefgens := entries transpose nefgens; - vs := for s in drop(subsets (#enefgens), 1) list sum enefgens_s; - apply(vs, v -> nefContraction(X,v)) + enefgens := entries transpose nefgens; + vs := for s in drop(subsets (#enefgens), 1) list sum enefgens_s; + apply(vs, v -> nefContraction(X,v)) ) ) -- computes the eigencharacters of R^i phi_* O_X(D). See Algorithm A.5. -- INPUT: phi - a surjective toric morphism -- i - an integer --- D - a list indexing a toric divisor on source phi. +-- D - a list indexing a toric divisor on source phi. -- OUTPUT: a hashtable whose keys are the eigencharacters, and the values -- are the pairs (D,E) from Theorem 5.6. -- NOTE: the eigencharacters are embedded in M_X as opposed to M_K from -- the paper--this is for convenience for the computations. -computeEigencharacters = method(); +computeEigencharacters = method() computeEigencharacters (ToricMap, ZZ, ToricDivisor) := (phi, i, D) -> computeEigencharacters (phi, i, entries D) computeEigencharacters (ToricMap, ZZ, List) := (phi, i, D) -> ( X := source phi; @@ -332,46 +330,46 @@ computeEigencharacters (ToricMap, ZZ, List) := (phi, i, D) -> ( -- local map from M_Y to CDiv(Y) sending chi -> \sum_{rho in w} D_rho. iota := affineSemigroupGenerators(Y, sigma); -- local embedding of M_Y into M_X using iota. - localTorusEmbedding := solve(matrix rays X, phiCDiv * iota); - suppset := unique flatten preimages#sigma; + localTorusEmbedding := solve(matrix rays X, phiCDiv * iota); + suppset := unique flatten preimages#sigma; -- these are the restricted negsets. - rnegsets := for u in negsets list ( + rnegsets := for u in negsets list ( if isSubset(u,suppset) then u else continue ); -- Compute Gamma_sigma as described in the algorithm. -- First we compute all of the polyhedra. - Ps := for neg in rnegsets list ( + Ps := for neg in rnegsets list ( (I, v) := toSequence transpose for j in suppset list ( - if member(j,neg) then {(rays X)_j, {-D_j - 1}} - else {-(rays X)_j, {D_j}} - ); + if member(j,neg) then {(rays X)_j, {-D_j - 1}} + else {-(rays X)_j, {D_j}} + ); P := polyhedronFromHData(matrix I, matrix v); if not isEmpty P then P else continue ); - if Ps == {} then {} else ( + if Ps == {} then {} else ( -- take the bounded part of the polyhedron, then compute -- the lattice points of this. this gives Gamma_sigma. pts := (flatten (Ps/generatingLatticePoints))/vector/matrix; -- here is the set of eigencharacters C(L,i). - CLi := unique for p in pts list MK * (transpose MK) * p; + CLi := unique for p in pts list MK * (transpose MK) * p; -- for each character, compute this minimizing divisor D_sigma. for mu in CLi list ( -- these are all of the divisors to minimize over. slicepts := select(pts, p -> MK * (transpose MK) * p == mu); newpts := for p in slicepts list ( - newp := p - MK * (transpose MK) * p; - solve(localTorusEmbedding, newp) - ); - mmin := for j to dim Y - 1 list min apply(newpts, k -> k_0_j); - Dsigma := iota * transpose matrix {mmin}; - {mu, Dsigma} + newp := p - MK * (transpose MK) * p; + solve(localTorusEmbedding, newp) + ); + mmin := for j to dim Y - 1 list min apply(newpts, k -> k_0_j); + Dsigma := iota * transpose matrix {mmin}; + {mu, Dsigma} ) ) ); -- now compute the global (D,E) pairs for each eigencharacter. if chardata == {} then hashTable {} else ( - CLi := unique (transpose chardata)_0; - hashTable for mu in CLi list mu => ( + CLi := unique (transpose chardata)_0; + hashTable for mu in CLi list mu => ( allDs := apply(select(chardata, x -> x_0 == mu), y -> y_1); -- initialize a choice of Dsigma Dtau := allDs_0; @@ -381,12 +379,12 @@ computeEigencharacters (ToricMap, ZZ, List) := (phi, i, D) -> ( ) else ( -- translate by Dtau. - differences := for j to #allDs - 2 list allDs_(j+1) - Dtau; - Emu := -matrix for e in entries matrix {differences} list {min prepend(0,e)}; + differences := for j to #allDs - 2 list allDs_(j+1) - Dtau; + Emu := -matrix for e in entries matrix {differences} list {min prepend(0,e)}; -- since we just need to twist at the end, might as well compute it here. - twist := transpose entries (fromCDivToPic Y * Emu); - ((matrix rays X * mu) + (matrix vector D) + phiCDiv * (Dtau - Emu), twist) - ) + twist := transpose entries (fromCDivToPic Y * Emu); + ((matrix rays X * mu) + (matrix vector D) + phiCDiv * (Dtau - Emu), twist) + ) ) ) ) @@ -396,9 +394,9 @@ computeEigencharacters (ToricMap, ZZ, List) := (phi, i, D) -> ( -- constructs the complex \v{C}(I_P(D)); see Algorithm A.8. -- INPUT: phi - a surjective toric morphism -- i - an integer --- D - a list indexing a toric divisor on source phi. +-- D - a list indexing a toric divisor on source phi. -- OUTPUT: a complex supported in degrees -1, 0, and 1 whose cohomology is equal to R^i phi_* O_X(D). -HDIComplex = method(); +HDIComplex = method() HDIComplex (ToricMap, ZZ, ToricDivisor) := (phi, i, D) -> HDIComplex(phi, i, entries D) HDIComplex (ToricMap, ZZ, Module) := (phi, i, D) -> ( if ring D != ring source phi then error("-- must be a module over the source of the map"); @@ -431,9 +429,9 @@ HDIComplex (ToricMap, ZZ, List) := (phi, i, D) -> ( -- the terms in degrees i-1, i, and i+1, which saves a lot of work. -- the parameter j is tracking these terms. directSum for mu in (sortBy entries) keys eigenchars list ( - (Dmu,Emu) := eigenchars#mu; + (Dmu,Emu) := eigenchars#mu; -- this hashtable keeps track of the non-zero terms of the complex. - nonzerow := new MutableHashTable from hashTable for j from -1 to 1 list j => {}; + nonzerow := new MutableHashTable from hashTable for j from -1 to 1 list j => {}; -- here are the terms of the complex. complexterms := for j from -1 to 1 list ( if i+j < 0 or i+j > dim X then {S^0} else ( @@ -468,16 +466,16 @@ HDIComplex (ToricMap, ZZ, List) := (phi, i, D) -> ( if not member(allorbits_j_k, nonzerow#(j-1)) then continue else ( L := #allorbits_(j+1) - 1; - for l to L list ( - if not member(allorbits_(j+1)_l, nonzerow#j) then continue - else ( + for l to L list ( + if not member(allorbits_(j+1)_l, nonzerow#j) then continue + else ( -- this picks out the appropriate sign in the complex C, and multiplies -- it by the natural inclusion map between ideals. - C.dd_(-i-j)_(K-k)_(L-l) * (gens complexterms_j_k // gens complexterms_(j+1)_l) - ) + C.dd_(-i-j)_(K-k)_(L-l) * (gens complexterms_j_k // gens complexterms_(j+1)_l) + ) ) ) - ) + ) ); -- once we have the maps and the terms, we can put them into a complex. map(directSum complexterms_(j+1), directSum complexterms_j, mat) @@ -488,9 +486,9 @@ HDIComplex (ToricMap, ZZ, List) := (phi, i, D) -> ( -- method for computing higher direct images. -- INPUT: phi - a surjective toric morphism -- i - an integer --- D - a list indexing a toric divisor on source phi +-- D - a list indexing a toric divisor on source phi -- OUTPUT: the i-th cohomology of HDIComplex(phi,i,D). -HDI = method(); +HDI = method() HDI (ToricMap, ZZ, List) := Module => (phi, i, D) -> ( -- check if we've computed this for i and D already. phi.cache.HDI ??= new MutableHashTable; @@ -525,415 +523,351 @@ doc /// computations involving pushforwards and higher direct images of toric maps Description Text - Given a morphism of varieties $\varphi \colon X \rightarrow Y$, we have - the pushforward functor $\varphi_*$ from the category of coherent sheaves - on $X$ to coherent sheaves on $Y$. This functor is not right-exact, and so - there is a right-derived functor $R^i \varphi_*$ from the bounded derived - category $D^b(X)$ to $D^b(Y)$, which we call the higher direct image functor. - When both $X$ and $Y$ are toric varieties, coherent sheaves arise from - finitely generated multigraded modules over their Cox rings. Thus, for a - given coherent sheaf $\mathcal{F}$, there are finitely generated graded - modules over the Cox ring of $Y$ which sheafifies to $R^i \varphi_* \mathcal{F}$. + Given a morphism of varieties $\varphi \colon X \rightarrow Y$, we have + the pushforward functor $\varphi_*$ from the category of coherent sheaves + on $X$ to coherent sheaves on $Y$. This functor is not right-exact, and so + there is a right-derived functor $R^i \varphi_*$ from the bounded derived + category $D^b(X)$ to $D^b(Y)$, which we call the higher direct image functor. + When both $X$ and $Y$ are toric varieties, coherent sheaves arise from + finitely generated multigraded modules over their Cox rings. Thus, for a + given coherent sheaf $\mathcal{F}$, there are finitely generated graded + modules over the Cox ring of $Y$ which sheafifies to $R^i \varphi_* \mathcal{F}$. Text - The purpose of this package is to compute (higher) direct images of - toric morphisms $\varphi \colon X \rightarrow Y$ between smooth - projective toric varieties. This is currently implemented in two - situations: - Text - 1) When $\varphi$ is a toric fibration, i.e. a surjective - toric morphism with $\varphi_* \mathcal{O}_X = \mathcal{O}_Y$, - then the method \texttt{HDI} allows one to compute the higher direct - images of a line bundle $\mathcal{O}_X(D)$ on $X$. In "Reduced \v{C}ech complexes and computing higher direct - images under toric maps", M. Roth provides a constructive method for producing - this module and S. Zotine adapts that method into an algorithm. This package - provides the implementation of this algorithm. - Text - For instance, if $X$ is a blowup of $\mathbb{P}^2$, then the first higher direct - image of $O_X(3E)$ is nontrivial, where $E$ is the exceptional divisor. - Example - X = hirzebruchSurface 1; - Y = toricProjectiveSpace 2; - phi = map(Y, X, matrix{{0,-1},{1,0}}); - D = {0,3,0,0}; - prune HDI(phi, 1, D) - Text - 2) When $\varphi \colon Y \rightarrow Y$ is a toric Frobenius map, i.e. - induced from the natural morphism on the dense torus given by raising all - of the coordinates to the same power, then the method \texttt{frobeniusDirectImage} - allows one to compute pushforwards of any coherent sheaf. For instance, - the pushforward of the trivial bundle on $\mathbb{P}^2$ by the second Frobenius map - splits into 4 line bundles. - Example - S = ring Y; - frobeniusDirectImage(2,S^1) - Text - @SUBSECTION "References"@ - Text - M. Roth and S. Zotine, \textit{Reduced \v{C}ech complexes and computing higher direct images under toric maps}, to appear on arXiv - Text - @SUBSECTION "Contributors"@ - Text - The following people have generously contributed code, improved existing code, or - enhanced the documentation: - @HREF("https://www-users.cse.umn.edu/~mahrud/", "Mahrud Sayrafi")@, and - @HREF("https://mast.queensu.ca/~ggsmith/", "Gregory G. Smith")@. + The purpose of this package is to compute (higher) direct images of + toric morphisms $\varphi \colon X \rightarrow Y$ between smooth + projective toric varieties. This is currently implemented in two + situations: + Text + 1) When $\varphi$ is a toric fibration, i.e. a surjective + toric morphism with $\varphi_* \mathcal{O}_X = \mathcal{O}_Y$, + then the method \texttt{HDI} allows one to compute the higher direct + images of a line bundle $\mathcal{O}_X(D)$ on $X$. In "Reduced \v{C}ech complexes and computing higher direct + images under toric maps", M. Roth provides a constructive method for producing + this module and S. Zotine adapts that method into an algorithm. This package + provides the implementation of this algorithm. + Text + For instance, if $X$ is a blowup of $\mathbb{P}^2$, then the first higher direct + image of $O_X(3E)$ is nontrivial, where $E$ is the exceptional divisor. + Example + X = hirzebruchSurface 1; + Y = toricProjectiveSpace 2; + phi = map(Y, X, matrix{{0,-1},{1,0}}); + D = {0,3,0,0}; + prune HDI(phi, 1, D) + Text + 2) When $\varphi \colon Y \rightarrow Y$ is a toric Frobenius map, i.e. + induced from the natural morphism on the dense torus given by raising all + of the coordinates to the same power, then the method \texttt{frobeniusPushforward} + allows one to compute pushforwards of any coherent sheaf. For instance, + the pushforward of the trivial bundle on $\mathbb{P}^2$ by the second Frobenius map + splits into 4 line bundles. + Example + S = ring Y; + frobeniusPushforward(2,S^1) + References + M. Roth and S. Zotine, \textit{Reduced \v{C}ech complexes and computing higher direct images under toric maps}, to appear on arXiv + Contributors + The following people have generously contributed code, improved existing code, or + enhanced the documentation: + @HREF("https://www-users.cse.umn.edu/~mahrud/", "Mahrud Sayrafi")@, and + @HREF("https://mast.queensu.ca/~ggsmith/", "Gregory G. Smith")@. Caveat - In case 1), we have only implemented the computation for line bundles, and if the - target is simplicial. The only finite maps for which the pushforward is - implemented are the Frobenius maps. + In case 1), we have only implemented the computation for line bundles, and if the + target is simplicial. The only finite maps for which the pushforward is + implemented are the Frobenius maps. /// doc /// Key - frobeniusDirectImage - (frobeniusDirectImage, ZZ, Module) + frobeniusPushforward + (frobeniusPushforward, ZZ, Module) + (frobeniusPushforward, ZZ, Matrix) + (frobeniusPushforward, ZZ, Complex) Headline compute the pushforward of a module under the $p$th toric Frobenius map Usage - frobeniusDirectImage(p, M) - frobeniusDirectImage_p M + frobeniusPushforward(p, M) + frobeniusPushforward_p M Inputs - p : ZZ - M : Module + p : ZZ + M : {Module, Matrix, Complex} Outputs - : Module - which is the pushforward of M under the $p$th toric Frobenius. + : {Module, Matrix, Complex} + which is the pushforward of M under the $p$th toric Frobenius. Description Text - The $p$th toric Frobenius is a toric morphism $F_p \colon X \rightarrow X$ - which is the extension of the natural group homomorphism $T_X \rightarrow T_X$ - given by raising all coordinates to the $p$th power. This allows one to view the - Cox ring $R$ of $X$ as a module over itself, with the module action being - $r \cdot m :\!= r^p m$. The extension of this action to modules also allows one - to compute the pushforward by $F_p$. Note that $p$ need not be prime, nor related - to the characteristic of the ground field in any way. - Here is the 4th pushforward of the Cox ring of the first Hirzebruch surface. - Example - X = hirzebruchSurface 1; - R = ring X; - frobeniusDirectImage(4, R^1) - Text - We can pushforward many kinds of modules. Here is the pushforward of an ideal. - Example - I = module ideal {R_0, R_2}; - prune frobeniusDirectImage(2, I) - Text - Here is the pushforward of a free module. - Example - F = R^{{1,0},{0,1}}; - frobeniusDirectImage(2, F) - Text - Here is the pushforward of a torsion module. - Example - M = R^1/(module I) - prune frobeniusDirectImage(2, M) - Text - As mentioned, $p$ is not related to the characteristic of the field, and the - outputs will be the same modules over a different coefficient ring. - Example - Y = hirzebruchSurface(1, CoefficientRing => ZZ/2); - S = ring Y; - I' = module ideal {S_0, S_2}; - prune frobeniusDirectImage(2, I') - SeeAlso - (frobeniusDirectImage, ZZ, Matrix) - (frobeniusDirectImage, ZZ, Complex) -/// - -doc /// - Key - (frobeniusDirectImage, ZZ, Matrix) - Headline - compute the pushforward of map of modules under the $p$th toric Frobenius map - Usage - frobeniusDirectImage(p, M) - frobeniusDirectImage_p M - Inputs - p : ZZ - f : Matrix - which gives a map between modules - Outputs - : Matrix - which is the pushforward of f under the $p$th toric Frobenius. - Description + The $p$th toric Frobenius is a toric morphism $F_p \colon X \rightarrow X$ + which is the extension of the natural group homomorphism $T_X \rightarrow T_X$ + given by raising all coordinates to the $p$th power. This allows one to view the + Cox ring $R$ of $X$ as a module over itself, with the module action being + $r \cdot m :\!= r^p m$. The extension of this action to modules also allows one + to compute the pushforward by $F_p$. Note that $p$ need not be prime, nor related + to the characteristic of the ground field in any way. + Here is the 4th pushforward of the Cox ring of the first Hirzebruch surface. + Example + X = hirzebruchSurface 1; + R = ring X; + frobeniusPushforward(4, R^1) Text - The $p$th toric Frobenius is a toric morphism $F_p \colon X \rightarrow X$ - which is the extension of the natural group homomorphism $T_X \rightarrow T_X$ - given by raising all coordinates to the $p$th power. This allows one to view the - Cox ring $R$ of $X$ as a module over itself, with the module action being - $r \cdot m :\!= r^p m$. The extension of this action to modules also allows one - to compute the pushforward by $F_p$. Note that $p$ need not be prime, nor related - to the characteristic of the ground field in any way. - The pushforward is an endofunctor on the category of $R$-modules, so we may - apply it to maps between modules. - Example - X = hirzebruchSurface 1; - R = ring X; - M = koszul_1 vars R - frobeniusDirectImage_2 M - SeeAlso - (frobeniusDirectImage, ZZ, Module) - (frobeniusDirectImage, ZZ, Complex) -/// - -doc /// - Key - (frobeniusDirectImage, ZZ, Complex) - Headline - compute the pushforward of a complex of modules under the $p$th toric Frobenius map - Usage - frobeniusDirectImage(p, C) - frobeniusDirectImage_p C - Inputs - p : ZZ - C : Complex - of modules - Outputs - : Complex - which is the pushforward of C under the $p$th toric Frobenius. - Description + We can pushforward many kinds of modules. Here is the pushforward of an ideal. + Example + I = module ideal {R_0, R_2}; + prune frobeniusPushforward(2, I) + Text + Here is the pushforward of a free module. + Example + F = R^{{1,0},{0,1}}; + frobeniusPushforward(2, F) Text - The $p$th toric Frobenius is a toric morphism $F_p \colon X \rightarrow X$ - which is the extension of the natural group homomorphism $T_X \rightarrow T_X$ - given by raising all coordinates to the $p$th power. This allows one to view the - Cox ring $R$ of $X$ as a module over itself, with the module action being - $r \cdot m :\!= r^p m$. The extension of this action to modules also allows one - to compute the pushforward by $F_p$. Note that $p$ need not be prime, nor related - to the characteristic of the ground field in any way. + Here is the pushforward of a torsion module. + Example + M = R^1/(module I) + prune frobeniusPushforward(2, M) + Text + As mentioned, $p$ is not related to the characteristic of the field, and the + outputs will be the same modules over a different coefficient ring. + Example + Y = hirzebruchSurface(1, CoefficientRing => ZZ/2); + S = ring Y; + I' = module ideal {S_0, S_2}; + prune frobeniusPushforward(2, I') + Text The pushforward is an endofunctor on the category of $R$-modules, so we may - apply it to complexes of $R$-modules. - Example - X = hirzebruchSurface 1; - R = ring X; - C = complex koszul vars R - frobeniusDirectImage_2 C + apply it to morphisms and complexes of $R$-modules. + Example + X = hirzebruchSurface 1; + R = ring X; + C = complex koszul vars R + frobeniusPushforward_2 C SeeAlso - (frobeniusDirectImage, ZZ, Module) - (frobeniusDirectImage, ZZ, Matrix) + (HDI, ToricMap, ZZ, List) + (HDI, ToricMap, ZZ, ToricDivisor) + (HDI, ToricMap, ZZ, Module) + (HDI, ToricMap, ZZ, CoherentSheaf) /// doc /// Key - nefContraction - (nefContraction, NormalToricVariety, List) - (nefContraction, NormalToricVariety, Vector) - (nefContraction, NormalToricVariety, Matrix) + nefContraction + (nefContraction, NormalToricVariety, List) + (nefContraction, NormalToricVariety, Vector) + (nefContraction, NormalToricVariety, Matrix) Headline produce a contraction from a vector in the nef cone Usage - nefContraction(X, v) + nefContraction(X, v) Inputs - X : NormalToricVariety - v : {List, Vector, Matrix} - a vector presumed to be in the nef cone of $X$ + X : NormalToricVariety + v : {List, Vector, Matrix} + a vector presumed to be in the nef cone of $X$ Outputs - : ToricMap + : ToricMap Description Text - A vector in the nef cone of a toric variety determines an embedding - of the toric variety via GIT quotients. When the vector lies on the boundary - of the nef cone, the resulting embedding is typically a "smaller" toric variety - on which the original variety can surject. These types of maps are commonly - used in the minimal model program (MMP) - Text - More precisely, see Lemma 14.4.6 and Theorem 15.1.10 in HREF("https://dacox.people.amherst.edu/toric.html", - "Toric varieties"). The nef cone of a toric variety is a GKZ chamber and each - face of this cone is also a GKZ chamber, hence the nef cone of another toric variety. - Since both of these are GIT quotients, there is a natural map induced by the projection - of vector spaces between the quotients, which this method produces. - Text - The first Hirzebruch surface $\mathbb{F}_1$ comes with two natural maps to - $\mathbb{P}^1$ (projection to the base) and $\mathbb{P}^2$ (the blowdown map). - Both of these can be realized as maps obtained from the MMP. - Example - X = hirzebruchSurface 1; - pi1 = nefContraction(X, vector {1,0}) - assert(isWellDefined pi1); - assert(isProjective target pi1 and dim target pi1 == 1); - pi2 = nefContraction(X, matrix {{0},{1}}) - assert(isProjective target pi1 and dim target pi2 == 2); - assert(rank picardGroup target pi2 == 1); - Text - If the vector is in the interior of the nef cone, then the resulting toric variety - is the same as the original, and so the resulting surjection is just the identity. - Example - idty = nefContraction(X,{1,1}) - assert(rays target idty == rays X); - assert(max target idty == max X); + A vector in the nef cone of a toric variety determines an embedding + of the toric variety via GIT quotients. When the vector lies on the boundary + of the nef cone, the resulting embedding is typically a "smaller" toric variety + on which the original variety can surject. These types of maps are commonly + used in the minimal model program (MMP) + Text + More precisely, see Lemma 14.4.6 and Theorem 15.1.10 in HREF("https://dacox.people.amherst.edu/toric.html", + "Toric varieties"). The nef cone of a toric variety is a GKZ chamber and each + face of this cone is also a GKZ chamber, hence the nef cone of another toric variety. + Since both of these are GIT quotients, there is a natural map induced by the projection + of vector spaces between the quotients, which this method produces. + Text + The first Hirzebruch surface $\mathbb{F}_1$ comes with two natural maps to + $\mathbb{P}^1$ (projection to the base) and $\mathbb{P}^2$ (the blowdown map). + Both of these can be realized as maps obtained from the MMP. + Example + X = hirzebruchSurface 1; + pi1 = nefContraction(X, vector {1,0}) + assert(isWellDefined pi1); + assert(isProjective target pi1 and dim target pi1 == 1); + pi2 = nefContraction(X, matrix {{0},{1}}) + assert(isProjective target pi1 and dim target pi2 == 2); + assert(rank picardGroup target pi2 == 1); + Text + If the vector is in the interior of the nef cone, then the resulting toric variety + is the same as the original, and so the resulting surjection is just the identity. + Example + idty = nefContraction(X,{1,1}) + assert(rays target idty == rays X); + assert(max target idty == max X); SeeAlso - (nefRayContractions, NormalToricVariety) - (allNefContractions, NormalToricVariety) + (nefRayContractions, NormalToricVariety) + (allNefContractions, NormalToricVariety) /// doc /// Key - nefRayContractions - (nefRayContractions, NormalToricVariety) + nefRayContractions + (nefRayContractions, NormalToricVariety) Headline produces all contractions corresponding to rays in the nef cone Usage - nefRayContractions X + nefRayContractions X Inputs - X : NormalToricVariety + X : NormalToricVariety Outputs - : List - of ToricMaps obtained by each ray of the nef cone + : List + of ToricMaps obtained by each ray of the nef cone Description Text - This method is a convenient way to produce all of the nef contractions - corresponding to the rays of the nef cone, without the user needing to - know those rays. - Example - X = hirzebruchSurface 1; - L = nefRayContractions X - assert(isProjective target L_0 and dim target L_0 == 1); - assert(isProjective target L_1 and dim target L_1 == 2); - assert(rank picardGroup target L_1 == 1); + This method is a convenient way to produce all of the nef contractions + corresponding to the rays of the nef cone, without the user needing to + know those rays. + Example + X = hirzebruchSurface 1; + L = nefRayContractions X + assert(isProjective target L_0 and dim target L_0 == 1); + assert(isProjective target L_1 and dim target L_1 == 2); + assert(rank picardGroup target L_1 == 1); SeeAlso - (nefContraction, NormalToricVariety, List) - (nefContraction, NormalToricVariety, Vector) - (nefContraction, NormalToricVariety, Matrix) - (nefRayContractions, NormalToricVariety) + (nefContraction, NormalToricVariety, List) + (nefContraction, NormalToricVariety, Vector) + (nefContraction, NormalToricVariety, Matrix) + (nefRayContractions, NormalToricVariety) /// doc /// Key - allNefContractions - (allNefContractions, NormalToricVariety) + allNefContractions + (allNefContractions, NormalToricVariety) Headline produces all contractions corresponding to each face in the nef cone Usage - nefRayContractions X + nefRayContractions X Inputs - X : NormalToricVariety + X : NormalToricVariety Outputs - : List - of ToricMaps obtained by contracting a face of the nef cone + : List + of ToricMaps obtained by contracting a face of the nef cone Description Text - This method chooses a vector in the relative interior of - every face of the nef cone and produces the corresponding map. - Example - X = hirzebruchSurface 1; - L = allNefContractions X - assert(all(L, phi -> isWellDefined phi)) + This method chooses a vector in the relative interior of + every face of the nef cone and produces the corresponding map. + Example + X = hirzebruchSurface 1; + L = allNefContractions X + assert(all(L, phi -> isWellDefined phi)) Caveat - This method is exponential in the number of rays of the fan, so the runtime - may be quite long! + This method is exponential in the number of rays of the fan, so the runtime + may be quite long! SeeAlso - (nefContraction, NormalToricVariety, List) - (nefContraction, NormalToricVariety, Vector) - (nefContraction, NormalToricVariety, Matrix) - (nefRayContractions, NormalToricVariety) + (nefContraction, NormalToricVariety, List) + (nefContraction, NormalToricVariety, Vector) + (nefContraction, NormalToricVariety, Matrix) + (nefRayContractions, NormalToricVariety) /// doc /// Key - computeEigencharacters - (computeEigencharacters, ToricMap, ZZ, List) - (computeEigencharacters, ToricMap, ZZ, ToricDivisor) + computeEigencharacters + (computeEigencharacters, ToricMap, ZZ, List) + (computeEigencharacters, ToricMap, ZZ, ToricDivisor) Headline compute the eigencharacters Usage - computeEigencharacters(phi, i, D) + computeEigencharacters(phi, i, D) Inputs - phi : ToricMap - a surjective toric morphism - i : ZZ - D : {List, ToricDivisor} - a list indexing a Cartier divisor on the source of phi + phi : ToricMap + a surjective toric morphism + i : ZZ + D : {List, ToricDivisor} + a list indexing a Cartier divisor on the source of phi Outputs - : HashTable - whose keys are the eigencharacters and values are the (D,E) pairs + : HashTable + whose keys are the eigencharacters and values are the (D,E) pairs Description Text - This method is the implementation of Algorithm A.5 in - "Reduced \v{C}ech complexes and computing higher direct - images under toric maps". More precisely, the higher direct image - sheaf inherits a trivial torus action by the kernel torus - $T_K :\!= \ker \phi\vert_{T_X}$, and so splits into a direct sum - of sheaves indexed by eigencharacters. This method computes these - characters, as well as a pair of divisors $(D,E)$ for use in - Algorithm A.8 which computes the higher direct image sheaf. - Example - X = hirzebruchSurface 1; - Y = toricProjectiveSpace 1; - phi = map(Y,X,matrix{{1,0}}); - D = {0,-3,0,0}; - computeEigencharacters(phi,1,D) - Text - One might hope that the set of eigencharacters is a convex set, - but this is not always the case. For instance, when $X$ - is the blowup of $\mathbb{P}^1 \times \mathbb{P}^1$ at the four - torus fixed points and phi is the projection to $\mathbb{P}^1$, - we can find an example of a line bundle whose eigencharacters are not convex. - Example - Z = normalToricVariety({{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}, - {0,-1},{1,-1}},{{0,1},{1,2},{2,3},{3,4},{4,5},{5,6},{6,7},{0,7}}); - phi = map(Y,Z,matrix{{1,0}}); - D = toricDivisor({0,-2,0,0,0,-2,1,0},Z); - keys computeEigencharacters(phi,1,D) + This method is the implementation of Algorithm A.5 in + "Reduced \v{C}ech complexes and computing higher direct + images under toric maps". More precisely, the higher direct image + sheaf inherits a trivial torus action by the kernel torus + $T_K :\!= \ker \phi\vert_{T_X}$, and so splits into a direct sum + of sheaves indexed by eigencharacters. This method computes these + characters, as well as a pair of divisors $(D,E)$ for use in + Algorithm A.8 which computes the higher direct image sheaf. + Example + X = hirzebruchSurface 1; + Y = toricProjectiveSpace 1; + phi = map(Y,X,matrix{{1,0}}); + D = {0,-3,0,0}; + computeEigencharacters(phi,1,D) + Text + One might hope that the set of eigencharacters is a convex set, + but this is not always the case. For instance, when $X$ + is the blowup of $\mathbb{P}^1 \times \mathbb{P}^1$ at the four + torus fixed points and phi is the projection to $\mathbb{P}^1$, + we can find an example of a line bundle whose eigencharacters are not convex. + Example + Z = normalToricVariety({{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}, + {0,-1},{1,-1}},{{0,1},{1,2},{2,3},{3,4},{4,5},{5,6},{6,7},{0,7}}); + phi = map(Y,Z,matrix{{1,0}}); + D = toricDivisor({0,-2,0,0,0,-2,1,0},Z); + keys computeEigencharacters(phi,1,D) SeeAlso - (HDI, ToricMap, ZZ, List) - (HDI, ToricMap, ZZ, ToricDivisor) - (HDI, ToricMap, ZZ, Module) - (HDI, ToricMap, ZZ, CoherentSheaf) + (HDI, ToricMap, ZZ, List) + (HDI, ToricMap, ZZ, ToricDivisor) + (HDI, ToricMap, ZZ, Module) + (HDI, ToricMap, ZZ, CoherentSheaf) /// doc /// Key - HDI - (HDI, ToricMap, ZZ, List) - (HDI, ToricMap, ZZ, ToricDivisor) - (HDI, ToricMap, ZZ, Module) - (HDI, ToricMap, ZZ, CoherentSheaf) - (symbol_*, ToricMap) + HDI + (HDI, ToricMap, ZZ, List) + (HDI, ToricMap, ZZ, ToricDivisor) + (HDI, ToricMap, ZZ, Module) + (HDI, ToricMap, ZZ, CoherentSheaf) + (symbol_*, ToricMap) Headline compute the $i$th higher direct image Usage - HDI(phi, i, D) - phi_*^i D + HDI(phi, i, D) + phi_*^i D Inputs - phi : ToricMap - a surjective toric morphism - i : ZZ - D : {List, ToricDivisor, Module, CoherentSheaf} - a Cartier divisor or rank one free module or line bundle on the source of phi. + phi : ToricMap + a surjective toric morphism + i : ZZ + D : {List, ToricDivisor, Module, CoherentSheaf} + a Cartier divisor or rank one free module or line bundle on the source of phi. Outputs - : {Module, CoherentSheaf} - which is the $i$th higher direct image of $\mathcal{O}_X(D)$. + : {Module, CoherentSheaf} + which is the $i$th higher direct image of $\mathcal{O}_X(D)$. Description Text - This method implements Algorithm A.8 in "Reduced \v{C}ech complexes and computing higher direct - images under toric maps". - Text - The first Hirzebruch surface $\mathbb{F}_1$ has two geometric interpretations: 1) as the projective - bundle $\mathbb{P}(\mathcal{O}_{\mathbb{P}^1} \oplus \mathcal{O}_{\mathbb{P}^1})$ - or as the blowup of $\mathbb{P}^2$ at a torus-fixed point. In both cases we get a - toric morphism, the projection and blowdown map. In the first case, the (higher) direct images - of a line bundle are themselves vector bundles, and hence split. - Example - X = hirzebruchSurface 1; - Y = toricProjectiveSpace(1, Variable => y); - phi = map(Y, X, matrix {{1,0}}); - D1 = {3,4,0,0}; - RD1 = prune HDI(phi,0,D1) - D2 = toricDivisor({3,-4,0,0},X); - RD2 = prune HDI(phi,1,D2) - Text - In the second case, we can find line bundles whose (higher) direct images which have relations or torsion. - Example - Z = toricProjectiveSpace(2, Variable => z); - phi = map(Z, X, matrix {{0,-1},{1,0}}); - M = (ring X)^{{-6,3}}; - RM = prune phi_*^0 M - L = sheaf_X M; - RL = prune phi_*^1 L - annihilator RL + This method implements Algorithm A.8 in "Reduced \v{C}ech complexes and computing higher direct + images under toric maps". + Text + The first Hirzebruch surface $\mathbb{F}_1$ has two geometric interpretations: 1) as the projective + bundle $\mathbb{P}(\mathcal{O}_{\mathbb{P}^1} \oplus \mathcal{O}_{\mathbb{P}^1})$ + or as the blowup of $\mathbb{P}^2$ at a torus-fixed point. In both cases we get a + toric morphism, the projection and blowdown map. In the first case, the (higher) direct images + of a line bundle are themselves vector bundles, and hence split. + Example + X = hirzebruchSurface 1; + Y = toricProjectiveSpace(1, Variable => y); + phi = map(Y, X, matrix {{1,0}}); + D1 = {3,4,0,0}; + RD1 = prune HDI(phi,0,D1) + D2 = toricDivisor({3,-4,0,0},X); + RD2 = prune HDI(phi,1,D2) + Text + In the second case, we can find line bundles whose (higher) direct images which have relations or torsion. + Example + Z = toricProjectiveSpace(2, Variable => z); + phi = map(Z, X, matrix {{0,-1},{1,0}}); + M = (ring X)^{{-6,3}}; + RM = prune phi_*^0 M + L = sheaf_X M; + RL = prune phi_*^1 L + annihilator RL SeeAlso - (frobeniusDirectImage, ZZ, Module) - (frobeniusDirectImage, ZZ, Matrix) - (frobeniusDirectImage, ZZ, Complex) + (frobeniusPushforward, ZZ, Module) + (frobeniusPushforward, ZZ, Matrix) + (frobeniusPushforward, ZZ, Complex) /// ------------------------------------------------------------------------------ @@ -943,7 +877,7 @@ doc /// TEST /// X = toricProjectiveSpace 2; S = ring X; -FS = frobeniusDirectImage_2 S^1; +FS = frobeniusPushforward_2 S^1; assert(degrees FS == {{0}, {1}, {1}, {1}}) /// @@ -952,17 +886,17 @@ X = smoothFanoToricVariety(3,7); d = dim X; p = 4; S = ring X; -FS = frobeniusDirectImage_p S^1 +FS = frobeniusPushforward_p S^1 assert(rank FS == p^d) /// TEST /// X = hirzebruchSurface 3; S = ring X; -M = frobeniusDirectImage_2 koszul_1 vars S; +M = frobeniusPushforward_2 koszul_1 vars S; M' = map(S^{{0, 0}, {1, -1}, {-1, 0}, {1, -1}}, S^{{-1, 0}, {0, -1}, {-1, 0}, {1, -1}, {1, -1}, {2, -1}, {1, -1}, {3, -1}, - {-1, 0}, {0, -1}, {-1, 0}, {1, -1}, {0, -1}, {1, -1}, {-1, -1}, {1, -1}}, + {-1, 0}, {0, -1}, {-1, 0}, {1, -1}, {0, -1}, {1, -1}, {-1, -1}, {1, -1}}, matrix {{x_0, 0, 0, 0, 0, 0, 0, x_1, 0, 0, x_2, 0, x_3, 0, 0, 0}, {0, x_0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0}, {0, 0, 1, 0, 0, x_1, 0, 0, 1, 0, 0, 0, 0, 0, x_3, 0}, @@ -976,7 +910,7 @@ d = dim X; p = 2; S = ring X; C = complex koszul vars S; -FC = frobeniusDirectImage_p C; +FC = frobeniusPushforward_p C; assert(isWellDefined FC) assert(length FC == length C) assert(all(length C, i -> rank FC_i == (rank C_i) * p^d)) diff --git a/M2/Macaulay2/packages/ToricTopology.m2 b/M2/Macaulay2/packages/ToricTopology.m2 index c4805da9716..25d4073a2f2 100644 --- a/M2/Macaulay2/packages/ToricTopology.m2 +++ b/M2/Macaulay2/packages/ToricTopology.m2 @@ -29,7 +29,7 @@ newPackage( {Name => "Kumar Sannidhya Shukla", Email => "kshukla5@uwo.ca"} }, Keywords => {"Toric Geometry"}, - PackageImports => { "OldChainComplexes", "SimplicialComplexes" }, + PackageImports => { "Complexes", "SimplicialComplexes" }, Headline => "toric topology" ) @@ -796,7 +796,7 @@ doc/// the Euler characteristic of the moment-angle complex Description Text - This method computes the Euler characterisitc of moment-angle + This method computes the Euler characteristic of moment-angle complexes. Text The Euler characteristic of a moment-angle complex is $0$ if the @@ -855,7 +855,7 @@ doc /// hessenbergVariety (hessenbergVariety,ZZ) Headline - Hessenberg variety asscoiated to the n-permutahedron + Hessenberg variety associated to the n-permutahedron Usage hessenbergVariety(n) Inputs @@ -864,7 +864,7 @@ doc /// :SmallCover Description Text - Hessenberg variety asscoiated to the n-permutahedron, as small + Hessenberg variety associated to the n-permutahedron, as small cover. SeeAlso /// diff --git a/M2/Macaulay2/packages/TropicalToric/TropicalToricCode.m2 b/M2/Macaulay2/packages/TropicalToric/TropicalToricCode.m2 index 0c9dcd12619..b3ab6a3cbaf 100644 --- a/M2/Macaulay2/packages/TropicalToric/TropicalToricCode.m2 +++ b/M2/Macaulay2/packages/TropicalToric/TropicalToricCode.m2 @@ -100,7 +100,7 @@ poincareMatrix (NormalToricVariety,ZZ) := (X,k) ->( --input: smooth normal toric variety X, list of integers l representing -- a function in Hom(A^k(X),Z) ---output: the corresponding class in A^(m-k)(X) by Poincare duality, +--output: the corresponding class in A^(m-k)(X) by Poincaré duality, -- where m is the maximum codimension such that A^m(X) is not zero -- ( recall that A^k(X) corresponds to orbits(X,n-k) ) poincareDuality = method(TypicalValue => List) @@ -122,7 +122,7 @@ classFromTropical (NormalToricVariety,Ideal) := (X, I) ->( mult := refineMultiplicity(T,X'); --vector of products [V(J)]*[V(sigma)] in X prod := pushforwardMultiplicity(X,X',mult,k); - --apply Poincare duality + --apply Poincaré duality Y := poincareDuality(prod,X,k); m := #(max X)_0; O := orbits(X,dim X-(m-k)); @@ -146,7 +146,7 @@ classWonderfulCompactification (NormalToricVariety, Ideal, Ideal) := (X,I,J) ->( (multiplicities T)_(position(maxCones T, c -> isSubset(C,c))) ); --faster than mult := refineMultiplicity(T,X'); prod := pushforwardMultiplicity(X,X',mult,k); - --apply Poincare duality + --apply Poincaré duality Y := poincareDuality(prod,X,k); O := orbits(X,n-m+k); --we want the cycle to have codimension inside X the same that T has inside T' D := sum apply(#O, s -> Y_s * X_(O_s)); diff --git a/M2/Macaulay2/packages/Valuations.m2 b/M2/Macaulay2/packages/Valuations.m2 index 0baf8cddece..a65e1f3a224 100644 --- a/M2/Macaulay2/packages/Valuations.m2 +++ b/M2/Macaulay2/packages/Valuations.m2 @@ -38,6 +38,8 @@ export{"valuation", "OrderedQQVector", "orderedQQn"} +importFrom(Core, "isPromotable") + OrderedQQn = new Type of Module OrderedQQVector = new Type of Vector @@ -57,7 +59,7 @@ valuation Function := v -> ( internalValuation(v, null, null) ) -ourSources := {Ring,Subring,LocalRing,RingOfInvariants} +ourSources := {Ring,Subring,LocalRing,RingOfInvariants,RingFamily} ourTargets := {Ring,Subring,LocalRing,RingOfInvariants,OrderedQQn} -- Create different valuation functions for various inputs @@ -89,16 +91,15 @@ ourInputs := {Number, RingElement, Constant} -- Other inputs will need to be added to that list, if needed for i in ourInputs do ( Valuation i := (v,t) -> ( - num := try numerator t then numerator t else t; - den := try denominator t then denominator t else 1_(ring t); - if (v#source === null) or (ring t) === v#source then - v#"function" t - else if (isMember(ring t, v#source.baseRings)) then - v#"function" promote(t, v#source) - else if (ring t) === v#source then - v#"function" num - v#"function" den - else if (isMember(ring num, v#source.baseRings)) then - v#"function" promote(num, v#source) - v#"function" promote(den, v#source) + (f, R) := (v#"function", v#source); + if R === null then f t + else ( + if not isPromotable(ring t, R) + then error("expected an element of ", R); + if lookup(numerator, class t) =!= null then ( + (num, den) := (numerator t, denominator t); + f num - f den) + else f t) ) ) @@ -222,16 +223,14 @@ countPrimeFactor (ZZ, ZZ) := (p, x) -> ( ) -- p-adic Valuation valuation construction --- Allows for inputs to be rationals and computes difference of the --- valuations for the numerator and denominator padicValuation = method() padicValuation ZZ := p -> ( if not isPrime p then error "expected a prime integer"; func := x -> ( if x == 0 then infinity - else countPrimeFactor(p, numerator x_QQ) - countPrimeFactor(p, denominator x_QQ) + else countPrimeFactor(p, x) ); - valuation(func,QQ,QQ) + valuation(func,QQ,ZZ) ) -- Leading Term Valuation, @@ -279,7 +278,7 @@ localRingValuation LocalRing := R -> ( if x == 0 then infinity else getMExponent(m, sub(x, S)) ); - valuation(func, R, ZZ) + valuation(func, frac R, ZZ) ) -------------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/Varieties.m2 b/M2/Macaulay2/packages/Varieties.m2 index 0242364e44d..04c563ab168 100644 --- a/M2/Macaulay2/packages/Varieties.m2 +++ b/M2/Macaulay2/packages/Varieties.m2 @@ -71,7 +71,6 @@ export { -- Functors "hh", -- TODO: should this be defined in Core? "OO", - "RHom", -- Symbols "GlobalSectionLimit", "SaturationMap", @@ -85,6 +84,7 @@ importFrom_Core { "toString'", "expressionValue", "unhold", -- TODO: prune these "concatBlocks", "concatCols", "concatRows", "addHook", "tryHooks", "cacheHooks", + "liftModule", "liftMorphism", "isMorphism", "isAbelianCategory", "BinaryPowerMethod", } @@ -298,10 +298,6 @@ sheaf(Variety, Module) := CoherentSheaf => (X, M) -> ( sheaf Ideal := Ideal^~ := CoherentSheaf => I -> sheaf(variety ring I, module I) sheaf(Variety, Ideal) := CoherentSheaf => (X, I) -> sheaf(X, module I) --- TODO: remove by M2 1.25 -tildeWarn = true -Thing~ := x -> (if tildeWarn then (tildeWarn = false; printerr "Note: M~ is deprecated; use M^~ or sheaf instead."); x^~) - OO = new ScriptedFunctor from { subscript => X -> applyMethod((symbol _, OO, class X), (OO, X)), argument => X -> applyMethod((symbol SPACE, OO, class X), (OO, X)), @@ -497,21 +493,18 @@ checkVariety := (X, F) -> ( if not isAffineRing ring X then error "expected a variety defined over a field"; ) --- pushforward the module to PP^n via S/I <-- S -flattenModule = M -> cokernel flattenMorphism presentation M -flattenMorphism = f -> ( - g := presentation ring f; - S := ring g; - -- TODO: sometimes lifting to ring g is enough, how can we detect this? - -- TODO: why doesn't lift(f, ring g) do this automatically? - map(target f ** S, source f ** S, lift(cover f, S)) ** cokernel g) --* -flattenComplex = C -> ( +-- these synonyms are added to avoid a merge conflict +flattenModule = liftModule +flattenMorphism = liftMorphism + +-- pushforward the complex to PP^n via S/I <-- S +-- TODO: move to Complexes? +flattenComplex = C -> C.cache#"flattenComplex" ??= ( + if instance(ring C, PolynomialRing) then return C; (lo, hi) := C.concentration; if lo === hi then complex(flattenModule C_lo, Base => lo) else complex applyValues(C.dd.map, flattenMorphism)) -*- -- TODO: this is called twice -- TODO: implement for multigraded ring diff --git a/M2/Macaulay2/packages/Varieties/SheafComplexes.m2 b/M2/Macaulay2/packages/Varieties/SheafComplexes.m2 index ed66cc116a3..5a2b10121e3 100644 --- a/M2/Macaulay2/packages/Varieties/SheafComplexes.m2 +++ b/M2/Macaulay2/packages/Varieties/SheafComplexes.m2 @@ -1,4 +1,5 @@ export { + "RHom", } ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/Varieties/SheafMaps.m2 b/M2/Macaulay2/packages/Varieties/SheafMaps.m2 index 7a13111422e..f07cf3f1a43 100644 --- a/M2/Macaulay2/packages/Varieties/SheafMaps.m2 +++ b/M2/Macaulay2/packages/Varieties/SheafMaps.m2 @@ -2,15 +2,14 @@ export { -- Types "SheafMap", -- Methods - "sheafMap", -- "isLiftable", - "yonedaSheafExtension", +-- "yonedaSheafExtension", -- "yonedaSheafExtension'", - "cotangentSurjection", - "eulerSequence", - "idealSheafSequence", - "embeddedToAbstract", - "ExtLongExactSequence", +-- "cotangentSurjection", +-- "eulerSequence", +-- "idealSheafSequence", +-- "embeddedToAbstract", +-- "ExtLongExactSequence", } ----------------------------------------------------------------------------- @@ -28,10 +27,6 @@ autotruncate = { MinimalGenerators => false } >> opts -> L -> ( deg := max apply(L, f -> f.degree); apply(L, f -> subtruncate(deg, f.map, opts))) --- TODO: confirm that this is the right choice --- should regularity defined in Complexes do this?? -regularity' = M -> regularity flattenModule M - ----------------------------------------------------------------------------- -- SheafHom type declarations and basic constructors ----------------------------------------------------------------------------- @@ -89,10 +84,6 @@ sheaf(Variety, Matrix) := SheafMap => (X, phi) -> map(sheaf_X target phi, sheaf(Variety, Matrix, ZZ) := SheafMap => (X, phi, d) -> map(sheaf_X target phi, sheaf_X source phi, truncate(d, phi, MinimalGenerators => false), d) --- TODO: remove by M2 1.25 -sheafMapWarn = true -sheafMap = x -> (if sheafMapWarn then (sheafMapWarn = false; printerr "Note: sheafMap is deprecated; use sheaf instead."); sheaf x) - random(CoherentSheaf, CoherentSheaf) := SheafMap => o -> (F, G) -> map(F, G, random(F.module, G.module, o)) isWellDefined SheafMap := f -> ( @@ -162,8 +153,8 @@ SheafMap == SheafMap := Boolean => (psi, phi) -> psi === phi or ( -- g := if phi.cache.?minimalPresentation then phi.cache.minimalPresentation.map else phi.map; -- if f == g then return true; -- r := 1 + max( - -- regularity' target f, regularity' source f, - -- regularity' target g, regularity' source g); + -- regularity target f, regularity source f, + -- regularity target g, regularity source g); -- truncate(r, f, MinimalGenerators => false) == truncate(r, g, MinimalGenerators => false)) SheafMap == ZZ := Boolean => (f, n) -> ( if n === 0 then image f == n else matrix(prune f) == n) @@ -201,7 +192,7 @@ isIsomorphic(CoherentSheaf, CoherentSheaf) := Boolean => o -> (F, G) -> F === G -- TODO: using regularity in won't suffice in the multigraded case, -- and multigradedRegularity may not be optimal. What should methods -- that truncate the base module do instead? - r := 1 + max(regularity' F.module, regularity' G.module); + r := 1 + max(regularity F.module, regularity G.module); truncate(r, F.module, MinimalGenerators => false), truncate(r, G.module, MinimalGenerators => false)); -- FIXME: this is incomplete, because we need to store pruning maps or embedding maps @@ -411,7 +402,7 @@ SheafMap.InverseMethod = (cacheValue symbol inverse) (f -> ( g := matrix f; -- truncate the underlying map so it is an isomorphism -- TODO: make this more efficient, e.g. look at degrees of ann coker g - e := max(regularity' ker g, regularity' coker g); + e := max(regularity ker g, regularity coker g); -- TODO: this is kludgy, but maybe it works? h := try inverse g else inverse truncate(e + 1, g); -- then invert and sheafify the new map @@ -610,7 +601,7 @@ ExtLongExactSequence(CoherentSheaf, SheafMap, SheafMap) := Matrix => opts -> (F, --probably just add in l3, P3, etc., take max as above M = truncate(r, M, MinimalGenerators => false)); -- TODO: can we truncate at the regularity of homology(f,g) instead? - reg := 1 + max(regularity' coker matrix f, regularity' ker matrix g); + reg := 1 + max(regularity coker matrix f, regularity ker matrix g); -- TODO: verify the Base of the complex -- TODO: should lo be used somewhere? part_0 ExtLES(M, diff --git a/M2/Macaulay2/packages/Varieties/doc-functors.m2 b/M2/Macaulay2/packages/Varieties/doc-functors.m2 index e37ea81f21a..4a88d857ee4 100644 --- a/M2/Macaulay2/packages/Varieties/doc-functors.m2 +++ b/M2/Macaulay2/packages/Varieties/doc-functors.m2 @@ -411,7 +411,7 @@ Node Hom HH sheafExt - yonedaSheafExtension +-- yonedaSheafExtension Node Key @@ -549,14 +549,14 @@ Node HH sheafExt -Node - Key - ExtLongExactSequence +-- Node +-- Key +-- ExtLongExactSequence -- (ExtLongExactSequence, CoherentSheaf, SheafMap) -- (ExtLongExactSequence, CoherentSheaf, SheafMap, SheafMap) -- [ExtLongExactSequence, Concentration] - Headline - the long exact sequence of the Ext functor +-- Headline +-- the long exact sequence of the Ext functor ----------------------------------------------------------------------------- -- sheafExt diff --git a/M2/Macaulay2/packages/Varieties/doc-maps.m2 b/M2/Macaulay2/packages/Varieties/doc-maps.m2 index ee2a80bc6c7..600b65a8705 100644 --- a/M2/Macaulay2/packages/Varieties/doc-maps.m2 +++ b/M2/Macaulay2/packages/Varieties/doc-maps.m2 @@ -1,5 +1,4 @@ undocumented { - sheafMap, -- deprecated -- TODO: document some of these (symbol SPACE, SheafMap, ZZ), (symbol +, SheafMap, SheafMap), @@ -92,14 +91,14 @@ Node Headline induced maps on coherent sheaves -Node - Key - cotangentSurjection +-- Node +-- Key +-- cotangentSurjection -- (cotangentSurjection, ProjectiveVariety) -Node - Key - embeddedToAbstract +-- Node +-- Key +-- embeddedToAbstract -- (embeddedToAbstract, ProjectiveVariety) ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/Varieties/doc-sheaves.m2 b/M2/Macaulay2/packages/Varieties/doc-sheaves.m2 index 3c82199ec21..d734c4ffd48 100644 --- a/M2/Macaulay2/packages/Varieties/doc-sheaves.m2 +++ b/M2/Macaulay2/packages/Varieties/doc-sheaves.m2 @@ -426,7 +426,7 @@ Node SeeAlso idealSheaf tangentSheaf - cotangentSurjection +-- cotangentSurjection -- (cotangentSheaf, ZZ, ProjectiveVariety) ProjectiveVariety diff --git a/M2/Macaulay2/packages/VirtualResolutions.m2 b/M2/Macaulay2/packages/VirtualResolutions.m2 index a0cff820757..c92ac992da5 100644 --- a/M2/Macaulay2/packages/VirtualResolutions.m2 +++ b/M2/Macaulay2/packages/VirtualResolutions.m2 @@ -25,7 +25,7 @@ newPackage ("VirtualResolutions", }, Keywords => {"Commutative Algebra", "Homological Algebra"}, PackageImports => {"Elimination", "Depth", "Saturation", "SpaceCurves"}, - PackageExports => {"NormalToricVarieties", "TateOnProducts", "LinearTruncations"}, + PackageExports => {"NormalToricVarieties", "LinearTruncations", "TateOnProducts"}, AuxiliaryFiles => true, DebuggingMode => false, Certification => { @@ -114,10 +114,10 @@ submatrixWinnowMap = (phi, alphas) -> ( -------------------------------------------------------------------- protect winnowingMap virtualOfPair = method(Options => {LengthLimit => infinity}) --- TODO: return a Matrix in the Module case and ChainComplexMap in the ChainComplex case +-- TODO: return a Matrix in the Module case and ComplexMap in the Complex case -- TODO: document the winnoingMap -virtualOfPair(Ideal, List) := ChainComplex => opts -> (I, alphas) -> virtualOfPair(comodule I, alphas, opts) -virtualOfPair(Module, List) := ChainComplex => opts -> (M, alphas) -> ( +virtualOfPair(Ideal, List) := Complex => opts -> (I, alphas) -> virtualOfPair(comodule I, alphas, opts) +virtualOfPair(Module, List) := Complex => opts -> (M, alphas) -> ( R := ring M; if M.cache.?resolution then return virtualOfPair(M.cache.resolution, alphas, opts); if any(alphas, alpha -> #alpha =!= degreeLength R) then error "degree has wrong length"; @@ -126,10 +126,10 @@ virtualOfPair(Module, List) := ChainComplex => opts -> (M, alphas) -> ( i := 2; L := {m} | while m != 0 and i <= opts.LengthLimit list ( i = i + 1; m = map(R, rawKernelOfGB raw m); m = submatrixWinnow(m, alphas)); - chainComplex L) -virtualOfPair(ChainComplex, List) := ChainComplex => opts -> (F, alphas) -> ( + complex L) +virtualOfPair(Complex, List) := Complex => opts -> (F, alphas) -> ( if any(alphas, alpha -> #alpha =!= degreeLength ring F) then error "degree has wrong length"; - L := chainComplex apply(min F .. max F - 1, i -> submatrixWinnow(F.dd_(i+1), alphas)); + L := complex toList apply(min F .. max F, i -> submatrixWinnow(F.dd_(i+1), alphas)); -- winnowingMap is the map M --> HH_0 F M := HH_0 F; N := HH_0 L; @@ -154,7 +154,7 @@ virtualOfPair(ChainComplex, List) := ChainComplex => opts -> (F, alphas) -> ( -------------------------------------------------------------------- -------------------------------------------------------------------- resolveViaFatPoint = method() -resolveViaFatPoint(Ideal, Ideal, List) := ChainComplex => (J, irr, A) -> ( +resolveViaFatPoint(Ideal, Ideal, List) := Complex => (J, irr, A) -> ( L := decompose irr; if #A != #L then error("resolveViaFatPoint: expected exponent vector of length " | toString degreeLength irr); -- note: decompose doesn't necessarily return in the right order @@ -178,8 +178,8 @@ resolveViaFatPoint(Ideal, Ideal, List) := ChainComplex => (J, irr, A) -> ( -------------------------------------------------------------------- -- TODO: can this use winnowingMap? isVirtual = method(TypicalValue => Boolean, Options => {Strategy => null}) -isVirtual(NormalToricVariety, ChainComplex) := opts -> (X, C) -> isVirtual(ideal X, C) -isVirtual(Ideal, ChainComplex) := opts -> (irr, C) -> ( +isVirtual(NormalToricVariety, Complex) := opts -> (X, C) -> isVirtual(ideal X, C) +isVirtual(Ideal, Complex) := opts -> (irr, C) -> ( S := ring irr; if S =!= ring C then error "isVirtual: expected objects in the same ring"; -- if strategy "determinantal is selected, the method checks virtuality @@ -319,7 +319,7 @@ randomMonomialCurve (ZZ,ZZ,Ring) := Ideal => (d,e,F) -> ( uVars := flatten entries vars U; --- Choose random monomial to define map to P2. B := drop(drop(flatten entries basis({e,0,0},U),1),-1); - f := (random(B))#0; + f := randomElement B; --- Defines graph of morphisms in P1x(P1xP2) M1 := matrix {{(uVars#0)^d,(uVars#1)^d},{uVars#2,uVars#3}}; M2 := matrix {{(uVars#0)^e,(uVars#1)^e,f},{uVars#4,uVars#5,uVars#6}}; @@ -612,7 +612,7 @@ load "./VirtualResolutions/development.m2" -------------------------------------------------------------------- -------------------------------------------------------------------- ------ Input: (C)=(ChainComplex) +----- Input: (C)=(Complex) ----- Output: A resolution of the tail end of the complex appended ----- to the given complex. ----- Description: This function is not currently being exported, @@ -626,14 +626,14 @@ load "./VirtualResolutions/development.m2" --TODO: Finish test -- Add length limit resolveTail = method() -resolveTail(ChainComplex) := ChainComplex => C -> ( +resolveTail(Complex) := Complex => C -> ( N := max support C; M := coker syz C.dd_N; -- TODO: add some component of the irrelevant ideal to M here. T := res M; L1 := for i from min C to max support C - 1 list matrix C.dd_(i+1); L2 := for i from min T to max support T - 1 list matrix T.dd_(i+1); - chainComplex(L1 | L2) + complex(L1 | L2) ); -------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/VirtualResolutions/doc.m2 b/M2/Macaulay2/packages/VirtualResolutions/doc.m2 index 9295b02233e..de6310c7bf6 100644 --- a/M2/Macaulay2/packages/VirtualResolutions/doc.m2 +++ b/M2/Macaulay2/packages/VirtualResolutions/doc.m2 @@ -78,8 +78,8 @@ doc /// doc /// Key isVirtual - (isVirtual,Ideal,ChainComplex) - (isVirtual,NormalToricVariety,ChainComplex) + (isVirtual,Ideal,Complex) + (isVirtual,NormalToricVariety,Complex) Headline checks whether a chain complex is a virtual resolution Usage @@ -90,7 +90,7 @@ doc /// irrelevant ideal of the ring X:NormalToricVariety normal toric variety - C:ChainComplex + C:Complex chain complex we want to check if is a virtual resolution Outputs :Boolean @@ -394,7 +394,7 @@ doc /// A:List power you want to take the irrelevant ideal to Outputs - :ChainComplex + :Complex virtual resolution of our ideal Description Text @@ -427,7 +427,7 @@ doc /// virtualOfPair (virtualOfPair, Ideal, List) (virtualOfPair, Module, List) - (virtualOfPair, ChainComplex, List) + (virtualOfPair, Complex, List) Headline creates a virtual resolution from a free resolution by keeping only summands of specified degrees Usage @@ -439,12 +439,12 @@ doc /// ideal over multigraded ring M:Module module over multigraded ring - C:ChainComplex + C:Complex free resolution of a module L:List multidegrees of summands to keep Outputs - :ChainComplex + :Complex Description Text Given an ideal I or module M and a list of multidegrees L, this function produces a chain complex by iteratively diff --git a/M2/Macaulay2/packages/VirtualResolutions/tests.m2 b/M2/Macaulay2/packages/VirtualResolutions/tests.m2 index 9d52bbc6387..861c169c86a 100644 --- a/M2/Macaulay2/packages/VirtualResolutions/tests.m2 +++ b/M2/Macaulay2/packages/VirtualResolutions/tests.m2 @@ -58,7 +58,7 @@ TEST /// d1 = matrix{{x_0^2*x_2^2+x_1^2*x_3^2+x_0*x_1*x_4^2, x_0*x_1*x_2^3+x_0*x_1*x_2^2*x_3-x_0^2*x_3^2*x_4+x_1^2*x_2*x_4^2+x_1^2*x_3*x_4^2, x_1^2*x_2^3+x_1^2*x_2^2*x_3-x_0*x_1*x_3^2*x_4-x_0^2*x_4^3}}; - C = chainComplex({d1}); + C = complex({d1}); elapsedTime assert(isVirtual(irr,C) == false) -- 0.022 -- d1 = matrix{{x_1^3*x_2+x_1^3*x_3+x_0^3*x_4, @@ -69,7 +69,7 @@ TEST /// {-x_1*x_2-x_1*x_3, 0, x_0*x_4}, {x_0, -x_1, 0}, {0, x_0, x_1}}); - C = chainComplex({d1,d2}); + C = complex({d1,d2}); elapsedTime assert(isVirtual(irr,C) == true) -- 0.028 /// diff --git a/M2/Macaulay2/packages/Visualize.m2 b/M2/Macaulay2/packages/Visualize.m2 index b95e57fa985..475b9d05f00 100644 --- a/M2/Macaulay2/packages/Visualize.m2 +++ b/M2/Macaulay2/packages/Visualize.m2 @@ -18,8 +18,8 @@ newPackage( "Visualize", - Version => "1.8", - Date => "October 27, 2025", + Version => "1.9", + Date => "January 2, 2026", Authors => { {Name => "Brett Barwick", Email => "bbarwick@uscupstate.edu", HomePage => "http://faculty.uscupstate.edu/bbarwick/"}, {Name => "Thomas Enkosky", Email => "tomenk@bu.edu", HomePage => "http://math.bu.edu/people/tomenk/"}, @@ -68,6 +68,11 @@ export { -* +1.9 (2026-01-02, M2 1.26.05) +* bump three.js to 0.182.0 and update 3d ideal code +* bump bootsidemenu to 2.2.0 (new M2 fork that supports bootstrap 5 and can be + used as a module) + 1.8 (2025-10-27, M2 1.25.11) * use webpack to bundle most of the javascript libraries * bootstrap 3 -> bootstrap 5 diff --git a/M2/Macaulay2/packages/Visualize/css/BootSideMenu.css b/M2/Macaulay2/packages/Visualize/css/BootSideMenu.css deleted file mode 100644 index 8d59d124041..00000000000 --- a/M2/Macaulay2/packages/Visualize/css/BootSideMenu.css +++ /dev/null @@ -1,193 +0,0 @@ -.sidebar { - z-index: 999999; - position: fixed; - top: -1px; - bottom: -1px; - padding: 0; - width: auto; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, .15); - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} - -.sidebar > .row > .col-xs-12, .sidebar > .row > .col-sm-12, .sidebar > .row > .col-md-12, .sidebar > .row > .col-lg-12 { - padding: 0; - position: absolute; - bottom: 0; - top: 0; - overflow: auto; -} - -.sidebar > .row { - margin: 0; -} - -.sidebar { - width: auto; -} - -.sidebar.sidebar-left { - left: 0; - right: 20px; -} - -.sidebar.sidebar-right { - right: 0; - left: 20px; -} - - -.toggler { - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, .15); - width: 20px; - height: 48px; - position: absolute; - top: 47%; - cursor: pointer; -} - -.sidebar-left > .toggler { - -webkit-border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-bottomright: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - border-left: 1px solid #fff; - -webkit-box-shadow: 1px 0px 8px rgba(0, 0, 0, .175); - box-shadow: 1px 0px 8px rgba(0, 0, 0, .175); - right: -20px; -} - -.sidebar-left > .toggler > span { - margin: 15px 2px; -} - -.sidebar-right > .toggler { - -webkit-border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-topleft: 4px; - -moz-border-radius-bottomleft: 4px; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - border-right: 1px solid #fff; - -webkit-box-shadow: -1px 0px 8px rgba(0, 0, 0, .175); - box-shadow: -1px 0px 8px rgba(0, 0, 0, .175); - left: -20px; -} - -.sidebar-right > .toggler > span { - margin: 15px 2px; -} - -/*Native BootStrap Hack*/ -.sidebar .list-group-item:last-child, .sidebar .list-group-item:first-child { - border-radius: 0; -} - -.sidebar .list-group { - margin-bottom: 0; -} - -.sidebar .list-group-item { - border: 1px solid #DDD; - border-left: 0; - border-right: 0; - margin-bottom: 0; - margin-top: -1px; -} - -.list-group .collapse a { - padding-left: 25px; -} - -.list-group-item .glyphicon { - margin-right: 5px; -} - - - -@media (min-width: 400px) { - .sidebar { - width: 80%; - } - - .sidebar.sidebar-left { - left: 0; - right: auto; - } - - .sidebar.sidebar-right { - right: 0; - left: auto; - } -} - -@media (min-width: 528px) { - .sidebar { - width: 300px; - } - - .sidebar.sidebar-left { - left: 0; - right: auto; - } - - .sidebar.sidebar-right { - right: 0; - left: auto; - } -} - -@media (min-width: 768px) { - .sidebar { - width: 230px; - } - - .sidebar.sidebar-left { - left: 0; - right: auto; - } - - .sidebar.sidebar-right { - right: 0; - left: auto; - } -} - -@media (min-width: 992px) { - .sidebar { - width: 230px; - } - - .sidebar.sidebar-left { - left: 0; - right: auto; - } - - .sidebar.sidebar-right { - right: 0; - left: auto; - } -} - -@media (min-width: 1200px) { - .sidebar { - width: 230px; - } - - .sidebar.sidebar-left { - left: 0; - right: auto; - } - - .sidebar.sidebar-right { - right: 0; - left: auto; - } -} \ No newline at end of file diff --git a/M2/Macaulay2/packages/Visualize/digraph-example.html b/M2/Macaulay2/packages/Visualize/digraph-example.html index fe0b039b216..99aebd944f8 100644 --- a/M2/Macaulay2/packages/Visualize/digraph-example.html +++ b/M2/Macaulay2/packages/Visualize/digraph-example.html @@ -3,7 +3,6 @@ Macaulay2: Digraph Visualization - - diff --git a/M2/Macaulay2/packages/Visualize/fonts/fa-solid-900.0ae4e6254fc19607c79e.woff2 b/M2/Macaulay2/packages/Visualize/fonts/fa-solid-900.0ae4e6254fc19607c79e.woff2 deleted file mode 100644 index a8ec7c06809..00000000000 Binary files a/M2/Macaulay2/packages/Visualize/fonts/fa-solid-900.0ae4e6254fc19607c79e.woff2 and /dev/null differ diff --git a/M2/Macaulay2/packages/Visualize/fonts/fa-solid-900.8ae0d37556ff1e685de2.woff2 b/M2/Macaulay2/packages/Visualize/fonts/fa-solid-900.8ae0d37556ff1e685de2.woff2 new file mode 100644 index 00000000000..c20c7f4feb4 Binary files /dev/null and b/M2/Macaulay2/packages/Visualize/fonts/fa-solid-900.8ae0d37556ff1e685de2.woff2 differ diff --git a/M2/Macaulay2/packages/Visualize/graph-example.html b/M2/Macaulay2/packages/Visualize/graph-example.html index cfd1ce715e6..e35101241f1 100644 --- a/M2/Macaulay2/packages/Visualize/graph-example.html +++ b/M2/Macaulay2/packages/Visualize/graph-example.html @@ -3,7 +3,6 @@ Macaulay2: Graph Visualization - - diff --git a/M2/Macaulay2/packages/Visualize/ideal2d-example.html b/M2/Macaulay2/packages/Visualize/ideal2d-example.html index 1773ab198d5..ff865f1c1f1 100644 --- a/M2/Macaulay2/packages/Visualize/ideal2d-example.html +++ b/M2/Macaulay2/packages/Visualize/ideal2d-example.html @@ -3,7 +3,6 @@ VisIdeal2D -