diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e3a27170eb9..3c3ed0ddab1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,7 +14,7 @@ cirq-ionq/**/*.* @dabacon @ColemanCollins @nakardo @gmauricio @quantumlib/cirq-m cirq-aqt/**/*.* @ma5x @pschindler @alfrisch @quantumlib/cirq-maintainers @vtomole @cduck -cirq-core/cirq/pasqal/**/*.* @HGSilveri @quantumlib/cirq-maintainers @vtomole @cduck +cirq-pasqal/**/*.* @HGSilveri @quantumlib/cirq-maintainers @vtomole @cduck ################################################ # qcvv maintainers @mrwojtek + cirq maintainers diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a285c2100f0..ce892c54377 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,10 @@ name: Continuous Integration on: [ pull_request ] +concurrency: + group: ${{ github.head_ref }} + cancel-in-progress: true + jobs: quick_test: name: Misc check @@ -10,10 +14,23 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Misc run: check/misc + packaging_test: + name: Packaging test + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: '3.7' + architecture: 'x64' + - name: Install dependencies + run: pip install -r dev_tools/requirements/deps/packaging.txt + - name: Run packaging test + run: ./dev_tools/packaging/packaging_test.sh format: name: Format check runs-on: ubuntu-20.04 @@ -23,7 +40,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install dependencies run: pip install -r dev_tools/requirements/deps/format.txt @@ -36,7 +53,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install mypy run: pip install -r dev_tools/requirements/mypy.env.txt @@ -49,7 +66,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install mypy run: pip install -r dev_tools/requirements/mypy.env.txt @@ -64,7 +81,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install changed files test dependencies run: dev_tools/conf/pip-install-minimal-for-pytest-changed-files.sh @@ -77,7 +94,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install pylint run: pip install -r dev_tools/requirements/deps/pylint.txt @@ -92,7 +109,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: pip install -r dev_tools/requirements/dev.env.txt @@ -107,7 +124,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: pip install -r dev_tools/requirements/deps/tensorflow-docs.txt @@ -130,8 +147,7 @@ jobs: name: Pytest Ubuntu strategy: matrix: - # TODO(#3800): remove 3.6 when Colab switches to 3.7 - python-version: [ '3.6', '3.7', '3.8' ] + python-version: [ '3.6', '3.7', '3.8', '3.9' ] runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -154,7 +170,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: | @@ -170,16 +186,11 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: | pip install -r dev_tools/requirements/deps/protos.txt - sudo apt install curl gnupg - curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg - sudo mv bazel.gpg /etc/apt/trusted.gpg.d/ - echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list - sudo apt update && sudo apt install bazel - name: Build protos run: check/build-changed-protos coverage: @@ -191,7 +202,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: pip install -r dev_tools/requirements/dev.env.txt @@ -201,7 +212,7 @@ jobs: name: Pytest Windows strategy: matrix: - python-version: [ '3.7', '3.8' ] + python-version: [ '3.7', '3.8', '3.9' ] runs-on: windows-2019 steps: - uses: actions/checkout@v2 @@ -218,7 +229,7 @@ jobs: name: Pytest MacOS strategy: matrix: - python-version: [ '3.7', '3.8' ] + python-version: [ '3.7', '3.8', '3.9' ] runs-on: macos-10.15 steps: - uses: actions/checkout@v2 @@ -245,7 +256,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: pip install -r dev_tools/requirements/isolated-notebooks.env.txt @@ -263,7 +274,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install requirements run: pip install -r dev_tools/requirements/notebooks.env.txt @@ -274,3 +285,49 @@ jobs: with: name: notebook-outputs path: out + ts-build: + name: Bundle file consistency + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + node-version: '14.16.1' + - name: Install npm + run: check/npm ci + - name: Check build matches the current + run: check/ts-build-current + ts-lint: + name: Typescript lint check + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + node-version: '14.16.1' + - name: Install npm + run: check/npm ci + - name: Lint + run: check/ts-lint + ts-test: + name: Typescript tests + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + node-version: '14.16.1' + - name: Install npm dependencies + run: check/npm ci + - name: Unit tests + run: check/ts-test + - name: End to end tests + run: check/ts-test-e2e + ts-coverage: + name: Typescript tests coverage + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + node-version: '14.16.1' + - name: Install npm dependencies + run: check/npm ci + - name: Test + run: check/ts-coverage diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index d9724a21b61..988d11756ce 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: '3.7' + python-version: '3.8' architecture: 'x64' - name: Install dependencies run: | diff --git a/.gitignore b/.gitignore index 8baad27df7f..e3d01d87b35 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,4 @@ docs/api_docs .python-version # notebook test output -out \ No newline at end of file +out diff --git a/Dockerfile b/Dockerfile index 6cd64b05c83..2e8a0ff09e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,9 @@ -FROM python:3.8-slim AS compile-image +FROM python:3.8-slim AS cirq_base # Install dependencies. # rm -rf /var/lib/apt/lists/* cleans up apt cache. See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ RUN DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ python3-pip \ - python3-tk \ - texlive-latex-base \ - latexmk \ - git \ locales \ && rm -rf /var/lib/apt/lists/* @@ -21,28 +17,10 @@ ENV LC_ALL en_US.UTF-8 # Make python3 default RUN rm -f /usr/bin/python \ && ln -s /usr/bin/python3 /usr/bin/python - -# Create a virtual enironment to copy over into -# the final docker image -RUN python -m venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" - -# Copy current folder instead of cloning. -COPY ./ . -COPY requirements.txt . -COPY ./cirq/contrib/contrib-requirements.txt . -COPY ./dev_tools/conf/pip-list-dev-tools.txt . - -RUN pip3 install -r requirements.txt -r contrib-requirements.txt -r pip-list-dev-tools.txt - -# Install cirq +#cirq stable image +FROM cirq_base AS cirq_stable RUN pip3 install cirq -FROM python:3.8-slim AS build-image -COPY --from=compile-image /opt/venv /opt/venv - -# Make sure scripts in .local are usable: -ENV PATH="/opt/venv/bin:$PATH" - -WORKDIR /Cirq -EXPOSE 8888 \ No newline at end of file +##cirq pre_release image +FROM cirq_base AS cirq_pre_release +RUN pip3 install cirq --pre diff --git a/MANIFEST.in b/MANIFEST.in index a7da3fcb495..1aba38f67a2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1 @@ -include requirements.txt include LICENSE diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index dbc8e4fbc88..00000000000 --- a/WORKSPACE +++ /dev/null @@ -1,62 +0,0 @@ -# General bazel utility -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -# ========================= -# Proto dependencies below. -# ========================= - -# Native proto_library is deprecated; this is a replacement. See -# https://docs.bazel.build/versions/master/be/protocol-buffer.html#proto_library -http_archive( - name = "rules_proto", - sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208", - strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", - "https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", - ], -) - -# Use this zlib rule that depends on github since it is more reliable than zlib.net. -http_archive( - name = "zlib", - build_file = "@com_google_protobuf//:third_party/zlib.BUILD", - sha256 = "629380c90a77b964d896ed37163f5c3a34f6e6d897311f1df2a7016355c45eff", - strip_prefix = "zlib-1.2.11", - urls = ["https://github.com/madler/zlib/archive/v1.2.11.tar.gz"], -) - -load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") -rules_proto_dependencies() -rules_proto_toolchains() - -# In protobuf 3.8.0, this function is used only to load the zlib dependency. -load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") -protobuf_deps() - -http_archive( - name = "bazel_skylib", - url = "https://github.com/bazelbuild/bazel-skylib/releases/download/0.9.0/bazel_skylib-0.9.0.tar.gz", - sha256 = "1dde365491125a3db70731e25658dfdd3bc5dbdfd11b840b3e987ecf043c7ca0", -) - -# Python compatibility. -http_archive( - name = "six_archive", - build_file = "@com_google_protobuf//:six.BUILD", - sha256 = "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a", - url = "https://pypi.python.org/packages/source/s/six/six-1.10.0.tar.gz#md5=34eed507548117b2ab523ab14b2f8b55", -) -bind( - name = "six", - actual = "@six_archive//:six", -) - -# proto_library, py_proto_library, and cc_proto_library rules implicitly -# depend on @com_google_protobuf for protoc and proto runtimes. -http_archive( - name = "com_google_protobuf", - sha256 = "1e622ce4b84b88b6d2cdf1db38d1a634fe2392d74f0b7b74ff98f3a51838ee53", - strip_prefix = "protobuf-3.8.0", - urls = ["https://github.com/google/protobuf/archive/v3.8.0.zip"], -) diff --git a/check/build-changed-protos b/check/build-changed-protos index 7b2b3657e4a..b48824abafa 100755 --- a/check/build-changed-protos +++ b/check/build-changed-protos @@ -71,30 +71,9 @@ if [[ ! -z "$changed" ]]; then done fi -# Check if any of the build files have changed. -build_changed=$(git diff --name-only ${rev} -- \ - | grep "google/api/.*BUILD$" \ - | sed -e "s/BUILD//") - -if [[ -z "$build_changed" ]]; then - echo -e "No BUILD files changed." -else - # Build all the targets for the changed BUILD rule. - echo -e "BUILD file changed in directories: '${build_changed[@]}'" - for build_prefix in $build_changed - do - (set -x; bazel build "${build_prefix}...:*") - done -fi - -# We potentially will attempt to build a second time, but this is not -# a problem as bazel returns immediately. +# We potentially will attempt to build a second time. for base in $changed do - (set -x; bazel build "${base}_proto"; \ - bazel build "${base}_py_proto"; \ - bazel build "${base}_cc_proto") - python -m grpc_tools.protoc -I=cirq-google --python_out=cirq-google --mypy_out=cirq-google ${base}.proto done @@ -109,4 +88,4 @@ if [[ ! -z "$untracked" ]]; then echo -e "\033[31m ${generated}\033[0m" done exit 1 -fi \ No newline at end of file +fi diff --git a/check/build-protos b/check/build-protos deleted file mode 100755 index 65ccc8a3467..00000000000 --- a/check/build-protos +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -################################################################################ -# Runs bazel build for proto files in cirq/google/api/* -# -# See build-changed-protos for a version of this that only builds changed -# protos. -# -# Usage: -# check/build-protos -################################################################################ - -# Get the working directory to the repo root. -cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd "$(git rev-parse --show-toplevel)" - -protos=$(find -type f -name "*.proto" -printf '%P\n' \ - | grep "google/api/.*\.proto$" \ - | sed -e "s/\.proto$//") -for base in $protos: -do - echo -e "Found proto: '${base}.proto'" -done - -for base in $protos -do - bazel build "${base}_proto" - bazel build "${base}_py_proto" - bazel build "${base}_cc_proto" -done diff --git a/check/mypy b/check/mypy index a02c4d873a3..b0e1f141beb 100755 --- a/check/mypy +++ b/check/mypy @@ -19,8 +19,10 @@ for arg in $@; do fi done +CIRQ_PACKAGES=$(env PYTHONPATH=. python dev_tools/modules.py list --mode package-path) + echo -e -n "\033[31m" -mypy --config-file=dev_tools/conf/mypy.ini $@ cirq-google/cirq_google cirq-core/cirq dev_tools +mypy --config-file=dev_tools/conf/mypy.ini $@ $CIRQ_PACKAGES dev_tools examples result=$? echo -e -n "\033[0m" diff --git a/check/npm b/check/npm new file mode 100755 index 00000000000..cf2ec980614 --- /dev/null +++ b/check/npm @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Allows the user to run npm commands from the top-level Cirq +# directory. +# +# Usage: +# check/npm +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +npm --prefix 'cirq-web/cirq_ts' $@ \ No newline at end of file diff --git a/check/npx b/check/npx new file mode 100755 index 00000000000..08ea59eb9ae --- /dev/null +++ b/check/npx @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Allows the user to run npx commands from the top-level directory +# +# Usage: +# check/ts-build +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +cd 'cirq-web/cirq_ts' +npx $@ \ No newline at end of file diff --git a/check/ts-build b/check/ts-build new file mode 100755 index 00000000000..824c7607b49 --- /dev/null +++ b/check/ts-build @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Build the bundle files for Cirq-web +# +# Usage: +# check/ts-build +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +check/npx webpack --mode production $@ diff --git a/check/ts-build-current b/check/ts-build-current new file mode 100755 index 00000000000..0c6352b5f89 --- /dev/null +++ b/check/ts-build-current @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Checks if all bundled files are up to date. +# +# Usage: +# check/ts-build-changes +################################################################################ + +check/ts-build + +# Find the changed bundle.js files, if any +untracked=$(git status --porcelain 2>/dev/null | grep "cirq-web/cirq_ts/dist/" | cut -d " " -f 3) + +if [[ ! -z "$untracked" ]]; then + echo -e "\033[31mERROR: Uncommitted changes to bundle file(s) found! Please commit these files:\033[0m" + for generated in $untracked + do + echo -e "\033[31m ${generated}\033[0m" + done + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/check/ts-coverage b/check/ts-coverage new file mode 100755 index 00000000000..32c8f548d6a --- /dev/null +++ b/check/ts-coverage @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Runs the Istanbul coverage checker on the Typescript. Primarily used for CI. +# +# Usage: +# check/ts-lint +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +check/npm run coverage $@ \ No newline at end of file diff --git a/check/ts-lint b/check/ts-lint new file mode 100755 index 00000000000..03918f5bc29 --- /dev/null +++ b/check/ts-lint @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Runs the linter on the Typescript. Primarily used for CI. +# +# Usage: +# check/ts-lint +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +check/npm run lint $@ \ No newline at end of file diff --git a/check/ts-lint-and-format b/check/ts-lint-and-format new file mode 100755 index 00000000000..23d32ce1c29 --- /dev/null +++ b/check/ts-lint-and-format @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Runs gts on the Typescript files in the cirq-web package +# +# Usage: +# check/ts-lint-and-format +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +check/npm run fix $@ \ No newline at end of file diff --git a/check/ts-test b/check/ts-test new file mode 100755 index 00000000000..0ec6c8984a6 --- /dev/null +++ b/check/ts-test @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Runs the test files +# +# Usage: +# check/ts-test +# +# "npm run test" runs unit tests +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +check/npm run test $@ \ No newline at end of file diff --git a/check/ts-test-e2e b/check/ts-test-e2e new file mode 100755 index 00000000000..dbf09de3dba --- /dev/null +++ b/check/ts-test-e2e @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# Runs the end to end tests and ensures that they're all passing +# +# Usage: +# check/ts-test-e2e +# +# "npm run test-e2e" runs end to end tests +################################################################################ + +# Get the working directory to the repo root. +cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$(git rev-parse --show-toplevel)" + +check/npm run test-e2e $@ diff --git a/cirq-aqt/cirq_aqt/conftest.py b/cirq-aqt/cirq_aqt/conftest.py new file mode 100644 index 00000000000..f7ba25dbfcd --- /dev/null +++ b/cirq-aqt/cirq_aqt/conftest.py @@ -0,0 +1,5 @@ +import os + + +def pytest_configure(config): + os.environ['CIRQ_TESTING'] = "true" diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index 2557ca74b55..48c72551b63 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -259,6 +259,7 @@ QubitOrderOrList, QubitPermutationGate, reset, + reset_each, ResetChannel, riswap, Rx, @@ -271,6 +272,8 @@ SingleQubitCliffordGate, SingleQubitGate, SingleQubitPauliStringGateOperation, + SQRT_ISWAP, + SQRT_ISWAP_INV, SWAP, SwapPowGate, T, @@ -316,6 +319,7 @@ merge_single_qubit_gates_into_phased_x_z, merge_single_qubit_gates_into_phxz, MergeInteractions, + MergeInteractionsToSqrtIswap, MergeSingleQubitGates, single_qubit_matrix_to_gates, single_qubit_matrix_to_pauli_rotations, @@ -326,6 +330,7 @@ SynchronizeTerminalMeasurements, two_qubit_matrix_to_operations, two_qubit_matrix_to_diagonal_and_operations, + two_qubit_matrix_to_sqrt_iswap_operations, three_qubit_matrix_to_operations, ) @@ -358,6 +363,7 @@ from cirq.sim import ( ActOnArgs, + ActOnArgsContainer, ActOnCliffordTableauArgs, ActOnDensityMatrixArgs, ActOnStabilizerCHFormArgs, @@ -376,6 +382,7 @@ measure_state_vector, final_density_matrix, final_state_vector, + OperationTarget, sample, sample_density_matrix, sample_state_vector, @@ -396,6 +403,7 @@ StateVectorStepResult, StateVectorTrialResult, StepResult, + StepResultBase, ) from cirq.study import ( @@ -579,7 +587,6 @@ # Unflattened sub-modules. from cirq import ( - pasqal, testing, ) @@ -621,6 +628,19 @@ warning("Can't import cirq_ionq: ", exc_info=ex) +try: + _compat.deprecated_submodule( + new_module_name='cirq_pasqal', + old_parent=__name__, + old_child='pasqal', + deadline="v0.14", + create_attribute=True, + ) +except ImportError as ex: + # coverage: ignore + warning("Can't import cirq_pasqal: ", exc_info=ex) + + def _register_resolver() -> None: """Registers the cirq module's public classes for JSON serialization.""" from cirq.protocols.json_serialization import _internal_register_resolver diff --git a/cirq-core/cirq/_compat.py b/cirq-core/cirq/_compat.py index a8e43b3716c..bade54f8bfa 100644 --- a/cirq-core/cirq/_compat.py +++ b/cirq-core/cirq/_compat.py @@ -91,10 +91,25 @@ def proper_eq(a: Any, b: Any) -> bool: def _warn_or_error(msg): from cirq.testing.deprecation import ALLOW_DEPRECATION_IN_TEST - called_from_test = 'CIRQ_TESTING' in os.environ deprecation_allowed = ALLOW_DEPRECATION_IN_TEST in os.environ - if called_from_test and not deprecation_allowed: - raise ValueError(f"Cirq should not use deprecated functionality: {msg}") + if _called_from_test() and not deprecation_allowed: + for filename, line_number, function_name, text in reversed(traceback.extract_stack()): + if ( + not _is_internal(filename) + and not filename.endswith(os.path.join("cirq", "_compat.py")) + and "_test.py" in filename + ): + break + raise ValueError( + f"During testing using Cirq deprecated functionality is not allowed: {msg}" + f"Update to non-deprecated functionality, or alternatively, you can quiet " + f"this error by removing the CIRQ_TESTING environment variable " + f"temporarily with `@mock.patch.dict(os.environ, clear='CIRQ_TESTING')`.\n" + f"In case the usage of deprecated cirq is intentional, use " + f"`with cirq.testing.assert_deprecated(...):` around this line:\n" + f"{filename}:{line_number}: in {function_name}\n" + f"\t{text}" + ) # we have to dynamically count the non-internal frames # due to the potentially multiple nested module wrappers @@ -396,8 +411,22 @@ def _is_internal(filename: str) -> bool: _warned: Set[str] = set() -def _deduped_module_warn_or_error(old_module_name, new_module_name, deadline): - if old_module_name in _warned: +def _called_from_test() -> bool: + return 'CIRQ_TESTING' in os.environ + + +def _should_dedupe_module_deprecation() -> bool: + """Whether module deprecation warnings should be deduped or not. + + We should always dedupe when not called from test. + We should only dedupe during tests if forced. + """ + force_dedupe = "CIRQ_FORCE_DEDUPE_MODULE_DEPRECATION" in os.environ + return not _called_from_test() or force_dedupe + + +def _deduped_module_warn_or_error(old_module_name: str, new_module_name: str, deadline: str): + if _should_dedupe_module_deprecation() and old_module_name in _warned: return _warned.add(old_module_name) diff --git a/cirq-core/cirq/_compat_test.py b/cirq-core/cirq/_compat_test.py index d033b844979..8663372e7da 100644 --- a/cirq-core/cirq/_compat_test.py +++ b/cirq-core/cirq/_compat_test.py @@ -14,6 +14,7 @@ import importlib import logging import multiprocessing +import os import sys import traceback import types @@ -21,6 +22,7 @@ from types import ModuleType from typing import Callable, Optional from importlib.machinery import ModuleSpec +from unittest import mock import numpy as np import pandas as pd @@ -125,7 +127,9 @@ def old_func(*args, **kwargs): ): assert old_func(1, 2) == 3 - with pytest.raises(ValueError, match='Cirq should not use deprecated functionality'): + with pytest.raises( + ValueError, match='During testing using Cirq deprecated functionality is not allowed' + ): old_func(1, 2) with pytest.raises(AssertionError, match='deadline should match vX.Y'): @@ -168,7 +172,9 @@ def f(new_count): # pylint: enable=no-value-for-parameter # pylint: enable=unexpected-keyword-arg - with pytest.raises(ValueError, match='Cirq should not use deprecated functionality'): + with pytest.raises( + ValueError, match='During testing using Cirq deprecated functionality is not allowed' + ): # pylint: disable=unexpected-keyword-arg # pylint: disable=no-value-for-parameter f(double_count=1) @@ -223,7 +229,9 @@ def test_wrap_module(): ): _ = wrapped.foo - with pytest.raises(ValueError, match='Cirq should not use deprecated functionality'): + with pytest.raises( + ValueError, match='During testing using Cirq deprecated functionality is not allowed' + ): _ = wrapped.foo with cirq.testing.assert_logs(count=0): @@ -264,7 +272,9 @@ class OldClass(NewClass): assert repr(old_obj) == 'NewClass: 1' assert 'OldClass' in old_obj.hello() - with pytest.raises(ValueError, match='Cirq should not use deprecated functionality'): + with pytest.raises( + ValueError, match='During testing using Cirq deprecated functionality is not allowed' + ): OldClass('1') with pytest.raises(AssertionError, match='deadline should match vX.Y'): @@ -516,6 +526,7 @@ def isolated_func(*args, **kwargs): return isolated_func +@mock.patch.dict(os.environ, {"CIRQ_FORCE_DEDUPE_MODULE_DEPRECATION": "1"}) @pytest.mark.parametrize( 'outdated_method,deprecation_messages', [ diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index 1bba7de47b2..f0a01cef49c 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -57,6 +57,7 @@ from cirq.circuits.qasm_output import QasmOutput from cirq.circuits.quil_output import QuilOutput from cirq.circuits.text_diagram_drawer import TextDiagramDrawer +from cirq.protocols import circuit_diagram_info_protocol from cirq.type_workarounds import NotImplementedType if TYPE_CHECKING: @@ -224,7 +225,7 @@ def __getitem__(self, key): # pylint: enable=function-redefined @abc.abstractmethod - def _with_sliced_moments(self, moments: Sequence['cirq.Moment']): + def _with_sliced_moments(self: CIRCUIT_TYPE, moments: Iterable['cirq.Moment']) -> CIRCUIT_TYPE: """Helper method for constructing circuits from __getitem__.""" def __str__(self) -> str: @@ -883,6 +884,25 @@ def all_operations(self) -> Iterator[ops.Operation]: """ return (op for moment in self for op in moment.operations) + def map_operations( + self: CIRCUIT_TYPE, func: Callable[['cirq.Operation'], 'cirq.OP_TREE'] + ) -> CIRCUIT_TYPE: + """Applies the given function to all operations in this circuit. + + Args: + func: a mapping function from operations to OP_TREEs. + + Returns: + A circuit with the same basic structure as the original, but with + each operation `op` replaced with `func(op)`. + """ + + def map_moment(moment: 'cirq.Moment') -> 'cirq.Circuit': + """Apply func to expand each op into a circuit, then zip up the circuits.""" + return Circuit.zip(*[Circuit(func(op)) for op in moment]) + + return self._with_sliced_moments(m for moment in self for m in map_moment(moment)) + def qid_shape( self, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT ) -> Tuple[int, ...]: @@ -1147,21 +1167,24 @@ def to_text_diagram_drawer( diagram.write(0, 0, '') for q, i in qubit_map.items(): diagram.write(0, i, qubit_namer(q)) + first_annotation_row = max(qubit_map.values(), default=0) + 1 if any(isinstance(op.untagged, cirq.GlobalPhaseOperation) for op in self.all_operations()): diagram.write(0, max(qubit_map.values(), default=0) + 1, 'global phase:') + first_annotation_row += 1 moment_groups = [] # type: List[Tuple[int, int]] for moment in self.moments: _draw_moment_in_diagram( - moment, - use_unicode_characters, - qubit_map, - diagram, - precision, - moment_groups, - get_circuit_diagram_info, - include_tags, + moment=moment, + use_unicode_characters=use_unicode_characters, + qubit_map=qubit_map, + out_diagram=diagram, + precision=precision, + moment_groups=moment_groups, + get_circuit_diagram_info=get_circuit_diagram_info, + include_tags=include_tags, + first_annotation_row=first_annotation_row, ) w = diagram.width() @@ -1649,7 +1672,7 @@ def copy(self) -> 'Circuit': copied_circuit._moments = self._moments[:] return copied_circuit - def _with_sliced_moments(self, moments: Sequence['cirq.Moment']) -> 'Circuit': + def _with_sliced_moments(self, moments: Iterable['cirq.Moment']) -> 'Circuit': new_circuit = Circuit(device=self.device) new_circuit._moments = list(moments) return new_circuit @@ -1988,36 +2011,6 @@ def insert_into_range(self, operations: 'cirq.OP_TREE', start: int, end: int) -> return self.insert(end, flat_ops[op_index:]) - @staticmethod - def _pick_inserted_ops_moment_indices( - operations: Sequence['cirq.Operation'], - start: int = 0, - frontier: Dict['cirq.Qid', int] = None, - ) -> Tuple[Sequence[int], Dict['cirq.Qid', int]]: - """Greedily assigns operations to moments. - - Args: - operations: The operations to assign to moments. - start: The first moment to consider assignment to. - frontier: The first moment to which an operation acting on a qubit - can be assigned. Updated in place as operations are assigned. - - Returns: - The frontier giving the index of the moment after the last one to - which an operation that acts on each qubit is assigned. If a - frontier was specified as an argument, this is the same object. - """ - if frontier is None: - frontier = defaultdict(lambda: 0) - moment_indices = [] - for op in operations: - op_start = max(start, max(frontier[q] for q in op.qubits)) - moment_indices.append(op_start) - for q in op.qubits: - frontier[q] = max(frontier[q], op_start + 1) - - return moment_indices, frontier - def _push_frontier( self, early_frontier: Dict['cirq.Qid', int], @@ -2116,7 +2109,7 @@ def insert_at_frontier( next_moments = self.next_moments_operating_on(qubits, start) - insertion_indices, _ = self._pick_inserted_ops_moment_indices(flat_ops, start, frontier) + insertion_indices, _ = _pick_inserted_ops_moment_indices(flat_ops, start, frontier) self._push_frontier(frontier, next_moments) @@ -2297,6 +2290,36 @@ def with_noise(self, noise: 'cirq.NOISE_MODEL_LIKE') -> 'cirq.Circuit': return c_noisy +def _pick_inserted_ops_moment_indices( + operations: Sequence['cirq.Operation'], + start: int = 0, + frontier: Dict['cirq.Qid', int] = None, +) -> Tuple[Sequence[int], Dict['cirq.Qid', int]]: + """Greedily assigns operations to moments. + + Args: + operations: The operations to assign to moments. + start: The first moment to consider assignment to. + frontier: The first moment to which an operation acting on a qubit + can be assigned. Updated in place as operations are assigned. + + Returns: + The frontier giving the index of the moment after the last one to + which an operation that acts on each qubit is assigned. If a + frontier was specified as an argument, this is the same object. + """ + if frontier is None: + frontier = defaultdict(lambda: 0) + moment_indices = [] + for op in operations: + op_start = max(start, max((frontier[q] for q in op.qubits), default=0)) + moment_indices.append(op_start) + for q in op.qubits: + frontier[q] = max(frontier[q], op_start + 1) + + return moment_indices, frontier + + def _resolve_operations( operations: Iterable['cirq.Operation'], param_resolver: 'cirq.ParamResolver', recursive: bool ) -> List['cirq.Operation']: @@ -2306,7 +2329,55 @@ def _resolve_operations( return resolved_operations +def _get_moment_annotations( + moment: 'cirq.Moment', +) -> Iterator['cirq.Operation']: + for op in moment.operations: + if op.qubits: + continue + op = op.untagged + if isinstance(op, ops.GlobalPhaseOperation): + continue + if isinstance(op, CircuitOperation): + for m in op.circuit: + yield from _get_moment_annotations(m) + else: + yield op + + +def _draw_moment_annotations( + *, + moment: 'cirq.Moment', + col: int, + use_unicode_characters: bool, + qubit_map: Dict['cirq.Qid', int], + out_diagram: TextDiagramDrawer, + precision: Optional[int], + get_circuit_diagram_info: Callable[ + ['cirq.Operation', 'cirq.CircuitDiagramInfoArgs'], 'cirq.CircuitDiagramInfo' + ], + include_tags: bool, + first_annotation_row: int, +): + + for k, annotation in enumerate(_get_moment_annotations(moment)): + args = protocols.CircuitDiagramInfoArgs( + known_qubits=(), + known_qubit_count=0, + use_unicode_characters=use_unicode_characters, + qubit_map=qubit_map, + precision=precision, + include_tags=include_tags, + ) + info = get_circuit_diagram_info(annotation, args) + symbols = info._wire_symbols_including_formatted_exponent(args) + text = symbols[0] if symbols else str(annotation) + out_diagram.force_vertical_padding_after(first_annotation_row + k - 1, 0) + out_diagram.write(col, first_annotation_row + k, text) + + def _draw_moment_in_diagram( + *, moment: 'cirq.Moment', use_unicode_characters: bool, qubit_map: Dict['cirq.Qid', int], @@ -2315,11 +2386,12 @@ def _draw_moment_in_diagram( moment_groups: List[Tuple[int, int]], get_circuit_diagram_info: Optional[ Callable[['cirq.Operation', 'cirq.CircuitDiagramInfoArgs'], 'cirq.CircuitDiagramInfo'] - ] = None, - include_tags: bool = True, + ], + include_tags: bool, + first_annotation_row: int, ): if get_circuit_diagram_info is None: - get_circuit_diagram_info = protocols.CircuitDiagramInfo._op_info_with_fallback + get_circuit_diagram_info = circuit_diagram_info_protocol._op_info_with_fallback x0 = out_diagram.width() non_global_ops = [op for op in moment.operations if op.qubits] @@ -2363,6 +2435,18 @@ def _draw_moment_in_diagram( if x > max_x: max_x = x + _draw_moment_annotations( + moment=moment, + use_unicode_characters=use_unicode_characters, + col=x0, + qubit_map=qubit_map, + out_diagram=out_diagram, + precision=precision, + get_circuit_diagram_info=get_circuit_diagram_info, + include_tags=include_tags, + first_annotation_row=first_annotation_row, + ) + global_phase, tags = _get_global_phase_and_tags_for_ops(moment) # Print out global phase, unless it's 1 (phase of 0pi) or it's the only op. diff --git a/cirq-core/cirq/circuits/circuit_operation.py b/cirq-core/cirq/circuits/circuit_operation.py index ad0b3bc83cc..f9a228cf147 100644 --- a/cirq-core/cirq/circuits/circuit_operation.py +++ b/cirq-core/cirq/circuits/circuit_operation.py @@ -188,24 +188,65 @@ def _parameter_names_(self) -> AbstractSet[str]: ) } - def _decompose_(self) -> 'cirq.OP_TREE': - result = self.circuit.unfreeze() - result = result.transform_qubits(lambda q: self.qubit_map.get(q, q)) + def mapped_circuit(self, deep: bool = False) -> 'cirq.Circuit': + """Applies all maps to the contained circuit and returns the result. + + Args: + deep: If true, this will also call mapped_circuit on any + CircuitOperations this object contains. + + Returns: + The contained circuit with all other member variables (repetitions, + qubit mapping, parameterization, etc.) applied to it. This behaves + like `cirq.decompose(self)`, but preserving moment structure. + """ + circuit = self.circuit.unfreeze() + circuit = circuit.transform_qubits(lambda q: self.qubit_map.get(q, q)) if self.repetitions < 0: - result = result ** -1 - result = protocols.with_measurement_key_mapping(result, self.measurement_key_map) - result = protocols.resolve_parameters(result, self.param_resolver, recursive=False) - # repetition_ids don't need to be taken into account if the circuit has no measurements - # or if repetition_ids are unset. - if self.repetition_ids is None or not protocols.is_measurement(result): - return list(result.all_operations()) * abs(self.repetitions) - # If it's a measurement circuit with repetitions/repetition_ids, prefix the repetition_ids - # to measurements. Details at https://tinyurl.com/measurement-repeated-circuitop. - ops = [] # type: List[cirq.Operation] - for repetition_id in self.repetition_ids: - path = self.parent_path + (repetition_id,) - ops += protocols.with_key_path(result, path).all_operations() - return ops + circuit = circuit ** -1 + has_measurements = protocols.is_measurement(circuit) + if has_measurements: + circuit = protocols.with_measurement_key_mapping(circuit, self.measurement_key_map) + circuit = protocols.resolve_parameters(circuit, self.param_resolver, recursive=False) + if deep: + + def map_deep(op: 'cirq.Operation') -> 'cirq.OP_TREE': + return op.mapped_circuit(deep=True) if isinstance(op, CircuitOperation) else op + + if self.repetition_ids is None: + return circuit.map_operations(map_deep) + if not has_measurements: + return circuit.map_operations(map_deep) * abs(self.repetitions) + + # Path must be constructed from the top down. + rekeyed_circuit = circuits.Circuit( + protocols.with_key_path(circuit, self.parent_path + (rep,)) + for rep in self.repetition_ids + ) + return rekeyed_circuit.map_operations(map_deep) + + if self.repetition_ids is None: + return circuit + if not has_measurements: + return circuit * abs(self.repetitions) + + def rekey_op(op: 'cirq.Operation', rep: str): + """Update measurement keys in `op` to include repetition ID `rep`.""" + rekeyed_op = protocols.with_key_path(op, self.parent_path + (rep,)) + if rekeyed_op is NotImplemented: + return op + return rekeyed_op + + return circuits.Circuit( + circuit.map_operations(lambda op: rekey_op(op, rep)) for rep in self.repetition_ids + ) + + def mapped_op(self, deep: bool = False) -> 'cirq.CircuitOperation': + """As `mapped_circuit`, but wraps the result in a CircuitOperation.""" + return CircuitOperation(circuit=self.mapped_circuit(deep=deep).freeze()) + + def _decompose_(self) -> 'cirq.OP_TREE': + return self.mapped_circuit(deep=False).all_operations() # Methods for string representation of the operation. diff --git a/cirq-core/cirq/circuits/circuit_operation_test.py b/cirq-core/cirq/circuits/circuit_operation_test.py index 6e1d3f14250..4cf78afb469 100644 --- a/cirq-core/cirq/circuits/circuit_operation_test.py +++ b/cirq-core/cirq/circuits/circuit_operation_test.py @@ -702,6 +702,8 @@ def test_decompose_nested(): cirq.measure(d, key='md'), ) assert cirq.Circuit(cirq.decompose(final_op)) == expected_circuit + # Verify that mapped_circuit gives the same operations. + assert final_op.mapped_circuit(deep=True) == expected_circuit def test_decompose_repeated_nested_measurements(): @@ -752,6 +754,33 @@ def test_decompose_repeated_nested_measurements(): assert cirq.Circuit(cirq.decompose(op3)) == expected_circuit assert cirq.measurement_keys(expected_circuit) == set(expected_measurement_keys_in_order) + # Verify that mapped_circuit gives the same operations. + assert op3.mapped_circuit(deep=True) == expected_circuit + + +def test_mapped_circuit_preserves_moments(): + q0, q1 = cirq.LineQubit.range(2) + fc = cirq.FrozenCircuit(cirq.Moment(cirq.X(q0)), cirq.Moment(cirq.X(q1))) + op = cirq.CircuitOperation(fc) + assert op.mapped_circuit() == fc + assert op.repeat(3).mapped_circuit(deep=True) == fc * 3 + + +def test_mapped_op(): + q0, q1 = cirq.LineQubit.range(2) + a, b = (sympy.Symbol(x) for x in 'ab') + fc1 = cirq.FrozenCircuit(cirq.X(q0) ** a, cirq.measure(q0, q1, key='m')) + op1 = ( + cirq.CircuitOperation(fc1) + .with_params({'a': 'b'}) + .with_qubits(q1, q0) + .with_measurement_key_mapping({'m': 'k'}) + ) + fc2 = cirq.FrozenCircuit(cirq.X(q1) ** b, cirq.measure(q1, q0, key='k')) + op2 = cirq.CircuitOperation(fc2) + + assert op1.mapped_op() == op2 + def test_tag_propagation(): # Tags are not propagated from the CircuitOperation to its components. diff --git a/cirq-core/cirq/circuits/circuit_test.py b/cirq-core/cirq/circuits/circuit_test.py index 33c48498843..e30016e8de7 100644 --- a/cirq-core/cirq/circuits/circuit_test.py +++ b/cirq-core/cirq/circuits/circuit_test.py @@ -22,6 +22,7 @@ import cirq import cirq.testing +from cirq import circuits from cirq import ops from cirq.testing.devices import ValidatingTestDevice @@ -3356,7 +3357,7 @@ def test_pick_inserted_ops_moment_indices(): expected_circuit._moments += [ cirq.Moment() for _ in range(len(circuit) - len(expected_circuit)) ] - insert_indices, _ = circuit._pick_inserted_ops_moment_indices(operations, start) + insert_indices, _ = circuits.circuit._pick_inserted_ops_moment_indices(operations, start) actual_circuit = cirq.Circuit( first_half._moments + [cirq.Moment() for _ in range(n_moments - start)] ) @@ -4726,3 +4727,126 @@ def test_factorize_large_circuit(): assert len(factors) == 5 for f, d in zip(factors, desired): cirq.testing.assert_has_diagram(f, d) + + +def test_zero_target_operations_go_below_diagram(): + class CustomOperationAnnotation(cirq.Operation): + def __init__(self, text: str): + self.text = text + + def with_qubits(self, *new_qubits): + raise NotImplementedError() + + @property + def qubits(self): + return () + + def _circuit_diagram_info_(self, args) -> str: + return self.text + + class CustomOperationAnnotationNoInfo(cirq.Operation): + def with_qubits(self, *new_qubits): + raise NotImplementedError() + + @property + def qubits(self): + return () + + def __str__(self): + return "custom!" + + class CustomGateAnnotation(cirq.Gate): + def __init__(self, text: str): + self.text = text + + def _num_qubits_(self): + return 0 + + def _circuit_diagram_info_(self, args) -> str: + return self.text + + cirq.testing.assert_has_diagram( + cirq.Circuit( + cirq.Moment( + CustomOperationAnnotation("a"), + CustomGateAnnotation("b").on(), + CustomOperationAnnotation("c"), + ), + cirq.Moment( + CustomOperationAnnotation("e"), + CustomOperationAnnotation("d"), + ), + ), + """ + a e + b d + c + """, + ) + + cirq.testing.assert_has_diagram( + cirq.Circuit( + cirq.Moment( + cirq.H(cirq.LineQubit(0)), + CustomOperationAnnotation("a"), + cirq.GlobalPhaseOperation(1j), + ), + ), + """ +0: ─────────────H────── + +global phase: 0.5π + a + """, + ) + + cirq.testing.assert_has_diagram( + cirq.Circuit( + cirq.Moment( + cirq.H(cirq.LineQubit(0)), + cirq.CircuitOperation(cirq.FrozenCircuit(CustomOperationAnnotation("a"))), + ), + ), + """ +0: ───H─── + a + """, + ) + + cirq.testing.assert_has_diagram( + cirq.Circuit( + cirq.Moment( + cirq.X(cirq.LineQubit(0)), + CustomOperationAnnotation("a"), + CustomGateAnnotation("b").on(), + CustomOperationAnnotation("c"), + ), + cirq.Moment( + CustomOperationAnnotation("eee"), + CustomOperationAnnotation("d"), + ), + cirq.Moment( + cirq.CNOT(cirq.LineQubit(0), cirq.LineQubit(2)), + cirq.CNOT(cirq.LineQubit(1), cirq.LineQubit(3)), + CustomOperationAnnotationNoInfo(), + CustomOperationAnnotation("zzz"), + ), + cirq.Moment( + cirq.H(cirq.LineQubit(2)), + ), + ), + """ + ┌────────┐ +0: ───X──────────@─────────────── + │ +1: ──────────────┼──────@──────── + │ │ +2: ──────────────X──────┼────H─── + │ +3: ─────────────────────X──────── + a eee custom! + b d zzz + c + └────────┘ + """, + ) diff --git a/cirq-core/cirq/circuits/frozen_circuit.py b/cirq-core/cirq/circuits/frozen_circuit.py index c964061fc59..8bbc102c930 100644 --- a/cirq-core/cirq/circuits/frozen_circuit.py +++ b/cirq-core/cirq/circuits/frozen_circuit.py @@ -17,6 +17,7 @@ AbstractSet, Callable, FrozenSet, + Iterable, Iterator, Optional, Sequence, @@ -163,7 +164,7 @@ def __pow__(self, other) -> 'FrozenCircuit': except: return NotImplemented - def _with_sliced_moments(self, moments: Sequence['cirq.Moment']) -> 'FrozenCircuit': + def _with_sliced_moments(self, moments: Iterable['cirq.Moment']) -> 'FrozenCircuit': new_circuit = FrozenCircuit(device=self.device) new_circuit._moments = tuple(moments) return new_circuit diff --git a/cirq-core/cirq/contrib/quimb/mps_simulator.py b/cirq-core/cirq/contrib/quimb/mps_simulator.py index 46491f4baf7..44aa303fe11 100644 --- a/cirq-core/cirq/contrib/quimb/mps_simulator.py +++ b/cirq-core/cirq/contrib/quimb/mps_simulator.py @@ -85,10 +85,11 @@ def __init__( seed=seed, ) - def _create_act_on_args( + def _create_partial_act_on_args( self, initial_state: Union[int, 'MPSState'], qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], ) -> 'MPSState': """Creates MPSState args for simulating the Circuit. @@ -111,28 +112,25 @@ def _create_act_on_args( simulation_options=self.simulation_options, grouping=self.grouping, initial_state=initial_state, + log_of_measurement_results=logs, ) def _create_step_result( self, - sim_state: 'MPSState', - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[MPSState]', ): - return MPSSimulatorStepResult( - measurements=sim_state.log_of_measurement_results, state=sim_state - ) + return MPSSimulatorStepResult(sim_state) def _create_simulator_trial_result( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: 'MPSState', + final_simulator_state: Union['MPSSimulatorStepResult', 'MPSState'], ) -> 'MPSTrialResult': """Creates a single trial results with the measurements. Args: - circuit: The circuit to simulate. - param_resolver: A ParamResolver for determining values of + params: A ParamResolver for determining values of Symbols. measurements: A dictionary from measurement key (e.g. qubit) to the actual measurement array. @@ -153,13 +151,15 @@ def __init__( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: 'MPSState', + final_simulator_state: Union['MPSSimulatorStepResult', 'MPSState'], ) -> None: super().__init__( params=params, measurements=measurements, final_simulator_state=final_simulator_state ) - self.final_state = final_simulator_state + @property + def final_state(self): + return self._final_simulator_state def __str__(self) -> str: samples = super().__str__() @@ -167,22 +167,22 @@ def __str__(self) -> str: return f'measurements: {samples}\noutput state: {final}' -class MPSSimulatorStepResult(simulator.StepResult['MPSState']): +class MPSSimulatorStepResult(simulator_base.StepResultBase['MPSState', 'MPSState']): """A `StepResult` that can perform measurements.""" - def __init__(self, state, measurements): + def __init__( + self, + sim_state: 'cirq.OperationTarget[MPSState]', + ): """Results of a step of the simulator. Attributes: - state: A MPSState - measurements: A dictionary from measurement gate key to measurement - results, ordered by the qubits that the measurement operates on. - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). + sim_state: The qubit:ActOnArgs lookup for this step. """ - self.measurements = measurements - self.state = state.copy() + super().__init__(sim_state) + + @property + def state(self): + return self._merged_sim_state def __str__(self) -> str: def bitstring(vals): @@ -202,24 +202,6 @@ def bitstring(vals): def _simulator_state(self): return self.state - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - - measurements: List[int] = [] - - for _ in range(repetitions): - measurements.append( - self.state.perform_measurement( - qubits, value.parse_random_state(seed), collapse_state_vector=False - ) - ) - - return np.array(measurements, dtype=int) - @value.value_equality class MPSState(ActOnArgs): @@ -317,6 +299,7 @@ def copy(self) -> 'MPSState': prng=self.prng, simulation_options=self.simulation_options, grouping=self.grouping, + log_of_measurement_results=self.log_of_measurement_results.copy(), ) state.M = [x.copy() for x in self.M] state.estimated_gate_error_list = self.estimated_gate_error_list @@ -534,3 +517,21 @@ def perform_measurement( def _perform_measurement(self, qubits: Sequence['cirq.Qid']) -> List[int]: """Measures the axes specified by the simulator.""" return self.perform_measurement(qubits, self.prng) + + def sample( + self, + qubits: Sequence[ops.Qid], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + + measurements: List[List[int]] = [] + + for _ in range(repetitions): + measurements.append( + self.perform_measurement( + qubits, value.parse_random_state(seed), collapse_state_vector=False + ) + ) + + return np.array(measurements, dtype=int) diff --git a/cirq-core/cirq/contrib/quimb/mps_simulator_test.py b/cirq-core/cirq/contrib/quimb/mps_simulator_test.py index bff931da378..a5660621c75 100644 --- a/cirq-core/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq-core/cirq/contrib/quimb/mps_simulator_test.py @@ -274,11 +274,11 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState(qubits=(q0,), prng=value.parse_random_state(0)) - step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) + sim = ccq.mps_simulator.MPSSimulator() + step_result = next(sim.simulate_moment_steps(cirq.Circuit(cirq.measure(q0)))) assert ( str(step_result) - == """0=1 + == """0=0 TensorNetwork([ Tensor(shape=(2,), inds=('i_0',), tags=set()), ])""" diff --git a/cirq-core/cirq/contrib/requirements.txt b/cirq-core/cirq/contrib/requirements.txt index 9eff4f301a1..aafaf391d50 100644 --- a/cirq-core/cirq/contrib/requirements.txt +++ b/cirq-core/cirq/contrib/requirements.txt @@ -7,6 +7,8 @@ pylatex~=1.3.0 quimb opt_einsum autoray +# required for py39 opcodes. +numba>=0.53.0 # quil import pyquil~=2.21.0 diff --git a/cirq-core/cirq/devices/noise_model.py b/cirq-core/cirq/devices/noise_model.py index e8430dc4fcb..bf00dd29c5d 100644 --- a/cirq-core/cirq/devices/noise_model.py +++ b/cirq-core/cirq/devices/noise_model.py @@ -88,12 +88,7 @@ def is_virtual_moment(self, moment: 'cirq.Moment') -> bool: """ if not moment.operations: return False - return all( - [ - isinstance(op, ops.TaggedOperation) and ops.VirtualTag() in op.tags - for op in moment.operations - ] - ) + return all(ops.VirtualTag() in op.tags for op in moment) def _noisy_moments_impl_moment( self, moments: 'Iterable[cirq.Moment]', system_qubits: Sequence['cirq.Qid'] diff --git a/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py b/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py index a8ae6b02e6a..23d13382ce2 100644 --- a/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py +++ b/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py @@ -20,7 +20,7 @@ def test_estimate_parallel_two_qubit_xeb_fidelity_on_grid_no_noise(tmpdir): two_qubit_gate = cirq.ISWAP ** 0.5 cycles = [5, 10, 15] data_collection_id = collect_grid_parallel_two_qubit_xeb_data( - sampler=cirq.Simulator(seed=34310), + sampler=cirq.Simulator(seed=34310, split_untangled_states=False), qubits=qubits, two_qubit_gate=two_qubit_gate, num_circuits=2, @@ -53,7 +53,9 @@ def test_estimate_parallel_two_qubit_xeb_fidelity_on_grid_depolarizing(tmpdir): cycles = [5, 10, 15] e = 0.01 data_collection_id = collect_grid_parallel_two_qubit_xeb_data( - sampler=cirq.DensityMatrixSimulator(noise=cirq.depolarize(e), seed=65008), + sampler=cirq.DensityMatrixSimulator( + noise=cirq.depolarize(e), seed=65008, split_untangled_states=False + ), qubits=qubits, two_qubit_gate=two_qubit_gate, num_circuits=2, diff --git a/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py b/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py index cea97210a63..8c5675fcb84 100644 --- a/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py +++ b/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py @@ -31,7 +31,7 @@ def __init__(self, p0: float, p1: float, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' self.p0 = p0 self.p1 = p1 self.prng = cirq.value.parse_random_state(seed) - self.simulator = cirq.Simulator(seed=self.prng) + self.simulator = cirq.Simulator(seed=self.prng, split_untangled_states=False) def run_sweep( self, diff --git a/cirq-core/cirq/experiments/xeb_fitting.py b/cirq-core/cirq/experiments/xeb_fitting.py index 402e84ec477..389aa2794cd 100644 --- a/cirq-core/cirq/experiments/xeb_fitting.py +++ b/cirq-core/cirq/experiments/xeb_fitting.py @@ -52,7 +52,7 @@ def benchmark_2q_xeb_fidelities( sampled_df: pd.DataFrame, circuits: Sequence['cirq.Circuit'], - cycle_depths: Sequence[int], + cycle_depths: Optional[Sequence[int]] = None, param_resolver: 'cirq.ParamResolverOrSimilarType' = None, pool: Optional['multiprocessing.pool.Pool'] = None, ) -> pd.DataFrame: @@ -66,7 +66,9 @@ def benchmark_2q_xeb_fidelities( sampled_df: The sampled results to benchmark. This is likely produced by a call to `sample_2q_xeb_circuits`. circuits: The library of circuits corresponding to the sampled results in `sampled_df`. - cycle_depths: The sequence of cycle depths to simulate the circuits. + cycle_depths: The sequence of cycle depths to benchmark the circuits. If not provided, + we use the cycle depths found in `sampled_df`. All requested `cycle_depths` must be + present in `sampled_df`. param_resolver: If circuits contain parameters, resolve according to this ParamResolver prior to simulation pool: If provided, execute the simulations in parallel. @@ -74,10 +76,27 @@ def benchmark_2q_xeb_fidelities( Returns: A DataFrame with columns 'cycle_depth' and 'fidelity'. """ + sampled_cycle_depths = ( + sampled_df.index.get_level_values('cycle_depth').drop_duplicates().sort_values() + ) + if cycle_depths is not None: + if len(cycle_depths) == 0: + raise ValueError("`cycle_depths` should be a non-empty array_like") + not_in_sampled = np.setdiff1d(cycle_depths, sampled_cycle_depths) + if len(not_in_sampled) > 0: + raise ValueError( + f"The `cycle_depths` provided include some not " + f"available in `sampled_df`: {not_in_sampled}" + ) + sim_cycle_depths = cycle_depths + else: + sim_cycle_depths = sampled_cycle_depths simulated_df = simulate_2q_xeb_circuits( - circuits=circuits, cycle_depths=cycle_depths, param_resolver=param_resolver, pool=pool + circuits=circuits, cycle_depths=sim_cycle_depths, param_resolver=param_resolver, pool=pool ) - df = sampled_df.join(simulated_df) + # Join the `pure_probs` onto `sampled_df`. By using 'inner', we let + # the `cycle_depths` argument to this function control what cycle depths are benchmarked. + df = sampled_df.join(simulated_df, how='inner').reset_index() D = 4 # two qubits pure_probs = np.array(df['pure_probs'].to_list()) @@ -117,7 +136,7 @@ def _try_keep(k): else: groupby_names = ['cycle_depth'] - return df.reset_index().groupby(groupby_names).apply(per_cycle_depth).reset_index() + return df.groupby(groupby_names).apply(per_cycle_depth).reset_index() class XEBCharacterizationOptions(ABC): diff --git a/cirq-core/cirq/experiments/xeb_fitting_test.py b/cirq-core/cirq/experiments/xeb_fitting_test.py index 37fba32101e..6c90af64547 100644 --- a/cirq-core/cirq/experiments/xeb_fitting_test.py +++ b/cirq-core/cirq/experiments/xeb_fitting_test.py @@ -37,7 +37,8 @@ from cirq.experiments.xeb_sampling import sample_2q_xeb_circuits -def test_benchmark_2q_xeb_fidelities(): +@pytest.fixture(scope='module') +def circuits_cycle_depths_sampled_df(): q0, q1 = cirq.LineQubit.range(2) circuits = [ rqcg.random_rotations_between_two_qubit_circuit( @@ -45,16 +46,25 @@ def test_benchmark_2q_xeb_fidelities(): ) for _ in range(2) ] - cycle_depths = np.arange(3, 50, 9) + cycle_depths = np.arange(10, 40 + 1, 10) sampled_df = sample_2q_xeb_circuits( sampler=cirq.Simulator(seed=53), circuits=circuits, cycle_depths=cycle_depths ) - fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths) + return circuits, cycle_depths, sampled_df + + +@pytest.mark.parametrize('pass_cycle_depths', (True, False)) +def test_benchmark_2q_xeb_fidelities(circuits_cycle_depths_sampled_df, pass_cycle_depths): + circuits, cycle_depths, sampled_df = circuits_cycle_depths_sampled_df + + if pass_cycle_depths: + fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths) + else: + fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits) assert len(fid_df) == len(cycle_depths) - for _, row in fid_df.iterrows(): - assert row['cycle_depth'] in cycle_depths - assert row['fidelity'] > 0.98 + assert sorted(fid_df['cycle_depth'].unique()) == cycle_depths.tolist() + assert np.all(fid_df['fidelity'] > 0.98) fit_df = fit_exponential_decays(fid_df) for _, row in fit_df.iterrows(): @@ -62,6 +72,26 @@ def test_benchmark_2q_xeb_fidelities(): assert len(row['fidelities']) == len(cycle_depths) +def test_benchmark_2q_xeb_subsample_depths(circuits_cycle_depths_sampled_df): + circuits, _, sampled_df = circuits_cycle_depths_sampled_df + cycle_depths = [10, 20] + fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths) + assert len(fid_df) == len(cycle_depths) + assert sorted(fid_df['cycle_depth'].unique()) == cycle_depths + + cycle_depths = [11, 21] + with pytest.raises(ValueError): + _ = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths) + + cycle_depths = [10, 100_000] + with pytest.raises(ValueError): + _ = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths) + + cycle_depths = [] + with pytest.raises(ValueError): + _ = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths) + + def _gridqubits_to_graph_device(qubits: Iterable[cirq.GridQubit]): # cirq contrib: routing.gridqubits_to_graph_device def _manhattan_distance(qubit1: cirq.GridQubit, qubit2: cirq.GridQubit) -> int: diff --git a/cirq-core/cirq/ion/ion_device.py b/cirq-core/cirq/ion/ion_device.py index 82625425f9d..0643012df26 100644 --- a/cirq-core/cirq/ion/ion_device.py +++ b/cirq-core/cirq/ion/ion_device.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, cast, FrozenSet, Iterable, Optional, Set, TYPE_CHECKING +from typing import Any, FrozenSet, Iterable, Optional, Set, TYPE_CHECKING from cirq import circuits, value, devices, ops, protocols from cirq.ion import convert_to_ion_gates @@ -104,37 +104,10 @@ def validate_operation(self, operation): if q not in self.qubits: raise ValueError(f'Qubit not on device: {q!r}') - def _check_if_XXPow_operation_interacts_with_any( - self, XXPow_op: ops.GateOperation, others: Iterable[ops.GateOperation] - ) -> bool: - return any(self._check_if_XXPow_operation_interacts(XXPow_op, op) for op in others) - - def _check_if_XXPow_operation_interacts( - self, XXPow_op: ops.GateOperation, other_op: ops.GateOperation - ) -> bool: - if isinstance( - other_op.gate, - (ops.XPowGate, ops.YPowGate, ops.PhasedXPowGate, ops.MeasurementGate, ops.ZPowGate), - ): - return False - - return any(q == p for q in XXPow_op.qubits for p in other_op.qubits) - def validate_circuit(self, circuit: circuits.Circuit): super().validate_circuit(circuit) _verify_unique_measurement_keys(circuit.all_operations()) - def can_add_operation_into_moment(self, operation: ops.Operation, moment: ops.Moment) -> bool: - - if not super().can_add_operation_into_moment(operation, moment): - return False - if isinstance(operation.gate, ops.XXPowGate): - return not self._check_if_XXPow_operation_interacts_with_any( - cast(ops.GateOperation, operation), - cast(Iterable[ops.GateOperation], moment.operations), - ) - return True - def at(self, position: int) -> Optional[devices.LineQubit]: """Returns the qubit at the given position, if there is one, else None.""" q = devices.LineQubit(position) diff --git a/cirq-core/cirq/ion/ion_device_test.py b/cirq-core/cirq/ion/ion_device_test.py index 8d04c083ab6..94736d346f0 100644 --- a/cirq-core/cirq/ion/ion_device_test.py +++ b/cirq-core/cirq/ion/ion_device_test.py @@ -13,9 +13,9 @@ # limitations under the License. from datetime import timedelta -import pytest import numpy as np +import pytest import cirq import cirq.ion as ci @@ -160,6 +160,8 @@ def test_can_add_operation_into_moment(): assert not d.can_add_operation_into_moment(cirq.XX(q1, q2), moment) assert d.can_add_operation_into_moment(cirq.XX(q2, q3), moment) assert d.can_add_operation_into_moment(cirq.Z(q3), moment) + circuit = cirq.Circuit([cirq.X(q0)]) + assert d.can_add_operation_into_moment(cirq.XX(q1, q2), circuit[0]) def test_ion_device_eq(): diff --git a/cirq-core/cirq/json_resolver_cache.py b/cirq-core/cirq/json_resolver_cache.py index 8a3b699d67e..33a6848ed65 100644 --- a/cirq-core/cirq/json_resolver_cache.py +++ b/cirq-core/cirq/json_resolver_cache.py @@ -108,8 +108,6 @@ def two_qubit_matrix_gate(matrix): '_PauliY': cirq.ops.pauli_gates._PauliY, '_PauliZ': cirq.ops.pauli_gates._PauliZ, 'ParamResolver': cirq.ParamResolver, - 'PasqalDevice': cirq.pasqal.PasqalDevice, - 'PasqalVirtualDevice': cirq.pasqal.PasqalVirtualDevice, 'ParallelGateOperation': cirq.ParallelGateOperation, 'PauliString': cirq.PauliString, 'PhaseDampingChannel': cirq.PhaseDampingChannel, @@ -130,12 +128,10 @@ def two_qubit_matrix_gate(matrix): 'SwapPowGate': cirq.SwapPowGate, 'SymmetricalQidPair': cirq.SymmetricalQidPair, 'TaggedOperation': cirq.TaggedOperation, - 'ThreeDQubit': cirq.pasqal.ThreeDQubit, 'Result': cirq.Result, 'Rx': cirq.Rx, 'Ry': cirq.Ry, 'Rz': cirq.Rz, - 'TwoDQubit': cirq.pasqal.TwoDQubit, 'TwoQubitMatrixGate': two_qubit_matrix_gate, '_UnconstrainedDevice': cirq.devices.unconstrained_device._UnconstrainedDevice, 'VarianceStoppingCriteria': cirq.work.VarianceStoppingCriteria, diff --git a/cirq-core/cirq/linalg/transformations.py b/cirq-core/cirq/linalg/transformations.py index bfe89a6e965..1efbdde48e1 100644 --- a/cirq-core/cirq/linalg/transformations.py +++ b/cirq-core/cirq/linalg/transformations.py @@ -287,7 +287,7 @@ def fractional_swap(target): return out -def partial_trace(tensor: np.ndarray, keep_indices: List[int]) -> np.ndarray: +def partial_trace(tensor: np.ndarray, keep_indices: Sequence[int]) -> np.ndarray: """Takes the partial trace of a given tensor. The input tensor must have shape `(d_0, ..., d_{k-1}, d_0, ..., d_{k-1})`. @@ -500,3 +500,161 @@ def to_special(u: np.ndarray) -> np.ndarray: the special unitary matrix """ return u * (np.linalg.det(u) ** (-1 / len(u))) + + +def state_vector_kronecker_product( + t1: np.ndarray, + t2: np.ndarray, +) -> np.ndarray: + """Merges two state vectors into a single unified state vector. + + The resulting vector's shape will be `t1.shape + t2.shape`. + + Args: + t1: The first state vector. + t2: The second state vector. + Returns: + A new state vector representing the unified state. + """ + return np.outer(t1, t2).reshape(t1.shape + t2.shape) + + +def density_matrix_kronecker_product( + t1: np.ndarray, + t2: np.ndarray, +) -> np.ndarray: + """Merges two density matrices into a single unified density matrix. + + The resulting matrix's shape will be `(t1.shape/2 + t2.shape/2) * 2`. In + other words, if t1 has shape [A,B,C,A,B,C] and t2 has shape [X,Y,Z,X,Y,Z], + the resulting matrix will have shape [A,B,C,X,Y,Z,A,B,C,X,Y,Z]. + + Args: + t1: The first density matrix. + t2: The second density matrix. + Returns: + A density matrix representing the unified state. + """ + t = state_vector_kronecker_product(t1, t2) + t1_len = len(t1.shape) + t1_dim = int(t1_len / 2) + t2_len = len(t2.shape) + t2_dim = int(t2_len / 2) + shape = t1.shape[:t1_dim] + t2.shape[:t2_dim] + return np.moveaxis(t, range(t1_len, t1_len + t2_dim), range(t1_dim, t1_dim + t2_dim)).reshape( + shape * 2 + ) + + +def factor_state_vector( + t: np.ndarray, + axes: Sequence[int], + *, + validate=True, + atol=1e-07, +) -> Tuple[np.ndarray, np.ndarray]: + """Factors a state vector into two independent state vectors. + + This function should only be called on state vectors that are known to be + separable, such as immediately after a measurement or reset operation. It + does not verify that the provided state vector is indeed separable, and + will return nonsense results for vectors representing entangled states. + + Args: + t: The state vector to factor. + axes: The axes to factor out. + validate: Perform a validation that the density matrix factors cleanly. + atol: The absolute tolerance for the validation. + Returns: + A tuple with the `(extracted, remainder)` state vectors, where + `extracted` means the sub-state vector which corresponds to the axes + requested, and with the axes in the requested order, and where + `remainder` means the sub-state vector on the remaining axes, in the + same order as the original state vector. + """ + n_axes = len(axes) + t1 = np.moveaxis(t, axes, range(n_axes)) + pivot = np.unravel_index(np.abs(t1).argmax(), t1.shape) + slices1 = (slice(None),) * n_axes + pivot[n_axes:] + slices2 = pivot[:n_axes] + (slice(None),) * (t1.ndim - n_axes) + extracted = t1[slices1] + extracted = extracted / np.sum(abs(extracted) ** 2) ** 0.5 + remainder = t1[slices2] + remainder = remainder / np.sum(abs(remainder) ** 2) ** 0.5 + if validate: + t2 = state_vector_kronecker_product(extracted, remainder) + axes2 = list(axes) + [i for i in range(t1.ndim) if i not in axes] + t3 = transpose_state_vector_to_axis_order(t2, axes2) + if not np.allclose(t3, t, atol=atol): + raise ValueError('The tensor cannot be factored by the requested axes') + return extracted, remainder + + +def factor_density_matrix( + t: np.ndarray, + axes: Sequence[int], + *, + validate=True, + atol=1e-07, +) -> Tuple[np.ndarray, np.ndarray]: + """Factors a density matrix into two independent density matrices. + + This function should only be called on density matrices that are known to + be separable, such as immediately after a measurement or reset operation. + It does not verify that the provided density matrix is indeed separable, + and will return nonsense results for matrices representing entangled + states. + + Args: + t: The density matrix to factor. + axes: The axes to factor out. Only the left axes should be provided. + For example, to extract [C,A] from density matrix of shape + [A,B,C,D,A,B,C,D], `axes` should be [2,0], and the return value + will be two density matrices ([C,A,C,A], [B,D,B,D]). + validate: Perform a validation that the density matrix factors cleanly. + atol: The absolute tolerance for the validation. + Returns: + A tuple with the `(extracted, remainder)` density matrices, where + `extracted` means the sub-matrix which corresponds to the axes + requested, and with the axes in the requested order, and where + `remainder` means the sub-matrix on the remaining axes, in the same + order as the original density matrix. + """ + extracted = partial_trace(t, axes) + remaining_axes = [i for i in range(t.ndim // 2) if i not in axes] + remainder = partial_trace(t, remaining_axes) + if validate: + t1 = density_matrix_kronecker_product(extracted, remainder) + product_axes = list(axes) + remaining_axes + t2 = transpose_density_matrix_to_axis_order(t1, product_axes) + if not np.allclose(t2, t, atol=atol): + raise ValueError('The tensor cannot be factored by the requested axes') + return extracted, remainder + + +def transpose_state_vector_to_axis_order(t: np.ndarray, axes: Sequence[int]): + """Transposes the axes of a state vector to a specified order. + + Args: + t: The state vector to transpose. + axes: The desired axis order. + Returns: + The transposed state vector. + """ + assert set(axes) == set(range(int(t.ndim))), "All axes must be provided." + return np.moveaxis(t, axes, range(len(axes))) + + +def transpose_density_matrix_to_axis_order(t: np.ndarray, axes: Sequence[int]): + """Transposes the axes of a density matrix to a specified order. + + Args: + t: The density matrix to transpose. + axes: The desired axis order. Only the left axes should be provided. + For example, to transpose [A,B,C,A,B,C] to [C,B,A,C,B,A], `axes` + should be [2,1,0]. + Returns: + The transposed density matrix. + """ + axes = list(axes) + [i + len(axes) for i in axes] + return transpose_state_vector_to_axis_order(t, axes) diff --git a/cirq-core/cirq/ops/__init__.py b/cirq-core/cirq/ops/__init__.py index 1be292eb516..02ea527eb9c 100644 --- a/cirq-core/cirq/ops/__init__.py +++ b/cirq-core/cirq/ops/__init__.py @@ -45,6 +45,7 @@ PhaseDampingChannel, PhaseFlipChannel, reset, + reset_each, ResetChannel, ) @@ -241,6 +242,8 @@ ISWAP, ISwapPowGate, riswap, + SQRT_ISWAP, + SQRT_ISWAP_INV, SWAP, SwapPowGate, ) diff --git a/cirq-core/cirq/ops/clifford_gate.py b/cirq-core/cirq/ops/clifford_gate.py index bf452b25e6d..bd6aa61045c 100644 --- a/cirq-core/cirq/ops/clifford_gate.py +++ b/cirq-core/cirq/ops/clifford_gate.py @@ -49,6 +49,35 @@ def _pretend_initialized() -> 'SingleQubitCliffordGate': pass +def _validate_map_input( + required_transform_count: int, + pauli_map_to: Optional[Dict[Pauli, Tuple[Pauli, bool]]], + x_to: Optional[Tuple[Pauli, bool]], + y_to: Optional[Tuple[Pauli, bool]], + z_to: Optional[Tuple[Pauli, bool]], +) -> Dict[Pauli, PauliTransform]: + if pauli_map_to is None: + xyz_to = {pauli_gates.X: x_to, pauli_gates.Y: y_to, pauli_gates.Z: z_to} + pauli_map_to = {cast(Pauli, p): trans for p, trans in xyz_to.items() if trans is not None} + elif x_to is not None or y_to is not None or z_to is not None: + raise ValueError( + '{} can take either pauli_map_to or a combination' + ' of x_to, y_to, and z_to but both were given' + ) + if len(pauli_map_to) != required_transform_count: + raise ValueError( + 'Method takes {} transform{} but {} {} given'.format( + required_transform_count, + '' if required_transform_count == 1 else 's', + len(pauli_map_to), + 'was' if len(pauli_map_to) == 1 else 'were', + ) + ) + if len(set((to for to, _ in pauli_map_to.values()))) != len(pauli_map_to): + raise ValueError('A rotation cannot map two Paulis to the same') + return {frm: PauliTransform(to, flip) for frm, (to, flip) in pauli_map_to.items()} + + @value.value_equality class SingleQubitCliffordGate(gate_features.SingleQubitGate): """Any single qubit Clifford rotation.""" @@ -107,9 +136,7 @@ def from_single_map( y_to: The transform from cirq.Y z_to: The transform from cirq.Z """ - rotation_map = SingleQubitCliffordGate._validate_map_input( - 1, pauli_map_to, x_to=x_to, y_to=y_to, z_to=z_to - ) + rotation_map = _validate_map_input(1, pauli_map_to, x_to=x_to, y_to=y_to, z_to=z_to) ((trans_from, (trans_to, flip)),) = tuple(rotation_map.items()) if trans_from == trans_to: trans_from2 = Pauli.by_relative_index(trans_to, 1) # 1 or 2 work @@ -144,9 +171,7 @@ def from_double_map( y_to: The transform from cirq.Y z_to: The transform from cirq.Z """ - rotation_map = SingleQubitCliffordGate._validate_map_input( - 2, pauli_map_to, x_to=x_to, y_to=y_to, z_to=z_to - ) + rotation_map = _validate_map_input(2, pauli_map_to, x_to=x_to, y_to=y_to, z_to=z_to) (from1, trans1), (from2, trans2) = tuple(rotation_map.items()) from3 = from1.third(from2) to3 = trans1.to.third(trans2.to) @@ -186,37 +211,6 @@ def from_quarter_turns(pauli: Pauli, quarter_turns: int) -> 'SingleQubitClifford return SingleQubitCliffordGate.from_pauli(pauli, True) ** -1 - @staticmethod - def _validate_map_input( - required_transform_count: int, - pauli_map_to: Optional[Dict[Pauli, Tuple[Pauli, bool]]], - x_to: Optional[Tuple[Pauli, bool]], - y_to: Optional[Tuple[Pauli, bool]], - z_to: Optional[Tuple[Pauli, bool]], - ) -> Dict[Pauli, PauliTransform]: - if pauli_map_to is None: - xyz_to = {pauli_gates.X: x_to, pauli_gates.Y: y_to, pauli_gates.Z: z_to} - pauli_map_to = { - cast(Pauli, p): trans for p, trans in xyz_to.items() if trans is not None - } - elif x_to is not None or y_to is not None or z_to is not None: - raise ValueError( - '{} can take either pauli_map_to or a combination' - ' of x_to, y_to, and z_to but both were given' - ) - if len(pauli_map_to) != required_transform_count: - raise ValueError( - 'Method takes {} transform{} but {} {} given'.format( - required_transform_count, - '' if required_transform_count == 1 else 's', - len(pauli_map_to), - 'was' if len(pauli_map_to) == 1 else 'were', - ) - ) - if len(set((to for to, _ in pauli_map_to.values()))) != len(pauli_map_to): - raise ValueError('A rotation cannot map two Paulis to the same') - return {frm: PauliTransform(to, flip) for frm, (to, flip) in pauli_map_to.items()} - @staticmethod def from_unitary(u: np.ndarray) -> Optional['SingleQubitCliffordGate']: """Creates Clifford gate with given unitary (up to global phase). diff --git a/cirq-core/cirq/ops/common_channels.py b/cirq-core/cirq/ops/common_channels.py index 7e917a41449..7548cc0c113 100644 --- a/cirq-core/cirq/ops/common_channels.py +++ b/cirq-core/cirq/ops/common_channels.py @@ -15,7 +15,7 @@ """Quantum channels that are commonly used in the literature.""" import itertools -from typing import Any, Dict, Iterable, Optional, Sequence, Tuple, Union, TYPE_CHECKING +from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union, TYPE_CHECKING import numpy as np @@ -246,7 +246,7 @@ def asymmetric_depolarize( @value.value_equality -class DepolarizingChannel(gate_features.SupportsOnEachGate, raw_types.Gate): +class DepolarizingChannel(raw_types.Gate): """A channel that depolarizes one or several qubits.""" def __init__(self, p: float, n_qubits: int = 1) -> None: @@ -789,6 +789,11 @@ def reset(qubit: 'cirq.Qid') -> raw_types.Operation: return ResetChannel(qubit.dimension).on(qubit) +def reset_each(*qubits: 'cirq.Qid') -> List[raw_types.Operation]: + """Returns a list of `ResetChannel` instances on the given qubits.""" + return [ResetChannel(q.dimension).on(q) for q in qubits] + + @value.value_equality class PhaseDampingChannel(gate_features.SingleQubitGate): """Dampen qubit phase. diff --git a/cirq-core/cirq/ops/common_channels_test.py b/cirq-core/cirq/ops/common_channels_test.py index f5e429def99..035989f3203 100644 --- a/cirq-core/cirq/ops/common_channels_test.py +++ b/cirq-core/cirq/ops/common_channels_test.py @@ -241,11 +241,22 @@ def test_deprecated_on_each_for_depolarizing_channel_one_qubit(): def test_deprecated_on_each_for_depolarizing_channel_two_qubits(): - q0, q1 = cirq.LineQubit.range(2) + q0, q1, q2, q3, q4, q5 = cirq.LineQubit.range(6) op = cirq.DepolarizingChannel(p=0.1, n_qubits=2) - with pytest.raises(ValueError, match="one qubit"): + op.on_each([(q0, q1)]) + op.on_each([(q0, q1), (q2, q3)]) + op.on_each(zip([q0, q2, q4], [q1, q3, q5])) + op.on_each((q0, q1)) + op.on_each([q0, q1]) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): op.on_each(q0, q1) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + op.on_each([('bogus object 0', 'bogus object 1')]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + op.on_each(['01']) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + op.on_each([(False, None)]) def test_depolarizing_channel_apply_two_qubits(): @@ -506,6 +517,16 @@ def test_reset_act_on(): ) +def test_reset_each(): + qubits = cirq.LineQubit.range(8) + for n in range(len(qubits) + 1): + ops = cirq.reset_each(*qubits[:n]) + assert len(ops) == n + for i, op in enumerate(ops): + assert isinstance(op.gate, cirq.ResetChannel) + assert op.qubits == (qubits[i],) + + def test_phase_damping_channel(): d = cirq.phase_damp(0.3) np.testing.assert_almost_equal( diff --git a/cirq-core/cirq/ops/common_gates.py b/cirq-core/cirq/ops/common_gates.py index 9ff5892cbfa..6659a6a0367 100644 --- a/cirq-core/cirq/ops/common_gates.py +++ b/cirq-core/cirq/ops/common_gates.py @@ -1079,8 +1079,11 @@ def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']): tableau.zs[:, q2].copy(), tableau.xs[:, q2].copy(), ) + tableau.rs[:] ^= tableau.xs[:, q2] & tableau.zs[:, q2] tableau.rs[:] ^= ( - tableau.xs[:, q1] & tableau.zs[:, q2] & (tableau.xs[:, q2] ^ tableau.zs[:, q1]) + tableau.xs[:, q1] + & tableau.zs[:, q2] + & (~(tableau.xs[:, q2] ^ tableau.zs[:, q1])) ) tableau.xs[:, q2] ^= tableau.xs[:, q1] tableau.zs[:, q1] ^= tableau.zs[:, q2] diff --git a/cirq-core/cirq/ops/common_gates_test.py b/cirq-core/cirq/ops/common_gates_test.py index 5e5580e07bd..a3475474028 100644 --- a/cirq-core/cirq/ops/common_gates_test.py +++ b/cirq-core/cirq/ops/common_gates_test.py @@ -532,6 +532,32 @@ def test_cz_act_on_tableau(): cirq.act_on(cirq.CZ ** 1.5, args, cirq.LineQubit.range(2)) +def test_cz_act_on_equivalent_to_h_cx_h_tableau(): + args1 = cirq.ActOnCliffordTableauArgs( + tableau=cirq.CliffordTableau(num_qubits=2), + qubits=cirq.LineQubit.range(2), + prng=np.random.RandomState(), + log_of_measurement_results={}, + ) + args2 = cirq.ActOnCliffordTableauArgs( + tableau=cirq.CliffordTableau(num_qubits=2), + qubits=cirq.LineQubit.range(2), + prng=np.random.RandomState(), + log_of_measurement_results={}, + ) + cirq.act_on(cirq.S, args=args1, qubits=[cirq.LineQubit(1)], allow_decompose=False) + cirq.act_on(cirq.S, args=args2, qubits=[cirq.LineQubit(1)], allow_decompose=False) + + # Args1 uses H*CNOT*H + cirq.act_on(cirq.H, args=args1, qubits=[cirq.LineQubit(1)], allow_decompose=False) + cirq.act_on(cirq.CNOT, args=args1, qubits=cirq.LineQubit.range(2), allow_decompose=False) + cirq.act_on(cirq.H, args=args1, qubits=[cirq.LineQubit(1)], allow_decompose=False) + # Args2 uses CZ + cirq.act_on(cirq.CZ, args=args2, qubits=cirq.LineQubit.range(2), allow_decompose=False) + + assert args1.tableau == args2.tableau + + foo = sympy.Symbol('foo') diff --git a/cirq-core/cirq/ops/gate_features.py b/cirq-core/cirq/ops/gate_features.py index e977d770ce3..36ff0628408 100644 --- a/cirq-core/cirq/ops/gate_features.py +++ b/cirq-core/cirq/ops/gate_features.py @@ -18,8 +18,9 @@ """ import abc -from typing import Union, Iterable, Any, List +from cirq import value, ops +from cirq._compat import deprecated_class from cirq.ops import raw_types @@ -31,38 +32,23 @@ def qubit_index_to_equivalence_group_key(self, index: int) -> int: return 0 -class SupportsOnEachGate(raw_types.Gate, metaclass=abc.ABCMeta): - """A gate that can be applied to exactly one qubit.""" +class _SupportsOnEachGateMeta(value.ABCMetaImplementAnyOneOf): + def __instancecheck__(cls, instance): + return isinstance(instance, (SingleQubitGate, ops.DepolarizingChannel)) or issubclass( + type(instance), SupportsOnEachGate + ) - def on_each(self, *targets: Union[raw_types.Qid, Iterable[Any]]) -> List[raw_types.Operation]: - """Returns a list of operations applying the gate to all targets. - Args: - *targets: The qubits to apply this gate to. +@deprecated_class( + deadline='v0.14', + fix='Remove `SupportsOnEachGate` from the list of parent classes. ' + '`on_each` is now directly supported in the `Gate` base class.', +) +class SupportsOnEachGate(raw_types.Gate, metaclass=_SupportsOnEachGateMeta): + pass - Returns: - Operations applying this gate to the target qubits. - Raises: - ValueError if targets are not instances of Qid or List[Qid]. - ValueError if the gate operates on two or more Qids. - """ - if self._num_qubits_() > 1: - raise ValueError('This gate only supports on_each when it is a one qubit gate.') - operations = [] # type: List[raw_types.Operation] - for target in targets: - if isinstance(target, raw_types.Qid): - operations.append(self.on(target)) - elif isinstance(target, Iterable) and not isinstance(target, str): - operations.extend(self.on_each(*target)) - else: - raise ValueError( - f'Gate was called with type different than Qid. Type: {type(target)}' - ) - return operations - - -class SingleQubitGate(SupportsOnEachGate, metaclass=abc.ABCMeta): +class SingleQubitGate(raw_types.Gate, metaclass=abc.ABCMeta): """A gate that must be applied to exactly one qubit.""" def _num_qubits_(self) -> int: diff --git a/cirq-core/cirq/ops/gate_features_test.py b/cirq-core/cirq/ops/gate_features_test.py index ffe074cccdf..8c8ecbb2b18 100644 --- a/cirq-core/cirq/ops/gate_features_test.py +++ b/cirq-core/cirq/ops/gate_features_test.py @@ -12,12 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Iterator -from typing import Any - import pytest import cirq +from cirq.testing import assert_deprecated def test_single_qubit_gate_validate_args(): @@ -137,41 +135,6 @@ def matrix(self): g.validate_args([a, b, c, d]) -def test_on_each(): - class CustomGate(cirq.SingleQubitGate): - pass - - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - c = CustomGate() - - assert c.on_each() == [] - assert c.on_each(a) == [c(a)] - assert c.on_each(a, b) == [c(a), c(b)] - assert c.on_each(b, a) == [c(b), c(a)] - - assert c.on_each([]) == [] - assert c.on_each([a]) == [c(a)] - assert c.on_each([a, b]) == [c(a), c(b)] - assert c.on_each([b, a]) == [c(b), c(a)] - assert c.on_each([a, [b, a], b]) == [c(a), c(b), c(a), c(b)] - - with pytest.raises(ValueError): - c.on_each('abcd') - with pytest.raises(ValueError): - c.on_each(['abcd']) - with pytest.raises(ValueError): - c.on_each([a, 'abcd']) - - def iterator(qubits): - for i in range(len(qubits)): - yield qubits[i] - - qubit_iterator = iterator([a, b, a, b]) - assert isinstance(qubit_iterator, Iterator) - assert c.on_each(qubit_iterator) == [c(a), c(b), c(a), c(b)] - - def test_qasm_output_args_validate(): args = cirq.QasmArgs(version='2.0') args.validate_version('2.0') @@ -231,16 +194,40 @@ def __init__(self, num_qubits): g.validate_args([a, b, c, d]) -def test_on_each_iterable_qid(): - class QidIter(cirq.Qid): - @property - def dimension(self) -> int: - return 2 +def test_supports_on_each_inheritance_shim(): + class NotOnEach(cirq.Gate): + def num_qubits(self): + return 1 # coverage: ignore + + class OnEach(cirq.ops.gate_features.SupportsOnEachGate): + def num_qubits(self): + return 1 # coverage: ignore + + class SingleQ(cirq.SingleQubitGate): + pass + + class TwoQ(cirq.TwoQubitGate): + pass + + not_on_each = NotOnEach() + single_q = SingleQ() + two_q = TwoQ() + with assert_deprecated(deadline="v0.14"): + on_each = OnEach() + + assert not isinstance(not_on_each, cirq.ops.gate_features.SupportsOnEachGate) + assert isinstance(on_each, cirq.ops.gate_features.SupportsOnEachGate) + assert isinstance(single_q, cirq.ops.gate_features.SupportsOnEachGate) + assert not isinstance(two_q, cirq.ops.gate_features.SupportsOnEachGate) + assert isinstance(cirq.X, cirq.ops.gate_features.SupportsOnEachGate) + assert not isinstance(cirq.CX, cirq.ops.gate_features.SupportsOnEachGate) + assert isinstance(cirq.DepolarizingChannel(0.01), cirq.ops.gate_features.SupportsOnEachGate) - def _comparison_key(self) -> Any: - return 1 - def __iter__(self): - raise NotImplementedError() +def test_supports_on_each_deprecation(): + class CustomGate(cirq.ops.gate_features.SupportsOnEachGate): + def num_qubits(self): + return 1 # coverage: ignore - assert cirq.H.on_each(QidIter())[0] == cirq.H.on(QidIter()) + with assert_deprecated(deadline="v0.14"): + assert isinstance(CustomGate(), cirq.ops.gate_features.SupportsOnEachGate) diff --git a/cirq-core/cirq/ops/gate_operation.py b/cirq-core/cirq/ops/gate_operation.py index 8d5f61be42d..75969fcbdb3 100644 --- a/cirq-core/cirq/ops/gate_operation.py +++ b/cirq-core/cirq/ops/gate_operation.py @@ -197,6 +197,18 @@ def _mixture_(self) -> Sequence[Tuple[float, Any]]: return getter() return NotImplemented + def _has_channel_(self) -> bool: + getter = getattr(self.gate, '_has_channel_', None) + if getter is not None: + return getter() + return NotImplemented + + def _channel_(self) -> Union[Tuple[np.ndarray], NotImplementedType]: + getter = getattr(self.gate, '_channel_', None) + if getter is not None: + return getter() + return NotImplemented + def _has_kraus_(self) -> bool: getter = getattr(self.gate, '_has_kraus_', None) if getter is not None: diff --git a/cirq-core/cirq/ops/gate_operation_test.py b/cirq-core/cirq/ops/gate_operation_test.py index a6d6073647a..8b6aab17875 100644 --- a/cirq-core/cirq/ops/gate_operation_test.py +++ b/cirq-core/cirq/ops/gate_operation_test.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from typing import Tuple import numpy as np import pytest @@ -433,3 +434,27 @@ def _is_parameterized_(self): assert not cirq.is_parameterized(No1().on(q)) assert not cirq.is_parameterized(No2().on(q)) assert cirq.is_parameterized(Yes().on(q)) + + +def test_channel_propagates_to_gate(): + class TestGate(cirq.SingleQubitGate): + def _channel_(self) -> np.ndarray: + return (np.eye(2),) + + def _has_channel_(self) -> bool: + return True + + def assert_kraus_eq(ks1: Tuple[np.ndarray, ...], ks2: Tuple[np.ndarray, ...]) -> None: + assert len(ks1) == len(ks2) + for k1, k2 in zip(ks1, ks2): + assert np.all(k1 == k2) + + identity_kraus = (np.eye(2),) + q = cirq.LineQubit(0) + gate = TestGate() + gate_op = TestGate().on(q) + with cirq.testing.assert_deprecated(deadline='v0.13', count=None): + assert cirq.has_channel(gate) + assert cirq.has_channel(gate_op) + assert_kraus_eq(cirq.channel(gate), identity_kraus) + assert_kraus_eq(cirq.channel(gate_op), identity_kraus) diff --git a/cirq-core/cirq/ops/identity.py b/cirq-core/cirq/ops/identity.py index c705b2fc9aa..d642e3b7fa1 100644 --- a/cirq-core/cirq/ops/identity.py +++ b/cirq-core/cirq/ops/identity.py @@ -20,14 +20,14 @@ from cirq import protocols, value from cirq._doc import document -from cirq.ops import gate_features, raw_types +from cirq.ops import raw_types if TYPE_CHECKING: import cirq @value.value_equality -class IdentityGate(gate_features.SupportsOnEachGate, raw_types.Gate): +class IdentityGate(raw_types.Gate): """A Gate that perform no operation on qubits. The unitary matrix of this gate is a diagonal matrix with all 1s on the diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index 2ec9191b741..9220d4dcb3c 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -66,8 +66,28 @@ def test_identity_on_each_only_single_qubit(): cirq.IdentityGate(1, (3,)).on(q0_3), cirq.IdentityGate(1, (3,)).on(q1_3), ] - with pytest.raises(ValueError, match='one qubit'): - cirq.IdentityGate(num_qubits=2).on_each(q0, q1) + + +def test_identity_on_each_two_qubits(): + q0, q1, q2, q3 = cirq.LineQubit.range(4) + q0_3, q1_3 = q0.with_dimension(3), q1.with_dimension(3) + assert cirq.IdentityGate(2).on_each([(q0, q1)]) == [cirq.IdentityGate(2)(q0, q1)] + assert cirq.IdentityGate(2).on_each([(q0, q1), (q2, q3)]) == [ + cirq.IdentityGate(2)(q0, q1), + cirq.IdentityGate(2)(q2, q3), + ] + assert cirq.IdentityGate(2, (3, 3)).on_each([(q0_3, q1_3)]) == [ + cirq.IdentityGate(2, (3, 3))(q0_3, q1_3), + ] + assert cirq.IdentityGate(2).on_each((q0, q1)) == [cirq.IdentityGate(2)(q0, q1)] + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + cirq.IdentityGate(2).on_each(q0, q1) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + cirq.IdentityGate(2).on_each([[(q0, q1)]]) + with pytest.raises(ValueError, match='Expected 2 qubits'): + cirq.IdentityGate(2).on_each([(q0,)]) + with pytest.raises(ValueError, match='Expected 2 qubits'): + cirq.IdentityGate(2).on_each([(q0, q1, q2)]) @pytest.mark.parametrize('num_qubits', [1, 2, 4]) diff --git a/cirq-core/cirq/ops/linear_combinations.py b/cirq-core/cirq/ops/linear_combinations.py index a8433d414e6..8be34fef6ae 100644 --- a/cirq-core/cirq/ops/linear_combinations.py +++ b/cirq-core/cirq/ops/linear_combinations.py @@ -14,6 +14,8 @@ from collections import defaultdict from typing import ( AbstractSet, + Dict, + Iterable, Mapping, Optional, Tuple, @@ -26,6 +28,9 @@ import numbers import numpy as np +from sympy.logic.boolalg import And, Not, Or, Xor +from sympy.core.expr import Expr +from sympy.core.symbol import Symbol from cirq import linalg, protocols, qis, value from cirq._doc import document @@ -397,6 +402,56 @@ def from_pauli_strings(cls, terms: Union[PauliString, List[PauliString]]) -> 'Pa termdict[key] += pstring.coefficient return cls(linear_dict=value.LinearDict(termdict)) + @classmethod + def from_boolean_expression( + cls, boolean_expr: Expr, qubit_map: Dict[str, 'cirq.Qid'] + ) -> 'PauliSum': + """Builds the Hamiltonian representation of a Boolean expression. + + This is based on "On the representation of Boolean and real functions as Hamiltonians for + quantum computing" by Stuart Hadfield, https://arxiv.org/abs/1804.09130 + + Args: + boolean_expr: A Sympy expression containing symbols and Boolean operations + qubit_map: map of string (boolean variable name) to qubit. + + Return: + The PauliString that represents the Boolean expression. + """ + if isinstance(boolean_expr, Symbol): + # In table 1, the entry for 'x' is '1/2.I - 1/2.Z' + return cls.from_pauli_strings( + [ + PauliString({}, 0.5), + PauliString({qubit_map[boolean_expr.name]: pauli_gates.Z}, -0.5), + ] + ) + + if isinstance(boolean_expr, (And, Not, Or, Xor)): + sub_pauli_sums = [ + cls.from_boolean_expression(sub_boolean_expr, qubit_map) + for sub_boolean_expr in boolean_expr.args + ] + # We apply the equalities of theorem 1. + if isinstance(boolean_expr, And): + pauli_sum = cls.from_pauli_strings(PauliString({}, 1.0)) + for sub_pauli_sum in sub_pauli_sums: + pauli_sum = pauli_sum * sub_pauli_sum + elif isinstance(boolean_expr, Not): + assert len(sub_pauli_sums) == 1 + pauli_sum = cls.from_pauli_strings(PauliString({}, 1.0)) - sub_pauli_sums[0] + elif isinstance(boolean_expr, Or): + pauli_sum = cls.from_pauli_strings(PauliString({}, 0.0)) + for sub_pauli_sum in sub_pauli_sums: + pauli_sum = pauli_sum + sub_pauli_sum - pauli_sum * sub_pauli_sum + elif isinstance(boolean_expr, Xor): + pauli_sum = cls.from_pauli_strings(PauliString({}, 0.0)) + for sub_pauli_sum in sub_pauli_sums: + pauli_sum = pauli_sum + sub_pauli_sum - 2.0 * pauli_sum * sub_pauli_sum + return pauli_sum + + raise ValueError(f'Unsupported type: {type(boolean_expr)}') + @property def qubits(self) -> Tuple[raw_types.Qid, ...]: qs = {q for k in self._linear_dict.keys() for q, _ in k} @@ -416,18 +471,21 @@ def copy(self) -> 'PauliSum': factory = type(self) return factory(self._linear_dict.copy()) - def matrix(self) -> np.ndarray: - """Reconstructs matrix of self from underlying Pauli operations. + def matrix(self, qubits: Optional[Iterable[raw_types.Qid]] = None) -> np.ndarray: + """Reconstructs matrix of self from underlying Pauli operations in + computational basis of qubits. Raises: TypeError: if any of the gates in self does not provide a unitary. """ - num_qubits = len(self.qubits) + + qubits = self.qubits if qubits is None else tuple(qubits) + num_qubits = len(qubits) num_dim = 2 ** num_qubits result = np.zeros((num_dim, num_dim), dtype=np.complex128) for vec, coeff in self._linear_dict.items(): op = _pauli_string_from_unit(vec) - result += coeff * op.matrix(self.qubits) + result += coeff * op.matrix(qubits) return result def _has_unitary_(self) -> bool: @@ -571,7 +629,10 @@ def __iadd__(self, other): def __add__(self, other): if not isinstance(other, (numbers.Complex, PauliString, PauliSum)): - return NotImplemented + if hasattr(other, 'gate') and isinstance(other.gate, identity.IdentityGate): + other = PauliString(other) + else: + return NotImplemented result = self.copy() result += other return result diff --git a/cirq-core/cirq/ops/linear_combinations_test.py b/cirq-core/cirq/ops/linear_combinations_test.py index 317c5d5ca7e..370d1bce478 100644 --- a/cirq-core/cirq/ops/linear_combinations_test.py +++ b/cirq-core/cirq/ops/linear_combinations_test.py @@ -18,6 +18,7 @@ import numpy as np import pytest import sympy +import sympy.parsing.sympy_parser as sympy_parser import cirq import cirq.testing @@ -1035,6 +1036,7 @@ def test_non_pauli_sum_has_no_unitary(psum): (cirq.Z(q1), (q1,)), (cirq.X(q0) + cirq.Y(q0), (q0,)), (cirq.X(q0) + cirq.Y(q2), (q0, q2)), + (cirq.X(q2) + cirq.Y(q0), (q0, q2)), (cirq.X(q0) * cirq.Y(q1) + cirq.Y(q1) * cirq.Z(q3), (q0, q1, q3)), ), ) @@ -1136,6 +1138,10 @@ def test_paulisum_validation(): ld = cirq.LinearDict({key: 2.0}) assert cirq.PauliSum(ld) == cirq.PauliSum.from_pauli_strings([2 * cirq.X(q[0])]) + ps = cirq.PauliSum() + ps += cirq.I(cirq.LineQubit(0)) + assert ps == cirq.PauliSum(cirq.LinearDict({frozenset(): complex(1)})) + def test_add_number_paulisum(): q = cirq.LineQubit.range(2) @@ -1189,6 +1195,35 @@ def test_pauli_sum_formatting(): assert str(empty) == "0.000" +def test_pauli_sum_matrix(): + q = cirq.LineQubit.range(3) + paulisum = cirq.X(q[0]) * cirq.X(q[1]) + cirq.Z(q[0]) + H1 = np.array( + [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 1.0, 0.0], [0.0, 1.0, -1.0, 0.0], [1.0, 0.0, 0.0, -1.0]] + ) + assert np.allclose(H1, paulisum.matrix()) + assert np.allclose(H1, paulisum.matrix([q[0], q[1]])) + # Expects a different matrix when change qubits order. + H2 = np.array( + [[1.0, 0.0, 0.0, 1.0], [0.0, -1.0, 1.0, 0.0], [0.0, 1.0, 1.0, 0.0], [1.0, 0.0, 0.0, -1.0]] + ) + assert np.allclose(H2, paulisum.matrix([q[1], q[0]])) + # Expects matrix with a different size when add a new qubit. + H3 = np.array( + [ + [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0], + ] + ) + assert np.allclose(H3, paulisum.matrix([q[1], q[2], q[0]])) + + def test_pauli_sum_repr(): q = cirq.LineQubit.range(2) pstr1 = cirq.X(q[0]) * cirq.X(q[1]) @@ -1318,6 +1353,42 @@ def test_pauli_sum_pow(): assert cirq.approx_eq(psum ** 0, identity) +# Using the entries of table 1 of https://arxiv.org/abs/1804.09130 as golden values. +@pytest.mark.parametrize( + 'boolean_expr,expected_pauli_sum', + [ + ('x', ['(-0.5+0j)*Z(x)', '(0.5+0j)*I']), + ('~x', ['(0.5+0j)*I', '(0.5+0j)*Z(x)']), + ('x0 ^ x1', ['(-0.5+0j)*Z(x0)*Z(x1)', '(0.5+0j)*I']), + ( + 'x0 & x1', + ['(-0.25+0j)*Z(x0)', '(-0.25+0j)*Z(x1)', '(0.25+0j)*I', '(0.25+0j)*Z(x0)*Z(x1)'], + ), + ( + 'x0 | x1', + ['(-0.25+0j)*Z(x0)', '(-0.25+0j)*Z(x0)*Z(x1)', '(-0.25+0j)*Z(x1)', '(0.75+0j)*I'], + ), + ('x0 ^ x1 ^ x2', ['(-0.5+0j)*Z(x0)*Z(x1)*Z(x2)', '(0.5+0j)*I']), + ], +) +def test_from_boolean_expression(boolean_expr, expected_pauli_sum): + boolean = sympy_parser.parse_expr(boolean_expr) + qubit_map = {name: cirq.NamedQubit(name) for name in sorted(cirq.parameter_names(boolean))} + actual = cirq.PauliSum.from_boolean_expression(boolean, qubit_map) + # Instead of calling str() directly, first make sure that the items are sorted. This is to make + # the unit test more robut in case Sympy would result in a different parsing order. By sorting + # the individual items, we would have a canonical representation. + actual_items = list(sorted(str(pauli_string) for pauli_string in actual)) + assert expected_pauli_sum == actual_items + + +def test_unsupported_op(): + not_a_boolean = sympy_parser.parse_expr('x * x') + qubit_map = {name: cirq.NamedQubit(name) for name in cirq.parameter_names(not_a_boolean)} + with pytest.raises(ValueError, match='Unsupported type'): + cirq.PauliSum.from_boolean_expression(not_a_boolean, qubit_map) + + def test_imul_aliasing(): q0, q1, q2 = cirq.LineQubit.range(3) psum1 = cirq.X(q0) + cirq.Y(q1) diff --git a/cirq-core/cirq/ops/matrix_gates.py b/cirq-core/cirq/ops/matrix_gates.py index c1b2ca86b93..84c909ed635 100644 --- a/cirq-core/cirq/ops/matrix_gates.py +++ b/cirq-core/cirq/ops/matrix_gates.py @@ -29,13 +29,22 @@ class MatrixGate(raw_types.Gate): """A unitary qubit or qudit gate defined entirely by its matrix.""" - def __init__(self, matrix: np.ndarray, *, qid_shape: Optional[Iterable[int]] = None) -> None: + def __init__( + self, + matrix: np.ndarray, + *, + name: str = None, + qid_shape: Optional[Iterable[int]] = None, + unitary_check_rtol: float = 1e-5, + unitary_check_atol: float = 1e-8, + ) -> None: """Initializes a matrix gate. Args: matrix: The matrix that defines the gate. qid_shape: The shape of state tensor that the matrix applies to. If not specified, this value is inferred by assuming that the matrix is supposed to apply to qubits. + name: The optional name of the gate to be displayed. """ if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]: raise ValueError('`matrix` must be a square 2d numpy array.') @@ -51,6 +60,7 @@ def __init__(self, matrix: np.ndarray, *, qid_shape: Optional[Iterable[int]] = N self._matrix = matrix self._qid_shape = tuple(qid_shape) + self._name = name m = int(np.prod(self._qid_shape)) if self._matrix.shape != (m, m): raise ValueError( @@ -59,7 +69,7 @@ def __init__(self, matrix: np.ndarray, *, qid_shape: Optional[Iterable[int]] = N f'qid_shape: {self._qid_shape}\n' ) - if not linalg.is_unitary(matrix): + if not linalg.is_unitary(matrix, rtol=unitary_check_rtol, atol=unitary_check_atol): raise ValueError(f'Not a unitary matrix: {self._matrix}') def _json_dict_(self) -> Dict[str, Any]: @@ -106,8 +116,16 @@ def _unitary_(self) -> np.ndarray: def _circuit_diagram_info_( self, args: 'cirq.CircuitDiagramInfoArgs' ) -> 'cirq.CircuitDiagramInfo': + n_qubits = len(self._qid_shape) + if self._name is not None: + symbols = ( + [self._name] + if n_qubits == 1 + else [f'{self._name}[{i+1}]' for i in range(0, n_qubits)] + ) + return protocols.CircuitDiagramInfo(wire_symbols=symbols) main = _matrix_to_diagram_symbol(self._matrix, args) - rest = [f'#{i+1}' for i in range(1, len(self._qid_shape))] + rest = [f'#{i+1}' for i in range(1, n_qubits)] return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest]) def __hash__(self) -> int: diff --git a/cirq-core/cirq/ops/matrix_gates_test.py b/cirq-core/cirq/ops/matrix_gates_test.py index 9f5c0d74cc3..16d21a45d18 100644 --- a/cirq-core/cirq/ops/matrix_gates_test.py +++ b/cirq-core/cirq/ops/matrix_gates_test.py @@ -218,6 +218,59 @@ def test_two_qubit_diagram(): ) +def test_named_single_qubit_diagram(): + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + m = np.array([[1, 1j], [1j, 1]]) * np.sqrt(0.5) + c = cirq.Circuit(cirq.MatrixGate(m, name='Foo').on(a), cirq.CZ(a, b)) + + expected_horizontal = """ +a: ───Foo───@─── + │ +b: ─────────@─── + """.strip() + assert expected_horizontal == c.to_text_diagram().strip() + + expected_vertical = """ +a b +│ │ +Foo │ +│ │ +@───@ +│ │ + """.strip() + assert expected_vertical == c.to_text_diagram(transpose=True).strip() + + +def test_named_two_qubit_diagram(): + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + c = cirq.NamedQubit('c') + c = cirq.Circuit( + cirq.MatrixGate(cirq.unitary(cirq.CZ), name='Foo').on(a, b), + cirq.MatrixGate(cirq.unitary(cirq.CZ), name='Bar').on(c, a), + ) + + expected_horizontal = """ +a: ───Foo[1]───Bar[2]─── + │ │ +b: ───Foo[2]───┼──────── + │ +c: ────────────Bar[1]─── + """.strip() + assert expected_horizontal == c.to_text_diagram().strip() + + expected_vertical = """ +a b c +│ │ │ +Foo[1]─Foo[2] │ +│ │ │ +Bar[2]─┼──────Bar[1] +│ │ │ + """.strip() + assert expected_vertical == c.to_text_diagram(transpose=True).strip() + + def test_str_executes(): assert '1' in str(cirq.MatrixGate(np.eye(2))) assert '0' in str(cirq.MatrixGate(np.eye(4))) @@ -299,3 +352,26 @@ def test_protocols_and_repr(): cirq.testing.assert_implements_consistent_protocols( cirq.MatrixGate(np.diag([1, 1j, -1]), qid_shape=(3,)) ) + + +def test_matrixgate_unitary_tolerance(): + ## non-unitary matrix + with pytest.raises(ValueError): + _ = cirq.MatrixGate(np.array([[1, 0], [0, -0.6]]), unitary_check_atol=0.5) + + # very high atol -> check converges quickly + _ = cirq.MatrixGate(np.array([[1, 0], [0, 1]]), unitary_check_atol=1) + + # very high rtol -> check converges quickly + _ = cirq.MatrixGate(np.array([[1, 0], [0, -0.6]]), unitary_check_rtol=1) + + ## unitary matrix + _ = cirq.MatrixGate(np.array([[0.707, 0.707], [-0.707, 0.707]]), unitary_check_atol=0.5) + + # very low atol -> the check never converges + with pytest.raises(ValueError): + _ = cirq.MatrixGate(np.array([[0.707, 0.707], [-0.707, 0.707]]), unitary_check_atol=1e-10) + + # very low atol -> the check never converges + with pytest.raises(ValueError): + _ = cirq.MatrixGate(np.array([[0.707, 0.707], [-0.707, 0.707]]), unitary_check_rtol=1e-10) diff --git a/cirq-core/cirq/ops/measurement_gate_test.py b/cirq-core/cirq/ops/measurement_gate_test.py index 91765e55372..7d08f7cb605 100644 --- a/cirq-core/cirq/ops/measurement_gate_test.py +++ b/cirq-core/cirq/ops/measurement_gate_test.py @@ -18,6 +18,15 @@ import cirq +def test_eval_repr(): + # Basic safeguard against repr-inequality. + op = cirq.GateOperation( + gate=cirq.MeasurementGate(1, cirq.MeasurementKey(path=(), name='q0_1_0'), ()), + qubits=[cirq.GridQubit(0, 1)], + ) + cirq.testing.assert_equivalent_repr(op) + + @pytest.mark.parametrize('num_qubits', [1, 2, 4]) def test_measure_init(num_qubits): assert cirq.MeasurementGate(num_qubits).num_qubits() == num_qubits diff --git a/cirq-core/cirq/ops/moment.py b/cirq-core/cirq/ops/moment.py index bce44b10cdd..a757d043981 100644 --- a/cirq-core/cirq/ops/moment.py +++ b/cirq-core/cirq/ops/moment.py @@ -31,6 +31,7 @@ from cirq import protocols, ops from cirq.ops import raw_types +from cirq.protocols import circuit_diagram_info_protocol from cirq.type_workarounds import NotImplementedType if TYPE_CHECKING: @@ -278,6 +279,10 @@ def __repr__(self) -> str: def __str__(self) -> str: return self.to_text_diagram() + def _decompose_(self) -> 'cirq.OP_TREE': + """See `cirq.SupportsDecompose`.""" + return self._operations + def transform_qubits( self: TSelf_Moment, qubit_map: Union[Dict['cirq.Qid', 'cirq.Qid'], Callable[['cirq.Qid'], 'cirq.Qid']], @@ -420,7 +425,7 @@ def cleanup_key(key: Any) -> Any: precision=precision, include_tags=include_tags, ) - info = protocols.CircuitDiagramInfo._op_info_with_fallback(op, args=args) + info = circuit_diagram_info_protocol._op_info_with_fallback(op, args=args) symbols = info._wire_symbols_including_formatted_exponent(args) for label, q in zip(symbols, op.qubits): x, y = qubit_positions[q] diff --git a/cirq-core/cirq/ops/moment_test.py b/cirq-core/cirq/ops/moment_test.py index 0cebe246a48..9df26d22609 100644 --- a/cirq-core/cirq/ops/moment_test.py +++ b/cirq-core/cirq/ops/moment_test.py @@ -335,6 +335,25 @@ def test_container_methods(): assert len(m) == 2 +def test_decompose(): + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + m = cirq.Moment(cirq.X(a), cirq.X(b)) + assert list(cirq.decompose(m)) == list(m.operations) + + +def test_measurement_keys(): + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + m = cirq.Moment(cirq.X(a), cirq.X(b)) + assert cirq.measurement_keys(m) == set() + assert not cirq.is_measurement(m) + + m2 = cirq.Moment(cirq.measure(a, b, key='foo')) + assert cirq.measurement_keys(m2) == {'foo'} + assert cirq.is_measurement(m2) + + def test_bool(): assert not cirq.Moment() a = cirq.NamedQubit('a') diff --git a/cirq-core/cirq/ops/pauli_string.py b/cirq-core/cirq/ops/pauli_string.py index 9924e3eca6b..0a4f913f688 100644 --- a/cirq-core/cirq/ops/pauli_string.py +++ b/cirq-core/cirq/ops/pauli_string.py @@ -857,7 +857,7 @@ def conjugated_by(self, clifford: 'cirq.OP_TREE') -> 'PauliString': for clifford_op in _decompose_into_cliffords(op)[::-1]: if pauli_map.keys().isdisjoint(set(clifford_op.qubits)): continue - should_negate ^= PauliString._pass_operation_over(pauli_map, clifford_op, False) + should_negate ^= _pass_operation_over(pauli_map, clifford_op, False) coef = -self._coefficient if should_negate else self.coefficient return PauliString(qubit_pauli_map=pauli_map, coefficient=coef) @@ -933,91 +933,10 @@ def pass_operations_over( for clifford_op in decomposed: if pauli_map.keys().isdisjoint(set(clifford_op.qubits)): continue - should_negate ^= PauliString._pass_operation_over( - pauli_map, clifford_op, after_to_before - ) + should_negate ^= _pass_operation_over(pauli_map, clifford_op, after_to_before) coef = -self._coefficient if should_negate else self.coefficient return PauliString(qubit_pauli_map=pauli_map, coefficient=coef) - @staticmethod - def _pass_operation_over( - pauli_map: Dict[TKey, pauli_gates.Pauli], - op: 'cirq.Operation', - after_to_before: bool = False, - ) -> bool: - if isinstance(op, gate_operation.GateOperation): - gate = op.gate - if isinstance(gate, clifford_gate.SingleQubitCliffordGate): - return PauliString._pass_single_clifford_gate_over( - pauli_map, gate, cast(TKey, op.qubits[0]), after_to_before=after_to_before - ) - if isinstance(gate, pauli_interaction_gate.PauliInteractionGate): - return PauliString._pass_pauli_interaction_gate_over( - pauli_map, - gate, - cast(TKey, op.qubits[0]), - cast(TKey, op.qubits[1]), - after_to_before=after_to_before, - ) - raise NotImplementedError(f'Unsupported operation: {op!r}') - - @staticmethod - def _pass_single_clifford_gate_over( - pauli_map: Dict[TKey, pauli_gates.Pauli], - gate: clifford_gate.SingleQubitCliffordGate, - qubit: TKey, - after_to_before: bool = False, - ) -> bool: - if qubit not in pauli_map: - return False - if not after_to_before: - gate **= -1 - pauli, inv = gate.transform(pauli_map[qubit]) - pauli_map[qubit] = pauli - return inv - - @staticmethod - def _pass_pauli_interaction_gate_over( - pauli_map: Dict[TKey, pauli_gates.Pauli], - gate: pauli_interaction_gate.PauliInteractionGate, - qubit0: TKey, - qubit1: TKey, - after_to_before: bool = False, - ) -> bool: - def merge_and_kickback( - qubit: TKey, - pauli_left: Optional[pauli_gates.Pauli], - pauli_right: Optional[pauli_gates.Pauli], - inv: bool, - ) -> int: - assert pauli_left is not None or pauli_right is not None - if pauli_left is None or pauli_right is None: - pauli_map[qubit] = cast(pauli_gates.Pauli, pauli_left or pauli_right) - return 0 - if pauli_left == pauli_right: - del pauli_map[qubit] - return 0 - - pauli_map[qubit] = pauli_left.third(pauli_right) - if (pauli_left < pauli_right) ^ after_to_before: - return int(inv) * 2 + 1 - - return int(inv) * 2 - 1 - - quarter_kickback = 0 - if qubit0 in pauli_map and not protocols.commutes(pauli_map[qubit0], gate.pauli0): - quarter_kickback += merge_and_kickback( - qubit1, gate.pauli1, pauli_map.get(qubit1), gate.invert1 - ) - if qubit1 in pauli_map and not protocols.commutes(pauli_map[qubit1], gate.pauli1): - quarter_kickback += merge_and_kickback( - qubit0, pauli_map.get(qubit0), gate.pauli0, gate.invert0 - ) - assert ( - quarter_kickback % 2 == 0 - ), 'Impossible condition. quarter_kickback is either incremented twice or never.' - return quarter_kickback % 4 == 2 - def _validate_qubit_mapping( qubit_map: Mapping[TKey, int], pauli_qubits: Tuple[TKey, ...], num_state_qubits: int @@ -1489,6 +1408,85 @@ def _decompose_into_cliffords(op: 'cirq.Operation') -> List['cirq.Operation']: ) +def _pass_operation_over( + pauli_map: Dict[TKey, pauli_gates.Pauli], + op: 'cirq.Operation', + after_to_before: bool = False, +) -> bool: + if isinstance(op, gate_operation.GateOperation): + gate = op.gate + if isinstance(gate, clifford_gate.SingleQubitCliffordGate): + return _pass_single_clifford_gate_over( + pauli_map, gate, cast(TKey, op.qubits[0]), after_to_before=after_to_before + ) + if isinstance(gate, pauli_interaction_gate.PauliInteractionGate): + return _pass_pauli_interaction_gate_over( + pauli_map, + gate, + cast(TKey, op.qubits[0]), + cast(TKey, op.qubits[1]), + after_to_before=after_to_before, + ) + raise NotImplementedError(f'Unsupported operation: {op!r}') + + +def _pass_single_clifford_gate_over( + pauli_map: Dict[TKey, pauli_gates.Pauli], + gate: clifford_gate.SingleQubitCliffordGate, + qubit: TKey, + after_to_before: bool = False, +) -> bool: + if qubit not in pauli_map: + return False # coverage: ignore + if not after_to_before: + gate **= -1 + pauli, inv = gate.transform(pauli_map[qubit]) + pauli_map[qubit] = pauli + return inv + + +def _pass_pauli_interaction_gate_over( + pauli_map: Dict[TKey, pauli_gates.Pauli], + gate: pauli_interaction_gate.PauliInteractionGate, + qubit0: TKey, + qubit1: TKey, + after_to_before: bool = False, +) -> bool: + def merge_and_kickback( + qubit: TKey, + pauli_left: Optional[pauli_gates.Pauli], + pauli_right: Optional[pauli_gates.Pauli], + inv: bool, + ) -> int: + assert pauli_left is not None or pauli_right is not None + if pauli_left is None or pauli_right is None: + pauli_map[qubit] = cast(pauli_gates.Pauli, pauli_left or pauli_right) + return 0 + if pauli_left == pauli_right: + del pauli_map[qubit] + return 0 + + pauli_map[qubit] = pauli_left.third(pauli_right) + if (pauli_left < pauli_right) ^ after_to_before: + return int(inv) * 2 + 1 + + return int(inv) * 2 - 1 + + quarter_kickback = 0 + if qubit0 in pauli_map and not protocols.commutes(pauli_map[qubit0], gate.pauli0): + quarter_kickback += merge_and_kickback( + qubit1, gate.pauli1, pauli_map.get(qubit1), gate.invert1 + ) + if qubit1 in pauli_map and not protocols.commutes(pauli_map[qubit1], gate.pauli1): + quarter_kickback += merge_and_kickback( + qubit0, pauli_map.get(qubit0), gate.pauli0, gate.invert0 + ) + assert ( + quarter_kickback % 2 == 0 + ), 'Impossible condition. quarter_kickback is either incremented twice or never.' + return quarter_kickback % 4 == 2 + + # Mypy has extreme difficulty with these constants for some reason. _i = cast(identity.IdentityGate, identity.I) # type: ignore _x = cast(pauli_gates.Pauli, pauli_gates.X) # type: ignore diff --git a/cirq-core/cirq/ops/raw_types.py b/cirq-core/cirq/ops/raw_types.py index 36afcc7624b..e3d4c0c0f03 100644 --- a/cirq-core/cirq/ops/raw_types.py +++ b/cirq-core/cirq/ops/raw_types.py @@ -23,6 +23,8 @@ Collection, Dict, Hashable, + Iterable, + List, Optional, Sequence, Tuple, @@ -211,6 +213,52 @@ def on(self, *qubits: Qid) -> 'Operation': return gate_operation.GateOperation(self, list(qubits)) + def on_each(self, *targets: Union[Qid, Iterable[Any]]) -> List['cirq.Operation']: + """Returns a list of operations applying the gate to all targets. + Args: + *targets: The qubits to apply this gate to. For single-qubit gates + this can be provided as varargs or a combination of nested + iterables. For multi-qubit gates this must be provided as an + `Iterable[Sequence[Qid]]`, where each sequence has `num_qubits` + qubits. + Returns: + Operations applying this gate to the target qubits. + Raises: + ValueError if targets are not instances of Qid or Iterable[Qid]. + ValueError if the gate qubit number is incompatible. + """ + operations: List['cirq.Operation'] = [] + if self._num_qubits_() > 1: + iterator: Iterable = targets + if len(targets) == 1: + if not isinstance(targets[0], Iterable): + raise TypeError(f'{targets[0]} object is not iterable.') + t0 = list(targets[0]) + iterator = [t0] if t0 and isinstance(t0[0], Qid) else t0 + for target in iterator: + if not isinstance(target, Sequence): + raise ValueError( + f'Inputs to multi-qubit gates must be Sequence[Qid].' + f' Type: {type(target)}' + ) + if not all(isinstance(x, Qid) for x in target): + raise ValueError(f'All values in sequence should be Qids, but got {target}') + if len(target) != self._num_qubits_(): + raise ValueError(f'Expected {self._num_qubits_()} qubits, got {target}') + operations.append(self.on(*target)) + return operations + + for target in targets: + if isinstance(target, Qid): + operations.append(self.on(target)) + elif isinstance(target, Iterable) and not isinstance(target, str): + operations.extend(self.on_each(*target)) + else: + raise ValueError( + f'Gate was called with type different than Qid. Type: {type(target)}' + ) + return operations + def wrap_in_linear_combination( self, coefficient: Union[complex, float, int] = 1 ) -> 'cirq.LinearCombinationOfGates': @@ -425,7 +473,7 @@ def untagged(self) -> 'cirq.Operation': """Returns the underlying operation without any tags.""" return self - def with_tags(self, *new_tags: Hashable) -> 'cirq.TaggedOperation': + def with_tags(self, *new_tags: Hashable) -> 'cirq.Operation': """Creates a new TaggedOperation, with this op and the specified tags. This method can be used to attach meta-data to specific operations @@ -443,6 +491,8 @@ def with_tags(self, *new_tags: Hashable) -> 'cirq.TaggedOperation': Args: new_tags: The tags to wrap this operation in. """ + if not new_tags: + return self return TaggedOperation(self, *new_tags) def transform_qubits( @@ -608,6 +658,8 @@ def with_tags(self, *new_tags: Hashable) -> 'cirq.TaggedOperation': that has the tags of this operation combined with the new_tags specified as the parameter. """ + if not new_tags: + return self return TaggedOperation(self.sub_operation, *self._tags, *new_tags) def __str__(self) -> str: diff --git a/cirq-core/cirq/ops/raw_types_test.py b/cirq-core/cirq/ops/raw_types_test.py index afb85664751..2510ecdedd9 100644 --- a/cirq-core/cirq/ops/raw_types_test.py +++ b/cirq-core/cirq/ops/raw_types_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import AbstractSet +from typing import AbstractSet, Iterator, Any import pytest import numpy as np @@ -434,6 +434,14 @@ def test_tagged_operation(): assert not cirq.is_measurement(op) +def test_with_tags_returns_same_instance_if_possible(): + untagged = cirq.X(cirq.GridQubit(1, 1)) + assert untagged.with_tags() is untagged + + tagged = untagged.with_tags('foo') + assert tagged.with_tags() is tagged + + def test_tagged_measurement(): assert not cirq.is_measurement(cirq.GlobalPhaseOperation(coefficient=-1.0).with_tags('tag0')) @@ -739,3 +747,176 @@ def qubits(self): cirq.act_on(NoActOn()(q).with_tags("test"), args) with pytest.raises(TypeError, match="Failed to act"): cirq.act_on(MissingActOn().with_tags("test"), args) + + +def test_single_qubit_gate_validates_on_each(): + class Dummy(cirq.SingleQubitGate): + def matrix(self): + pass + + g = Dummy() + assert g.num_qubits() == 1 + + test_qubits = [cirq.NamedQubit(str(i)) for i in range(3)] + + _ = g.on_each(*test_qubits) + _ = g.on_each(test_qubits) + + test_non_qubits = [str(i) for i in range(3)] + with pytest.raises(ValueError): + _ = g.on_each(*test_non_qubits) + with pytest.raises(ValueError): + _ = g.on_each(*test_non_qubits) + + +def test_on_each(): + class CustomGate(cirq.SingleQubitGate): + pass + + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + c = CustomGate() + + assert c.on_each() == [] + assert c.on_each(a) == [c(a)] + assert c.on_each(a, b) == [c(a), c(b)] + assert c.on_each(b, a) == [c(b), c(a)] + + assert c.on_each([]) == [] + assert c.on_each([a]) == [c(a)] + assert c.on_each([a, b]) == [c(a), c(b)] + assert c.on_each([b, a]) == [c(b), c(a)] + assert c.on_each([a, [b, a], b]) == [c(a), c(b), c(a), c(b)] + + with pytest.raises(ValueError): + c.on_each('abcd') + with pytest.raises(ValueError): + c.on_each(['abcd']) + with pytest.raises(ValueError): + c.on_each([a, 'abcd']) + + qubit_iterator = (q for q in [a, b, a, b]) + assert isinstance(qubit_iterator, Iterator) + assert c.on_each(qubit_iterator) == [c(a), c(b), c(a), c(b)] + + +def test_on_each_two_qubits(): + class CustomGate(cirq.TwoQubitGate): + pass + + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + g = CustomGate() + + assert g.on_each([]) == [] + assert g.on_each([(a, b)]) == [g(a, b)] + assert g.on_each([[a, b]]) == [g(a, b)] + assert g.on_each([(b, a)]) == [g(b, a)] + assert g.on_each([(a, b), (b, a)]) == [g(a, b), g(b, a)] + assert g.on_each(zip([a, b], [b, a])) == [g(a, b), g(b, a)] + assert g.on_each() == [] + assert g.on_each((b, a)) == [g(b, a)] + assert g.on_each((a, b), (a, b)) == [g(a, b), g(a, b)] + assert g.on_each(*zip([a, b], [b, a])) == [g(a, b), g(b, a)] + with pytest.raises(TypeError, match='object is not iterable'): + g.on_each(a) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + g.on_each(a, b) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + g.on_each([12]) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + g.on_each([(a, b), 12]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([(a, b), [(a, b)]]) + with pytest.raises(ValueError, match='Expected 2 qubits'): + g.on_each([()]) + with pytest.raises(ValueError, match='Expected 2 qubits'): + g.on_each([(a,)]) + with pytest.raises(ValueError, match='Expected 2 qubits'): + g.on_each([(a, b, a)]) + with pytest.raises(ValueError, match='Expected 2 qubits'): + g.on_each(zip([a, a])) + with pytest.raises(ValueError, match='Expected 2 qubits'): + g.on_each(zip([a, a], [b, b], [a, a])) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each('ab') + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each(('ab',)) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([('ab',)]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([(a, 'ab')]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([(a, 'b')]) + + qubit_iterator = (qs for qs in [[a, b], [a, b]]) + assert isinstance(qubit_iterator, Iterator) + assert g.on_each(qubit_iterator) == [g(a, b), g(a, b)] + + +def test_on_each_three_qubits(): + class CustomGate(cirq.ThreeQubitGate): + pass + + a = cirq.NamedQubit('a') + b = cirq.NamedQubit('b') + c = cirq.NamedQubit('c') + g = CustomGate() + + assert g.on_each([]) == [] + assert g.on_each([(a, b, c)]) == [g(a, b, c)] + assert g.on_each([[a, b, c]]) == [g(a, b, c)] + assert g.on_each([(c, b, a)]) == [g(c, b, a)] + assert g.on_each([(a, b, c), (c, b, a)]) == [g(a, b, c), g(c, b, a)] + assert g.on_each(zip([a, c], [b, b], [c, a])) == [g(a, b, c), g(c, b, a)] + assert g.on_each() == [] + assert g.on_each((c, b, a)) == [g(c, b, a)] + assert g.on_each((a, b, c), (c, b, a)) == [g(a, b, c), g(c, b, a)] + assert g.on_each(*zip([a, c], [b, b], [c, a])) == [g(a, b, c), g(c, b, a)] + with pytest.raises(TypeError, match='object is not iterable'): + g.on_each(a) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + g.on_each(a, b, c) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + g.on_each([12]) + with pytest.raises(ValueError, match='Inputs to multi-qubit gates must be Sequence'): + g.on_each([(a, b, c), 12]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([(a, b, c), [(a, b, c)]]) + with pytest.raises(ValueError, match='Expected 3 qubits'): + g.on_each([(a,)]) + with pytest.raises(ValueError, match='Expected 3 qubits'): + g.on_each([(a, b)]) + with pytest.raises(ValueError, match='Expected 3 qubits'): + g.on_each([(a, b, c, a)]) + with pytest.raises(ValueError, match='Expected 3 qubits'): + g.on_each(zip([a, a], [b, b])) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each('abc') + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each(('abc',)) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([('abc',)]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([(a, 'abc')]) + with pytest.raises(ValueError, match='All values in sequence should be Qids'): + g.on_each([(a, 'bc')]) + + qubit_iterator = (qs for qs in [[a, b, c], [a, b, c]]) + assert isinstance(qubit_iterator, Iterator) + assert g.on_each(qubit_iterator) == [g(a, b, c), g(a, b, c)] + + +def test_on_each_iterable_qid(): + class QidIter(cirq.Qid): + @property + def dimension(self) -> int: + return 2 + + def _comparison_key(self) -> Any: + return 1 + + def __iter__(self): + raise NotImplementedError() + + assert cirq.H.on_each(QidIter())[0] == cirq.H.on(QidIter()) diff --git a/cirq-core/cirq/ops/swap_gates.py b/cirq-core/cirq/ops/swap_gates.py index 8acba124f97..7ea26c831e1 100644 --- a/cirq-core/cirq/ops/swap_gates.py +++ b/cirq-core/cirq/ops/swap_gates.py @@ -16,9 +16,12 @@ This module creates Gate instances for the following gates: SWAP: the swap gate. ISWAP: a swap gate with a phase on the swapped subspace. + SQRT_ISWAP: square root of the ISWAP gate. + SQRT_ISWAP_INV: inverse square root of the ISWAP gate. Each of these are implemented as EigenGates, which means that they can be -raised to a power (i.e. cirq.ISWAP**0.5). See the definition in EigenGate. +raised to a power (i.e. SQRT_ISWAP_INV=cirq.ISWAP**-0.5). See the definition in +EigenGate. """ from typing import Optional, Tuple, TYPE_CHECKING, List, Sequence @@ -332,3 +335,33 @@ def riswap(rads: value.TParamVal) -> ISwapPowGate: ``` """, ) + +SQRT_ISWAP = ISwapPowGate(exponent=0.5) +document( + SQRT_ISWAP, + """The square root of iswap gate. + + Matrix: + ``` + [[1, 0, 0, 0], + [0, 1/√2, i/√2, 0], + [0, i/√2, 1/√2, 0], + [0, 0, 0, 1]] + ``` + """, +) + +SQRT_ISWAP_INV = ISwapPowGate(exponent=-0.5) +document( + SQRT_ISWAP_INV, + """The inverse square root of iswap gate. + + Matrix: + ``` + [[1, 0, 0, 0], + [0, 1/√2, -i/√2, 0], + [0, -i/√2, 1/√2, 0], + [0, 0, 0, 1]] + ``` + """, +) diff --git a/cirq-core/cirq/ops/swap_gates_test.py b/cirq-core/cirq/ops/swap_gates_test.py index 5c135f65723..3f96bd9c237 100644 --- a/cirq-core/cirq/ops/swap_gates_test.py +++ b/cirq-core/cirq/ops/swap_gates_test.py @@ -109,6 +109,34 @@ def test_iswap_unitary(): # yapf: enable +def test_sqrt_iswap_unitary(): + # yapf: disable + cirq.testing.assert_allclose_up_to_global_phase( + cirq.unitary(cirq.SQRT_ISWAP), + # Reference for the sqrt-iSWAP gate's matrix: + # https://arxiv.org/abs/2105.06074 + np.array([[1, 0, 0, 0], + [0, 1/2**0.5, 1j/2**0.5, 0], + [0, 1j/2**0.5, 1/2**0.5, 0], + [0, 0, 0, 1]]), + atol=1e-8) + # yapf: enable + + +def test_sqrt_iswap_inv_unitary(): + # yapf: disable + cirq.testing.assert_allclose_up_to_global_phase( + cirq.unitary(cirq.SQRT_ISWAP_INV), + # Reference for the inv-sqrt-iSWAP gate's matrix: + # https://arxiv.org/abs/2105.06074 + np.array([[1, 0, 0, 0], + [0, 1/2**0.5, -1j/2**0.5, 0], + [0, -1j/2**0.5, 1/2**0.5, 0], + [0, 0, 0, 1]]), + atol=1e-8) + # yapf: enable + + def test_repr(): assert repr(cirq.SWAP) == 'cirq.SWAP' assert repr(cirq.SWAP ** 0.5) == '(cirq.SWAP**0.5)' diff --git a/cirq-core/cirq/optimizers/__init__.py b/cirq-core/cirq/optimizers/__init__.py index 28ccc11cede..f3c739f90db 100644 --- a/cirq-core/cirq/optimizers/__init__.py +++ b/cirq-core/cirq/optimizers/__init__.py @@ -60,6 +60,10 @@ MergeInteractions, ) +from cirq.optimizers.merge_interactions_to_sqrt_iswap import ( + MergeInteractionsToSqrtIswap, +) + from cirq.optimizers.merge_single_qubit_gates import ( merge_single_qubit_gates_into_phased_x_z, merge_single_qubit_gates_into_phxz, @@ -90,6 +94,9 @@ two_qubit_matrix_to_operations, two_qubit_matrix_to_diagonal_and_operations, ) +from cirq.optimizers.two_qubit_to_sqrt_iswap import ( + two_qubit_matrix_to_sqrt_iswap_operations, +) from cirq.optimizers.two_qubit_to_fsim import ( decompose_two_qubit_interaction_into_four_fsim_gates, diff --git a/cirq-core/cirq/optimizers/merge_interactions.py b/cirq-core/cirq/optimizers/merge_interactions.py index d9a3b560995..96665bc92dc 100644 --- a/cirq-core/cirq/optimizers/merge_interactions.py +++ b/cirq-core/cirq/optimizers/merge_interactions.py @@ -12,10 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""An optimization pass that combines adjacent single-qubit rotations.""" +"""An optimization pass that combines adjacent series of gates on two qubits.""" from typing import Callable, List, Optional, Sequence, Tuple, cast, TYPE_CHECKING +import abc import numpy as np from cirq import circuits, ops, protocols @@ -25,19 +26,25 @@ import cirq -class MergeInteractions(circuits.PointOptimizer): - """Combines series of adjacent one and two-qubit gates operating on a pair - of qubits.""" +class MergeInteractionsAbc(circuits.PointOptimizer, metaclass=abc.ABCMeta): + """Combines series of adjacent one- and two-qubit, non-parametrized gates + operating on a pair of qubits.""" def __init__( self, tolerance: float = 1e-8, - allow_partial_czs: bool = True, post_clean_up: Callable[[Sequence[ops.Operation]], ops.OP_TREE] = lambda op_list: op_list, ) -> None: + """ + Args: + tolerance: A limit on the amount of absolute error introduced by the + construction. + post_clean_up: This function is called on each set of optimized + operations before they are put into the circuit to replace the + old operations. + """ super().__init__(post_clean_up=post_clean_up) self.tolerance = tolerance - self.allow_partial_czs = allow_partial_czs def optimization_at( self, circuit: circuits.Circuit, index: int, op: ops.Operation @@ -55,25 +62,16 @@ def optimization_at( switch_to_new = False switch_to_new |= any( - len(old_op.qubits) == 2 and not isinstance(old_op.gate, ops.CZPowGate) + len(old_op.qubits) == 2 and not self._may_keep_old_op(old_op) for old_op in old_operations ) - if not self.allow_partial_czs: - switch_to_new |= any( - isinstance(old_op, ops.GateOperation) - and isinstance(old_op.gate, ops.CZPowGate) - and old_op.gate.exponent != 1 - for old_op in old_operations - ) # This point cannot be optimized using this method if not switch_to_new and old_interaction_count <= 1: return None - # Find a max-3-cz construction. - new_operations = two_qubit_decompositions.two_qubit_matrix_to_operations( - op.qubits[0], op.qubits[1], matrix, self.allow_partial_czs, self.tolerance, False - ) + # Find a (possibly ideal) decomposition of the merged operations. + new_operations = self._two_qubit_matrix_to_operations(op.qubits[0], op.qubits[1], matrix) new_interaction_count = len( [new_op for new_op in new_operations if len(new_op.qubits) == 2] ) @@ -89,6 +87,30 @@ def optimization_at( new_operations=new_operations, ) + @abc.abstractmethod + def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool: + """Returns True if the old two-qubit operation may be left unchanged + without decomposition.""" + + @abc.abstractmethod + def _two_qubit_matrix_to_operations( + self, + q0: 'cirq.Qid', + q1: 'cirq.Qid', + mat: np.ndarray, + ) -> Sequence['cirq.Operation']: + """Decomposes the merged two-qubit gate unitary into the minimum number + of two-qubit gates. + + Args: + q0: The first qubit being operated on. + q1: The other qubit being operated on. + mat: Defines the operation to apply to the pair of qubits. + + Returns: + A list of operations implementing the matrix. + """ + def _op_to_matrix( self, op: ops.Operation, qubits: Tuple['cirq.Qid', ...] ) -> Optional[np.ndarray]: @@ -120,7 +142,7 @@ def _op_to_matrix( if op.qubits == qubits: return matrix if op.qubits == (q2, q1): - return MergeInteractions._flip_kron_order(matrix) + return _flip_kron_order(matrix) if op.qubits == (q1,): return np.kron(matrix, np.eye(2)) if op.qubits == (q2,): @@ -130,7 +152,7 @@ def _op_to_matrix( def _scan_two_qubit_ops_into_matrix( self, circuit: circuits.Circuit, index: Optional[int], qubits: Tuple['cirq.Qid', ...] - ) -> Tuple[List[ops.Operation], List[int], np.ndarray]: + ) -> Tuple[Sequence[ops.Operation], List[int], np.ndarray]: """Accumulates operations affecting the given pair of qubits. The scan terminates when it hits the end of the circuit, finds an @@ -172,12 +194,64 @@ def _scan_two_qubit_ops_into_matrix( return all_operations, touched_indices, product - @staticmethod - def _flip_kron_order(mat4x4: np.ndarray) -> np.ndarray: - """Given M = sum(kron(a_i, b_i)), returns M' = sum(kron(b_i, a_i)).""" - result = np.array([[0] * 4] * 4, dtype=np.complex128) - order = [0, 2, 1, 3] - for i in range(4): - for j in range(4): - result[order[i], order[j]] = mat4x4[i, j] - return result + +def _flip_kron_order(mat4x4: np.ndarray) -> np.ndarray: + """Given M = sum(kron(a_i, b_i)), returns M' = sum(kron(b_i, a_i)).""" + result = np.array([[0] * 4] * 4, dtype=np.complex128) + order = [0, 2, 1, 3] + for i in range(4): + for j in range(4): + result[order[i], order[j]] = mat4x4[i, j] + return result + + +class MergeInteractions(MergeInteractionsAbc): + """Combines series of adjacent one- and two-qubit, non-parametrized gates + operating on a pair of qubits and replaces each series with the minimum + number of CZ gates.""" + + def __init__( + self, + tolerance: float = 1e-8, + allow_partial_czs: bool = True, + post_clean_up: Callable[[Sequence[ops.Operation]], ops.OP_TREE] = lambda op_list: op_list, + ) -> None: + """ + Args: + tolerance: A limit on the amount of absolute error introduced by the + construction. + allow_partial_czs: Enables the use of Partial-CZ gates. + post_clean_up: This function is called on each set of optimized + operations before they are put into the circuit to replace the + old operations. + """ + super().__init__(tolerance=tolerance, post_clean_up=post_clean_up) + self.allow_partial_czs = allow_partial_czs + + def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool: + """Returns True if the old two-qubit operation may be left unchanged + without decomposition.""" + if self.allow_partial_czs: + return isinstance(old_op.gate, ops.CZPowGate) + return isinstance(old_op.gate, ops.CZPowGate) and old_op.gate.exponent == 1 + + def _two_qubit_matrix_to_operations( + self, + q0: 'cirq.Qid', + q1: 'cirq.Qid', + mat: np.ndarray, + ) -> Sequence['cirq.Operation']: + """Decomposes the merged two-qubit gate unitary into the minimum number + of CZ gates. + + Args: + q0: The first qubit being operated on. + q1: The other qubit being operated on. + mat: Defines the operation to apply to the pair of qubits. + + Returns: + A list of operations implementing the matrix. + """ + return two_qubit_decompositions.two_qubit_matrix_to_operations( + q0, q1, mat, self.allow_partial_czs, self.tolerance, False + ) diff --git a/cirq-core/cirq/optimizers/merge_interactions_test.py b/cirq-core/cirq/optimizers/merge_interactions_test.py index d0593087b85..d1e414aa053 100644 --- a/cirq-core/cirq/optimizers/merge_interactions_test.py +++ b/cirq-core/cirq/optimizers/merge_interactions_test.py @@ -163,6 +163,16 @@ def test_optimizes_single_iswap(): assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 +def test_optimizes_tagged_partial_cz(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit((cirq.CZ ** 0.5)(a, b).with_tags('mytag')) + assert_optimization_not_broken(c) + cirq.MergeInteractions(allow_partial_czs=False).optimize_circuit(c) + assert ( + len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 + ), 'It should take 2 CZ gates to decompose a CZ**0.5 gate' + + @pytest.mark.parametrize( 'circuit', ( diff --git a/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py b/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py new file mode 100644 index 00000000000..9d3f38c0ffd --- /dev/null +++ b/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py @@ -0,0 +1,104 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""An optimization pass that combines adjacent series of gates on two qubits and +outputs a circuit with SQRT_ISWAP or SQRT_ISWAP_INV gates.""" + +from typing import Callable, Optional, Sequence, TYPE_CHECKING + +import numpy as np + +from cirq import ops +from cirq.optimizers import two_qubit_to_sqrt_iswap, merge_interactions + +if TYPE_CHECKING: + import cirq + + +class MergeInteractionsToSqrtIswap(merge_interactions.MergeInteractionsAbc): + """Combines series of adjacent one- and two-qubit, non-parametrized gates + operating on a pair of qubits and replaces each series with the minimum + number of SQRT_ISWAP gates. + + See also: ``two_qubit_matrix_to_sqrt_iswap_operations`` + """ + + def __init__( + self, + tolerance: float = 1e-8, + *, + required_sqrt_iswap_count: Optional[int] = None, + use_sqrt_iswap_inv: bool = False, + post_clean_up: Callable[[Sequence[ops.Operation]], ops.OP_TREE] = lambda op_list: op_list, + ) -> None: + """ + Args: + tolerance: A limit on the amount of absolute error introduced by the + construction. + required_sqrt_iswap_count: When specified, each merged group of + two-qubit gates will be decomposed into exactly this many + sqrt-iSWAP gates even if fewer is possible (maximum 3). Circuit + optimization will raise a ``ValueError`` if this number is 2 or + lower and synthesis of any set of merged interactions requires + more. + use_sqrt_iswap_inv: If True, optimizes circuits using + ``SQRT_ISWAP_INV`` gates instead of ``SQRT_ISWAP``. + post_clean_up: This function is called on each set of optimized + operations before they are put into the circuit to replace the + old operations. + + Raises: + ValueError: + If ``required_sqrt_iswap_count`` is not one of the supported + values 0, 1, 2, or 3. + """ + if required_sqrt_iswap_count is not None and not 0 <= required_sqrt_iswap_count <= 3: + raise ValueError('the argument `required_sqrt_iswap_count` must be 0, 1, 2, or 3.') + super().__init__(tolerance=tolerance, post_clean_up=post_clean_up) + self.required_sqrt_iswap_count = required_sqrt_iswap_count + self.use_sqrt_iswap_inv = use_sqrt_iswap_inv + + def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool: + """Returns True if the old two-qubit operation may be left unchanged + without decomposition.""" + if self.use_sqrt_iswap_inv: + return isinstance(old_op.gate, ops.ISwapPowGate) and old_op.gate.exponent == -0.5 + return isinstance(old_op.gate, ops.ISwapPowGate) and old_op.gate.exponent == 0.5 + + def _two_qubit_matrix_to_operations( + self, + q0: 'cirq.Qid', + q1: 'cirq.Qid', + mat: np.ndarray, + ) -> Sequence['cirq.Operation']: + """Decomposes the merged two-qubit gate unitary into the minimum number + of SQRT_ISWAP gates. + + Args: + q0: The first qubit being operated on. + q1: The other qubit being operated on. + mat: Defines the operation to apply to the pair of qubits. + + Returns: + A list of operations implementing the matrix. + """ + return two_qubit_to_sqrt_iswap.two_qubit_matrix_to_sqrt_iswap_operations( + q0, + q1, + mat, + required_sqrt_iswap_count=self.required_sqrt_iswap_count, + use_sqrt_iswap_inv=self.use_sqrt_iswap_inv, + atol=self.tolerance, + check_preconditions=False, + ) diff --git a/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py b/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py new file mode 100644 index 00000000000..12bd5415d8f --- /dev/null +++ b/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py @@ -0,0 +1,317 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Callable, List + +import pytest + +import cirq + + +def assert_optimizes(before: cirq.Circuit, expected: cirq.Circuit, **kwargs): + """Check that optimizing the circuit ``before`` produces the circuit ``expected``. + + The optimized circuit is cleaned up with follow up optimizations to make the + comparison more robust to extra moments or extra gates nearly equal to + identity that don't matter. + + Args: + before: The input circuit to optimize. + expected: The expected result of optimization to compare against. + kwargs: Any extra arguments to pass to the + ``MergeInteractionsToSqrtIswap`` constructor. + """ + actual = before.copy() + opt = cirq.MergeInteractionsToSqrtIswap(**kwargs) + opt.optimize_circuit(actual) + + # Ignore differences that would be caught by follow-up optimizations. + followup_optimizations: List[Callable[[cirq.Circuit], None]] = [ + cirq.merge_single_qubit_gates_into_phased_x_z, + cirq.EjectPhasedPaulis().optimize_circuit, + cirq.EjectZ().optimize_circuit, + cirq.DropNegligible().optimize_circuit, + cirq.DropEmptyMoments().optimize_circuit, + ] + for post in followup_optimizations: + post(actual) + post(expected) + + assert actual == expected, f'ACTUAL {actual} : EXPECTED {expected}' + + +def assert_optimization_not_broken(circuit: cirq.Circuit, **kwargs): + """Check that the unitary matrix for the input circuit is the same (up to + global phase and rounding error) as the unitary matrix of the optimized + circuit.""" + u_before = circuit.unitary(sorted(circuit.all_qubits())) + c_sqrt_iswap = circuit.copy() + cirq.MergeInteractionsToSqrtIswap(**kwargs).optimize_circuit(c_sqrt_iswap) + u_after = c_sqrt_iswap.unitary(sorted(circuit.all_qubits())) + + # Not 1e-8 because of some unaccounted accumulated error in some of Cirq's linalg functions + cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after, atol=1e-6) + + # Also test optimization with SQRT_ISWAP_INV + c_sqrt_iswap_inv = circuit.copy() + cirq.MergeInteractionsToSqrtIswap(use_sqrt_iswap_inv=True).optimize_circuit(c_sqrt_iswap_inv) + u_after2 = c_sqrt_iswap_inv.unitary(sorted(circuit.all_qubits())) + + cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after2, atol=1e-6) + + +def test_clears_paired_cnot(): + a, b = cirq.LineQubit.range(2) + assert_optimizes( + before=cirq.Circuit( + [ + cirq.Moment([cirq.CNOT(a, b)]), + cirq.Moment([cirq.CNOT(a, b)]), + ] + ), + expected=cirq.Circuit(), + ) + + +def test_simplifies_sqrt_iswap(): + a, b = cirq.LineQubit.range(2) + assert_optimizes( + before=cirq.Circuit( + [ + # SQRT_ISWAP**8 == Identity + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + ] + ), + expected=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + ] + ), + ) + + +def test_simplifies_sqrt_iswap_inv(): + a, b = cirq.LineQubit.range(2) + assert_optimizes( + use_sqrt_iswap_inv=True, + before=cirq.Circuit( + [ + # SQRT_ISWAP**8 == Identity + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP_INV(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + ] + ), + expected=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP_INV(a, b)]), + ] + ), + ) + + +def test_works_with_tags(): + a, b = cirq.LineQubit.range(2) + assert_optimizes( + before=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP(a, b).with_tags('mytag1')]), + cirq.Moment([cirq.SQRT_ISWAP(a, b).with_tags('mytag2')]), + cirq.Moment([cirq.SQRT_ISWAP_INV(a, b).with_tags('mytag3')]), + ] + ), + expected=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP(a, b)]), + ] + ), + ) + + +def test_no_touch_single_sqrt_iswap(): + a, b = cirq.LineQubit.range(2) + assert_optimizes( + before=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP(a, b).with_tags('mytag')]), + ] + ), + expected=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP(a, b).with_tags('mytag')]), + ] + ), + ) + + +def test_no_touch_single_sqrt_iswap_inv(): + a, b = cirq.LineQubit.range(2) + assert_optimizes( + use_sqrt_iswap_inv=True, + before=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP_INV(a, b).with_tags('mytag')]), + ] + ), + expected=cirq.Circuit( + [ + cirq.Moment([cirq.SQRT_ISWAP_INV(a, b).with_tags('mytag')]), + ] + ), + ) + + +def test_cnots_separated_by_single_gates_correct(): + a, b = cirq.LineQubit.range(2) + assert_optimization_not_broken( + cirq.Circuit( + cirq.CNOT(a, b), + cirq.H(b), + cirq.CNOT(a, b), + ) + ) + + +def test_czs_separated_by_single_gates_correct(): + a, b = cirq.LineQubit.range(2) + assert_optimization_not_broken( + cirq.Circuit( + cirq.CZ(a, b), + cirq.X(b), + cirq.X(b), + cirq.X(b), + cirq.CZ(a, b), + ) + ) + + +def test_inefficient_circuit_correct(): + t = 0.1 + v = 0.11 + a, b = cirq.LineQubit.range(2) + assert_optimization_not_broken( + cirq.Circuit( + cirq.H(b), + cirq.CNOT(a, b), + cirq.H(b), + cirq.CNOT(a, b), + cirq.CNOT(b, a), + cirq.H(a), + cirq.CNOT(a, b), + cirq.Z(a) ** t, + cirq.Z(b) ** -t, + cirq.CNOT(a, b), + cirq.H(a), + cirq.Z(b) ** v, + cirq.CNOT(a, b), + cirq.Z(a) ** -v, + cirq.Z(b) ** -v, + ) + ) + + +def test_optimizes_single_iswap(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.ISWAP(a, b)) + assert_optimization_not_broken(c) + cirq.MergeInteractionsToSqrtIswap().optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 + + +def test_optimizes_single_inv_sqrt_iswap(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) + assert_optimization_not_broken(c) + cirq.MergeInteractionsToSqrtIswap().optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 1 + + +def test_init_raises(): + with pytest.raises(ValueError, match='must be 0, 1, 2, or 3'): + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=4) + + +def test_optimizes_single_iswap_require0(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(a, b)) # Minimum 0 sqrt-iSWAP + assert_optimization_not_broken(c, required_sqrt_iswap_count=0) + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=0).optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 0 + + +def test_optimizes_single_iswap_require0_raises(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.CNOT(a, b)) # Minimum 2 sqrt-iSWAP + with pytest.raises(ValueError, match='cannot be decomposed into exactly 0 sqrt-iSWAP gates'): + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=0).optimize_circuit(c) + + +def test_optimizes_single_iswap_require1(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) # Minimum 1 sqrt-iSWAP + assert_optimization_not_broken(c, required_sqrt_iswap_count=1) + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=1).optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 1 + + +def test_optimizes_single_iswap_require1_raises(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.CNOT(a, b)) # Minimum 2 sqrt-iSWAP + with pytest.raises(ValueError, match='cannot be decomposed into exactly 1 sqrt-iSWAP gates'): + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=1).optimize_circuit(c) + + +def test_optimizes_single_iswap_require2(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) # Minimum 1 sqrt-iSWAP but 2 possible + assert_optimization_not_broken(c, required_sqrt_iswap_count=2) + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=2).optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 + + +def test_optimizes_single_iswap_require2_raises(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.SWAP(a, b)) # Minimum 3 sqrt-iSWAP + with pytest.raises(ValueError, match='cannot be decomposed into exactly 2 sqrt-iSWAP gates'): + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=2).optimize_circuit(c) + + +def test_optimizes_single_iswap_require3(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.ISWAP(a, b)) # Minimum 2 sqrt-iSWAP but 3 possible + assert_optimization_not_broken(c, required_sqrt_iswap_count=3) + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=3).optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 3 + + +def test_optimizes_single_inv_sqrt_iswap_require3(): + a, b = cirq.LineQubit.range(2) + c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) + assert_optimization_not_broken(c, required_sqrt_iswap_count=3) + cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=3).optimize_circuit(c) + assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 3 diff --git a/cirq-core/cirq/optimizers/two_qubit_decompositions.py b/cirq-core/cirq/optimizers/two_qubit_decompositions.py index adc0bcd4994..d7bd12e42b8 100644 --- a/cirq-core/cirq/optimizers/two_qubit_decompositions.py +++ b/cirq-core/cirq/optimizers/two_qubit_decompositions.py @@ -14,7 +14,7 @@ """Utility methods related to optimizing quantum circuits.""" -from typing import Iterable, List, Tuple, Optional, cast, TYPE_CHECKING +from typing import Iterable, List, Sequence, Tuple, Optional, cast, TYPE_CHECKING import numpy as np @@ -161,7 +161,7 @@ def _xx_yy_zz_interaction_via_full_czs( yield ops.H(q1) -def _cleanup_operations(operations: List[ops.Operation]): +def _cleanup_operations(operations: Sequence[ops.Operation]): circuit = circuits.Circuit(operations) merge_single_qubit_gates.merge_single_qubit_gates_into_phased_x_z(circuit) eject_phased_paulis.EjectPhasedPaulis().optimize_circuit(circuit) diff --git a/cirq-core/cirq/optimizers/two_qubit_to_sqrt_iswap.py b/cirq-core/cirq/optimizers/two_qubit_to_sqrt_iswap.py new file mode 100644 index 00000000000..ae9d0e7afb7 --- /dev/null +++ b/cirq-core/cirq/optimizers/two_qubit_to_sqrt_iswap.py @@ -0,0 +1,430 @@ +"""Utility methods for decomposing two-qubit unitaries into sqrt-iSWAP gates. + +References: + Towards ultra-high fidelity quantum operations: SQiSW gate as a native + two-qubit gate + https://arxiv.org/abs/2105.06074 +""" + +from typing import Optional, Sequence, Tuple, TYPE_CHECKING + +import numpy as np + +from cirq import ops, linalg, protocols +from cirq.optimizers import decompositions + +if TYPE_CHECKING: + import cirq + + +def two_qubit_matrix_to_sqrt_iswap_operations( + q0: 'cirq.Qid', + q1: 'cirq.Qid', + mat: np.ndarray, + *, + required_sqrt_iswap_count: Optional[int] = None, + use_sqrt_iswap_inv: bool = False, + atol: float = 1e-8, + check_preconditions: bool = True, +) -> Sequence['cirq.Operation']: + """Decomposes a two-qubit operation into ZPow/XPow/YPow/sqrt-iSWAP gates. + + This method uses the KAK decomposition of the matrix to determine how many + sqrt-iSWAP gates are needed and which single-qubit gates to use in between + each sqrt-iSWAP. + + All operations can be synthesized with exactly three sqrt-iSWAP gates and + about 79% of operations (randomly chosen under the Haar measure) can also be + synthesized with two sqrt-iSWAP gates. Only special cases locally + equivalent to identity or sqrt-iSWAP can be synthesized with zero or one + sqrt-iSWAP gates respectively. Unless ``required_sqrt_iswap_count`` is + specified, the fewest possible number of sqrt-iSWAP will be used. + + Args: + q0: The first qubit being operated on. + q1: The other qubit being operated on. + mat: Defines the operation to apply to the pair of qubits. + required_sqrt_iswap_count: When specified, exactly this many sqrt-iSWAP + gates will be used even if fewer is possible (maximum 3). Raises + ``ValueError`` if impossible. + use_sqrt_iswap_inv: If True, returns a decomposition using + ``SQRT_ISWAP_INV`` gates instead of ``SQRT_ISWAP``. This + decomposition is identical except for the addition of single-qubit + Z gates. + atol: A limit on the amount of absolute error introduced by the + construction. + check_preconditions: If set, verifies that the input corresponds to a + 4x4 unitary before decomposing. + + Returns: + A list of operations implementing the matrix including at most three + ``SQRT_ISWAP`` (sqrt-iSWAP) gates and ZPow, XPow, and YPow single-qubit + gates. + + Raises: + ValueError: + If ``required_sqrt_iswap_count`` is specified, the minimum number of + sqrt-iSWAP gates needed to decompose the given matrix is greater + than ``required_sqrt_iswap_count``. + + References: + Towards ultra-high fidelity quantum operations: SQiSW gate as a native + two-qubit gate + https://arxiv.org/abs/2105.06074 + """ + kak = linalg.kak_decomposition( + mat, atol=atol / 10, rtol=0, check_preconditions=check_preconditions + ) + operations = _kak_decomposition_to_sqrt_iswap_operations( + q0, q1, kak, required_sqrt_iswap_count, use_sqrt_iswap_inv, atol=atol + ) + return operations + + +def _kak_decomposition_to_sqrt_iswap_operations( + q0: 'cirq.Qid', + q1: 'cirq.Qid', + kak: linalg.KakDecomposition, + required_sqrt_iswap_count: Optional[int] = None, + use_sqrt_iswap_inv: bool = False, + atol: float = 1e-8, +) -> Sequence['cirq.Operation']: + single_qubit_operations, _ = _single_qubit_matrices_with_sqrt_iswap( + kak, required_sqrt_iswap_count, atol=atol + ) + if use_sqrt_iswap_inv: + z_unitary = protocols.unitary(ops.Z) + return _decomp_to_operations( + q0, + q1, + ops.SQRT_ISWAP_INV, + single_qubit_operations, + u0_before=z_unitary, + u0_after=z_unitary, + atol=atol, + ) + return _decomp_to_operations( + q0, + q1, + ops.SQRT_ISWAP, + single_qubit_operations, + atol=atol, + ) + + +def _decomp_to_operations( + q0: 'cirq.Qid', + q1: 'cirq.Qid', + two_qubit_gate: 'cirq.Gate', + single_qubit_operations: Sequence[Tuple[np.ndarray, np.ndarray]], + u0_before: np.ndarray = np.eye(2), + u0_after: np.ndarray = np.eye(2), + atol: float = 1e-8, +) -> Sequence['cirq.Operation']: + """Converts a sequence of single-qubit unitary matrices on two qubits into a + list of operations with interleaved two-qubit gates.""" + two_qubit_op = two_qubit_gate(q0, q1) + operations = [] + + prev_commute = 1 + + def append(matrix0, matrix1, final_layer=False): + """Appends the decomposed single-qubit operations for matrix0 and + matrix1. + + The cleanup logic, specific to sqrt-iSWAP, commutes the final Z**a gate + and any whole X or Y gate on q1 through the following sqrt-iSWAP. + + Commutation rules: + - Z(q0)**a, Z(q1)**a together commute with sqrt-iSWAP for all a + - X(q0), X(q0) together commute with sqrt-iSWAP + - Y(q0), Y(q0) together commute with sqrt-iSWAP + """ + nonlocal prev_commute + # Commute previous Z(q0)**a, Z(q1)**a through earlier sqrt-iSWAP + rots1 = list( + decompositions.single_qubit_matrix_to_pauli_rotations( + np.dot(matrix1, prev_commute), atol=atol + ) + ) + new_commute = np.eye(2, dtype=matrix0.dtype) + if not final_layer: + # Commute rightmost Z(q0)**b, Z(q1)**b through next sqrt-iSWAP + if len(rots1) > 0 and rots1[-1][0] == ops.Z: + _, prev_z = rots1.pop() + z_unitary = protocols.unitary(ops.Z ** prev_z) + new_commute = new_commute @ z_unitary + matrix0 = z_unitary.T.conj() @ matrix0 + # Commute rightmost whole X(q0), X(q0) or Y, Y through next sqrt-iSWAP + if len(rots1) > 0 and linalg.tolerance.near_zero_mod(rots1[-1][1], 1, atol=atol): + pauli, half_turns = rots1.pop() + p_unitary = protocols.unitary(pauli ** half_turns) + new_commute = new_commute @ p_unitary + matrix0 = p_unitary.T.conj() @ matrix0 + rots0 = list( + decompositions.single_qubit_matrix_to_pauli_rotations( + np.dot(matrix0, prev_commute), atol=atol + ) + ) + # Append single qubit ops + operations.extend((pauli ** half_turns).on(q0) for pauli, half_turns in rots0) + operations.extend((pauli ** half_turns).on(q1) for pauli, half_turns in rots1) + prev_commute = new_commute + + single_ops = list(single_qubit_operations) + if len(single_ops) <= 1: # Handle zero sqrt-iSWAP case separately + for matrix0, matrix1 in single_ops: # Only entry, if any + append(matrix0, matrix1, final_layer=True) # Append only pair of single qubit gates + return operations + for matrix0, matrix1 in single_ops[:1]: # First entry + append(u0_before @ matrix0, matrix1) # Append pair of single qubit gates + operations.append(two_qubit_op) # Append two-qubit gate between each pair + for matrix0, matrix1 in single_ops[1:-1]: # All middle entries + append(u0_before @ matrix0 @ u0_after, matrix1) # Append pair of single qubit gates + operations.append(two_qubit_op) # Append two-qubit gate between each pair + for matrix0, matrix1 in single_ops[-1:]: # Last entry + # Append final pair of single qubit gates + append(matrix0 @ u0_after, matrix1, final_layer=True) + return operations + + +def _single_qubit_matrices_with_sqrt_iswap( + kak: 'cirq.KakDecomposition', + required_sqrt_iswap_count: Optional[int] = None, + atol: float = 1e-8, +) -> Tuple[Sequence[Tuple[np.ndarray, np.ndarray]], complex]: + """Computes the sequence of interleaved single-qubit unitary matrices in the + sqrt-iSWAP decomposition.""" + decomposers = [ + (_in_0_region, _decomp_0_matrices), + (_in_1sqrt_iswap_region, _decomp_1sqrt_iswap_matrices), + (_in_2sqrt_iswap_region, _decomp_2sqrt_iswap_matrices), + (_in_3sqrt_iswap_region, _decomp_3sqrt_iswap_matrices), + ] + if required_sqrt_iswap_count is not None: + if not 0 <= required_sqrt_iswap_count <= 3: + raise ValueError('the argument `required_sqrt_iswap_count` must be 0, 1, 2, or 3.') + can_decompose, decomposer = decomposers[required_sqrt_iswap_count] + if not can_decompose(kak.interaction_coefficients, weyl_tol=atol / 10): + raise ValueError( + f'the given gate cannot be decomposed into exactly ' + f'{required_sqrt_iswap_count} sqrt-iSWAP gates.' + ) + return decomposer(kak, atol=atol) + for can_decompose, decomposer in decomposers: + if can_decompose(kak.interaction_coefficients, weyl_tol=atol / 10): + return decomposer(kak, atol) + assert False, 'The final can_decompose should always returns True' + + +def _in_0_region( + interaction_coefficients: Tuple[float, float, float], + weyl_tol: float = 1e-8, +) -> bool: + """Tests if (x, y, z) ~= (0, 0, 0) assuming x, y, z are canonical.""" + x, y, z = interaction_coefficients + return abs(x) <= weyl_tol and abs(y) <= weyl_tol and abs(z) <= weyl_tol + + +def _in_1sqrt_iswap_region( + interaction_coefficients: Tuple[float, float, float], + weyl_tol: float = 1e-8, +) -> bool: + """Tests if (x, y, z) ~= (π/8, π/8, 0), assuming x, y, z are canonical.""" + x, y, z = interaction_coefficients + return abs(x - np.pi / 8) <= weyl_tol and abs(y - np.pi / 8) <= weyl_tol and abs(z) <= weyl_tol + + +def _in_2sqrt_iswap_region( + interaction_coefficients: Tuple[float, float, float], + weyl_tol: float = 1e-8, +) -> bool: + """Tests if (x, y, z) is inside or within weyl_tol of the volume + x >= y + |z| assuming x, y, z are canonical. + + References: + Towards ultra-high fidelity quantum operations: SQiSW gate as a native + two-qubit gate + https://arxiv.org/abs/2105.06074 + """ + x, y, z = interaction_coefficients + # Lemma 1 of the paper + # The other constraint in Lemma 1 simply asserts x, y, z are canonical + return x + weyl_tol >= y + abs(z) + + +def _in_3sqrt_iswap_region( + interaction_coefficients: Tuple[float, float, float], + weyl_tol: float = 1e-8, +) -> bool: + """Any two-qubit operation is decomposable into three SQRT_ISWAP gates. + + References: + Towards ultra-high fidelity quantum operations: SQiSW gate as a native + two-qubit gate + https://arxiv.org/abs/2105.06074 + """ + return True + + +def _decomp_0_matrices( + kak: 'cirq.KakDecomposition', + atol: float = 1e-8, +) -> Tuple[Sequence[Tuple[np.ndarray, np.ndarray]], complex]: + """Returns the single-qubit matrices for the 0-SQRT_ISWAP decomposition. + + Assumes canonical x, y, z and (x, y, z) = (0, 0, 0) within tolerance. + """ + # Pairs of single-qubit unitaries, SQRT_ISWAP between each is implied + # Only a single pair of single-qubit unitaries is returned here so + # _decomp_to_operations will not insert any sqrt-iSWAP gates in between + return [ + ( + kak.single_qubit_operations_after[0] @ kak.single_qubit_operations_before[0], + kak.single_qubit_operations_after[1] @ kak.single_qubit_operations_before[1], + ) + ], kak.global_phase + + +def _decomp_1sqrt_iswap_matrices( + kak: 'cirq.KakDecomposition', + atol: float = 1e-8, +) -> Tuple[Sequence[Tuple[np.ndarray, np.ndarray]], complex]: + """Returns the single-qubit matrices for the 1-SQRT_ISWAP decomposition. + + Assumes canonical x, y, z and (x, y, z) = (π/8, π/8, 0) within tolerance. + """ + return [ # Pairs of single-qubit unitaries, SQRT_ISWAP between each is implied + kak.single_qubit_operations_before, + kak.single_qubit_operations_after, + ], kak.global_phase + + +def _decomp_2sqrt_iswap_matrices( + kak: 'cirq.KakDecomposition', + atol: float = 1e-8, +) -> Tuple[Sequence[Tuple[np.ndarray, np.ndarray]], complex]: + """Returns the single-qubit matrices for the 2-SQRT_ISWAP decomposition. + + Assumes canonical x, y, z and x >= y + |z| within tolerance. For x, y, z + that violate this inequality, three sqrt-iSWAP gates are required. + + References: + Towards ultra-high fidelity quantum operations: SQiSW gate as a native + two-qubit gate + https://arxiv.org/abs/2105.06074 + """ + # Follows the if-branch of procedure DECOMP(U) in Algorithm 1 of the paper + x, y, z = kak.interaction_coefficients + b0, b1 = kak.single_qubit_operations_before + a0, a1 = kak.single_qubit_operations_after + + # Computed gate parameters: Eq. 4, 6, 7, 8 of the paper + # range limits added for robustness to numerical error + def safe_arccos(v): + return np.arccos(np.clip(v, -1, 1)) + + def nonzero_sign(v): + return -1 if v < 0 else 1 + + _c = np.clip( + np.sin(x + y - z) * np.sin(x - y + z) * np.sin(-x - y - z) * np.sin(-x + y + z), 0, 1 + ) + alpha = safe_arccos(np.cos(2 * x) - np.cos(2 * y) + np.cos(2 * z) + 2 * np.sqrt(_c)) + beta = safe_arccos(np.cos(2 * x) - np.cos(2 * y) + np.cos(2 * z) - 2 * np.sqrt(_c)) + # Don't need to limit this value because it will always be positive and the clip in the + # following `safe_arccos` handles the cases where this could be slightly greater than 1. + _4ccs = 4 * (np.cos(x) * np.cos(z) * np.sin(y)) ** 2 # Intermediate value + gamma = safe_arccos( + nonzero_sign(z) + * np.sqrt(_4ccs / (_4ccs + np.clip(np.cos(2 * x) * np.cos(2 * y) * np.cos(2 * z), 0, 1))) + ) + + # Inner single-qubit gates: Fig. 4 of the paper + # Gate angles here are multiplied by -2 to adjust for non-standard gate definitions in the paper + c0 = ( + protocols.unitary(ops.rz(-gamma)) + @ protocols.unitary(ops.rx(-alpha)) + @ protocols.unitary(ops.rz(-gamma)) + ) + c1 = protocols.unitary(ops.rx(-beta)) + + # Compute KAK on the decomposition to determine outer single-qubit gates + # There is no known closed form solution for these gates + u_sqrt_iswap = protocols.unitary(ops.SQRT_ISWAP) + u = u_sqrt_iswap @ np.kron(c0, c1) @ u_sqrt_iswap # Unitary of decomposition + kak_fix = linalg.kak_decomposition(u, atol=atol / 10, rtol=0, check_preconditions=False) + e0, e1 = kak_fix.single_qubit_operations_before + d0, d1 = kak_fix.single_qubit_operations_after + + return [ # Pairs of single-qubit unitaries, SQRT_ISWAP between each is implied + (e0.T.conj() @ b0, e1.T.conj() @ b1), + (c0, c1), + (a0 @ d0.T.conj(), a1 @ d1.T.conj()), + ], kak.global_phase / kak_fix.global_phase + + +def _decomp_3sqrt_iswap_matrices( + kak: 'cirq.KakDecomposition', + atol: float = 1e-8, +) -> Tuple[Sequence[Tuple[np.ndarray, np.ndarray]], complex]: + """Returns the single-qubit matrices for the 3-SQRT_ISWAP decomposition. + + Assumes any canonical x, y, z. Three sqrt-iSWAP gates are only needed if + x < y + |z|. Only two are needed for other gates (most cases). + + References: + Towards ultra-high fidelity quantum operations: SQiSW gate as a native + two-qubit gate + https://arxiv.org/abs/2105.06074 + """ + # This somewhat follows the else-branch of procedure DECOMP(U) in Algorithm 1 of the paper. + # However the canonicalization conditions are different from the paper and allow any Weyl + # coordinate to be synthesized with 3 sqrt-iSWAPs. + # + # This method breaks the 3-sqrt-iSWAP synthesis problem into a sum of the 1-sqrt-iSWAP and + # 2-sqrt-iSWAP problems. This works because given two 2-qubit unitaries, U and V, with (not + # necessarily canonical) Weyl coordinates (x1, y1, z1) and (x2, y2, z2), both products U*V and + # V*U will have (non-canonical) Weyl coordinates (x1+x2, y1+y2, z1+z2) if both U and V are + # diagonal in the magic basis (i.e. all single-qubit operations of the pre-canonicalized KAK + # decomposition are identity). + x, y, z = kak.interaction_coefficients + b0, b1 = kak.single_qubit_operations_before + a0, a1 = kak.single_qubit_operations_after + + # Find x1, y1, z1, x2, y2, z2 + # such that x1+x2=x, y1+y2=y, z1+z2=z + # where x1, y1, z1 are implementable by one sqrt-iSWAP gate + # and x2, y2, z2 implementable by two sqrt-iSWAP gates + # No error tolerance needed + ieq1 = y > np.pi / 8 + ieq2 = z < 0 + if ieq1: + if ieq2: + # Non-canonical Weyl coordinates for the single sqrt-iSWAP + x1, y1, z1 = 0, np.pi / 8, -np.pi / 8 + else: + x1, y1, z1 = 0, np.pi / 8, np.pi / 8 + else: + x1, y1, z1 = -np.pi / 8, np.pi / 8, 0 + # Non-canonical Weyl coordinates for the two sqrt-iSWAP decomposition + x2, y2, z2 = x - x1, y - y1, z - z1 + + # Find fixup single-qubit gates for the canonical (i.e. diagonal in the magic basis) + # decompositions + kak1 = linalg.kak_canonicalize_vector(x1, y1, z1, atol) + kak2 = linalg.kak_canonicalize_vector(x2, y2, z2, atol) + + # Compute sub-decompositions + # F0 and F1 from Algorithm 1 of the paper are not needed + ((h0, h1), (g0, g1)), phase1 = _decomp_1sqrt_iswap_matrices(kak1, atol) + ((e0, e1), (c0, c1), (d0, d1)), phase2 = _decomp_2sqrt_iswap_matrices(kak2, atol) + + # There are two valid solutions at this point: kak1 before kak2 or kak2 before kak1 + # Arbitrarily pick kak1 before kak2 + return [ # Pairs of single-qubit unitaries, SQRT_ISWAP between each is implied + (h0 @ b0, h1 @ b1), + (e0 @ g0, e1 @ g1), + (c0, c1), + (a0 @ d0, a1 @ d1), + ], kak.global_phase * phase1 * phase2 diff --git a/cirq-core/cirq/optimizers/two_qubit_to_sqrt_iswap_test.py b/cirq-core/cirq/optimizers/two_qubit_to_sqrt_iswap_test.py new file mode 100644 index 00000000000..a3cd566be86 --- /dev/null +++ b/cirq-core/cirq/optimizers/two_qubit_to_sqrt_iswap_test.py @@ -0,0 +1,403 @@ +import itertools +import numpy as np +import pytest + +import cirq + + +def random_unitary(seed): + return cirq.testing.random_unitary(4, random_state=seed) + + +def random_locals(x, y, z, seed=None): + rng = np.random.RandomState(seed) + a0 = cirq.testing.random_unitary(2, random_state=rng) + a1 = cirq.testing.random_unitary(2, random_state=rng) + b0 = cirq.testing.random_unitary(2, random_state=rng) + b1 = cirq.testing.random_unitary(2, random_state=rng) + return cirq.unitary( + cirq.KakDecomposition( + interaction_coefficients=(x, y, z), + single_qubit_operations_before=(a0, a1), + single_qubit_operations_after=(b0, b1), + ) + ) + + +def perturbations_unitary(u, amount=1e-10): + """Returns several unitaries in the neighborhood of u to test for numerical + corner cases near critical values.""" + kak = cirq.kak_decomposition(u) + yield u + for i in range(3): + for neg in (-1, 1): + perturb_xyz = list(kak.interaction_coefficients) + perturb_xyz[i] += neg * amount + yield cirq.unitary( + cirq.KakDecomposition( + global_phase=kak.global_phase, + single_qubit_operations_before=kak.single_qubit_operations_before, + single_qubit_operations_after=kak.single_qubit_operations_after, + interaction_coefficients=tuple(perturb_xyz), + ) + ) + + +def perturbations_gate(gate, amount=1e-10): + return perturbations_unitary(cirq.unitary(gate), amount) + + +def perturbations_weyl(x, y, z, amount=1e-10): + return perturbations_gate( + cirq.KakDecomposition( + interaction_coefficients=(x, y, z), + ), + amount, + ) + + +THREE_SQRT_ISWAP_UNITARIES = [ + # Typical gates and nearby Weyl coordinates to simulate numerical noise + *perturbations_gate(cirq.SWAP), + *perturbations_gate(cirq.FSimGate(theta=np.pi / 6, phi=np.pi / 6)), + # Critical points in the Weyl chamber and nearby coordinates to simulate numerical noise + *perturbations_weyl(np.pi / 4, np.pi / 4, np.pi / 4), + *perturbations_weyl(np.pi / 4, np.pi / 4, -np.pi / 4), + *perturbations_weyl(np.pi / 4, np.pi / 4, np.pi / 8), + *perturbations_weyl(np.pi / 4, np.pi / 4, -np.pi / 8), + *perturbations_weyl(np.pi / 4, np.pi * 3 / 16, np.pi * 3 / 16), + *perturbations_weyl(np.pi / 4, np.pi * 3 / 16, -np.pi * 3 / 16), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 4, 3 / 4 * np.pi / 4), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 4, 3 / 4 * -np.pi / 4), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 4, 3 / 4 * np.pi / 8), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 4, 3 / 4 * -np.pi / 8), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi * 3 / 16, 3 / 4 * np.pi * 3 / 16), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi * 3 / 16, 3 / 4 * -np.pi * 3 / 16), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 4, 1 / 2 * np.pi / 4), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 4, 1 / 2 * -np.pi / 4), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 4, 1 / 2 * np.pi / 8), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 4, 1 / 2 * -np.pi / 8), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi * 3 / 16, 1 / 2 * np.pi * 3 / 16), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi * 3 / 16, 1 / 2 * -np.pi * 3 / 16), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 4, 1 / 4 * np.pi / 4), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 4, 1 / 4 * -np.pi / 4), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 4, 1 / 4 * np.pi / 8), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 4, 1 / 4 * -np.pi / 8), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 3 / 16, 1 / 4 * np.pi * 3 / 16), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 3 / 16, 1 / 4 * -np.pi * 3 / 16), + *perturbations_weyl(np.pi * 3 / 16, np.pi / 8, np.pi / 8), + *perturbations_weyl(np.pi * 3 / 16, np.pi / 8, -np.pi / 8), + *perturbations_weyl(np.pi * 3 / 32, np.pi / 16, np.pi / 16), + *perturbations_weyl(np.pi * 3 / 32, np.pi / 16, -np.pi / 16), + *perturbations_weyl(np.pi * 3 / 16, np.pi * 3 / 16, np.pi / 16), + *perturbations_weyl(np.pi * 3 / 16, np.pi * 3 / 16, -np.pi / 16), + *perturbations_weyl(np.pi * 3 / 32, np.pi * 3 / 32, np.pi / 32), + *perturbations_weyl(np.pi * 3 / 32, np.pi * 3 / 32, -np.pi / 32), + # Some random points + random_unitary(97154), + random_unitary(45375), + random_unitary(78061), + random_unitary(61474), + random_unitary(22897), +] +TWO_SQRT_ISWAP_UNITARIES = [ + # Typical gates and nearby Weyl coordinates to simulate numerical noise + *perturbations_gate(cirq.XX ** 0.25), + *perturbations_gate(cirq.YY ** 0.07), + *perturbations_gate(cirq.ZZ ** 0.15), + *perturbations_gate(cirq.CNOT), + *perturbations_gate(cirq.ISWAP), + *perturbations_gate(cirq.ISWAP ** 0.1), + # Critical points in the Weyl chamber and nearby coordinates to simulate numerical noise + *perturbations_weyl(np.pi / 4, 0, 0), + *perturbations_weyl(np.pi / 4, np.pi / 4, 0), + *perturbations_weyl(np.pi / 4, np.pi / 8, np.pi / 8), + *perturbations_weyl(np.pi / 4, np.pi / 8, -np.pi / 8), + *perturbations_weyl(np.pi / 4, np.pi * 1 / 16, np.pi / 16), + *perturbations_weyl(np.pi / 4, np.pi * 1 / 16, -np.pi / 16), + *perturbations_weyl(np.pi / 4, np.pi * 3 / 16, np.pi / 16), + *perturbations_weyl(np.pi / 4, np.pi * 3 / 16, -np.pi / 16), + *perturbations_weyl(np.pi / 4, np.pi / 8, 0), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * 0, 3 / 4 * 0), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 4, 3 / 4 * 0), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 8, 3 / 4 * np.pi / 8), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 8, 3 / 4 * -np.pi / 8), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi * 1 / 16, 3 / 4 * np.pi / 16), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi * 1 / 16, 3 / 4 * -np.pi / 16), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi * 3 / 16, 3 / 4 * np.pi / 16), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi * 3 / 16, 3 / 4 * -np.pi / 16), + *perturbations_weyl(3 / 4 * np.pi / 4, 3 / 4 * np.pi / 8, 3 / 4 * 0), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * 0, 1 / 2 * 0), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 8, 1 / 2 * np.pi / 8), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 8, 1 / 2 * -np.pi / 8), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi * 1 / 16, 1 / 2 * np.pi / 16), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi * 1 / 16, 1 / 2 * -np.pi / 16), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi * 3 / 16, 1 / 2 * np.pi / 16), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi * 3 / 16, 1 / 2 * -np.pi / 16), + *perturbations_weyl(1 / 2 * np.pi / 4, 1 / 2 * np.pi / 8, 1 / 2 * 0), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * 0, 1 / 4 * 0), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 4, 1 / 4 * 0), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 8, 1 / 4 * np.pi / 8), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 8, 1 / 4 * -np.pi / 8), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 1 / 16, 1 / 4 * np.pi / 16), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 1 / 16, 1 / 4 * -np.pi / 16), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 3 / 16, 1 / 4 * np.pi / 16), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi * 3 / 16, 1 / 4 * -np.pi / 16), + *perturbations_weyl(1 / 4 * np.pi / 4, 1 / 4 * np.pi / 8, 1 / 4 * 0), + # Some random points + random_unitary(17323), + random_unitary(99415), + random_unitary(65832), + random_unitary(45373), + random_unitary(27123), +] +ONE_SQRT_ISWAP_UNITARIES = [ + *perturbations_gate(cirq.SQRT_ISWAP), + *perturbations_gate(cirq.ISwapPowGate(exponent=-0.5, global_shift=1)), + *perturbations_unitary(random_locals(np.pi / 8, np.pi / 8, 0, seed=80342)), + *perturbations_unitary(random_locals(np.pi / 8, np.pi / 8, 0, seed=83551)), + *perturbations_unitary(random_locals(np.pi / 8, np.pi / 8, 0, seed=52450)), +] +ZERO_UNITARIES = [ + *perturbations_gate(cirq.IdentityGate(2)), + *perturbations_unitary(random_locals(0, 0, 0, seed=62933)), + *perturbations_unitary(random_locals(0, 0, 0, seed=50590)), +] + +# Lists of seeds for cirq.testing.random_unitary(4, random_state=seed) +# corresponding to different regions in the Weyl chamber and possible +# eigenvalue crossings in the 3-SQRT_ISWAP decomposition +# Key: 1 (fig. 4): x < y + abs(z) +# 2 (fig. 7a): x > pi/8 +# 4 (fig. 7b): z < 0 +# 8 (fig. 7c): y+abs(z) < pi/8 if x <= pi/8 else y+abs(z) < pi/4 +WEYL_REGION_UNITARIES = { + 1: list(map(random_unitary, [49903, 84819, 84769])), + 3: list(map(random_unitary, [89483, 79607, 96012])), + 5: list(map(random_unitary, [31979, 78758, 47661])), + 7: list(map(random_unitary, [46429, 80635, 49695])), + 8: list(map(random_unitary, [86872, 91499, 47843])), + 9: list(map(random_unitary, [48333, 48333, 95468])), + 10: list(map(random_unitary, [97633, 99062, 44397])), + 11: list(map(random_unitary, [42319, 41651, 11322])), + 12: list(map(random_unitary, [48530, 64117, 31880])), + 13: list(map(random_unitary, [95140, 13781, 60879])), + 14: list(map(random_unitary, [85837, 14090, 73607])), + 15: list(map(random_unitary, [67955, 86488, 24698])), +} +ALL_REGION_UNITARIES = list(itertools.chain(*WEYL_REGION_UNITARIES.values())) + + +def assert_valid_decomp( + u_target, + operations, + *, + single_qubit_gate_types=(cirq.ZPowGate, cirq.XPowGate, cirq.YPowGate), + two_qubit_gate=cirq.SQRT_ISWAP, + # Not 1e-8 because of some unaccounted accumulated error in some of Cirq's linalg functions + atol=1e-6, + rtol=0, + qubit_order=cirq.LineQubit.range(2), +): + # Check expected gates + for op in operations: + if len(op.qubits) == 0 and isinstance(op, cirq.GlobalPhaseOperation): + assert False, 'Global phase operation was output when it should not.' + elif len(op.qubits) == 1 and isinstance(op.gate, single_qubit_gate_types): + pass + elif len(op.qubits) == 2 and op.gate == two_qubit_gate: + pass + else: + assert False, f'Disallowed operation was output: {op}' + + # Compare unitaries + c = cirq.Circuit(operations) + u_decomp = c.unitary(qubit_order) + if not cirq.allclose_up_to_global_phase(u_decomp, u_target, atol=atol, rtol=rtol): + # Check if they are close and output a helpful message + cirq.testing.assert_allclose_up_to_global_phase( + u_decomp, + u_target, + atol=1e-2, + rtol=1e-2, + err_msg='Invalid decomposition. Unitaries are completely different.', + ) + worst_diff = np.max(np.abs(u_decomp - u_target)) + assert False, ( + f'Unitaries do not match closely enough (atol={atol}, rtol={rtol}, ' + f'worst element diff={worst_diff}).' + ) + + +def assert_specific_sqrt_iswap_count(operations, count): + actual = sum(len(op.qubits) == 2 for op in operations) + assert actual == count, f'Incorrect sqrt-iSWAP count. Expected {count} but got {actual}.' + + +@pytest.mark.parametrize('cnt', [-1, 4, 10]) +def test_invalid_required_sqrt_iswap_count(cnt): + u = TWO_SQRT_ISWAP_UNITARIES[0] + q0, q1 = cirq.LineQubit.range(2) + with pytest.raises(ValueError, match='required_sqrt_iswap_count'): + cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=cnt) + + +@pytest.mark.parametrize('u', ZERO_UNITARIES) +def test_decomp0(u): + # Decompose unitaries into zero sqrt-iSWAP gates + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=0) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 0) + + +@pytest.mark.parametrize( + 'u', ONE_SQRT_ISWAP_UNITARIES + TWO_SQRT_ISWAP_UNITARIES + THREE_SQRT_ISWAP_UNITARIES +) +def test_decomp0_invalid(u): + # Attempt to decompose other unitaries into zero SQRT_ISWAP gates + q0, q1 = cirq.LineQubit.range(2) + with pytest.raises(ValueError, match='cannot be decomposed into exactly 0 sqrt-iSWAP gates'): + cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=0) + + +@pytest.mark.parametrize('u', ONE_SQRT_ISWAP_UNITARIES) +def test_decomp1(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=1) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 1) + + +@pytest.mark.parametrize( + 'u', ZERO_UNITARIES + TWO_SQRT_ISWAP_UNITARIES + THREE_SQRT_ISWAP_UNITARIES +) +def test_decomp1_invalid(u): + q0, q1 = cirq.LineQubit.range(2) + with pytest.raises(ValueError, match='cannot be decomposed into exactly 1 sqrt-iSWAP gates'): + cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=1) + + +@pytest.mark.parametrize('u', ZERO_UNITARIES + ONE_SQRT_ISWAP_UNITARIES + TWO_SQRT_ISWAP_UNITARIES) +def test_decomp2(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=2) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 2) + + +@pytest.mark.parametrize('u', THREE_SQRT_ISWAP_UNITARIES) +def test_decomp2_invalid(u): + q0, q1 = cirq.LineQubit.range(2) + with pytest.raises(ValueError, match='cannot be decomposed into exactly 2 sqrt-iSWAP gates'): + cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=2) + + +@pytest.mark.parametrize( + 'u', + ZERO_UNITARIES + + ONE_SQRT_ISWAP_UNITARIES + + TWO_SQRT_ISWAP_UNITARIES + + THREE_SQRT_ISWAP_UNITARIES, +) +def test_decomp3(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=3) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 3) + + +def test_decomp3_invalid(): + # All two-qubit gates can be synthesized with three SQRT_ISWAP gates + u = cirq.unitary(cirq.X ** 0.2) # Pass an invalid size unitary + q0, q1 = cirq.LineQubit.range(2) + with pytest.raises(ValueError, match='Input must correspond to a 4x4 unitary matrix'): + cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, required_sqrt_iswap_count=3) + + +@pytest.mark.parametrize('u', TWO_SQRT_ISWAP_UNITARIES[:1]) +def test_qubit_order(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q1, q0, u, required_sqrt_iswap_count=2) + assert_valid_decomp(u, ops, qubit_order=(q1, q0)) + assert_specific_sqrt_iswap_count(ops, 2) + + +@pytest.mark.parametrize('u', ZERO_UNITARIES) +def test_decomp_optimal0(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 0) + + +@pytest.mark.parametrize('u', ONE_SQRT_ISWAP_UNITARIES) +def test_decomp_optimal1(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 1) + + +@pytest.mark.parametrize('u', TWO_SQRT_ISWAP_UNITARIES) +def test_decomp_optimal2(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 2) + + +@pytest.mark.parametrize('u', THREE_SQRT_ISWAP_UNITARIES) +def test_decomp_optimal3(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u) + assert_valid_decomp(u, ops) + assert_specific_sqrt_iswap_count(ops, 3) + + +@pytest.mark.parametrize('u', ALL_REGION_UNITARIES) +def test_all_weyl_regions(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u) + assert_valid_decomp(u, ops) + + +@pytest.mark.parametrize( + 'u', + [ # Don't need to check all Weyl chamber corner cases here + ZERO_UNITARIES[0], + ONE_SQRT_ISWAP_UNITARIES[0], + TWO_SQRT_ISWAP_UNITARIES[0], + THREE_SQRT_ISWAP_UNITARIES[0], + ], +) +def test_decomp_sqrt_iswap_inv(u): + q0, q1 = cirq.LineQubit.range(2) + ops = cirq.two_qubit_matrix_to_sqrt_iswap_operations(q0, q1, u, use_sqrt_iswap_inv=True) + assert_valid_decomp(u, ops, two_qubit_gate=cirq.SQRT_ISWAP_INV) + + +def test_valid_check_raises(): + q0 = cirq.LineQubit(0) + with pytest.raises(AssertionError, match='Unitaries are completely different'): + assert_valid_decomp( + np.eye(4), + [cirq.X(q0)], + single_qubit_gate_types=(cirq.XPowGate,), + ) + with pytest.raises(AssertionError, match='Unitaries do not match closely enough'): + assert_valid_decomp( + np.eye(4), + [cirq.rx(0.01)(q0)], + single_qubit_gate_types=(cirq.Rx,), + ) + with pytest.raises( + AssertionError, match='Global phase operation was output when it should not' + ): + assert_valid_decomp(np.eye(4), [cirq.GlobalPhaseOperation(np.exp(1j * 0.01))]) + with pytest.raises(AssertionError, match='Disallowed operation was output'): + assert_valid_decomp( + np.eye(4), + [cirq.X(q0)], + single_qubit_gate_types=(cirq.IdentityGate,), + ) diff --git a/cirq-core/cirq/protocols/act_on_protocol_test.py b/cirq-core/cirq/protocols/act_on_protocol_test.py index 352941395bf..11b056cb9d5 100644 --- a/cirq-core/cirq/protocols/act_on_protocol_test.py +++ b/cirq-core/cirq/protocols/act_on_protocol_test.py @@ -37,6 +37,9 @@ def copy(self): def _act_on_fallback_(self, action, qubits, allow_decompose): return self.fallback_result + def sample(self, qubits, repetitions=1, seed=None): + pass + op = cirq.X(cirq.LineQubit(0)) @@ -82,7 +85,7 @@ def _act_on_fallback_(self, action, qubits, allow_decompose): return True args = Args() - args.qubits = tuple(cirq.LineQubit.range(3)) + args._qubits = tuple(cirq.LineQubit.range(3)) with cirq.testing.assert_deprecated( "ActOnArgs.axes", "Use `protocols.act_on` instead.", deadline="v0.13" ): diff --git a/cirq-core/cirq/protocols/circuit_diagram_info_protocol.py b/cirq-core/cirq/protocols/circuit_diagram_info_protocol.py index 4d8b19e1798..dfe58fb08bf 100644 --- a/cirq-core/cirq/protocols/circuit_diagram_info_protocol.py +++ b/cirq-core/cirq/protocols/circuit_diagram_info_protocol.py @@ -151,37 +151,6 @@ def _formatted_exponent( return f'({self.exponent})' return s - @staticmethod - def _op_info_with_fallback( - op: 'cirq.Operation', args: 'cirq.CircuitDiagramInfoArgs' - ) -> 'cirq.CircuitDiagramInfo': - info = protocols.circuit_diagram_info(op, args, None) - if info is not None: - if len(op.qubits) != len(info.wire_symbols): - raise ValueError( - 'Wanted diagram info from {!r} for {} ' - 'qubits but got {!r}'.format(op, len(op.qubits), info) - ) - return info - - # Use the untagged operation's __str__. - name = str(op.untagged) - - # Representation usually looks like 'gate(qubit1, qubit2, etc)'. - # Try to cut off the qubit part, since that would be redundant. - redundant_tail = f"({', '.join(str(e) for e in op.qubits)})" - if name.endswith(redundant_tail): - name = name[: -len(redundant_tail)] - - # Add tags onto the representation, if they exist - if op.tags: - name += f'{list(op.tags)}' - - # Include ordering in the qubit labels. - symbols = (name,) + tuple(f'#{i + 1}' for i in range(1, len(op.qubits))) - - return protocols.CircuitDiagramInfo(wire_symbols=symbols) - def __repr__(self) -> str: return ( 'cirq.CircuitDiagramInfo(' @@ -348,6 +317,36 @@ def _circuit_diagram_info_( RaiseTypeErrorIfNotProvided = CircuitDiagramInfo(()) +def _op_info_with_fallback( + op: 'cirq.Operation', args: 'cirq.CircuitDiagramInfoArgs' +) -> 'cirq.CircuitDiagramInfo': + info = protocols.circuit_diagram_info(op, args, None) + if info is not None: + if max(1, len(op.qubits)) != len(info.wire_symbols): + raise ValueError( + f'Wanted diagram info from {op!r} for {len(op.qubits)} qubits but got {info!r}' + ) + return info + + # Use the untagged operation's __str__. + name = str(op.untagged) + + # Representation usually looks like 'gate(qubit1, qubit2, etc)'. + # Try to cut off the qubit part, since that would be redundant. + redundant_tail = f"({', '.join(str(e) for e in op.qubits)})" + if name.endswith(redundant_tail): + name = name[: -len(redundant_tail)] + + # Add tags onto the representation, if they exist + if op.tags: + name += f'{list(op.tags)}' + + # Include ordering in the qubit labels. + symbols = (name,) + tuple(f'#{i + 1}' for i in range(1, len(op.qubits))) + + return protocols.CircuitDiagramInfo(wire_symbols=symbols) + + # pylint: disable=function-redefined @overload def circuit_diagram_info( diff --git a/cirq-core/cirq/protocols/json_serialization_test.py b/cirq-core/cirq/protocols/json_serialization_test.py index d09ffdd70c9..6dcedfaf2f1 100644 --- a/cirq-core/cirq/protocols/json_serialization_test.py +++ b/cirq-core/cirq/protocols/json_serialization_test.py @@ -12,14 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. import contextlib - +import dataclasses import datetime -import importlib import io import json import os import pathlib +import warnings from typing import Dict, List, Optional, Tuple +from unittest import mock import numpy as np import pandas as pd @@ -33,18 +34,48 @@ from cirq.testing.json import ModuleJsonTestSpec, spec_for REPO_ROOT = pathlib.Path(__file__).parent.parent.parent.parent -TESTED_MODULES = [ - 'cirq_aqt', - 'cirq_ionq', - 'cirq_google', - 'cirq.protocols', - 'non_existent_should_be_fine', -] + + +@dataclasses.dataclass +class _ModuleDeprecation: + old_name: str + deprecation_assertion: contextlib.AbstractContextManager + + +# tested modules and their deprecation settings +TESTED_MODULES: Dict[str, Optional[_ModuleDeprecation]] = { + 'cirq_aqt': _ModuleDeprecation( + old_name="cirq.aqt", + deprecation_assertion=cirq.testing.assert_deprecated( + "cirq.aqt", deadline="v0.14", count=None + ), + ), + 'cirq_ionq': _ModuleDeprecation( + old_name="cirq.ionq", + deprecation_assertion=cirq.testing.assert_deprecated( + "cirq.ionq", deadline="v0.14", count=None + ), + ), + 'cirq_google': _ModuleDeprecation( + old_name="cirq.google", + deprecation_assertion=cirq.testing.assert_deprecated( + "cirq.google", deadline="v0.14", count=None + ), + ), + 'cirq_pasqal': _ModuleDeprecation( + old_name="cirq.pasqal", + deprecation_assertion=cirq.testing.assert_deprecated( + "cirq.pasqal", deadline="v0.14", count=None + ), + ), + 'cirq.protocols': None, + 'non_existent_should_be_fine': None, +} def _get_testspecs_for_modules(): modules = [] - for m in TESTED_MODULES: + for m in TESTED_MODULES.keys(): try: modules.append(spec_for(m)) except ModuleNotFoundError: @@ -165,7 +196,11 @@ def test_fail_to_resolve(): ### MODULE CONSISTENCY tests -@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS) +@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS, ids=repr) +# during test setup deprecated submodules are inspected and trigger the +# deprecation error in testing. It is cleaner to just turn it off than to assert +# deprecation for each submodule. +@mock.patch.dict(os.environ, clear='CIRQ_TESTING') def test_shouldnt_be_serialized_no_superfluous(mod_spec: ModuleJsonTestSpec): # everything in the list should be ignored for a reason names = set(mod_spec.get_all_names()) @@ -177,7 +212,11 @@ def test_shouldnt_be_serialized_no_superfluous(mod_spec: ModuleJsonTestSpec): ) -@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS) +@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS, ids=repr) +# during test setup deprecated submodules are inspected and trigger the +# deprecation error in testing. It is cleaner to just turn it off than to assert +# deprecation for each submodule. +@mock.patch.dict(os.environ, clear='CIRQ_TESTING') def test_not_yet_serializable_no_superfluous(mod_spec: ModuleJsonTestSpec): # everything in the list should be ignored for a reason names = set(mod_spec.get_all_names()) @@ -187,7 +226,7 @@ def test_not_yet_serializable_no_superfluous(mod_spec: ModuleJsonTestSpec): ) -@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS) +@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS, ids=repr) def test_mutually_exclusive_blacklist(mod_spec: ModuleJsonTestSpec): common = set(mod_spec.should_not_be_serialized) & set(mod_spec.not_yet_serializable) assert len(common) == 0, ( @@ -196,7 +235,7 @@ def test_mutually_exclusive_blacklist(mod_spec: ModuleJsonTestSpec): ) -@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS) +@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS, ids=repr) def test_resolver_cache_vs_should_not_serialize(mod_spec: ModuleJsonTestSpec): resolver_cache_types = set([n for (n, _) in mod_spec.get_resolver_cache_types()]) common = set(mod_spec.should_not_be_serialized) & resolver_cache_types @@ -208,7 +247,7 @@ def test_resolver_cache_vs_should_not_serialize(mod_spec: ModuleJsonTestSpec): ) -@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS) +@pytest.mark.parametrize('mod_spec', MODULE_TEST_SPECS, ids=repr) def test_resolver_cache_vs_not_yet_serializable(mod_spec: ModuleJsonTestSpec): resolver_cache_types = set([n for (n, _) in mod_spec.get_resolver_cache_types()]) common = set(mod_spec.not_yet_serializable) & resolver_cache_types @@ -414,15 +453,14 @@ def test_internal_serializer_types(): _ = json_serialization._ContextualSerialization._from_json_dict_(**serialization_json) +# during test setup deprecated submodules are inspected and trigger the +# deprecation error in testing. It is cleaner to just turn it off than to assert +# deprecation for each submodule. +@mock.patch.dict(os.environ, clear='CIRQ_TESTING') def _list_public_classes_for_tested_modules(): - cirq_google_on_path = importlib.util.find_spec("cirq_google") is not None - - ctx_manager = ( - cirq.testing.assert_deprecated("cirq.google", deadline="v0.14") - if cirq_google_on_path - else contextlib.suppress() - ) - with ctx_manager: + # to remove DeprecationWarning noise during test collection + with warnings.catch_warnings(): + warnings.simplefilter("ignore") return [ (mod_spec, o, n) for mod_spec in MODULE_TEST_SPECS @@ -538,32 +576,49 @@ def test_to_from_json_gzip(): def _eval_repr_data_file(path: pathlib.Path, deprecation_deadline: Optional[str]): - ctx_manager = ( - cirq.testing.assert_deprecated(deadline=deprecation_deadline, count=None) - if deprecation_deadline - else contextlib.suppress() - ) - with ctx_manager: - imports = { - 'cirq': cirq, - 'datetime': datetime, - 'pd': pd, - 'sympy': sympy, - 'np': np, - 'datetime': datetime, - } - try: - import cirq_google + content = path.read_text() + ctx_managers: List[contextlib.AbstractContextManager] = [contextlib.suppress()] + if deprecation_deadline: + # we ignore coverage here, because sometimes there are no deprecations at all in any of the + # modules + # coverage: ignore + ctx_managers = [cirq.testing.assert_deprecated(deadline=deprecation_deadline, count=None)] + + for deprecation in TESTED_MODULES.values(): + if deprecation is not None and deprecation.old_name in content: + ctx_managers.append(deprecation.deprecation_assertion) + + imports = { + 'cirq': cirq, + 'datetime': datetime, + 'pd': pd, + 'sympy': sympy, + 'np': np, + 'datetime': datetime, + } + try: + import cirq_google - imports['cirq_google'] = cirq_google - except ImportError: - pass + imports['cirq_google'] = cirq_google + except ImportError: + pass + + try: + import cirq_pasqal + + imports['cirq_pasqal'] = cirq_pasqal + except ImportError: + pass + + with contextlib.ExitStack() as stack: + for ctx_manager in ctx_managers: + stack.enter_context(ctx_manager) obj = eval( - path.read_text(), + content, imports, {}, ) - return obj + return obj def assert_repr_and_json_test_data_agree( diff --git a/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP.json b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP.json new file mode 100644 index 00000000000..a237c239cf5 --- /dev/null +++ b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP.json @@ -0,0 +1,5 @@ +{ + "cirq_type": "ISwapPowGate", + "exponent": 0.5, + "global_shift": 0.0 +} diff --git a/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP.repr b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP.repr new file mode 100644 index 00000000000..bd8b4fccedc --- /dev/null +++ b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP.repr @@ -0,0 +1 @@ +(cirq.ISWAP**0.5) diff --git a/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP_INV.json b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP_INV.json new file mode 100644 index 00000000000..4d98688ec5c --- /dev/null +++ b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP_INV.json @@ -0,0 +1,5 @@ +{ + "cirq_type": "ISwapPowGate", + "exponent": -0.5, + "global_shift": 0.0 +} diff --git a/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP_INV.repr b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP_INV.repr new file mode 100644 index 00000000000..659e526cdcf --- /dev/null +++ b/cirq-core/cirq/protocols/json_test_data/SQRT_ISWAP_INV.repr @@ -0,0 +1 @@ +(cirq.ISWAP**-0.5) diff --git a/cirq-core/cirq/protocols/json_test_data/spec.py b/cirq-core/cirq/protocols/json_test_data/spec.py index 1d9d0e51bac..e15ee68cb6a 100644 --- a/cirq-core/cirq/protocols/json_test_data/spec.py +++ b/cirq-core/cirq/protocols/json_test_data/spec.py @@ -83,6 +83,7 @@ 'TwoQubitInteractionHeatmap', # Intermediate states with work buffers and unknown external prng guts. 'ActOnArgs', + 'ActOnArgsContainer', 'ActOnCliffordTableauArgs', 'ActOnDensityMatrixArgs', 'ActOnStabilizerCHFormArgs', @@ -90,6 +91,7 @@ 'ApplyChannelArgs', 'ApplyMixtureArgs', 'ApplyUnitaryArgs', + 'OperationTarget', # Circuit optimizers are function-like. Only attributes # are ignore_failures, tolerance, and other feature flags 'AlignLeft', @@ -104,6 +106,7 @@ 'ExpandComposite', 'MEASUREMENT_KEY_SEPARATOR', 'MergeInteractions', + 'MergeInteractionsToSqrtIswap', 'MergeSingleQubitGates', 'PointOptimizer', 'SynchronizeTerminalMeasurements', diff --git a/cirq-core/cirq/sim/__init__.py b/cirq-core/cirq/sim/__init__.py index 8030d2d6451..e04a560733d 100644 --- a/cirq-core/cirq/sim/__init__.py +++ b/cirq-core/cirq/sim/__init__.py @@ -19,6 +19,10 @@ ActOnArgs, ) +from cirq.sim.act_on_args_container import ( + ActOnArgsContainer, +) + from cirq.sim.act_on_density_matrix_args import ( ActOnDensityMatrixArgs, ) @@ -39,6 +43,8 @@ DensityMatrixTrialResult, ) +from cirq.sim.operation_target import OperationTarget + from cirq.sim.mux import ( CIRCUIT_LIKE, final_density_matrix, @@ -58,6 +64,7 @@ ) from cirq.sim.simulator_base import ( + StepResultBase, SimulatorBase, ) diff --git a/cirq-core/cirq/sim/act_on_args.py b/cirq-core/cirq/sim/act_on_args.py index dc0191ff2b1..40b85a1390e 100644 --- a/cirq-core/cirq/sim/act_on_args.py +++ b/cirq-core/cirq/sim/act_on_args.py @@ -13,13 +13,26 @@ # limitations under the License. """Objects and methods for acting efficiently on a state tensor.""" import abc -from typing import Any, Dict, List, TypeVar, TYPE_CHECKING, Sequence, Tuple, Iterable +from typing import ( + Any, + Iterable, + Dict, + List, + TypeVar, + TYPE_CHECKING, + Sequence, + Tuple, + cast, + Optional, + Iterator, +) import numpy as np from cirq import protocols from cirq._compat import deprecated from cirq.protocols.decompose_protocol import _try_decompose_into_operations_and_qubits +from cirq.sim.operation_target import OperationTarget TSelf = TypeVar('TSelf', bound='ActOnArgs') @@ -27,12 +40,12 @@ import cirq -class ActOnArgs: +class ActOnArgs(OperationTarget[TSelf]): """State and context for an operation acting on a state tensor.""" def __init__( self, - prng: np.random.RandomState, + prng: np.random.RandomState = None, qubits: Sequence['cirq.Qid'] = None, axes: Iterable[int] = None, log_of_measurement_results: Dict[str, Any] = None, @@ -47,20 +60,21 @@ def __init__( axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStateVectorArgs.record_measurement_result`. + being recorded into. """ + if prng is None: + prng = cast(np.random.RandomState, np.random) if qubits is None: qubits = () if axes is None: axes = () if log_of_measurement_results is None: log_of_measurement_results = {} - self.qubits = tuple(qubits) - self.qubit_map = {q: i for i, q in enumerate(self.qubits)} + self._qubits = tuple(qubits) + self.qubit_map = {q: i for i, q in enumerate(qubits)} self._axes = tuple(axes) self.prng = prng - self.log_of_measurement_results = log_of_measurement_results + self._log_of_measurement_results = log_of_measurement_results def measure(self, qubits: Sequence['cirq.Qid'], key: str, invert_mask: Sequence[bool]): """Adds a measurement result to the log. @@ -74,9 +88,9 @@ def measure(self, qubits: Sequence['cirq.Qid'], key: str, invert_mask: Sequence[ """ bits = self._perform_measurement(qubits) corrected = [bit ^ (bit < 2 and mask) for bit, mask in zip(bits, invert_mask)] - if key in self.log_of_measurement_results: + if key in self._log_of_measurement_results: raise ValueError(f"Measurement already logged to key {key!r}") - self.log_of_measurement_results[key] = corrected + self._log_of_measurement_results[key] = corrected def get_axes(self, qubits: Sequence['cirq.Qid']) -> List[int]: return [self.qubit_map[q] for q in qubits] @@ -90,6 +104,60 @@ def _perform_measurement(self, qubits: Sequence['cirq.Qid']) -> List[int]: def copy(self: TSelf) -> TSelf: """Creates a copy of the object.""" + def create_merged_state(self: TSelf) -> TSelf: + """Creates a final merged state.""" + return self + + def apply_operation(self, op: 'cirq.Operation'): + """Applies the operation to the state.""" + protocols.act_on(op, self) + + def kronecker_product(self: TSelf, other: TSelf) -> TSelf: + """Joins two state spaces together.""" + raise NotImplementedError() + + def factor( + self: TSelf, + qubits: Sequence['cirq.Qid'], + *, + validate=True, + atol=1e-07, + ) -> Tuple[TSelf, TSelf]: + """Splits two state spaces after a measurement or reset.""" + raise NotImplementedError() + + def transpose_to_qubit_order(self: TSelf, qubits: Sequence['cirq.Qid']) -> TSelf: + """Physically reindexes the state by the new basis.""" + raise NotImplementedError() + + @property + def log_of_measurement_results(self) -> Dict[str, Any]: + return self._log_of_measurement_results + + @property + def qubits(self) -> Tuple['cirq.Qid', ...]: + return self._qubits + + @abc.abstractmethod + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + """Samples the qubits.""" + + def __getitem__(self: TSelf, item: Optional['cirq.Qid']) -> TSelf: + if item not in self.qubit_map: + raise IndexError(f'{item} not in {self.qubits}') + return self + + def __len__(self) -> int: + return len(self.qubits) + + def __iter__(self) -> Iterator[Optional['cirq.Qid']]: + return iter(self.qubits) + @abc.abstractmethod def _act_on_fallback_(self, action: Any, qubits: Sequence['cirq.Qid'], allow_decompose: bool): """Handles the act_on protocol fallback implementation.""" diff --git a/cirq-core/cirq/sim/act_on_args_container.py b/cirq-core/cirq/sim/act_on_args_container.py new file mode 100644 index 00000000000..6b2ac3a3296 --- /dev/null +++ b/cirq-core/cirq/sim/act_on_args_container.py @@ -0,0 +1,155 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import abc +from typing import ( + Dict, + TYPE_CHECKING, + Generic, + Sequence, + Optional, + Iterator, + Any, + Tuple, + List, +) + +import numpy as np + +from cirq import ops +from cirq.sim.operation_target import OperationTarget +from cirq.sim.simulator import ( + TActOnArgs, +) + +if TYPE_CHECKING: + import cirq + + +class ActOnArgsContainer( + Generic[TActOnArgs], + OperationTarget[TActOnArgs], + abc.Mapping, +): + """A container for a `Qid`-to-`ActOnArgs` dictionary.""" + + def __init__( + self, + args: Dict[Optional['cirq.Qid'], TActOnArgs], + qubits: Sequence['cirq.Qid'], + split_untangled_states: bool, + log_of_measurement_results: Dict[str, Any], + ): + """Initializes the class. + + Args: + args: The `ActOnArgs` dictionary. This will not be copied; the + original reference will be kept here. + qubits: The canonical ordering of qubits. + split_untangled_states: If True, optimizes operations by running + unentangled qubit sets independently and merging those states + at the end. + log_of_measurement_results: A mutable object that measurements are + being recorded into. + """ + self.args = args + self._qubits = tuple(qubits) + self.split_untangled_states = split_untangled_states + self._log_of_measurement_results = log_of_measurement_results + + def create_merged_state(self) -> TActOnArgs: + if not self.split_untangled_states: + return self.args[None] + final_args = self.args[None] + for args in set([self.args[k] for k in self.args.keys() if k is not None]): + final_args = final_args.kronecker_product(args) + return final_args.transpose_to_qubit_order(self.qubits) + + def apply_operation( + self, + op: 'cirq.Operation', + ): + # Go through the op's qubits and join any disparate ActOnArgs states + # into a new combined state. + op_args_opt: Optional[TActOnArgs] = None + for q in op.qubits: + if op_args_opt is None: + op_args_opt = self.args[q] + elif q not in op_args_opt.qubits: + op_args_opt = op_args_opt.kronecker_product(self.args[q]) + op_args = op_args_opt or self.args[None] + + # (Backfill the args map with the new value) + for q in op_args.qubits: + self.args[q] = op_args + + # Act on the args with the operation + op_args.apply_operation(op) + + # Decouple any measurements or resets + if self.split_untangled_states and isinstance( + op.gate, (ops.MeasurementGate, ops.ResetChannel) + ): + for q in op.qubits: + q_args, op_args = op_args.factor((q,), validate=False) + self.args[q] = q_args + + # (Backfill the args map with the new value) + for q in op_args.qubits: + self.args[q] = op_args + + def copy(self) -> 'ActOnArgsContainer[TActOnArgs]': + logs = self.log_of_measurement_results.copy() + copies = {a: a.copy() for a in set(self.args.values())} + for copy in copies.values(): + copy._log_of_measurement_results = logs + args = {q: copies[a] for q, a in self.args.items()} + return ActOnArgsContainer(args, self.qubits, self.split_untangled_states, logs) + + @property + def qubits(self) -> Tuple['cirq.Qid', ...]: + return self._qubits + + @property + def log_of_measurement_results(self) -> Dict[str, Any]: + return self._log_of_measurement_results + + def sample( + self, + qubits: List[ops.Qid], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + columns = [] + selected_order: List[ops.Qid] = [] + q_set = set(qubits) + for v in dict.fromkeys(self.args.values()): + qs = [q for q in v.qubits if q in q_set] + if any(qs): + column = v.sample(qs, repetitions, seed) + columns.append(column) + selected_order += qs + stacked = np.column_stack(columns) + qubit_map = {q: i for i, q in enumerate(selected_order)} + index_order = [qubit_map[q] for q in qubits] + return stacked[:, index_order] + + def __getitem__(self, item: Optional['cirq.Qid']) -> TActOnArgs: + return self.args[item] + + def __len__(self) -> int: + return len(self.args) + + def __iter__(self) -> Iterator[Optional['cirq.Qid']]: + return iter(self.args) diff --git a/cirq-core/cirq/sim/act_on_args_container_test.py b/cirq-core/cirq/sim/act_on_args_container_test.py new file mode 100644 index 00000000000..23025289bf0 --- /dev/null +++ b/cirq-core/cirq/sim/act_on_args_container_test.py @@ -0,0 +1,174 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from typing import List, Dict, Any, Sequence, Tuple, Optional + +import cirq + + +class EmptyActOnArgs(cirq.ActOnArgs): + def __init__(self, qubits, logs): + super().__init__( + qubits=qubits, + log_of_measurement_results=logs, + ) + + def _perform_measurement(self, qubits: Sequence[cirq.Qid]) -> List[int]: + return [] + + def copy(self) -> 'EmptyActOnArgs': + return EmptyActOnArgs( + qubits=self.qubits, + logs=self.log_of_measurement_results.copy(), + ) + + def _act_on_fallback_(self, action: Any, qubits: Sequence[cirq.Qid], allow_decompose: bool): + return True + + def kronecker_product(self, other: 'EmptyActOnArgs') -> 'EmptyActOnArgs': + return EmptyActOnArgs( + qubits=self.qubits + other.qubits, + logs=self.log_of_measurement_results, + ) + + def factor( + self, + qubits: Sequence['cirq.Qid'], + *, + validate=True, + atol=1e-07, + ) -> Tuple['EmptyActOnArgs', 'EmptyActOnArgs']: + extracted_args = EmptyActOnArgs( + qubits=qubits, + logs=self.log_of_measurement_results, + ) + remainder_args = EmptyActOnArgs( + qubits=tuple(q for q in self.qubits if q not in qubits), + logs=self.log_of_measurement_results, + ) + return extracted_args, remainder_args + + def transpose_to_qubit_order(self, qubits: Sequence['cirq.Qid']) -> 'EmptyActOnArgs': + return EmptyActOnArgs( + qubits=qubits, + logs=self.log_of_measurement_results, + ) + + def sample(self, qubits, repetitions=1, seed=None): + pass + + +q0, q1 = qs2 = cirq.LineQubit.range(2) + + +def create_container( + qubits: Sequence['cirq.Qid'], + split_untangled_states=True, +) -> cirq.ActOnArgsContainer[EmptyActOnArgs]: + args_map: Dict[Optional['cirq.Qid'], EmptyActOnArgs] = {} + log: Dict[str, Any] = {} + if split_untangled_states: + for q in reversed(qubits): + args_map[q] = EmptyActOnArgs([q], log) + args_map[None] = EmptyActOnArgs((), log) + else: + args = EmptyActOnArgs(qubits, log) + for q in qubits: + args_map[q] = args + args_map[None] = args if not split_untangled_states else EmptyActOnArgs((), log) + return cirq.ActOnArgsContainer(args_map, qubits, split_untangled_states, log) + + +def test_entanglement_causes_join(): + args = create_container(qs2) + assert len(set(args.values())) == 3 + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 2 + assert args[q0] is args[q1] + assert args[None] is not args[q0] + + +def test_measurement_causes_split(): + args = create_container(qs2) + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 2 + args.apply_operation(cirq.measure(q0)) + assert len(set(args.values())) == 3 + assert args[q0] is not args[q1] + assert args[q0] is not args[None] + + +def test_reset_causes_split(): + args = create_container(qs2) + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 2 + args.apply_operation(cirq.reset(q0)) + assert len(set(args.values())) == 3 + assert args[q0] is not args[q1] + assert args[q0] is not args[None] + + +def test_measurement_does_not_split_if_disabled(): + args = create_container(qs2, False) + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 1 + args.apply_operation(cirq.measure(q0)) + assert len(set(args.values())) == 1 + assert args[q1] is args[q0] + assert args[None] is args[q0] + + +def test_reset_does_not_split_if_disabled(): + args = create_container(qs2, False) + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 1 + args.apply_operation(cirq.reset(q0)) + assert len(set(args.values())) == 1 + assert args[q1] is args[q0] + assert args[None] is args[q0] + + +def test_measurement_of_all_qubits_causes_split(): + args = create_container(qs2) + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 2 + args.apply_operation(cirq.measure(q0, q1)) + assert len(set(args.values())) == 3 + assert args[q0] is not args[q1] + assert args[q0] is not args[None] + + +def test_measurement_in_single_qubit_circuit_passes(): + args = create_container([q0]) + assert len(set(args.values())) == 2 + args.apply_operation(cirq.measure(q0)) + assert len(set(args.values())) == 2 + assert args[q0] is not args[None] + + +def test_reorder_succeeds(): + args = create_container(qs2, False) + reordered = args[q0].transpose_to_qubit_order([q1, q0]) + assert reordered.qubits == (q1, q0) + + +def test_copy_succeeds(): + args = create_container(qs2, False) + copied = args[q0].copy() + assert copied.qubits == (q0, q1) + + +def test_merge_succeeds(): + args = create_container(qs2, False) + merged = args.create_merged_state() + assert merged.qubits == (q0, q1) diff --git a/cirq-core/cirq/sim/act_on_args_test.py b/cirq-core/cirq/sim/act_on_args_test.py index 535600c9615..dc323a6e140 100644 --- a/cirq-core/cirq/sim/act_on_args_test.py +++ b/cirq-core/cirq/sim/act_on_args_test.py @@ -11,29 +11,37 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import List - import numpy as np +import pytest import cirq from cirq.sim import act_on_args -def test_measurements(): - class DummyArgs(cirq.ActOnArgs): - def _perform_measurement(self, qubits) -> List[int]: - return [5, 3] +class DummyArgs(cirq.ActOnArgs): + def __init__(self): + super().__init__(qubits=cirq.LineQubit.range(2)) + + def copy(self): + pass + + def sample(self, qubits, repetitions=1, seed=None): + pass + + def _perform_measurement(self, qubits): + return [5, 3] + + def _act_on_fallback_(self, action, qubits, allow_decompose): + return True - args = DummyArgs(prng=np.random.RandomState(), log_of_measurement_results={}) - args.measure([], "test", [1]) + +def test_measurements(): + args = DummyArgs() + args.measure([cirq.LineQubit(0)], "test", [False]) assert args.log_of_measurement_results["test"] == [5] def test_decompose(): - class DummyArgs(cirq.ActOnArgs): - def _act_on_fallback_(self, action, qubits, allow_decompose): - return True - class Composite(cirq.Gate): def num_qubits(self) -> int: return 1 @@ -41,5 +49,14 @@ def num_qubits(self) -> int: def _decompose_(self, qubits): yield cirq.X(*qubits) - args = DummyArgs(prng=np.random.RandomState(), log_of_measurement_results={}) - assert act_on_args.strat_act_on_from_apply_decompose(Composite(), args, cirq.LineQubit.range(1)) + args = DummyArgs() + assert act_on_args.strat_act_on_from_apply_decompose(Composite(), args, [cirq.LineQubit(0)]) + + +def test_mapping(): + args = DummyArgs() + assert list(iter(args)) == cirq.LineQubit.range(2) + r1 = args[cirq.LineQubit(0)] + assert args is r1 + with pytest.raises(IndexError): + _ = args[cirq.LineQubit(2)] diff --git a/cirq-core/cirq/sim/act_on_density_matrix_args.py b/cirq-core/cirq/sim/act_on_density_matrix_args.py index 66d96bbcf13..ae47fae0bbb 100644 --- a/cirq-core/cirq/sim/act_on_density_matrix_args.py +++ b/cirq-core/cirq/sim/act_on_density_matrix_args.py @@ -20,6 +20,7 @@ from cirq import protocols, sim from cirq._compat import deprecated_parameter from cirq.sim.act_on_args import ActOnArgs, strat_act_on_from_apply_decompose +from cirq.linalg import transformations if TYPE_CHECKING: import cirq @@ -81,8 +82,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStateVectorArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -133,6 +133,84 @@ def copy(self) -> 'cirq.ActOnDensityMatrixArgs': log_of_measurement_results=self.log_of_measurement_results.copy(), ) + def kronecker_product( + self, other: 'cirq.ActOnDensityMatrixArgs' + ) -> 'cirq.ActOnDensityMatrixArgs': + target_tensor = transformations.density_matrix_kronecker_product( + self.target_tensor, other.target_tensor + ) + buffer = [np.empty_like(target_tensor) for _ in self.available_buffer] + return ActOnDensityMatrixArgs( + target_tensor=target_tensor, + available_buffer=buffer, + qubits=self.qubits + other.qubits, + qid_shape=target_tensor.shape[: int(target_tensor.ndim / 2)], + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + + def factor( + self, + qubits: Sequence['cirq.Qid'], + *, + validate=True, + atol=1e-07, + ) -> Tuple['cirq.ActOnDensityMatrixArgs', 'cirq.ActOnDensityMatrixArgs']: + axes = self.get_axes(qubits) + extracted_tensor, remainder_tensor = transformations.factor_density_matrix( + self.target_tensor, axes, validate=validate, atol=atol + ) + buffer = [np.empty_like(extracted_tensor) for _ in self.available_buffer] + extracted_args = ActOnDensityMatrixArgs( + target_tensor=extracted_tensor, + available_buffer=buffer, + qubits=qubits, + qid_shape=extracted_tensor.shape[: int(extracted_tensor.ndim / 2)], + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + buffer = [np.empty_like(remainder_tensor) for _ in self.available_buffer] + remainder_args = ActOnDensityMatrixArgs( + target_tensor=remainder_tensor, + available_buffer=buffer, + qubits=tuple(q for q in self.qubits if q not in qubits), + qid_shape=remainder_tensor.shape[: int(remainder_tensor.ndim / 2)], + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + return extracted_args, remainder_args + + def transpose_to_qubit_order( + self, qubits: Sequence['cirq.Qid'] + ) -> 'cirq.ActOnDensityMatrixArgs': + axes = self.get_axes(qubits) + axes = axes + [i + len(qubits) for i in axes] + new_tensor = np.moveaxis(self.target_tensor, axes, range(len(qubits) * 2)) + buffer = [np.empty_like(new_tensor) for _ in self.available_buffer] + return ActOnDensityMatrixArgs( + target_tensor=new_tensor, + available_buffer=buffer, + qubits=qubits, + qid_shape=new_tensor.shape[: int(new_tensor.ndim / 2)], + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + indices = [self.qubit_map[q] for q in qubits] + return sim.sample_density_matrix( + self.target_tensor, + indices, + qid_shape=tuple(q.dimension for q in self.qubits), + repetitions=repetitions, + seed=seed, + ) + def _strat_apply_channel_to_state( action: Any, args: ActOnDensityMatrixArgs, qubits: Sequence['cirq.Qid'] @@ -147,7 +225,7 @@ def _strat_apply_channel_to_state( auxiliary_buffer0=args.available_buffer[1], auxiliary_buffer1=args.available_buffer[2], left_axes=axes, - right_axes=[e + len(args.qid_shape) for e in axes], + right_axes=[e + len(args.qubits) for e in axes], ), default=None, ) diff --git a/cirq-core/cirq/sim/act_on_state_vector_args.py b/cirq-core/cirq/sim/act_on_state_vector_args.py index b9513269eb9..18630ddcf96 100644 --- a/cirq-core/cirq/sim/act_on_state_vector_args.py +++ b/cirq-core/cirq/sim/act_on_state_vector_args.py @@ -20,6 +20,7 @@ from cirq import linalg, protocols, sim from cirq._compat import deprecated_parameter from cirq.sim.act_on_args import ActOnArgs, strat_act_on_from_apply_decompose +from cirq.linalg import transformations if TYPE_CHECKING: import cirq @@ -40,13 +41,12 @@ def _rewrite_deprecated_args(args, kwargs): class ActOnStateVectorArgs(ActOnArgs): """State and context for an operation acting on a state vector. - There are three common ways to act on this object: + There are two common ways to act on this object: 1. Directly edit the `target_tensor` property, which is storing the state vector of the quantum system as a numpy array with one axis per qudit. 2. Overwrite the `available_buffer` property with the new state vector, and then pass `available_buffer` into `swap_target_tensor_for`. - 3. Call `record_measurement_result(key, val)` to log a measurement result. """ @deprecated_parameter( @@ -83,8 +83,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStateVectorArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -202,6 +201,73 @@ def copy(self) -> 'cirq.ActOnStateVectorArgs': log_of_measurement_results=self.log_of_measurement_results.copy(), ) + def kronecker_product(self, other: 'cirq.ActOnStateVectorArgs') -> 'cirq.ActOnStateVectorArgs': + target_tensor = transformations.state_vector_kronecker_product( + self.target_tensor, other.target_tensor + ) + buffer = np.empty_like(target_tensor) + return ActOnStateVectorArgs( + target_tensor=target_tensor, + available_buffer=buffer, + qubits=self.qubits + other.qubits, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + + def factor( + self, + qubits: Sequence['cirq.Qid'], + *, + validate=True, + atol=1e-07, + ) -> Tuple['cirq.ActOnStateVectorArgs', 'cirq.ActOnStateVectorArgs']: + axes = self.get_axes(qubits) + extracted_tensor, remainder_tensor = transformations.factor_state_vector( + self.target_tensor, axes, validate=validate, atol=atol + ) + extracted_args = ActOnStateVectorArgs( + target_tensor=extracted_tensor, + available_buffer=np.empty_like(extracted_tensor), + qubits=qubits, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + remainder_args = ActOnStateVectorArgs( + target_tensor=remainder_tensor, + available_buffer=np.empty_like(remainder_tensor), + qubits=tuple(q for q in self.qubits if q not in qubits), + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + return extracted_args, remainder_args + + def transpose_to_qubit_order(self, qubits: Sequence['cirq.Qid']) -> 'cirq.ActOnStateVectorArgs': + axes = self.get_axes(qubits) + new_tensor = transformations.transpose_state_vector_to_axis_order(self.target_tensor, axes) + new_args = ActOnStateVectorArgs( + target_tensor=new_tensor, + available_buffer=np.empty_like(new_tensor), + qubits=qubits, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results, + ) + return new_args + + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + indices = [self.qubit_map[q] for q in qubits] + return sim.sample_state_vector( + self.target_tensor, + indices, + qid_shape=tuple(q.dimension for q in self.qubits), + repetitions=repetitions, + seed=seed, + ) + def _strat_act_on_state_vector_from_apply_unitary( unitary_value: Any, diff --git a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py index 23a8e830ab7..8f423a0e667 100644 --- a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -44,10 +44,9 @@ def _rewrite_deprecated_args(args, kwargs): class ActOnCliffordTableauArgs(ActOnArgs): """State and context for an operation acting on a clifford tableau. - There are two common ways to act on this object: - 1. Directly edit the `tableau` property, which is storing the clifford - tableau of the quantum system with one axis per qubit. - 2. Call `record_measurement_result(key, val)` to log a measurement result. + + To act on this object, directly edit the `tableau` property, which is + storing the density matrix of the quantum system with one axis per qubit. """ @deprecated_parameter( @@ -77,8 +76,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnCliffordTableauArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -111,6 +109,15 @@ def copy(self) -> 'cirq.ActOnCliffordTableauArgs': log_of_measurement_results=self.log_of_measurement_results.copy(), ) + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + # Unnecessary for now but can be added later if there is a use case. + raise NotImplementedError() + def _strat_act_on_clifford_tableau_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnCliffordTableauArgs', qubits: Sequence['cirq.Qid'] diff --git a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py index dc25c0f10e9..ce53f6a9d56 100644 --- a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py +++ b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py @@ -100,7 +100,7 @@ def test_copy(): assert args.qubit_map == args1.qubit_map assert args.prng is args1.prng assert args.log_of_measurement_results is not args1.log_of_measurement_results - assert args.log_of_measurement_results == args.log_of_measurement_results + assert args.log_of_measurement_results == args1.log_of_measurement_results def test_axes_deprecation(): diff --git a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index 86911788c93..417e5fbcc56 100644 --- a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -16,6 +16,7 @@ import numpy as np +from cirq import value, ops, protocols from cirq._compat import deprecated_parameter from cirq.ops import common_gates, pauli_gates from cirq.ops.clifford_gate import SingleQubitCliffordGate @@ -74,8 +75,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStabilizerCHFormArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -106,6 +106,21 @@ def copy(self) -> 'cirq.ActOnStabilizerCHFormArgs': log_of_measurement_results=self.log_of_measurement_results.copy(), ) + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + measurements: Dict[str, List[np.ndarray]] = {} + prng = value.parse_random_state(seed) + for i in range(repetitions): + op = ops.measure(*qubits, key=str(i)) + state = self.state.copy() + ch_form_args = ActOnStabilizerCHFormArgs(state, prng, measurements, self.qubits) + protocols.act_on(op, ch_form_args) + return np.array(list(measurements.values()), dtype=bool) + def _strat_act_on_stabilizer_ch_form_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnStabilizerCHFormArgs', qubits: Sequence['cirq.Qid'] diff --git a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py index 5c97d837477..3047b78a832 100644 --- a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py +++ b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py @@ -123,7 +123,7 @@ def test_copy(): assert args.qubits == args1.qubits assert args.prng is args1.prng assert args.log_of_measurement_results is not args1.log_of_measurement_results - assert args.log_of_measurement_results == args.log_of_measurement_results + assert args.log_of_measurement_results == args1.log_of_measurement_results def test_axes_deprecation(): diff --git a/cirq-core/cirq/sim/clifford/clifford_simulator.py b/cirq-core/cirq/sim/clifford/clifford_simulator.py index 49067046d44..d506e88b3a1 100644 --- a/cirq-core/cirq/sim/clifford/clifford_simulator.py +++ b/cirq-core/cirq/sim/clifford/clifford_simulator.py @@ -34,7 +34,7 @@ import numpy as np import cirq -from cirq import study, ops, protocols, value +from cirq import study, protocols, value from cirq.protocols import act_on from cirq.sim import clifford, simulator, simulator_base @@ -64,10 +64,11 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: # TODO: support more general Pauli measurements return protocols.has_stabilizer_effect(op) - def _create_act_on_args( + def _create_partial_act_on_args( self, initial_state: Union[int, clifford.ActOnStabilizerCHFormArgs], qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], ) -> clifford.ActOnStabilizerCHFormArgs: """Creates the ActOnStabilizerChFormArgs for a circuit. @@ -90,26 +91,21 @@ def _create_act_on_args( return clifford.ActOnStabilizerCHFormArgs( state=state.ch_form, prng=self._prng, - log_of_measurement_results={}, + log_of_measurement_results=logs, qubits=qubits, ) def _create_step_result( self, - sim_state: clifford.ActOnStabilizerCHFormArgs, - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[clifford.ActOnStabilizerCHFormArgs]', ): - state = CliffordState(qubit_map) - state.ch_form = sim_state.state.copy() - return CliffordSimulatorStepResult( - measurements=sim_state.log_of_measurement_results, state=state - ) + return CliffordSimulatorStepResult(sim_state=sim_state) def _create_simulator_trial_result( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state, + final_simulator_state: Union['CliffordSimulatorStepResult', 'CliffordState'], ): return CliffordTrialResult( @@ -122,13 +118,15 @@ def __init__( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: 'CliffordState', + final_simulator_state: Union['CliffordSimulatorStepResult', 'CliffordState'], ) -> None: super().__init__( params=params, measurements=measurements, final_simulator_state=final_simulator_state ) - self.final_state = final_simulator_state + @property + def final_state(self): + return self._final_simulator_state def __str__(self) -> str: samples = super().__str__() @@ -136,22 +134,21 @@ def __str__(self) -> str: return f'measurements: {samples}\noutput state: {final}' -class CliffordSimulatorStepResult(simulator.StepResult['CliffordState']): +class CliffordSimulatorStepResult( + simulator_base.StepResultBase['clifford.CliffordState', 'clifford.ActOnStabilizerCHFormArgs'] +): """A `StepResult` that includes `StateVectorMixin` methods.""" - def __init__(self, state: 'CliffordState', measurements): + def __init__( + self, + sim_state: 'cirq.OperationTarget[clifford.ActOnStabilizerCHFormArgs]', + ): """Results of a step of the simulator. Attributes: - state: A CliffordState - measurements: A dictionary from measurement gate key to measurement - results, ordered by the qubits that the measurement operates on. - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). + sim_state: The qubit:ActOnArgs lookup for this step. """ - self.measurements = measurements - self.state = state.copy() + super().__init__(sim_state) + self._clifford_state = None def __str__(self) -> str: def bitstring(vals): @@ -168,28 +165,17 @@ def bitstring(vals): return f'{measurements}{final}' + @property + def state(self): + if self._clifford_state is None: + clifford_state = CliffordState(self._qubit_mapping) + clifford_state.ch_form = self._merged_sim_state.state.copy() + self._clifford_state = clifford_state + return self._clifford_state + def _simulator_state(self): return self.state - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - - measurements = {} # type: Dict[str, List[np.ndarray]] - - for i in range(repetitions): - self.state.apply_measurement( - cirq.measure(*qubits, key=str(i)), - measurements, - value.parse_random_state(seed), - collapse_state_vector=False, - ) - - return np.array(list(measurements.values()), dtype=bool) - @value.value_equality class CliffordState: diff --git a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py index b68e14c24c1..5527c7f8725 100644 --- a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py @@ -241,26 +241,16 @@ def test_clifford_trial_result_str(): def test_clifford_step_result_str(): q0 = cirq.LineQubit(0) - final_simulator_state = cirq.CliffordState(qubit_map={q0: 0}) - - assert ( - str( - cirq.CliffordSimulatorStepResult( - measurements={'m': np.array([[1]])}, state=final_simulator_state - ) - ) - == "m=1\n" - "|0⟩" + result = next( + cirq.CliffordSimulator().simulate_moment_steps(cirq.Circuit(cirq.measure(q0, key='m'))) ) + assert str(result) == "m=0\n" "|0⟩" def test_clifford_step_result_no_measurements_str(): q0 = cirq.LineQubit(0) - final_simulator_state = cirq.CliffordState(qubit_map={q0: 0}) - - assert ( - str(cirq.CliffordSimulatorStepResult(measurements={}, state=final_simulator_state)) == "|0⟩" - ) + result = next(cirq.CliffordSimulator().simulate_moment_steps(cirq.Circuit(cirq.I(q0)))) + assert str(result) == "|0⟩" def test_clifford_state_str(): diff --git a/cirq-core/cirq/sim/density_matrix_simulator.py b/cirq-core/cirq/sim/density_matrix_simulator.py index 1018ce5a619..b49bef07d0e 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator.py +++ b/cirq-core/cirq/sim/density_matrix_simulator.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. """Simulator for density matrices that simulates noisy quantum circuits.""" -from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Union, Sequence +from typing import Any, Dict, TYPE_CHECKING, Tuple, Union, Sequence, Optional, List import numpy as np from cirq import ops, protocols, qis, study, value -from cirq.sim import density_matrix_utils, simulator, act_on_density_matrix_args, simulator_base +from cirq.sim import ( + simulator, + act_on_density_matrix_args, + simulator_base, +) if TYPE_CHECKING: import cirq @@ -119,17 +123,21 @@ def __init__( noise: 'cirq.NOISE_MODEL_LIKE' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ignore_measurement_results: bool = False, + split_untangled_states: bool = True, ): """Density matrix simulator. Args: - dtype: The `numpy.dtype` used by the simulation. One of - `numpy.complex64` or `numpy.complex128` - noise: A noise model to apply while simulating. - seed: The random seed to use for this simulator. - ignore_measurement_results: if True, then the simulation - will treat measurement as dephasing instead of collapsing - process. + dtype: The `numpy.dtype` used by the simulation. One of + `numpy.complex64` or `numpy.complex128` + noise: A noise model to apply while simulating. + seed: The random seed to use for this simulator. + ignore_measurement_results: if True, then the simulation + will treat measurement as dephasing instead of collapsing + process. + split_untangled_states: If True, optimizes simulation by running + unentangled qubit sets independently and merging those states + at the end. Example: >>> (q0,) = cirq.LineQubit.range(1) @@ -154,14 +162,16 @@ def __init__( noise=noise, seed=seed, ignore_measurement_results=ignore_measurement_results, + split_untangled_states=split_untangled_states, ) if dtype not in {np.complex64, np.complex128}: raise ValueError(f'dtype must be complex64 or complex128, was {dtype}') - def _create_act_on_args( + def _create_partial_act_on_args( self, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE', 'cirq.ActOnDensityMatrixArgs'], qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], ) -> 'cirq.ActOnDensityMatrixArgs': """Creates the ActOnDensityMatrixArgs for a circuit. @@ -192,7 +202,7 @@ def _create_act_on_args( qubits=qubits, qid_shape=qid_shape, prng=self._prng, - log_of_measurement_results={}, + log_of_measurement_results=logs, ) def _can_be_in_run_prefix(self, val: Any): @@ -200,13 +210,11 @@ def _can_be_in_run_prefix(self, val: Any): def _create_step_result( self, - sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[cirq.ActOnDensityMatrixArgs]', ): return DensityMatrixStepResult( - density_matrix=sim_state.target_tensor, - measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + sim_state=sim_state, + simulator=self, dtype=self._dtype, ) @@ -214,7 +222,7 @@ def _create_simulator_trial_result( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: 'DensityMatrixSimulatorState', + final_simulator_state: Union['DensityMatrixSimulatorState', 'DensityMatrixStepResult'], ) -> 'DensityMatrixTrialResult': return DensityMatrixTrialResult( params=params, measurements=measurements, final_simulator_state=final_simulator_state @@ -255,49 +263,46 @@ def simulate_expectation_values_sweep( return swept_evs -class DensityMatrixStepResult(simulator.StepResult['DensityMatrixSimulatorState']): +class DensityMatrixStepResult( + simulator_base.StepResultBase[ + 'DensityMatrixSimulatorState', act_on_density_matrix_args.ActOnDensityMatrixArgs + ] +): """A single step in the simulation of the DensityMatrixSimulator. Attributes: - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). measurements: A dictionary from measurement gate key to measurement results, ordered by the qubits that the measurement operates on. """ def __init__( self, - density_matrix: np.ndarray, - measurements: Dict[str, np.ndarray], - qubit_map: Dict[ops.Qid, int], + sim_state: 'cirq.OperationTarget[cirq.ActOnDensityMatrixArgs]', + simulator: DensityMatrixSimulator, dtype: 'DTypeLike' = np.complex64, ): """DensityMatrixStepResult. Args: - density_matrix: The density matrix at this step. Can be mutated. - measurements: The measurements for this step of the simulation. - qubit_map: A map from qid to index used to define the - ordering of the basis in density_matrix. - dtype: The numpy dtype for the density matrix. + sim_state: The qubit:ActOnArgs lookup for this step. + simulator: The simulator used to create this. + dtype: The `numpy.dtype` used by the simulation. One of + `numpy.complex64` or `numpy.complex128`. """ - super().__init__(measurements) - self._density_matrix = density_matrix - self._qubit_map = qubit_map + super().__init__(sim_state) self._dtype = dtype - self._qid_shape = simulator._qubit_map_to_shape(qubit_map) - - def _qid_shape_(self): - return self._qid_shape + self._density_matrix: Optional[np.ndarray] = None + self._simulator = simulator def _simulator_state(self) -> 'DensityMatrixSimulatorState': - return DensityMatrixSimulatorState(self._density_matrix, self._qubit_map) + return DensityMatrixSimulatorState(self.density_matrix(copy=False), self._qubit_mapping) def set_density_matrix(self, density_matrix_repr: Union[int, np.ndarray]): """Set the density matrix to a new density matrix. + Note that this feature is incompatible with the simulation setting + `split_untangled_states=True`, and will throw an error if attempted. + Args: density_matrix_repr: If this is an int, the density matrix is set to the computational basis state corresponding to this state. Otherwise @@ -308,12 +313,7 @@ def set_density_matrix(self, density_matrix_repr: Union[int, np.ndarray]): mixed state it must be correctly sized and positive semidefinite with trace one. """ - density_matrix = qis.to_valid_density_matrix( - density_matrix_repr, len(self._qubit_map), qid_shape=self._qid_shape, dtype=self._dtype - ) - sim_state_matrix = self._simulator_state().density_matrix - density_matrix = np.reshape(density_matrix, sim_state_matrix.shape) - np.copyto(dst=sim_state_matrix, src=density_matrix) + self._sim_state = self._simulator._create_act_on_args(density_matrix_repr, self._qubits) def density_matrix(self, copy=True): """Returns the density matrix at this step in the simulation. @@ -350,24 +350,14 @@ def density_matrix(self, copy=True): parameters from the density matrix and store then using False can speed up simulation by eliminating a memory copy. """ - size = np.prod(self._qid_shape, dtype=int) - matrix = self._density_matrix.copy() if copy else self._density_matrix - return np.reshape(matrix, (size, size)) - - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - indices = [self._qubit_map[q] for q in qubits] - return density_matrix_utils.sample_density_matrix( - self._simulator_state().density_matrix, - indices, - qid_shape=self._qid_shape, - repetitions=repetitions, - seed=seed, - ) + if self._density_matrix is None: + self._density_matrix = np.array(1) + state = self._merged_sim_state + if state is not None: + matrix = state.target_tensor + size = int(np.sqrt(np.prod(matrix.shape, dtype=int))) + self._density_matrix = np.reshape(matrix, (size, size)) + return self._density_matrix.copy() if copy else self._density_matrix @value.value_equality(unhashable=True) @@ -436,22 +426,27 @@ class DensityMatrixTrialResult(simulator.SimulationTrialResult): measurement gate.) final_simulator_state: The final simulator state of the system after the trial finishes. - final_density_matrix: The final density matrix of the system. """ def __init__( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: DensityMatrixSimulatorState, + final_simulator_state: Union[DensityMatrixSimulatorState, DensityMatrixStepResult], ) -> None: super().__init__( params=params, measurements=measurements, final_simulator_state=final_simulator_state ) - size = np.prod(protocols.qid_shape(self), dtype=int) - self.final_density_matrix = np.reshape( - final_simulator_state.density_matrix.copy(), (size, size) - ) + self._final_density_matrix: Optional[np.ndarray] = None + + @property + def final_density_matrix(self): + if self._final_density_matrix is None: + size = np.prod(protocols.qid_shape(self), dtype=int) + self._final_density_matrix = np.reshape( + self._final_simulator_state.density_matrix.copy(), (size, size) + ) + return self._final_density_matrix def _value_equality_values_(self) -> Any: measurements = {k: v.tolist() for k, v in sorted(self.measurements.items())} diff --git a/cirq-core/cirq/sim/density_matrix_simulator_test.py b/cirq-core/cirq/sim/density_matrix_simulator_test.py index 846546c5bcf..1a22c2ab777 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator_test.py +++ b/cirq-core/cirq/sim/density_matrix_simulator_test.py @@ -11,9 +11,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unittest import mock import itertools +import platform import random +from typing import Type +from unittest import mock + import numpy as np import pytest import sympy @@ -67,9 +70,12 @@ def test_invalid_dtype(): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_with_ignore_measurement_results(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_with_ignore_measurement_results(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype, ignore_measurement_results=True) + simulator = cirq.DensityMatrixSimulator( + dtype=dtype, ignore_measurement_results=True, split_untangled_states=split + ) circuit = cirq.Circuit(cirq.X(q0), cirq.X(q1), cirq.measure(q0)) with pytest.raises(ValueError, match="ignore_measurement_results = True"): @@ -77,9 +83,10 @@ def test_run_with_ignore_measurement_results(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_no_measurements(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_no_measurements(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.X(q0), cirq.X(q1)) with pytest.raises(ValueError, match="no measurements"): @@ -87,9 +94,10 @@ def test_run_no_measurements(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_no_results(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_no_results(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.X(q0), cirq.X(q1)) with pytest.raises(ValueError, match="no measurements"): @@ -97,16 +105,18 @@ def test_run_no_results(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_empty_circuit(dtype): - simulator = cirq.DensityMatrixSimulator(dtype=dtype) +@pytest.mark.parametrize('split', [True, False]) +def test_run_empty_circuit(dtype: Type[np.number], split: bool): + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with pytest.raises(ValueError, match="no measurements"): simulator.run(cirq.Circuit()) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_bit_flips(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_bit_flips(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -117,9 +127,10 @@ def test_run_bit_flips(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_bit_flips_with_dephasing(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_bit_flips_with_dephasing(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -130,9 +141,10 @@ def test_run_bit_flips_with_dephasing(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_qudit_increments(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_qudit_increments(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((3, 4)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1, 2]: for b1 in [0, 1, 2, 3]: circuit = cirq.Circuit( @@ -146,7 +158,8 @@ def test_run_qudit_increments(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_not_channel_op(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_not_channel_op(dtype: Type[np.number], split: bool): class BadOp(cirq.Operation): def __init__(self, qubits): self._qubits = qubits @@ -160,17 +173,18 @@ def with_qubits(self, *new_qubits): return BadOp(self._qubits) q0 = cirq.LineQubit(0) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit([BadOp([q0])]) with pytest.raises(TypeError): simulator.simulate(circuit) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_mixture(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_mixture(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.bit_flip(0.5)(q0), cirq.measure(q0), cirq.measure(q1)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) result = simulator.run(circuit, repetitions=100) np.testing.assert_equal(result.measurements['1'], [[0]] * 100) # Test that we get at least one of each result. Probability of this test @@ -180,11 +194,12 @@ def test_run_mixture(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_qudit_mixture(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_qudit_mixture(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((3, 2)) mixture = _TestMixture([PlusGate(3, 0), PlusGate(3, 1), PlusGate(3, 2)]) circuit = cirq.Circuit(mixture(q0), cirq.measure(q0), cirq.measure(q1)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) result = simulator.run(circuit, repetitions=100) np.testing.assert_equal(result.measurements['1 (d=2)'], [[0]] * 100) # Test that we get at least one of each result. Probability of this test @@ -194,13 +209,14 @@ def test_run_qudit_mixture(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_channel(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_channel(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit( cirq.X(q0), cirq.amplitude_damp(0.5)(q0), cirq.measure(q0), cirq.measure(q1) ) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) result = simulator.run(circuit, repetitions=100) np.testing.assert_equal(result.measurements['1'], [[0]] * 100) # Test that we get at least one of each result. Probability of this test @@ -210,7 +226,8 @@ def test_run_channel(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_decomposable_channel(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_decomposable_channel(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit( @@ -225,7 +242,7 @@ def test_run_decomposable_channel(dtype): cirq.measure(q1), ) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) result = simulator.run(circuit, repetitions=100) np.testing.assert_equal(result.measurements['1'], [[0]] * 100) # Test that we get at least one of each result. Probability of this test @@ -235,7 +252,8 @@ def test_run_decomposable_channel(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_qudit_channel(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_qudit_channel(dtype: Type[np.number], split: bool): class TestChannel(cirq.Gate): def _qid_shape_(self): return (3,) @@ -256,7 +274,7 @@ def _kraus_(self): cirq.measure(q1), ) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) result = simulator.run(circuit, repetitions=100) np.testing.assert_equal(result.measurements['1 (d=4)'], [[0]] * 100) # Test that we get at least one of each result. Probability of this test @@ -266,9 +284,10 @@ def _kraus_(self): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measure_at_end_no_repetitions(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measure_at_end_no_repetitions(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -284,9 +303,10 @@ def test_run_measure_at_end_no_repetitions(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_repetitions_measure_at_end(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_repetitions_measure_at_end(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -300,9 +320,10 @@ def test_run_repetitions_measure_at_end(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_qudits_repetitions_measure_at_end(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_qudits_repetitions_measure_at_end(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: @@ -318,9 +339,10 @@ def test_run_qudits_repetitions_measure_at_end(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measurement_not_terminal_no_repetitions(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measurement_not_terminal_no_repetitions(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -341,9 +363,10 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_repetitions_measurement_not_terminal(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_repetitions_measurement_not_terminal(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -362,9 +385,10 @@ def test_run_repetitions_measurement_not_terminal(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_qudits_repetitions_measurement_not_terminal(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_qudits_repetitions_measurement_not_terminal(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: @@ -385,9 +409,10 @@ def test_run_qudits_repetitions_measurement_not_terminal(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_param_resolver(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_param_resolver(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -397,15 +422,17 @@ def test_run_param_resolver(dtype): cirq.measure(q1), ) param_resolver = {'b0': b0, 'b1': b1} - result = simulator.run(circuit, param_resolver=param_resolver) + result = simulator.run(circuit, param_resolver=param_resolver) # type: ignore np.testing.assert_equal(result.measurements, {'0': [[b0]], '1': [[b1]]}) - np.testing.assert_equal(result.params, cirq.ParamResolver(param_resolver)) + # pylint: disable=line-too-long + np.testing.assert_equal(result.params, cirq.ParamResolver(param_resolver)) # type: ignore @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_correlations(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_correlations(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.measure(q0, q1)) for _ in range(10): result = simulator.run(circuit) @@ -414,9 +441,10 @@ def test_run_correlations(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measure_multiple_qubits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measure_multiple_qubits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1), cirq.measure(q0, q1)) @@ -425,9 +453,10 @@ def test_run_measure_multiple_qubits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measure_multiple_qudits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measure_multiple_qudits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit((cirq.X ** b0)(q0), PlusGate(3, b1)(q1), cirq.measure(q0, q1)) @@ -436,9 +465,10 @@ def test_run_measure_multiple_qudits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_sweeps_param_resolvers(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_sweeps_param_resolvers(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -461,9 +491,10 @@ def test_run_sweeps_param_resolvers(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_no_circuit(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_no_circuit(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit() result = simulator.simulate(circuit, qubit_order=[q0, q1]) expected = np.zeros((4, 4)) @@ -473,9 +504,10 @@ def test_simulate_no_circuit(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1)) result = simulator.simulate(circuit, qubit_order=[q0, q1]) np.testing.assert_almost_equal(result.final_density_matrix, np.ones((4, 4)) * 0.25) @@ -483,9 +515,10 @@ def test_simulate(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_qudits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_qudits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.H(q0), PlusGate(3, 2)(q1)) result = simulator.simulate(circuit, qubit_order=[q1, q0]) expected = np.zeros((6, 6)) @@ -501,7 +534,7 @@ def test_simulate_qudits(dtype): [cirq.testing.random_circuit(cirq.LineQubit.range(4), 5, 0.9) for _ in range(20)], ), ) -def test_simulate_compare_to_state_vector_simulator(dtype, circuit): +def test_simulate_compare_to_state_vector_simulator(dtype: Type[np.number], circuit): qubits = cirq.LineQubit.range(4) pure_result = ( cirq.Simulator(dtype=dtype).simulate(circuit, qubit_order=qubits).density_matrix_of() @@ -512,13 +545,14 @@ def test_simulate_compare_to_state_vector_simulator(dtype, circuit): .final_density_matrix ) assert mixed_result.shape == (16, 16) - np.testing.assert_almost_equal(mixed_result, pure_result) + np.testing.assert_almost_equal(mixed_result, pure_result, decimal=6) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_bit_flips(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_bit_flips(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -532,9 +566,10 @@ def test_simulate_bit_flips(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_qudit_increments(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_qudit_increments(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -548,9 +583,10 @@ def test_simulate_qudit_increments(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_initial_state(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_initial_state(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) @@ -561,9 +597,10 @@ def test_simulate_initial_state(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_act_on_args(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_act_on_args(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) @@ -587,9 +624,10 @@ def test_simulate_tps_initial_state(): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_initial_qudit_state(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_initial_qudit_state(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((3, 4)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1, 2]: for b1 in [0, 1, 2, 3]: circuit = cirq.Circuit( @@ -605,9 +643,10 @@ def test_simulate_initial_qudit_state(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_qubit_order(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_qubit_order(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) @@ -618,9 +657,10 @@ def test_simulate_qubit_order(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_param_resolver(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_param_resolver(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -636,9 +676,10 @@ def test_simulate_param_resolver(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_measure_multiple_qubits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_measure_multiple_qubits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1), cirq.measure(q0, q1)) @@ -647,9 +688,10 @@ def test_simulate_measure_multiple_qubits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_measure_multiple_qudits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_measure_multiple_qudits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit((cirq.X ** b0)(q0), PlusGate(3, b1)(q1), cirq.measure(q0, q1)) @@ -658,9 +700,10 @@ def test_simulate_measure_multiple_qudits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_sweeps_param_resolver(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_sweeps_param_resolver(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -684,20 +727,22 @@ def test_simulate_sweeps_param_resolver(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1), cirq.H(q0), cirq.H(q1)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): assert cirq.qid_shape(step) == (2, 2) if i == 0: np.testing.assert_almost_equal(step.density_matrix(), np.ones((4, 4)) / 4) else: - np.testing.assert_almost_equal(step.density_matrix(), np.diag([1, 0, 0, 0])) + np.testing.assert_almost_equal(step.density_matrix(), np.diag([1, 0, 0, 0]), decimal=6) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_qudits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_qudits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) circuit = cirq.Circuit( PlusGate(2, 1)(q0), @@ -705,7 +750,7 @@ def test_simulate_moment_steps_qudits(dtype): cirq.reset(q1), PlusGate(3, 1)(q1), ) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): assert cirq.qid_shape(step) == (2, 3) if i == 0: @@ -717,22 +762,20 @@ def test_simulate_moment_steps_qudits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_empty_circuit(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_empty_circuit(dtype: Type[np.number], split: bool): circuit = cirq.Circuit() - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) step = None for step in simulator.simulate_moment_steps(circuit): pass assert step._simulator_state() == cirq.DensityMatrixSimulatorState( - density_matrix=np.array( - 1, - ), - qubit_map={}, + density_matrix=np.array([[1]]), qubit_map={} ) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_set_state(dtype): +def test_simulate_moment_steps_set_state(dtype: Type[np.number]): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1), cirq.H(q0), cirq.H(q1)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) @@ -745,10 +788,11 @@ def test_simulate_moment_steps_set_state(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_sample(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_sample(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): if i == 0: samples = step.sample([q0, q1], repetitions=10) @@ -765,7 +809,8 @@ def test_simulate_moment_steps_sample(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_sample_qudits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_sample_qudits(dtype: Type[np.number], split: bool): class TestGate(cirq.Gate): """Swaps the 2nd qid |0> and |2> states when the 1st is |1>.""" @@ -780,7 +825,7 @@ def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) circuit = cirq.Circuit(cirq.H(q0), TestGate()(q0, q1)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): if i == 0: samples = step.sample([q0, q1], repetitions=10) @@ -793,10 +838,11 @@ def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_intermediate_measurement(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_intermediate_measurement(dtype: Type[np.number], split: bool): q0 = cirq.LineQubit(0) circuit = cirq.Circuit(cirq.H(q0), cirq.measure(q0), cirq.H(q0)) - simulator = cirq.DensityMatrixSimulator(dtype=dtype) + simulator = cirq.DensityMatrixSimulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): if i == 1: result = int(step.measurements['0'][0]) @@ -1439,7 +1485,7 @@ def test_measuring_subcircuits_cause_sweep_repeat(): def test_density_matrix_copy(): - sim = cirq.DensityMatrixSimulator() + sim = cirq.DensityMatrixSimulator(split_untangled_states=False) q = cirq.LineQubit(0) circuit = cirq.Circuit(cirq.H(q), cirq.H(q)) @@ -1473,3 +1519,37 @@ def test_final_density_matrix_is_not_last_object(): assert result.final_density_matrix is not initial_state assert not np.shares_memory(result.final_density_matrix, initial_state) np.testing.assert_equal(result.final_density_matrix, initial_state) + + +def test_density_matrices_same_with_or_without_split_untangled_states(): + sim = cirq.DensityMatrixSimulator(split_untangled_states=False) + q0, q1 = cirq.LineQubit.range(2) + circuit = cirq.Circuit(cirq.H(q0), cirq.CX.on(q0, q1), cirq.reset(q1)) + result1 = sim.simulate(circuit).final_density_matrix + sim = cirq.DensityMatrixSimulator(split_untangled_states=True) + result2 = sim.simulate(circuit).final_density_matrix + assert np.allclose(result1, result2) + + +def test_large_untangled_okay(): + circuit = cirq.Circuit() + for i in range(59): + for _ in range(9): + circuit.append(cirq.X(cirq.LineQubit(i))) + circuit.append(cirq.measure(cirq.LineQubit(i))) + + # Validate this can't be allocated with entangled state + if platform.system() != "Windows": + with pytest.raises(MemoryError, match='Unable to allocate'): + _ = cirq.DensityMatrixSimulator(split_untangled_states=False).simulate(circuit) + + # Validate a simulation run + result = cirq.DensityMatrixSimulator(split_untangled_states=True).simulate(circuit) + assert set(result._step_result_or_state._qubits) == set(cirq.LineQubit.range(59)) + # _ = result.final_density_matrix hangs (as expected) + + # Validate a trial run and sampling + result = cirq.DensityMatrixSimulator(split_untangled_states=True).run(circuit, repetitions=1000) + assert len(result.measurements) == 59 + assert len(result.measurements['0']) == 1000 + assert (result.measurements['0'] == np.full(1000, 1)).all() diff --git a/cirq-core/cirq/sim/density_matrix_utils_test.py b/cirq-core/cirq/sim/density_matrix_utils_test.py index 020fa4a47a8..da8d672a04a 100644 --- a/cirq-core/cirq/sim/density_matrix_utils_test.py +++ b/cirq-core/cirq/sim/density_matrix_utils_test.py @@ -348,8 +348,24 @@ def test_measure_state_empty_density_matrix(): @pytest.mark.parametrize('seed', [17, 35, 48]) -def test_to_valid_density_matrix_on_simulator_output(seed): +@pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) +@pytest.mark.parametrize('split', [False, True]) +def test_to_valid_density_matrix_on_simulator_output(seed, dtype, split): circuit = cirq.testing.random_circuit(qubits=5, n_moments=20, op_density=0.9, random_state=seed) - simulator = cirq.DensityMatrixSimulator() + simulator = cirq.DensityMatrixSimulator(split_untangled_states=split, dtype=dtype) result = simulator.simulate(circuit) - _ = cirq.to_valid_density_matrix(result.final_density_matrix, num_qubits=5) + _ = cirq.to_valid_density_matrix(result.final_density_matrix, num_qubits=5, atol=1e-6) + + +def test_factor_validation(): + args = cirq.DensityMatrixSimulator()._create_act_on_args(0, qubits=cirq.LineQubit.range(2)) + args.apply_operation(cirq.H(cirq.LineQubit(0))) + t = args.create_merged_state().target_tensor + cirq.linalg.transformations.factor_density_matrix(t, [0]) + cirq.linalg.transformations.factor_density_matrix(t, [1]) + args.apply_operation(cirq.CNOT(cirq.LineQubit(0), cirq.LineQubit(1))) + t = args.create_merged_state().target_tensor + with pytest.raises(ValueError, match='factor'): + cirq.linalg.transformations.factor_density_matrix(t, [0]) + with pytest.raises(ValueError, match='factor'): + cirq.linalg.transformations.factor_density_matrix(t, [1]) diff --git a/cirq-core/cirq/sim/operation_target.py b/cirq-core/cirq/sim/operation_target.py new file mode 100644 index 00000000000..dcba5c7899f --- /dev/null +++ b/cirq-core/cirq/sim/operation_target.py @@ -0,0 +1,69 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""An interface for quantum states as targets for operations.""" +import abc +from typing import TypeVar, TYPE_CHECKING, Generic, Dict, Any, Tuple, Optional, Iterator, List + +import numpy as np + +if TYPE_CHECKING: + import cirq + + +TSelfTarget = TypeVar('TSelfTarget', bound='OperationTarget') +TActOnArgs = TypeVar('TActOnArgs', bound='cirq.ActOnArgs') + + +class OperationTarget(Generic[TActOnArgs], metaclass=abc.ABCMeta): + """An interface for quantum states as targets for operations.""" + + @abc.abstractmethod + def create_merged_state(self) -> TActOnArgs: + """Creates a final merged state.""" + + @abc.abstractmethod + def apply_operation(self, op: 'cirq.Operation'): + """Applies the operation to the state.""" + + @abc.abstractmethod + def copy(self: TSelfTarget) -> TSelfTarget: + """Copies the object.""" + + @property + @abc.abstractmethod + def qubits(self) -> Tuple['cirq.Qid', ...]: + """Gets the qubit order maintained by this target.""" + + @property + @abc.abstractmethod + def log_of_measurement_results(self) -> Dict[str, Any]: + """Gets the log of measurement results.""" + + @abc.abstractmethod + def sample( + self, + qubits: List['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + """Samples the state value.""" + + def __getitem__(self, item: Optional['cirq.Qid']) -> TActOnArgs: + """Gets the item associated with the qubit.""" + + def __len__(self) -> int: + """Gets the number of items in the mapping.""" + + def __iter__(self) -> Iterator[Optional['cirq.Qid']]: + """Iterates the keys of the mapping.""" diff --git a/cirq-core/cirq/sim/simulator.py b/cirq-core/cirq/sim/simulator.py index f3152a8c939..c445c01d71c 100644 --- a/cirq-core/cirq/sim/simulator.py +++ b/cirq-core/cirq/sim/simulator.py @@ -495,7 +495,7 @@ class SimulatesIntermediateState( state at the end of a circuit, a SimulatesIntermediateState can simulate stepping through the moments of a circuit. - Implementors of this interface should implement the _base_iterator + Implementors of this interface should implement the _core_iterator method. Note that state here refers to simulator state, which is not necessarily @@ -522,7 +522,7 @@ def simulate_sweep_iter( is often used in specifying the initial state, i.e. the ordering of the computational basis states. initial_state: The initial state for the simulation. This can be - either a raw state or a `TActOnArgs`. The form of the + either a raw state or an `OperationTarget`. The form of the raw state depends on the simulation implementation. See documentation of the implementing class for details. @@ -542,7 +542,7 @@ def simulate_sweep_iter( yield self._create_simulator_trial_result( params=param_resolver, measurements=measurements, - final_simulator_state=step_result._simulator_state(), + final_simulator_state=step_result, ) def simulate_moment_steps( @@ -613,8 +613,8 @@ def _create_act_on_args( self, initial_state: Any, qubits: Sequence['cirq.Qid'], - ) -> TActOnArgs: - """Creates the ActOnArgs state for a simulator. + ) -> 'cirq.OperationTarget[TActOnArgs]': + """Creates the OperationTarget state for a simulator. Custom simulators should implement this method. @@ -627,15 +627,15 @@ def _create_act_on_args( ordering of the computational basis states. Returns: - The ActOnArgs for this simulator. + The `OperationTarget` for this simulator. """ - raise NotImplementedError() @abc.abstractmethod def _core_iterator( self, circuit: circuits.Circuit, - sim_state: TActOnArgs, + sim_state: 'cirq.OperationTarget[TActOnArgs]', + all_measurements_are_terminal: bool = False, ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. @@ -650,14 +650,13 @@ def _core_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ - raise NotImplementedError() @abc.abstractmethod def _create_simulator_trial_result( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: TSimulatorState, + final_simulator_state: Union[TStepResult, TSimulatorState], ) -> TSimulationTrialResult: """This method can be implemented to create a trial result. @@ -819,7 +818,7 @@ def __init__( ) -> None: self.params = params self.measurements = measurements - self._final_simulator_state = final_simulator_state + self._step_result_or_state = final_simulator_state def __repr__(self) -> str: return ( @@ -828,6 +827,14 @@ def __repr__(self) -> str: f'final_simulator_state={self._final_simulator_state!r})' ) + @property + def _final_simulator_state(self): + if isinstance(self._step_result_or_state, StepResult) or hasattr( + self._step_result_or_state, "_simulator_state" + ): + self._step_result_or_state = self._step_result_or_state._simulator_state() + return self._step_result_or_state + def __str__(self) -> str: def bitstring(vals): separator = ' ' if np.max(vals) >= 10 else '' diff --git a/cirq-core/cirq/sim/simulator_base.py b/cirq-core/cirq/sim/simulator_base.py index 5dbaf1f97be..e1d64a46d7a 100644 --- a/cirq-core/cirq/sim/simulator_base.py +++ b/cirq-core/cirq/sim/simulator_base.py @@ -26,18 +26,23 @@ cast, Generic, Type, + Sequence, + Optional, + TypeVar, ) import numpy as np from cirq import circuits, ops, protocols, study, value, devices +from cirq.sim import ActOnArgsContainer +from cirq.sim.operation_target import OperationTarget from cirq.sim.simulator import ( - TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs, SimulatesIntermediateState, SimulatesSamples, + StepResult, check_all_resolved, split_into_matching_protocol_then_general, ) @@ -46,18 +51,23 @@ import cirq +TStepResultBase = TypeVar('TStepResultBase', bound='StepResultBase') + + class SimulatorBase( - Generic[TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs], - SimulatesIntermediateState[TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs], + Generic[TStepResultBase, TSimulationTrialResult, TSimulatorState, TActOnArgs], + SimulatesIntermediateState[ + TStepResultBase, TSimulationTrialResult, TSimulatorState, TActOnArgs + ], SimulatesSamples, metaclass=abc.ABCMeta, ): """A base class for the built-in simulators. Most implementors of this interface should implement the - `_create_act_on_args` and `_create_step_result` methods. The first one - creates the simulator's quantum state representation at the beginning of - the simulation. The second creates the step result emitted after each + `_create_partial_act_on_args` and `_create_step_result` methods. The first + one creates the simulator's quantum state representation at the beginning + of the simulation. The second creates the step result emitted after each `Moment` in the simulation. Iteration in the subclass is handled by the `_core_iterator` implementation @@ -86,36 +96,57 @@ def __init__( noise: 'cirq.NOISE_MODEL_LIKE' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ignore_measurement_results: bool = False, + split_untangled_states: bool = False, ): """Initializes the simulator. Args: - dtype: The `numpy.dtype` used by the simulation. - noise: A noise model to apply while simulating. - seed: The random seed to use for this simulator. - ignore_measurement_results: If True, then the simulation - will treat measurement as dephasing instead of collapsing - process. This is only applicable to simulators that can - model dephasing. + dtype: The `numpy.dtype` used by the simulation. + noise: A noise model to apply while simulating. + seed: The random seed to use for this simulator. + ignore_measurement_results: If True, then the simulation + will treat measurement as dephasing instead of collapsing + process. This is only applicable to simulators that can + model dephasing. + split_untangled_states: If True, optimizes simulation by running + unentangled qubit sets independently and merging those states + at the end. """ self._dtype = dtype self._prng = value.parse_random_state(seed) self.noise = devices.NoiseModel.from_noise_model_like(noise) self._ignore_measurement_results = ignore_measurement_results + self._split_untangled_states = split_untangled_states + + @abc.abstractmethod + def _create_partial_act_on_args( + self, + initial_state: Any, + qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], + ) -> TActOnArgs: + """Creates an instance of the TActOnArgs class for the simulator. + + It represents the supplied qubits initialized to the provided state. + + Args: + initial_state: The initial state to represent. An integer state is + understood to be a pure state. Other state representations are + simulator-dependent. + qubits: The sequence of qubits to represent. + logs: The structure to hold measurement logs. A single instance + should be shared among all ActOnArgs within the simulation. + """ @abc.abstractmethod def _create_step_result( self, - sim_state: TActOnArgs, - qubit_map: Dict['cirq.Qid', int], - ) -> TStepResult: + sim_state: OperationTarget[TActOnArgs], + ) -> TStepResultBase: """This method should be implemented to create a step result. Args: - sim_state: The TActOnArgs for this trial. - qubit_map: Determines the canonical ordering of the qubits. This - is often used in specifying the initial state, i.e. the - ordering of the computational basis states. + sim_state: The OperationTarget for this trial. Returns: The StepResult. @@ -146,9 +177,9 @@ def _can_be_in_run_prefix(self, val: Any): def _core_iterator( self, circuit: circuits.Circuit, - sim_state: TActOnArgs, + sim_state: OperationTarget[TActOnArgs], all_measurements_are_terminal: bool = False, - ) -> Iterator[TStepResult]: + ) -> Iterator[TStepResultBase]: """Standard iterator over StepResult from Moments of a Circuit. Args: @@ -160,17 +191,20 @@ def _core_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ + if len(circuit) == 0: - yield self._create_step_result(sim_state, sim_state.qubit_map) + yield self._create_step_result(sim_state) return noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) - measured: Dict[Tuple[cirq.Qid, ...], bool] = collections.defaultdict(bool) + measured: Dict[Tuple['cirq.Qid', ...], bool] = collections.defaultdict(bool) for moment in noisy_moments: for op in ops.flatten_to_ops(moment): try: # TODO: support more general measurements. # Github issue: https://github.com/quantumlib/Cirq/issues/3566 + + # Preprocess measurements if all_measurements_are_terminal and measured[op.qubits]: continue if isinstance(op.gate, ops.MeasurementGate): @@ -179,11 +213,15 @@ def _core_iterator( continue if self._ignore_measurement_results: op = ops.phase_damp(1).on(*op.qubits) - protocols.act_on(op, sim_state) + + # Simulate the operation + sim_state.apply_operation(op) except TypeError: raise TypeError(f"{self.__class__.__name__} doesn't support {op!r}") - yield self._create_step_result(sim_state, sim_state.qubit_map) + step_result = self._create_step_result(sim_state) + yield step_result + sim_state = step_result._sim_state sim_state.log_of_measurement_results.clear() def _run( @@ -224,10 +262,10 @@ def _run( return step_result.sample_measurement_ops(measurement_ops, repetitions, seed=self._prng) measurements: Dict[str, List[np.ndarray]] = {} - for _ in range(repetitions): + for i in range(repetitions): all_step_results = self._core_iterator( general_suffix, - sim_state=act_on_args.copy(), + sim_state=act_on_args.copy() if i < repetitions - 1 else act_on_args, ) for step_result in all_step_results: for k, v in step_result.measurements.items(): @@ -235,3 +273,77 @@ def _run( measurements[k] = [] measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} + + def _create_act_on_args( + self, + initial_state: Any, + qubits: Sequence['cirq.Qid'], + ) -> OperationTarget[TActOnArgs]: + if isinstance(initial_state, OperationTarget): + return initial_state + + log: Dict[str, Any] = {} + if self._split_untangled_states: + args_map: Dict[Optional['cirq.Qid'], TActOnArgs] = {} + if isinstance(initial_state, int): + for q in reversed(qubits): + args_map[q] = self._create_partial_act_on_args( + initial_state=initial_state % q.dimension, + qubits=[q], + logs=log, + ) + initial_state = int(initial_state / q.dimension) + else: + args = self._create_partial_act_on_args( + initial_state=initial_state, + qubits=qubits, + logs=log, + ) + for q in qubits: + args_map[q] = args + args_map[None] = self._create_partial_act_on_args(0, (), log) + return ActOnArgsContainer(args_map, qubits, self._split_untangled_states, log) + else: + return self._create_partial_act_on_args( + initial_state=initial_state, + qubits=qubits, + logs=log, + ) + + +class StepResultBase(Generic[TSimulatorState, TActOnArgs], StepResult[TSimulatorState], abc.ABC): + """A base class for step results.""" + + def __init__( + self, + sim_state: OperationTarget[TActOnArgs], + ): + """Initializes the step result. + + Args: + sim_state: The `OperationTarget` for this step. + """ + self._sim_state = sim_state + self._merged_sim_state_cache: Optional[TActOnArgs] = None + super().__init__(sim_state.log_of_measurement_results) + qubits = sim_state.qubits + self._qubits = qubits + self._qubit_mapping = {q: i for i, q in enumerate(qubits)} + self._qubit_shape = tuple(q.dimension for q in qubits) + + def _qid_shape_(self): + return self._qubit_shape + + @property + def _merged_sim_state(self): + if self._merged_sim_state_cache is None: + self._merged_sim_state_cache = self._sim_state.create_merged_state() + return self._merged_sim_state_cache + + def sample( + self, + qubits: List[ops.Qid], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + return self._sim_state.sample(qubits, repetitions, seed) diff --git a/cirq-core/cirq/sim/simulator_base_test.py b/cirq-core/cirq/sim/simulator_base_test.py index 4d3a2048a6f..84853987265 100644 --- a/cirq-core/cirq/sim/simulator_base_test.py +++ b/cirq-core/cirq/sim/simulator_base_test.py @@ -11,7 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Dict, Any, Sequence +import math +from typing import List, Dict, Any, Sequence, Tuple, Union import numpy as np import pytest @@ -23,6 +24,13 @@ class CountingActOnArgs(cirq.ActOnArgs): gate_count = 0 measurement_count = 0 + def __init__(self, state, qubits, logs): + super().__init__( + qubits=qubits, + log_of_measurement_results=logs, + ) + self.state = state + def _perform_measurement(self, qubits: Sequence['cirq.Qid']) -> List[int]: self.measurement_count += 1 return [self.gate_count] @@ -30,8 +38,8 @@ def _perform_measurement(self, qubits: Sequence['cirq.Qid']) -> List[int]: def copy(self) -> 'CountingActOnArgs': args = CountingActOnArgs( qubits=self.qubits, - prng=self.prng, - log_of_measurement_results=self.log_of_measurement_results.copy(), + logs=self.log_of_measurement_results.copy(), + state=self.state, ) args.gate_count = self.gate_count args.measurement_count = self.measurement_count @@ -41,17 +49,58 @@ def _act_on_fallback_(self, action: Any, qubits: Sequence['cirq.Qid'], allow_dec self.gate_count += 1 return True + def sample(self, qubits, repetitions=1, seed=None): + pass -class CountingStepResult(cirq.StepResult[CountingActOnArgs]): - def __init__( + +class SplittableCountingActOnArgs(CountingActOnArgs): + def kronecker_product( + self, other: 'SplittableCountingActOnArgs' + ) -> 'SplittableCountingActOnArgs': + args = SplittableCountingActOnArgs( + qubits=self.qubits + other.qubits, + logs=self.log_of_measurement_results, + state=None, + ) + args.gate_count = self.gate_count + other.gate_count + args.measurement_count = self.measurement_count + other.measurement_count + return args + + def factor( self, - sim_state: CountingActOnArgs, - qubit_map: Dict[cirq.Qid, int], - ): - super().__init__(measurements=sim_state.log_of_measurement_results.copy()) - self.sim_state = sim_state - self.qubit_map = qubit_map + qubits: Sequence['cirq.Qid'], + *, + validate=True, + atol=1e-07, + ) -> Tuple['SplittableCountingActOnArgs', 'SplittableCountingActOnArgs']: + extracted_args = SplittableCountingActOnArgs( + qubits=qubits, + logs=self.log_of_measurement_results, + state=None, + ) + extracted_args.gate_count = self.gate_count + extracted_args.measurement_count = self.measurement_count + remainder_args = SplittableCountingActOnArgs( + qubits=tuple(q for q in self.qubits if q not in qubits), + logs=self.log_of_measurement_results, + state=None, + ) + return extracted_args, remainder_args + + def transpose_to_qubit_order( + self, qubits: Sequence['cirq.Qid'] + ) -> 'SplittableCountingActOnArgs': + args = SplittableCountingActOnArgs( + qubits=qubits, + logs=self.log_of_measurement_results, + state=self.state, + ) + args.gate_count = self.gate_count + args.measurement_count = self.measurement_count + return args + +class CountingStepResult(cirq.StepResultBase[CountingActOnArgs, CountingActOnArgs]): def sample( self, qubits: List[cirq.Qid], @@ -60,11 +109,11 @@ def sample( ) -> np.ndarray: measurements: List[List[int]] = [] for _ in range(repetitions): - measurements.append(self.sim_state._perform_measurement(qubits)) + measurements.append(self._merged_sim_state._perform_measurement(qubits)) return np.array(measurements, dtype=int) def _simulator_state(self) -> CountingActOnArgs: - return self.sim_state + return self._merged_sim_state class CountingTrialResult(cirq.SimulationTrialResult): @@ -76,27 +125,53 @@ class CountingSimulator( CountingStepResult, CountingTrialResult, CountingActOnArgs, CountingActOnArgs ] ): - def _create_act_on_args( + def __init__(self, noise=None, split_untangled_states=False): + super().__init__( + noise=noise, + split_untangled_states=split_untangled_states, + ) + + def _create_partial_act_on_args( self, initial_state: Any, - qubits: Sequence[cirq.Qid], + qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], ) -> CountingActOnArgs: - return CountingActOnArgs(cirq.value.parse_random_state(0), qubits) + return CountingActOnArgs(qubits=qubits, state=initial_state, logs=logs) def _create_simulator_trial_result( self, params: cirq.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: CountingActOnArgs, + final_simulator_state: Union[CountingStepResult, CountingActOnArgs], ) -> CountingTrialResult: return CountingTrialResult(params, measurements, final_simulator_state) def _create_step_result( self, - sim_state: CountingActOnArgs, - qubit_map: Dict[cirq.Qid, int], + sim_state: cirq.OperationTarget[CountingActOnArgs], ) -> CountingStepResult: - return CountingStepResult(sim_state, qubit_map) + return CountingStepResult(sim_state) + + +class SplittableCountingSimulator(CountingSimulator): + def __init__(self, noise=None, split_untangled_states=True): + super().__init__( + noise=noise, + split_untangled_states=split_untangled_states, + ) + + def _create_partial_act_on_args( + self, + initial_state: Any, + qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], + ) -> CountingActOnArgs: + return SplittableCountingActOnArgs(qubits=qubits, state=initial_state, logs=logs) + + +q0, q1 = cirq.LineQubit.range(2) +entangled_state_repr = np.array([[math.sqrt(0.5), 0], [0, math.sqrt(0.5)]]) class TestOp(cirq.Operation): @@ -108,13 +183,11 @@ def qubits(self): return [q0] -q0 = cirq.LineQubit(0) - - def test_simulate_empty_circuit(): sim = CountingSimulator() r = sim.simulate(cirq.Circuit()) assert r._final_simulator_state.gate_count == 0 + assert r._final_simulator_state.measurement_count == 0 def test_simulate_one_gate_circuit(): @@ -191,3 +264,108 @@ def test_run_non_terminal_measurement(): sim = CountingSimulator() r = sim.run(cirq.Circuit(cirq.X(q0), cirq.measure(q0), cirq.X(q0)), repetitions=2) assert np.allclose(r.measurements['0'], [[1], [1]]) + + +def test_integer_initial_state_is_split(): + sim = SplittableCountingSimulator() + args = sim._create_act_on_args(2, (q0, q1)) + assert len(set(args.values())) == 3 + assert args[q0] is not args[q1] + assert args[q0].state == 1 + assert args[q1].state == 0 + assert args[None].state == 0 + + +def test_integer_initial_state_is_not_split_if_disabled(): + sim = SplittableCountingSimulator(split_untangled_states=False) + args = sim._create_act_on_args(2, (q0, q1)) + assert isinstance(args, SplittableCountingActOnArgs) + assert args[q0] is args[q1] + assert args.state == 2 + + +def test_integer_initial_state_is_not_split_if_impossible(): + sim = CountingSimulator() + args = sim._create_act_on_args(2, (q0, q1)) + assert isinstance(args, CountingActOnArgs) + assert not isinstance(args, SplittableCountingActOnArgs) + assert args[q0] is args[q1] + assert args.state == 2 + + +def test_non_integer_initial_state_is_not_split(): + sim = SplittableCountingSimulator() + args = sim._create_act_on_args(entangled_state_repr, (q0, q1)) + assert len(set(args.values())) == 2 + assert (args[q0].state == entangled_state_repr).all() + assert args[q1] is args[q0] + assert args[None].state == 0 + + +def test_entanglement_causes_join(): + sim = SplittableCountingSimulator() + args = sim._create_act_on_args(2, (q0, q1)) + assert len(set(args.values())) == 3 + args.apply_operation(cirq.CNOT(q0, q1)) + assert len(set(args.values())) == 2 + assert args[q0] is args[q1] + assert args[None] is not args[q0] + + +def test_measurement_causes_split(): + sim = SplittableCountingSimulator() + args = sim._create_act_on_args(entangled_state_repr, (q0, q1)) + assert len(set(args.values())) == 2 + args.apply_operation(cirq.measure(q0)) + assert len(set(args.values())) == 3 + assert args[q0] is not args[q1] + assert args[q0] is not args[None] + + +def test_measurement_does_not_split_if_disabled(): + sim = SplittableCountingSimulator(split_untangled_states=False) + args = sim._create_act_on_args(2, (q0, q1)) + assert isinstance(args, SplittableCountingActOnArgs) + args.apply_operation(cirq.measure(q0)) + assert isinstance(args, SplittableCountingActOnArgs) + assert args[q0] is args[q1] + + +def test_measurement_does_not_split_if_impossible(): + sim = CountingSimulator() + args = sim._create_act_on_args(2, (q0, q1)) + assert isinstance(args, CountingActOnArgs) + assert not isinstance(args, SplittableCountingActOnArgs) + args.apply_operation(cirq.measure(q0)) + assert isinstance(args, CountingActOnArgs) + assert not isinstance(args, SplittableCountingActOnArgs) + assert args[q0] is args[q1] + + +def test_reorder_succeeds(): + sim = SplittableCountingSimulator() + args = sim._create_act_on_args(entangled_state_repr, (q0, q1)) + reordered = args[q0].transpose_to_qubit_order([q1, q0]) + assert reordered.qubits == (q1, q0) + + +@pytest.mark.parametrize('split', [True, False]) +def test_sim_state_instance_unchanged_during_normal_sim(split: bool): + sim = SplittableCountingSimulator(split_untangled_states=split) + args = sim._create_act_on_args(0, (q0, q1)) + circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.reset(q1)) + for step in sim.simulate_moment_steps(circuit, initial_state=args): + assert step._sim_state is args + assert (step._merged_sim_state is not args) == split + + +@pytest.mark.parametrize('split', [True, False]) +def test_sim_state_instance_gets_changes_from_step_result(split: bool): + sim = SplittableCountingSimulator(split_untangled_states=split) + args = sim._create_act_on_args(0, (q0, q1)) + circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.reset(q1)) + for step in sim.simulate_moment_steps(circuit, initial_state=args): + assert step._sim_state is args + args = sim._create_act_on_args(0, (q0, q1)) + step._sim_state = args + assert (step._merged_sim_state is not args) == split diff --git a/cirq-core/cirq/sim/simulator_test.py b/cirq-core/cirq/sim/simulator_test.py index 9fee3d5650b..2acae3fd0b0 100644 --- a/cirq-core/cirq/sim/simulator_test.py +++ b/cirq-core/cirq/sim/simulator_test.py @@ -189,7 +189,7 @@ def state_vector(self): def __setstate__(self, state): pass - def sample(self, qubits, repetitions, seed): + def sample(self, qubits, repetitions=1, seed=None): return np.array([[qubit in self._ones_qubits for qubit in qubits]] * repetitions) diff --git a/cirq-core/cirq/sim/sparse_simulator.py b/cirq-core/cirq/sim/sparse_simulator.py index 7fcea5fd8c1..5cf26cd0846 100644 --- a/cirq-core/cirq/sim/sparse_simulator.py +++ b/cirq-core/cirq/sim/sparse_simulator.py @@ -23,11 +23,12 @@ TYPE_CHECKING, Union, Sequence, + Optional, ) import numpy as np -from cirq import ops, protocols, qis, devices +from cirq import ops, protocols, qis from cirq.sim import ( simulator, state_vector, @@ -37,6 +38,7 @@ if TYPE_CHECKING: import cirq + from numpy.typing import DTypeLike class Simulator( @@ -143,6 +145,7 @@ def __init__( dtype: Type[np.number] = np.complex64, noise: 'cirq.NOISE_MODEL_LIKE' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + split_untangled_states: bool = True, ): """A sparse matrix simulator. @@ -151,22 +154,24 @@ def __init__( `numpy.complex64` or `numpy.complex128`. noise: A noise model to apply while simulating. seed: The random seed to use for this simulator. + split_untangled_states: If True, optimizes simulation by running + unentangled qubit sets independently and merging those states + at the end. """ if np.dtype(dtype).kind != 'c': raise ValueError(f'dtype must be a complex type but was {dtype}') - noise_model = devices.NoiseModel.from_noise_model_like(noise) - if not protocols.has_mixture(noise_model): - raise ValueError(f'noise must be unitary or mixture but was {noise_model}') super().__init__( dtype=dtype, noise=noise, seed=seed, + split_untangled_states=split_untangled_states, ) - def _create_act_on_args( + def _create_partial_act_on_args( self, initial_state: Union['cirq.STATE_VECTOR_LIKE', 'cirq.ActOnStateVectorArgs'], qubits: Sequence['cirq.Qid'], + logs: Dict[str, Any], ): """Creates the ActOnStateVectorArgs for a circuit. @@ -183,10 +188,9 @@ def _create_act_on_args( if isinstance(initial_state, act_on_state_vector_args.ActOnStateVectorArgs): return initial_state - num_qubits = len(qubits) qid_shape = protocols.qid_shape(qubits) state = qis.to_valid_state_vector( - initial_state, num_qubits, qid_shape=qid_shape, dtype=self._dtype + initial_state, len(qubits), qid_shape=qid_shape, dtype=self._dtype ) return act_on_state_vector_args.ActOnStateVectorArgs( @@ -194,18 +198,16 @@ def _create_act_on_args( available_buffer=np.empty(qid_shape, dtype=self._dtype), qubits=qubits, prng=self._prng, - log_of_measurement_results={}, + log_of_measurement_results=logs, ) def _create_step_result( self, - sim_state: act_on_state_vector_args.ActOnStateVectorArgs, - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[cirq.ActOnStateVectorArgs]', ): return SparseSimulatorStep( - state_vector=sim_state.target_tensor, - measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + sim_state=sim_state, + simulator=self, dtype=self._dtype, ) @@ -238,29 +240,34 @@ def simulate_expectation_values_sweep_iter( class SparseSimulatorStep( - state_vector.StateVectorMixin, state_vector_simulator.StateVectorStepResult + state_vector.StateVectorMixin, + state_vector_simulator.StateVectorStepResult, ): """A `StepResult` that includes `StateVectorMixin` methods.""" - def __init__(self, state_vector, measurements, qubit_map, dtype): + def __init__( + self, + sim_state: 'cirq.OperationTarget[cirq.ActOnStateVectorArgs]', + simulator: Simulator, + dtype: 'DTypeLike' = np.complex64, + ): """Results of a step of the simulator. Args: - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). - measurements: A dictionary from measurement gate key to measurement - results, ordered by the qubits that the measurement operates on. + sim_state: The qubit:ActOnArgs lookup for this step. + simulator: The simulator used to create this. + dtype: The `numpy.dtype` used by the simulation. One of + `numpy.complex64` or `numpy.complex128`. """ - super().__init__(measurements=measurements, qubit_map=qubit_map) + qubit_map = {q: i for i, q in enumerate(sim_state.qubits)} + super().__init__(sim_state=sim_state, qubit_map=qubit_map) self._dtype = dtype - size = np.prod(protocols.qid_shape(self), dtype=int) - self._state_vector = np.reshape(state_vector, size) + self._state_vector: Optional[np.ndarray] = None + self._simulator = simulator def _simulator_state(self) -> state_vector_simulator.StateVectorSimulatorState: return state_vector_simulator.StateVectorSimulatorState( - qubit_map=self.qubit_map, state_vector=self._state_vector + qubit_map=self.qubit_map, state_vector=self.state_vector(copy=False) ) def state_vector(self, copy: bool = True): @@ -296,8 +303,14 @@ def state_vector(self, copy: bool = True): parameters from the state vector and store then using False can speed up simulation by eliminating a memory copy. """ - vector = self._simulator_state().state_vector - return vector.copy() if copy else vector + if self._state_vector is None: + self._state_vector = np.array([1]) + state = self._merged_sim_state + if state is not None: + vector = state.target_tensor + size = np.prod(vector.shape, dtype=int) + self._state_vector = np.reshape(vector, size) + return self._state_vector.copy() if copy else self._state_vector def set_state_vector(self, state: 'cirq.STATE_VECTOR_LIKE'): """Set the state vector. @@ -307,27 +320,12 @@ def set_state_vector(self, state: 'cirq.STATE_VECTOR_LIKE'): will be set to lie entirely in the computation basis state for the binary expansion of the passed integer. + Note that this feature is incompatible with the simulation setting + `split_untangled_states=True`, and will throw an error if attempted. + Args: state: If an int, the state vector set is the state vector corresponding to a computational basis state. If a numpy array this is the full state vector. """ - update_state = qis.to_valid_state_vector( - state, len(self.qubit_map), qid_shape=protocols.qid_shape(self, None), dtype=self._dtype - ) - np.copyto(self._state_vector, update_state) - - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - indices = [self.qubit_map[qubit] for qubit in qubits] - return state_vector.sample_state_vector( - self._state_vector, - indices, - qid_shape=protocols.qid_shape(self, None), - repetitions=repetitions, - seed=seed, - ) + self._sim_state = self._simulator._create_act_on_args(state, self._qubits) diff --git a/cirq-core/cirq/sim/sparse_simulator_test.py b/cirq-core/cirq/sim/sparse_simulator_test.py index e7a8b9f8a83..2b2143e0db4 100644 --- a/cirq-core/cirq/sim/sparse_simulator_test.py +++ b/cirq-core/cirq/sim/sparse_simulator_test.py @@ -13,6 +13,7 @@ # limitations under the License. import itertools import random +from typing import Type from unittest import mock import numpy as np import pytest @@ -27,9 +28,10 @@ def test_invalid_dtype(): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_no_measurements(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_no_measurements(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.X(q0), cirq.X(q1)) with pytest.raises(ValueError, match="no measurements"): @@ -37,9 +39,10 @@ def test_run_no_measurements(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_no_results(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_no_results(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.X(q0), cirq.X(q1)) with pytest.raises(ValueError, match="no measurements"): @@ -47,16 +50,18 @@ def test_run_no_results(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_empty_circuit(dtype): - simulator = cirq.Simulator(dtype=dtype) +@pytest.mark.parametrize('split', [True, False]) +def test_run_empty_circuit(dtype: Type[np.number], split: bool): + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with pytest.raises(ValueError, match="no measurements"): simulator.run(cirq.Circuit()) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_reset(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_reset(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit( cirq.H(q0), PlusGate(3, 2)(q1), @@ -73,9 +78,10 @@ def test_run_reset(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_bit_flips(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_bit_flips(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -86,9 +92,10 @@ def test_run_bit_flips(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measure_at_end_no_repetitions(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measure_at_end_no_repetitions(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -111,9 +118,10 @@ def test_run_repetitions_terminal_measurement_stochastic(): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_repetitions_measure_at_end(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_repetitions_measure_at_end(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -128,9 +136,10 @@ def test_run_repetitions_measure_at_end(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_invert_mask_measure_not_terminal(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_invert_mask_measure_not_terminal(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -148,9 +157,10 @@ def test_run_invert_mask_measure_not_terminal(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_partial_invert_mask_measure_not_terminal(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_partial_invert_mask_measure_not_terminal(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -168,9 +178,10 @@ def test_run_partial_invert_mask_measure_not_terminal(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measurement_not_terminal_no_repetitions(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measurement_not_terminal_no_repetitions(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -191,9 +202,10 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_repetitions_measurement_not_terminal(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_repetitions_measurement_not_terminal(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: @@ -213,9 +225,10 @@ def test_run_repetitions_measurement_not_terminal(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_param_resolver(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_param_resolver(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -231,28 +244,31 @@ def test_run_param_resolver(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_mixture(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_mixture(dtype: Type[np.number], split: bool): q0 = cirq.LineQubit(0) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.bit_flip(0.5)(q0), cirq.measure(q0)) result = simulator.run(circuit, repetitions=100) - assert 20 < sum(result.measurements['0'])[0] < 80 + assert 20 < sum(result.measurements['0'])[0] < 80 # type: ignore @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_mixture_with_gates(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_mixture_with_gates(dtype: Type[np.number], split: bool): q0 = cirq.LineQubit(0) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split, seed=23) circuit = cirq.Circuit(cirq.H(q0), cirq.phase_flip(0.5)(q0), cirq.H(q0), cirq.measure(q0)) result = simulator.run(circuit, repetitions=100) - assert sum(result.measurements['0'])[0] < 80 - assert sum(result.measurements['0'])[0] > 20 + assert sum(result.measurements['0'])[0] < 80 # type: ignore + assert sum(result.measurements['0'])[0] > 20 # type: ignore @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_correlations(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_correlations(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.measure(q0, q1)) for _ in range(10): result = simulator.run(circuit) @@ -261,9 +277,10 @@ def test_run_correlations(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_measure_multiple_qubits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_measure_multiple_qubits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1), cirq.measure(q0, q1)) @@ -272,9 +289,10 @@ def test_run_measure_multiple_qubits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_run_sweeps_param_resolvers(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_run_sweeps_param_resolvers(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -297,9 +315,10 @@ def test_run_sweeps_param_resolvers(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_random_unitary(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_random_unitary(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for _ in range(10): random_circuit = cirq.testing.random_circuit(qubits=[q0, q1], n_moments=8, op_density=0.99) circuit_unitary = [] @@ -312,11 +331,10 @@ def test_simulate_random_unitary(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_no_circuit( - dtype, -): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_no_circuit(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit() result = simulator.simulate(circuit, qubit_order=[q0, q1]) np.testing.assert_almost_equal(result.final_state_vector, np.array([1, 0, 0, 0])) @@ -324,11 +342,10 @@ def test_simulate_no_circuit( @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate( - dtype, -): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1)) result = simulator.simulate(circuit, qubit_order=[q0, q1]) np.testing.assert_almost_equal(result.final_state_vector, np.array([0.5, 0.5, 0.5, 0.5])) @@ -365,11 +382,10 @@ def _mixture_(self): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_qudits( - dtype, -): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_qudits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQid.for_qid_shape((3, 4)) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit( PlusGate(3)(q0), PlusGate(4, increment=3)(q1), @@ -382,11 +398,10 @@ def test_simulate_qudits( @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_mixtures( - dtype, -): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_mixtures(dtype: Type[np.number], split: bool): q0 = cirq.LineQubit(0) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) circuit = cirq.Circuit(cirq.bit_flip(0.5)(q0), cirq.measure(q0)) count = 0 for _ in range(100): @@ -399,12 +414,12 @@ def test_simulate_mixtures( assert count < 80 and count > 20 -@pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_qudit_mixtures( - dtype, -): +@pytest.mark.parametrize( + 'dtype, split', itertools.product([np.complex64, np.complex128], [True, False]) +) +def test_simulate_qudit_mixtures(dtype: Type[np.number], split: bool): q0 = cirq.LineQid(0, 3) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) mixture = _TestMixture([PlusGate(3, 0), PlusGate(3, 1), PlusGate(3, 2)]) circuit = cirq.Circuit(mixture(q0), cirq.measure(q0)) counts = {0: 0, 1: 0, 2: 0} @@ -421,9 +436,10 @@ def test_simulate_qudit_mixtures( @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_bit_flips(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_bit_flips(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -437,9 +453,10 @@ def test_simulate_bit_flips(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_initial_state(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_initial_state(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) @@ -450,9 +467,10 @@ def test_simulate_initial_state(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_act_on_args(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_act_on_args(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) @@ -464,9 +482,10 @@ def test_simulate_act_on_args(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_qubit_order(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_qubit_order(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) @@ -477,27 +496,29 @@ def test_simulate_qubit_order(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_param_resolver(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_param_resolver(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( (cirq.X ** sympy.Symbol('b0'))(q0), (cirq.X ** sympy.Symbol('b1'))(q1) ) resolver = {'b0': b0, 'b1': b1} - result = simulator.simulate(circuit, param_resolver=resolver) + result = simulator.simulate(circuit, param_resolver=resolver) # type: ignore expected_state = np.zeros(shape=(2, 2)) expected_state[b0][b1] = 1.0 np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) - assert result.params == cirq.ParamResolver(resolver) + assert result.params == cirq.ParamResolver(resolver) # type: ignore assert len(result.measurements) == 0 @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_measure_multiple_qubits(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_measure_multiple_qubits(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1), cirq.measure(q0, q1)) @@ -506,9 +527,10 @@ def test_simulate_measure_multiple_qubits(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_sweeps_param_resolver(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_sweeps_param_resolver(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -532,10 +554,11 @@ def test_simulate_sweeps_param_resolver(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1), cirq.H(q0), cirq.H(q1)) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): if i == 0: np.testing.assert_almost_equal(step.state_vector(), np.array([0.5] * 4)) @@ -544,9 +567,10 @@ def test_simulate_moment_steps(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_empty_circuit(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_empty_circuit(dtype: Type[np.number], split: bool): circuit = cirq.Circuit() - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) step = None for step in simulator.simulate_moment_steps(circuit): pass @@ -567,10 +591,11 @@ def test_simulate_moment_steps_set_state(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_sample(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_sample(dtype: Type[np.number], split: bool): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1)) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): if i == 0: samples = step.sample([q0, q1], repetitions=10) @@ -587,10 +612,11 @@ def test_simulate_moment_steps_sample(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_moment_steps_intermediate_measurement(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_moment_steps_intermediate_measurement(dtype: Type[np.number], split: bool): q0 = cirq.LineQubit(0) circuit = cirq.Circuit(cirq.H(q0), cirq.measure(q0), cirq.H(q0)) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) for i, step in enumerate(simulator.simulate_moment_steps(circuit)): if i == 1: result = int(step.measurements['0'][0]) @@ -603,14 +629,15 @@ def test_simulate_moment_steps_intermediate_measurement(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_expectation_values(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_expectation_values(dtype: Type[np.number], split: bool): # Compare with test_expectation_from_state_vector_two_qubit_states # in file: cirq/ops/linear_combinations_test.py q0, q1 = cirq.LineQubit.range(2) psum1 = cirq.Z(q0) + 3.2 * cirq.Z(q1) psum2 = -1 * cirq.X(q0) + 2 * cirq.X(q1) c1 = cirq.Circuit(cirq.I(q0), cirq.X(q1)) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) result = simulator.simulate_expectation_values(c1, [psum1, psum2]) assert cirq.approx_eq(result[0], -2.2, atol=1e-6) assert cirq.approx_eq(result[1], 0, atol=1e-6) @@ -627,11 +654,12 @@ def test_simulate_expectation_values(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_expectation_values_terminal_measure(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_expectation_values_terminal_measure(dtype: Type[np.number], split: bool): q0 = cirq.LineQubit(0) circuit = cirq.Circuit(cirq.H(q0), cirq.measure(q0)) obs = cirq.Z(q0) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) with pytest.raises(ValueError): _ = simulator.simulate_expectation_values(circuit, obs) @@ -664,11 +692,12 @@ def test_simulate_expectation_values_terminal_measure(dtype): @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) -def test_simulate_expectation_values_qubit_order(dtype): +@pytest.mark.parametrize('split', [True, False]) +def test_simulate_expectation_values_qubit_order(dtype: Type[np.number], split: bool): q0, q1, q2 = cirq.LineQubit.range(3) circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1), cirq.X(q2)) obs = cirq.X(q0) + cirq.X(q1) - cirq.Z(q2) - simulator = cirq.Simulator(dtype=dtype) + simulator = cirq.Simulator(dtype=dtype, split_untangled_states=split) result = simulator.simulate_expectation_values(circuit, obs) assert cirq.approx_eq(result[0], 3, atol=1e-6) @@ -735,12 +764,17 @@ def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs): def test_simulator_step_state_mixin(): qubits = cirq.LineQubit.range(2) - qubit_map = {qubits[i]: i for i in range(2)} + args = cirq.ActOnStateVectorArgs( + log_of_measurement_results={'m': np.array([1, 2])}, + target_tensor=np.array([0, 1, 0, 0]).reshape((2, 2)), + available_buffer=np.array([0, 1, 0, 0]).reshape((2, 2)), + prng=cirq.value.parse_random_state(0), + qubits=qubits, + ) result = cirq.SparseSimulatorStep( - measurements={'m': np.array([1, 2])}, - state_vector=np.array([0, 1, 0, 0]), - qubit_map=qubit_map, + sim_state=args, dtype=np.complex64, + simulator=None, # type: ignore ) rho = np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) np.testing.assert_array_almost_equal(rho, result.density_matrix_of(qubits)) @@ -1200,7 +1234,7 @@ def test_separated_measurements(): def test_state_vector_copy(): - sim = cirq.Simulator() + sim = cirq.Simulator(split_untangled_states=False) class InplaceGate(cirq.SingleQubitGate): """A gate that modifies the target tensor in place, multiply by -1.""" @@ -1270,6 +1304,28 @@ def test_nondeterministic_mixture_noise(): assert result1 != result2 -def test_unsupported_noise_fails(): - with pytest.raises(ValueError, match='noise'): - cirq.Simulator(noise=cirq.amplitude_damp(0.5)) +def test_act_on_args_pure_state_creation(): + sim = cirq.Simulator() + qids = cirq.LineQubit.range(3) + shape = cirq.qid_shape(qids) + args = sim._create_act_on_args(1, qids) + values = list(args.values()) + arg = ( + values[0] + .kronecker_product(values[1]) + .kronecker_product(values[2]) + .transpose_to_qubit_order(qids) + ) + expected = cirq.to_valid_state_vector(1, len(qids), qid_shape=shape) + np.testing.assert_allclose(arg.target_tensor, expected.reshape(shape)) + + +def test_noise_model(): + q = cirq.LineQubit(0) + circuit = cirq.Circuit(cirq.H(q), cirq.measure(q)) + + noise_model = cirq.NoiseModel.from_noise_model_like(cirq.depolarize(p=0.01)) + simulator = cirq.Simulator(noise=noise_model) + result = simulator.run(circuit, repetitions=100) + + assert 40 <= sum(result.measurements['0'])[0] < 60 diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index b6eb1166fbe..ec8ea6a051c 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -15,7 +15,19 @@ import abc -from typing import Any, Dict, Iterator, Sequence, TYPE_CHECKING, Tuple, Generic, TypeVar, Type +from typing import ( + Any, + Dict, + Iterator, + Sequence, + TYPE_CHECKING, + Tuple, + Generic, + TypeVar, + Type, + Optional, + Union, +) import numpy as np @@ -43,7 +55,7 @@ class SimulatesIntermediateStateVector( ): """A simulator that accesses its state vector as it does its simulation. - Implementors of this interface should implement the _base_iterator + Implementors of this interface should implement the _core_iterator method.""" def __init__( @@ -52,18 +64,20 @@ def __init__( dtype: Type[np.number] = np.complex64, noise: 'cirq.NOISE_MODEL_LIKE' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + split_untangled_states: bool = False, ): super().__init__( dtype=dtype, noise=noise, seed=seed, + split_untangled_states=split_untangled_states, ) def _create_simulator_trial_result( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: 'StateVectorSimulatorState', + final_simulator_state: Union['StateVectorStepResult', 'StateVectorSimulatorState'], ) -> 'StateVectorTrialResult': return StateVectorTrialResult( params=params, measurements=measurements, final_simulator_state=final_simulator_state @@ -96,7 +110,8 @@ def compute_amplitudes_sweep_iter( class StateVectorStepResult( - simulator.StepResult['StateVectorSimulatorState'], metaclass=abc.ABCMeta + simulator_base.StepResultBase['StateVectorSimulatorState', 'cirq.ActOnStateVectorArgs'], + metaclass=abc.ABCMeta, ): @abc.abstractmethod def _simulator_state(self) -> 'StateVectorSimulatorState': @@ -142,15 +157,26 @@ def __init__( self, params: study.ParamResolver, measurements: Dict[str, np.ndarray], - final_simulator_state: StateVectorSimulatorState, + final_simulator_state: Union[StateVectorStepResult, StateVectorSimulatorState], ) -> None: + qubit_map = ( + final_simulator_state._qubit_mapping + if isinstance(final_simulator_state, StateVectorStepResult) + else final_simulator_state.qubit_map + ) super().__init__( params=params, measurements=measurements, final_simulator_state=final_simulator_state, - qubit_map=final_simulator_state.qubit_map, + qubit_map=qubit_map, ) - self.final_state_vector = final_simulator_state.state_vector + self._final_state_vector: Optional[np.ndarray] = None + + @property + def final_state_vector(self): + if self._final_state_vector is None: + self._final_state_vector = self._final_simulator_state.state_vector + return self._final_state_vector def state_vector(self): """Return the state vector at the end of the computation. diff --git a/cirq-core/cirq/sim/state_vector_test.py b/cirq-core/cirq/sim/state_vector_test.py index dd523aea7ff..88261afb03b 100644 --- a/cirq-core/cirq/sim/state_vector_test.py +++ b/cirq-core/cirq/sim/state_vector_test.py @@ -375,3 +375,17 @@ def test_step_result_bloch_vector(): bloch0 = np.array([0, 0, 1]) np.testing.assert_array_almost_equal(bloch1, step_result.bloch_vector_of(q1)) np.testing.assert_array_almost_equal(bloch0, step_result.bloch_vector_of(q0)) + + +def test_factor_validation(): + args = cirq.Simulator()._create_act_on_args(0, qubits=cirq.LineQubit.range(2)) + args.apply_operation(cirq.H(cirq.LineQubit(0))) + t = args.create_merged_state().target_tensor + cirq.linalg.transformations.factor_state_vector(t, [0]) + cirq.linalg.transformations.factor_state_vector(t, [1], atol=1e-2) + args.apply_operation(cirq.CNOT(cirq.LineQubit(0), cirq.LineQubit(1))) + t = args.create_merged_state().target_tensor + with pytest.raises(ValueError, match='factor'): + cirq.linalg.transformations.factor_state_vector(t, [0]) + with pytest.raises(ValueError, match='factor'): + cirq.linalg.transformations.factor_state_vector(t, [1]) diff --git a/cirq-core/cirq/testing/circuit_compare.py b/cirq-core/cirq/testing/circuit_compare.py index 34dae4305c5..48969d3b447 100644 --- a/cirq-core/cirq/testing/circuit_compare.py +++ b/cirq-core/cirq/testing/circuit_compare.py @@ -232,6 +232,9 @@ def assert_has_diagram( beginning and whitespace at the end are ignored. **kwargs: Keyword arguments to be passed to actual.to_text_diagram(). """ + # pylint: disable=unused-variable + __tracebackhide__ = True + # pylint: enable=unused-variable actual_diagram = actual.to_text_diagram(**kwargs).lstrip("\n").rstrip() desired_diagram = desired.lstrip("\n").rstrip() assert actual_diagram == desired_diagram, ( @@ -402,6 +405,9 @@ def assert_has_consistent_qid_shape(val: Any) -> None: val: The value under test. Should have `_qid_shape_` and/or `num_qubits_` methods. Can optionally have a `qubits` property. """ + # pylint: disable=unused-variable + __tracebackhide__ = True + # pylint: enable=unused-variable default = (-1,) qid_shape = protocols.qid_shape(val, default) num_qubits = protocols.num_qubits(val, default) diff --git a/cirq-core/cirq/testing/consistent_act_on.py b/cirq-core/cirq/testing/consistent_act_on.py index f5646f99879..6c3a0555ed5 100644 --- a/cirq-core/cirq/testing/consistent_act_on.py +++ b/cirq-core/cirq/testing/consistent_act_on.py @@ -96,7 +96,7 @@ def assert_all_implemented_act_on_effects_match_unitary( ) return None - qubits = LineQubit.range(protocols.num_qubits(val) * 2) + qubits = LineQubit.range(num_qubits_val * 2) qubit_map = {qubit: i for i, qubit in enumerate(qubits)} circuit = Circuit() diff --git a/cirq-core/cirq/testing/deprecation.py b/cirq-core/cirq/testing/deprecation.py index 3ada85ffd36..fbd28420a52 100644 --- a/cirq-core/cirq/testing/deprecation.py +++ b/cirq-core/cirq/testing/deprecation.py @@ -13,7 +13,6 @@ # limitations under the License. import logging import os -from contextlib import contextmanager from typing import Optional from cirq._compat import deprecated_parameter @@ -22,7 +21,6 @@ ALLOW_DEPRECATION_IN_TEST = 'ALLOW_DEPRECATION_IN_TEST' -@contextmanager @deprecated_parameter( deadline='v0.12', fix='Use count instead.', @@ -51,27 +49,28 @@ def assert_deprecated(*msgs: str, deadline: str, count: Optional[int] = 1): messages have to equal count. """ - orig_exist, orig_value = ( - ALLOW_DEPRECATION_IN_TEST in os.environ, - os.environ.get(ALLOW_DEPRECATION_IN_TEST, None), - ) - os.environ[ALLOW_DEPRECATION_IN_TEST] = 'True' - try: - with assert_logs( - *(msgs + (deadline,)), - min_level=logging.WARNING, - max_level=logging.WARNING, - count=count, - ): - yield True - finally: - try: - if orig_exist: + class DeprecationAssertContext: + def __enter__(self): + self.orig_exist, self.orig_value = ( + ALLOW_DEPRECATION_IN_TEST in os.environ, + os.environ.get(ALLOW_DEPRECATION_IN_TEST, None), + ) + os.environ[ALLOW_DEPRECATION_IN_TEST] = 'True' + self.assert_logs = assert_logs( + *(msgs + (deadline,)), + min_level=logging.WARNING, + max_level=logging.WARNING, + count=count, + ) + self.assert_logs.__enter__() + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.orig_exist: # mypy can't resolve that orig_exist ensures that orig_value # of type Optional[str] can't be None - os.environ[ALLOW_DEPRECATION_IN_TEST] = orig_value # type: ignore + os.environ[ALLOW_DEPRECATION_IN_TEST] = self.orig_value # type: ignore else: del os.environ[ALLOW_DEPRECATION_IN_TEST] - except: - # this is only for nested deprecation checks - pass + self.assert_logs.__exit__(exc_type, exc_val, exc_tb) + + return DeprecationAssertContext() diff --git a/cirq-core/cirq/testing/deprecation_test.py b/cirq-core/cirq/testing/deprecation_test.py index fa736fc888b..5f678e3554f 100644 --- a/cirq-core/cirq/testing/deprecation_test.py +++ b/cirq-core/cirq/testing/deprecation_test.py @@ -18,6 +18,13 @@ from cirq.testing import assert_deprecated +def test_nested_assert_deprecation(): + with assert_deprecated(deadline="v1.2", count=1): + with assert_deprecated(deadline="v1.2", count=1): + with assert_deprecated(deadline="v1.2", count=1): + warnings.warn("hello, this is deprecated in v1.2") + + def test_assert_deprecated_log_handling(): # correct deprecation message with assert_deprecated("hello", deadline="v1.2"): diff --git a/cirq-core/cirq/testing/equals_tester.py b/cirq-core/cirq/testing/equals_tester.py index c704d6db17b..46234682433 100644 --- a/cirq-core/cirq/testing/equals_tester.py +++ b/cirq-core/cirq/testing/equals_tester.py @@ -33,14 +33,6 @@ class EqualsTester: def __init__(self): self._groups = [(_ClassUnknownToSubjects(),)] - @staticmethod - def _eq_check(v1: Any, v2: Any) -> bool: - eq = v1 == v2 - ne = v1 != v2 - - assert eq != ne, f"__eq__ is inconsistent with __ne__ between {v1!r} and {v2!r}" - return eq - def _verify_equality_group(self, *group_items: Any): """Verifies that a group is an equivalence group. @@ -61,7 +53,7 @@ def _verify_equality_group(self, *group_items: Any): # Within-group items must be equal. for v1, v2 in itertools.product(group_items, group_items): - same = EqualsTester._eq_check(v1, v2) + same = _eq_check(v1, v2) assert same or v1 is not v2, f"{v1!r} isn't equal to itself!" assert ( same @@ -70,7 +62,7 @@ def _verify_equality_group(self, *group_items: Any): # Between-group items must be unequal. for other_group in self._groups: for v1, v2 in itertools.product(group_items, other_group): - assert not EqualsTester._eq_check( + assert not _eq_check( v1, v2 ), f"{v1!r} and {v2!r} can't be in different equality groups. They're equal." @@ -163,3 +155,11 @@ def __init__(self, other): def __eq__(self, other): return True if other is self.other else NotImplemented + + +def _eq_check(v1: Any, v2: Any) -> bool: + eq = v1 == v2 + ne = v1 != v2 + + assert eq != ne, f"__eq__ is inconsistent with __ne__ between {v1!r} and {v2!r}" + return eq diff --git a/cirq-google/cirq_google/api/v1/BUILD b/cirq-google/cirq_google/api/v1/BUILD deleted file mode 100644 index 5a1b72d9328..00000000000 --- a/cirq-google/cirq_google/api/v1/BUILD +++ /dev/null @@ -1,62 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("@com_google_protobuf//:protobuf.bzl", "py_proto_library") - -proto_library( - name = "operations_proto", - srcs = ["operations.proto"], -) - -py_proto_library( - name = "operations_py_proto", - srcs = ["operations.proto"], -) - -cc_proto_library( - name = "operations_cc_proto", - deps = [ - ":operations_proto", - ], -) - -proto_library( - name = "params_proto", - srcs = ["params.proto"], -) - -py_proto_library( - name = "params_py_proto", - srcs = ["params.proto"], -) - -cc_proto_library( - name = "params_cc_proto", - deps = [ - ":params_proto", - ], -) - -proto_library( - name = "program_proto", - srcs = ["program.proto"], - deps = [ - ":operations_proto", - ":params_proto", - ], -) - -py_proto_library( - name = "program_py_proto", - srcs = ["program.proto"], - deps = [ - ":operations_py_proto", - ":params_py_proto", - ], -) - -cc_proto_library( - name = "program_cc_proto", - deps = [ - ":program_proto", - ], -) diff --git a/cirq-google/cirq_google/api/v2/BUILD b/cirq-google/cirq_google/api/v2/BUILD deleted file mode 100644 index a99f5d2ca39..00000000000 --- a/cirq-google/cirq_google/api/v2/BUILD +++ /dev/null @@ -1,153 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("@com_google_protobuf//:protobuf.bzl", "py_proto_library") - -proto_library( - name = "batch_proto", - srcs = ["batch.proto"], - deps = [ - ":program_proto", - ":result_proto", - ":run_context_proto" - ], -) - -py_proto_library( - name = "batch_py_proto", - srcs = ["batch.proto"], - deps = [ - ":program_py_proto", - ":result_py_proto", - ":run_context_py_proto" - ], -) - -cc_proto_library( - name = "batch_cc_proto", - deps = [ - ":batch_proto" - ], -) - - -proto_library( - name = "calibration_proto", - srcs = ["calibration.proto"], - deps = [ - ":metrics_proto", - ":program_proto" - ], -) - -py_proto_library( - name = "calibration_py_proto", - srcs = ["calibration.proto"], - deps = [ - ":metrics_py_proto", - ":program_py_proto" - ], -) - -cc_proto_library( - name = "calibration_cc_proto", - deps = [ - ":calibration_proto" - ], -) - -proto_library( - name = "device_proto", - srcs = ["device.proto"], -) - -py_proto_library( - name = "device_py_proto", - srcs = ["device.proto"], -) - -cc_proto_library( - name = "device_cc_proto", - deps = [ - ":device_proto" - ], -) - -proto_library( - name = "metrics_proto", - srcs = ["metrics.proto"], -) - -py_proto_library( - name = "metrics_py_proto", - srcs = ["metrics.proto"], -) - -cc_proto_library( - name = "metrics_cc_proto", - deps = [ - ":metrics_proto" - ], -) - -proto_library( - name = "program_proto", - srcs = ["program.proto"], -) - -py_proto_library( - name = "program_py_proto", - srcs = ["program.proto"], -) - -cc_proto_library( - name = "program_cc_proto", - deps = [ - ":program_proto" - ], -) - -proto_library( - name = "result_proto", - srcs = ["result.proto"], - deps = [ - ":program_proto", - ], -) - -py_proto_library( - name = "result_py_proto", - srcs = ["result.proto"], - deps = [ - ":program_py_proto", - ], -) - -cc_proto_library( - name = "result_cc_proto", - deps = [ - ":result_proto", - ], -) - -proto_library( - name = "run_context_proto", - srcs = ["run_context.proto"], - deps = [ - ":program_proto", - ], -) - -py_proto_library( - name = "run_context_py_proto", - srcs = ["run_context.proto"], - deps = [ - ":program_py_proto", - ], -) - -cc_proto_library( - name = "run_context_cc_proto", - deps = [ - ":run_context_proto", - ], -) diff --git a/cirq-google/cirq_google/calibration/__init__.py b/cirq-google/cirq_google/calibration/__init__.py index f1136c42f48..60c7b39e93f 100644 --- a/cirq-google/cirq_google/calibration/__init__.py +++ b/cirq-google/cirq_google/calibration/__init__.py @@ -34,6 +34,8 @@ THETA_ZETA_GAMMA_FLOQUET_PHASED_FSIM_CHARACTERIZATION, WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, merge_matching_results, + try_convert_sqrt_iswap_to_fsim, + try_convert_syc_or_sqrt_iswap_to_fsim, ) from cirq_google.calibration.workflow import ( @@ -50,5 +52,4 @@ run_calibrations, run_floquet_characterization_for_moments, run_zeta_chi_gamma_compensation_for_moments, - try_convert_sqrt_iswap_to_fsim, ) diff --git a/cirq-google/cirq_google/calibration/engine_simulator.py b/cirq-google/cirq_google/calibration/engine_simulator.py index 5de07b1689e..d8ce697ad6c 100644 --- a/cirq-google/cirq_google/calibration/engine_simulator.py +++ b/cirq-google/cirq_google/calibration/engine_simulator.py @@ -23,6 +23,7 @@ PhasedFSimCalibrationResult, PhasedFSimCharacterization, SQRT_ISWAP_INV_PARAMETERS, + try_convert_gate_to_fsim, try_convert_sqrt_iswap_to_fsim, ) @@ -244,6 +245,60 @@ def sample_gate( simulator, drift_generator=sample_gate, gates_translator=try_convert_sqrt_iswap_to_fsim ) + @classmethod + def create_from_dictionary( + cls, + parameters: Dict[ + Tuple[cirq.Qid, cirq.Qid], Dict[cirq.FSimGate, Union[PhasedFSimCharacterization, Dict]] + ], + *, + simulator: Optional[cirq.Simulator] = None, + ) -> 'PhasedFSimEngineSimulator': + """Creates PhasedFSimEngineSimulator with fixed drifts. + + Args: + parameters: maps every pair of qubits and engine gate on that pair to a + characterization for that gate. + simulator: Simulator object to use. When None, a new instance of cirq.Simulator() will + be created. + + Returns: + New PhasedFSimEngineSimulator instance. + """ + + for a, b in parameters.keys(): + if a > b: + raise ValueError( + f'All qubit pairs must be given in canonical order where the first qubit is ' + f'less than the second, got {a} > {b}' + ) + + def sample_gate( + a: cirq.Qid, b: cirq.Qid, gate: cirq.FSimGate + ) -> PhasedFSimCharacterization: + pair_parameters = None + swapped = False + if (a, b) in parameters: + pair_parameters = parameters[(a, b)].get(gate) + elif (b, a) in parameters: + pair_parameters = parameters[(b, a)].get(gate) + swapped = True + + if pair_parameters is None: + raise ValueError(f'Missing parameters for value for pair {(a, b)} and gate {gate}.') + if not isinstance(pair_parameters, PhasedFSimCharacterization): + pair_parameters = PhasedFSimCharacterization(**pair_parameters) + if swapped: + pair_parameters = pair_parameters.parameters_for_qubits_swapped() + + return pair_parameters + + if simulator is None: + simulator = cirq.Simulator() + return cls( + simulator, drift_generator=sample_gate, gates_translator=try_convert_gate_to_fsim + ) + @classmethod def create_from_characterizations_sqrt_iswap( cls, @@ -397,18 +452,18 @@ def simulate( converted = _convert_to_circuit_with_drift(self, program) return self._simulator.simulate(converted, param_resolver, qubit_order, initial_state) - def _create_act_on_args( + def _create_partial_act_on_args( self, initial_state: Union[int, cirq.ActOnStateVectorArgs], qubits: Sequence[cirq.Qid], + logs: Dict[str, Any], ) -> cirq.ActOnStateVectorArgs: # Needs an implementation since it's abstract but will never actually be called. raise NotImplementedError() def _create_step_result( self, - sim_state: cirq.ActOnStateVectorArgs, - qubit_map: Dict[cirq.Qid, int], + sim_state: cirq.OperationTarget, ) -> cirq.SparseSimulatorStep: # Needs an implementation since it's abstract but will never actually be called. raise NotImplementedError() diff --git a/cirq-google/cirq_google/calibration/engine_simulator_test.py b/cirq-google/cirq_google/calibration/engine_simulator_test.py index 4eca299c6d3..253e2472397 100644 --- a/cirq-google/cirq_google/calibration/engine_simulator_test.py +++ b/cirq-google/cirq_google/calibration/engine_simulator_test.py @@ -22,6 +22,8 @@ ) import cirq +SQRT_ISWAP_INV_GATE = cirq.FSimGate(np.pi / 4, 0.0) + class DummyPhasedFSimCalibrationRequest(PhasedFSimCalibrationRequest): def to_calibration_layer(self) -> cirq_google.CalibrationLayer: @@ -278,6 +280,50 @@ def test_from_dictionary_sqrt_iswap_simulates_correctly(): assert cirq.allclose_up_to_global_phase(actual, expected) +def test_create_from_dictionary_simulates_correctly(): + parameters_ab_1 = {'theta': 0.6, 'zeta': 0.5, 'chi': 0.4, 'gamma': 0.3, 'phi': 0.2} + parameters_ab_2 = {'theta': 0.1, 'zeta': 0.2, 'chi': 0.3, 'gamma': 0.4, 'phi': 0.5} + parameters_bc = {'theta': 0.8, 'zeta': -0.5, 'chi': -0.4, 'gamma': -0.3, 'phi': -0.2} + parameters_cd = {'theta': 0.1, 'zeta': 0.2, 'chi': 0.3, 'gamma': 0.4, 'phi': 0.5} + + a, b, c, d = cirq.LineQubit.range(4) + circuit = cirq.Circuit( + [ + [cirq.X(a), cirq.Y(b), cirq.Z(c), cirq.H(d)], + [SQRT_ISWAP_INV_GATE.on(a, b), SQRT_ISWAP_INV_GATE.on(d, c)], + [SQRT_ISWAP_INV_GATE.on(b, c)], + [cirq_google.SYC.on(a, b), SQRT_ISWAP_INV_GATE.on(c, d)], + ] + ) + expected_circuit = cirq.Circuit( + [ + [cirq.X(a), cirq.Y(b), cirq.Z(c), cirq.H(d)], + [ + cirq.PhasedFSimGate(**parameters_ab_1).on(a, b), + cirq.PhasedFSimGate(**parameters_cd).on(c, d), + ], + [cirq.PhasedFSimGate(**parameters_bc).on(b, c)], + [ + cirq.PhasedFSimGate(**parameters_ab_2).on(a, b), + cirq.PhasedFSimGate(**parameters_cd).on(c, d), + ], + ] + ) + + engine_simulator = PhasedFSimEngineSimulator.create_from_dictionary( + parameters={ + (a, b): {SQRT_ISWAP_INV_GATE: parameters_ab_1, cirq_google.SYC: parameters_ab_2}, + (b, c): {SQRT_ISWAP_INV_GATE: parameters_bc}, + (c, d): {SQRT_ISWAP_INV_GATE: parameters_cd}, + } + ) + + actual = engine_simulator.final_state_vector(circuit) + expected = cirq.final_state_vector(expected_circuit) + + assert cirq.allclose_up_to_global_phase(actual, expected) + + def test_from_dictionary_sqrt_iswap_ideal_when_missing_gate_fails(): a, b = cirq.LineQubit.range(2) circuit = cirq.Circuit(cirq.FSimGate(np.pi / 4, 0.0).on(a, b)) @@ -444,6 +490,20 @@ def test_from_characterizations_sqrt_iswap_when_invalid_arguments_fails(): ) +def test_create_from_dictionary_imvalid_parameters_fails(): + a, b = cirq.LineQubit.range(2) + circuit = cirq.Circuit(cirq.CZ(a, b)) + + simulator = PhasedFSimEngineSimulator.create_from_dictionary({}) + with pytest.raises(ValueError, match='Missing parameters'): + simulator.final_state_vector(circuit) + + with pytest.raises(ValueError, match='canonical order'): + PhasedFSimEngineSimulator.create_from_dictionary( + parameters={(b, a): {'theta': 0.6, 'phi': 0.2}} + ) + + def _create_sqrt_iswap_request( pairs: Iterable[Tuple[cirq.Qid, cirq.Qid]], options: FloquetPhasedFSimCalibrationOptions = ALL_ANGLES_FLOQUET_PHASED_FSIM_CHARACTERIZATION, diff --git a/cirq-google/cirq_google/calibration/phased_fsim.py b/cirq-google/cirq_google/calibration/phased_fsim.py index 785bbd3d328..18b3317c442 100644 --- a/cirq-google/cirq_google/calibration/phased_fsim.py +++ b/cirq-google/cirq_google/calibration/phased_fsim.py @@ -15,6 +15,7 @@ import collections import dataclasses import functools +import math import re from typing import ( Any, @@ -41,6 +42,7 @@ ) from cirq_google.api import v2 from cirq_google.engine import Calibration, CalibrationLayer, CalibrationResult, Engine, EngineJob +from cirq_google.ops import SycamoreGate if TYPE_CHECKING: import cirq_google @@ -996,6 +998,13 @@ def with_zeta_chi_gamma_compensated( (cirq.rz(0.5 * gamma - beta).on(a), cirq.rz(0.5 * gamma + beta).on(b)), ) + def _unitary_(self) -> np.array: + """Implements Cirq's `unitary` protocol for this object.""" + p = np.exp(-np.pi * 1j * self.phase_exponent) + return ( + np.diag([1, p, 1 / p, 1]) @ cirq.unitary(self.engine_gate) @ np.diag([1, 1 / p, p, 1]) + ) + def try_convert_sqrt_iswap_to_fsim(gate: cirq.Gate) -> Optional[PhaseCalibratedFSimGate]: """Converts an equivalent gate to FSimGate(theta=π/4, phi=0) if possible. @@ -1008,33 +1017,86 @@ def try_convert_sqrt_iswap_to_fsim(gate: cirq.Gate) -> Optional[PhaseCalibratedF either FSimGate, ISWapPowGate, PhasedFSimGate or PhasedISwapPowGate that is equivalent to FSimGate(theta=±π/4, phi=0). None otherwise. """ - if isinstance(gate, cirq.FSimGate): - if not np.isclose(gate.phi, 0.0): - return None - angle = gate.theta + result = try_convert_gate_to_fsim(gate) + if result is None: + return None + engine_gate = result.engine_gate + if math.isclose(engine_gate.theta, np.pi / 4) and math.isclose(engine_gate.phi, 0.0): + return result + return None + + +def try_convert_gate_to_fsim(gate: cirq.Gate) -> Optional[PhaseCalibratedFSimGate]: + """Converts a gate to equivalent PhaseCalibratedFSimGate if possible. + + Args: + gate: Gate to convert. + + Returns: + If provided gate is equivalent to some PhaseCalibratedFSimGate, returns that gate. + Otherwise returns None. + """ + if cirq.is_parameterized(gate): + return None + + phi = 0.0 + theta = 0.0 + phase_exponent = 0.0 + if isinstance(gate, SycamoreGate): + phi = np.pi / 6 + theta = np.pi / 2 + elif isinstance(gate, cirq.FSimGate): + theta = gate.theta + phi = gate.phi elif isinstance(gate, cirq.ISwapPowGate): - angle = -gate.exponent * np.pi / 2 + if not np.isclose(np.exp(np.pi * 1j * gate.global_shift * gate.exponent), 1.0): + return None + theta = -gate.exponent * np.pi / 2 elif isinstance(gate, cirq.PhasedFSimGate): - if ( - not np.isclose(gate.zeta, 0.0) - or not np.isclose(gate.chi, 0.0) - or not np.isclose(gate.gamma, 0.0) - or not np.isclose(gate.phi, 0.0) - ): + if not np.isclose(gate.zeta, 0.0) or not np.isclose(gate.gamma, 0.0): return None - angle = gate.theta + theta = gate.theta + phi = gate.phi + phase_exponent = -gate.chi / (2 * np.pi) elif isinstance(gate, cirq.PhasedISwapPowGate): - if not np.isclose(-gate.phase_exponent - 0.5, 0.0): + theta = -gate.exponent * np.pi / 2 + phase_exponent = -gate.phase_exponent + elif isinstance(gate, cirq.ops.CZPowGate): + if not np.isclose(np.exp(np.pi * 1j * gate.global_shift * gate.exponent), 1.0): return None - angle = gate.exponent * np.pi / 2 + phi = -np.pi * gate.exponent else: return None - angle_canonical = angle % (2 * np.pi) + phi = phi % (2 * np.pi) + theta = theta % (2 * np.pi) + if theta >= np.pi: + theta = 2 * np.pi - theta + phase_exponent = phase_exponent + 0.5 + phase_exponent %= 1 + return PhaseCalibratedFSimGate(cirq.FSimGate(theta=theta, phi=phi), phase_exponent) + - if np.isclose(angle_canonical, np.pi / 4): - return PhaseCalibratedFSimGate(cirq.FSimGate(theta=np.pi / 4, phi=0.0), 0.0) - elif np.isclose(angle_canonical, 7 * np.pi / 4): - return PhaseCalibratedFSimGate(cirq.FSimGate(theta=np.pi / 4, phi=0.0), 0.5) +def try_convert_syc_or_sqrt_iswap_to_fsim( + gate: cirq.Gate, +) -> Optional[PhaseCalibratedFSimGate]: + """Converts a gate to equivalent PhaseCalibratedFSimGate if possible. + Args: + gate: Gate to convert. + + Returns: + Equivalent PhaseCalibratedFSimGate if its `engine_gate` is Sycamore or inverse sqrt(iSWAP) + gate. Otherwise returns None. + """ + result = try_convert_gate_to_fsim(gate) + if result is None: + return None + engine_gate = result.engine_gate + if math.isclose(engine_gate.theta, np.pi / 2) and math.isclose(engine_gate.phi, np.pi / 6): + # Sycamore gate. + return result + if math.isclose(engine_gate.theta, np.pi / 4) and math.isclose(engine_gate.phi, 0.0): + # Inverse sqrt(iSWAP) gate. + return result return None diff --git a/cirq-google/cirq_google/calibration/phased_fsim_test.py b/cirq-google/cirq_google/calibration/phased_fsim_test.py index 530fb858d8c..52c0703d636 100644 --- a/cirq-google/cirq_google/calibration/phased_fsim_test.py +++ b/cirq-google/cirq_google/calibration/phased_fsim_test.py @@ -18,6 +18,7 @@ import numpy as np import pandas as pd import pytest +import sympy from google.protobuf import text_format import cirq @@ -35,6 +36,8 @@ PhasedFSimCalibrationResult, WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, merge_matching_results, + try_convert_gate_to_fsim, + try_convert_syc_or_sqrt_iswap_to_fsim, try_convert_sqrt_iswap_to_fsim, XEBPhasedFSimCalibrationRequest, XEBPhasedFSimCalibrationOptions, @@ -765,7 +768,7 @@ def test_try_convert_sqrt_iswap_to_fsim_converts_correctly(): expected, 0.0 ) assert ( - try_convert_sqrt_iswap_to_fsim(cirq.PhasedISwapPowGate(exponent=-0.5, phase_exponent=0.1)) + try_convert_sqrt_iswap_to_fsim(cirq.PhasedISwapPowGate(exponent=-0.6, phase_exponent=0.1)) is None ) @@ -874,3 +877,117 @@ def test_options_phase_corrected_override(): ).zeta_chi_gamma_correction_override() == PhasedFSimCharacterization() ) + + +def test_try_convert_gate_to_fsim(): + def check(gate: cirq.Gate, expected: PhaseCalibratedFSimGate): + assert np.allclose(cirq.unitary(gate), cirq.unitary(expected)) + assert try_convert_gate_to_fsim(gate) == expected + + check( + cirq.FSimGate(theta=0.3, phi=0.5), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.3, phi=0.5), 0.0), + ) + + check( + cirq.FSimGate(7 * np.pi / 4, 0.0), + PhaseCalibratedFSimGate(cirq.FSimGate(np.pi / 4, 0.0), 0.5), + ) + + check( + cirq.ISwapPowGate(exponent=-0.5), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.25 * np.pi, phi=0.0), 0.0), + ) + + check( + cirq.ops.ISwapPowGate(exponent=0.8, global_shift=2.5), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.4 * np.pi, phi=0.0), 0.5), + ) + + gate = cirq.ops.ISwapPowGate(exponent=0.3, global_shift=0.4) + assert try_convert_gate_to_fsim(gate) is None + + check( + cirq.PhasedFSimGate(theta=0.2, phi=0.5, chi=1.5 * np.pi), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.2, phi=0.5), 0.25), + ) + + gate = cirq.PhasedFSimGate(theta=0.2, phi=0.5, zeta=1.5 * np.pi) + assert try_convert_gate_to_fsim(gate) is None + + check( + cirq.PhasedISwapPowGate(exponent=-0.5, phase_exponent=0.75), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.25 * np.pi, phi=0.0), 0.25), + ) + + check(cirq.CZ, PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.0, phi=np.pi), 0.0)) + + check( + cirq.ops.CZPowGate(exponent=0.3), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.0, phi=-0.3 * np.pi), 0.0), + ) + + check( + cirq.ops.CZPowGate(exponent=0.8, global_shift=2.5), + PhaseCalibratedFSimGate(cirq.FSimGate(theta=0.0, phi=-0.8 * np.pi), 0.0), + ) + + gate = cirq.ops.CZPowGate(exponent=0.3, global_shift=0.4) + assert try_convert_gate_to_fsim(gate) is None + + check( + cirq_google.ops.SYC, + PhaseCalibratedFSimGate(cirq.FSimGate(phi=np.pi / 6, theta=np.pi / 2), 0.0), + ) + + assert try_convert_gate_to_fsim(cirq.CX) is None + + # Parameterized gates are not supported. + x = sympy.Symbol('x') + assert try_convert_gate_to_fsim(cirq.ops.ISwapPowGate(exponent=x)) is None + assert try_convert_gate_to_fsim(cirq.PhasedFSimGate(theta=x)) is None + assert try_convert_gate_to_fsim(cirq.PhasedISwapPowGate(exponent=x)) is None + assert try_convert_gate_to_fsim(cirq.CZPowGate(exponent=x)) is None + + +def test_try_convert_syc_or_sqrt_iswap_to_fsim(): + def check_converts(gate: cirq.Gate): + result = try_convert_syc_or_sqrt_iswap_to_fsim(gate) + assert np.allclose(cirq.unitary(gate), cirq.unitary(result)) + + def check_none(gate: cirq.Gate): + assert try_convert_syc_or_sqrt_iswap_to_fsim(gate) is None + + check_converts(cirq_google.ops.SYC) + check_converts(cirq.FSimGate(np.pi / 2, np.pi / 6)) + check_none(cirq.FSimGate(0, np.pi)) + check_converts(cirq.FSimGate(np.pi / 4, 0.0)) + check_none(cirq.FSimGate(0.2, 0.3)) + check_converts(cirq.ISwapPowGate(exponent=0.5)) + check_converts(cirq.ISwapPowGate(exponent=-0.5)) + check_none(cirq.ISwapPowGate(exponent=0.3)) + check_converts(cirq.PhasedFSimGate(theta=np.pi / 4, phi=0.0, chi=0.7)) + check_none(cirq.PhasedFSimGate(theta=0.3, phi=0.4)) + check_converts(cirq.PhasedISwapPowGate(exponent=0.5, phase_exponent=0.75)) + check_none(cirq.PhasedISwapPowGate(exponent=0.4, phase_exponent=0.75)) + check_none(cirq.ops.CZPowGate(exponent=1.0)) + check_none(cirq.CX) + + +# Test that try_convert_gate_to_fsim is extension of try_convert_sqrt_iswap_to_fsim. +# In other words, that both function return the same result for all gates on which +# try_convert_sqrt_iswap_to_fsim is defined. +def test_gate_translators_are_consistent(): + def check(gate): + result1 = try_convert_gate_to_fsim(gate) + result2 = try_convert_sqrt_iswap_to_fsim(gate) + assert result1 == result2 + assert result1 is not None + + check(cirq.FSimGate(theta=np.pi / 4, phi=0)) + check(cirq.FSimGate(theta=-np.pi / 4, phi=0)) + check(cirq.FSimGate(theta=7 * np.pi / 4, phi=0)) + check(cirq.PhasedFSimGate(theta=np.pi / 4, phi=0)) + check(cirq.ISwapPowGate(exponent=0.5)) + check(cirq.ISwapPowGate(exponent=-0.5)) + check(cirq.PhasedISwapPowGate(exponent=0.5, phase_exponent=-0.5)) diff --git a/cirq-google/cirq_google/calibration/workflow.py b/cirq-google/cirq_google/calibration/workflow.py index de630d3416e..09c0e3e6b1c 100644 --- a/cirq-google/cirq_google/calibration/workflow.py +++ b/cirq-google/cirq_google/calibration/workflow.py @@ -40,7 +40,8 @@ WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, THETA_ZETA_GAMMA_FLOQUET_PHASED_FSIM_CHARACTERIZATION, merge_matching_results, - try_convert_sqrt_iswap_to_fsim, + try_convert_gate_to_fsim, + try_convert_syc_or_sqrt_iswap_to_fsim, PhasedFSimCalibrationOptions, RequestT, LocalXEBPhasedFSimCalibrationRequest, @@ -72,7 +73,7 @@ def prepare_characterization_for_moment( *, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, canonicalize_pairs: bool = False, sort_pairs: bool = False, ) -> Optional[RequestT]: @@ -115,7 +116,7 @@ def prepare_floquet_characterization_for_moment( options: FloquetPhasedFSimCalibrationOptions, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, canonicalize_pairs: bool = False, sort_pairs: bool = False, ) -> Optional[FloquetPhasedFSimCalibrationRequest]: @@ -270,7 +271,7 @@ def prepare_characterization_for_moments( *, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, merge_subsets: bool = True, initial: Optional[Sequence[RequestT]] = None, ) -> Tuple[CircuitWithCalibration, List[RequestT]]: @@ -344,7 +345,7 @@ def prepare_characterization_for_circuits_moments( *, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, merge_subsets: bool = True, initial: Optional[Sequence[RequestT]] = None, ) -> Tuple[List[CircuitWithCalibration], List[RequestT]]: @@ -405,7 +406,7 @@ def prepare_floquet_characterization_for_moments( options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, merge_subsets: bool = True, initial: Optional[Sequence[FloquetPhasedFSimCalibrationRequest]] = None, ) -> Tuple[CircuitWithCalibration, List[FloquetPhasedFSimCalibrationRequest]]: @@ -465,7 +466,7 @@ def prepare_characterization_for_operations( *, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, permit_mixed_moments: bool = False, ) -> List[RequestT]: """Extracts a minimal set of characterization requests necessary to characterize all the @@ -530,7 +531,7 @@ def prepare_floquet_characterization_for_operations( options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, permit_mixed_moments: bool = False, ) -> List[FloquetPhasedFSimCalibrationRequest]: """Extracts a minimal set of Floquet characterization requests necessary to characterize all the @@ -678,8 +679,12 @@ def _merge_into_calibrations( """ new_pairs = set(calibration.pairs) for index in pairs_map.values(): - assert calibration.gate == calibrations[index].gate - assert calibration.options == calibrations[index].options + can_merge = ( + calibration.gate == calibrations[index].gate + and calibration.options == calibrations[index].options + ) + if not can_merge: + continue existing_pairs = calibrations[index].pairs if new_pairs.issubset(existing_pairs): return index @@ -847,7 +852,7 @@ def make_zeta_chi_gamma_compensation_for_moments( *, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_gate_to_fsim, merge_subsets: bool = True, ) -> CircuitWithCalibration: """Compensates circuit moments against errors in zeta, chi and gamma angles. @@ -897,7 +902,7 @@ def make_zeta_chi_gamma_compensation_for_operations( characterizations: List[PhasedFSimCalibrationResult], gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_gate_to_fsim, permit_mixed_moments: bool = False, ) -> cirq.Circuit: """Compensates circuit operations against errors in zeta, chi and gamma angles. @@ -951,7 +956,6 @@ def _make_zeta_chi_gamma_compensation( gates_translator: Callable[[cirq.Gate], Optional[PhaseCalibratedFSimGate]], permit_mixed_moments: bool, ) -> CircuitWithCalibration: - if permit_mixed_moments: raise NotImplementedError('Mixed moments compensation ist supported yet') @@ -992,6 +996,12 @@ def _make_zeta_chi_gamma_compensation( if parameters is None: raise ValueError(f'Missing characterization data for moment {moment}') + if translated.engine_gate != parameters.gate: + raise ValueError( + f"Engine gate {translated.engine_gate} doesn't match characterized gate " + f'{parameters.gate}' + ) + pair_parameters = parameters.get_parameters(a, b) if pair_parameters is None: raise ValueError(f'Missing characterization data for pair {(a, b)} in {parameters}') @@ -1051,6 +1061,7 @@ def from_characterization( """Creates an operation that compensates for zeta, chi and gamma angles of the supplied gate and characterization. + Args: Args: qubits: Qubits that the gate should act on. gate_calibration: Original, imperfect gate that is supposed to run on the hardware @@ -1075,7 +1086,7 @@ def run_floquet_characterization_for_moments( options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION, gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, merge_subsets: bool = True, max_layers_per_request: int = 1, progress_func: Optional[Callable[[int, int], None]] = None, @@ -1141,7 +1152,7 @@ def run_zeta_chi_gamma_compensation_for_moments( ), gates_translator: Callable[ [cirq.Gate], Optional[PhaseCalibratedFSimGate] - ] = try_convert_sqrt_iswap_to_fsim, + ] = try_convert_syc_or_sqrt_iswap_to_fsim, merge_subsets: bool = True, max_layers_per_request: int = 1, progress_func: Optional[Callable[[int, int], None]] = None, diff --git a/cirq-google/cirq_google/calibration/workflow_test.py b/cirq-google/cirq_google/calibration/workflow_test.py index 1ac8165790b..6f773db0c6f 100644 --- a/cirq-google/cirq_google/calibration/workflow_test.py +++ b/cirq-google/cirq_google/calibration/workflow_test.py @@ -45,6 +45,10 @@ SQRT_ISWAP_INV_PARAMETERS = cirq_google.PhasedFSimCharacterization( theta=np.pi / 4, zeta=0.0, chi=0.0, gamma=0.0, phi=0.0 ) +SYCAMORE_PARAMETERS = cirq_google.PhasedFSimCharacterization( + theta=np.pi / 2, zeta=0.0, chi=0.0, gamma=0.0, phi=np.pi / 6 +) +SQRT_ISWAP_GATE = cirq.FSimGate(7 * np.pi / 4, 0.0) SQRT_ISWAP_INV_GATE = cirq.FSimGate(np.pi / 4, 0.0) @@ -1232,19 +1236,36 @@ def test_phase_corrected_fsim_operations_with_phase_exponent( def test_make_zeta_chi_gamma_compensation_for_moments(): a, b = cirq.LineQubit.range(2) - characterizations = [ - PhasedFSimCalibrationResult( - parameters={(a, b): SQRT_ISWAP_INV_PARAMETERS}, - gate=SQRT_ISWAP_INV_GATE, - options=ALL_ANGLES_FLOQUET_PHASED_FSIM_CHARACTERIZATION, - ) - ] moment_allocations = [0] - for circuit in [ - cirq.Circuit(cirq.FSimGate(theta=np.pi / 4, phi=0.0).on(a, b)), - cirq.Circuit(cirq.FSimGate(theta=-np.pi / 4, phi=0.0).on(a, b)), + for gate_to_calibrate, engine_gate in [ + (cirq.FSimGate(theta=np.pi / 4, phi=0.0), SQRT_ISWAP_INV_GATE), + (cirq.FSimGate(theta=-np.pi / 4, phi=0.0), SQRT_ISWAP_INV_GATE), + (cirq.ISwapPowGate(exponent=0.2), cirq.FSimGate(theta=0.1 * np.pi, phi=0.0)), + (cirq.PhasedFSimGate(theta=0.1, phi=0.2), cirq.FSimGate(theta=0.1, phi=0.2)), + (cirq.PhasedFSimGate(theta=0.1, phi=0.2, chi=0.3), cirq.FSimGate(theta=0.1, phi=0.2)), + (cirq.PhasedISwapPowGate(exponent=0.2), cirq.FSimGate(theta=0.1 * np.pi, phi=0.0)), + ( + cirq.PhasedISwapPowGate(exponent=0.2, phase_exponent=0.4), + cirq.FSimGate(theta=0.1 * np.pi, phi=0.0), + ), + (cirq.CZ, cirq.FSimGate(theta=0.0, phi=np.pi)), + (cirq.ops.CZPowGate(exponent=0.5), cirq.FSimGate(theta=0.0, phi=1.5 * np.pi)), + (cirq_google.ops.SycamoreGate(), cirq.FSimGate(theta=np.pi / 2, phi=np.pi / 6)), ]: + circuit = cirq.Circuit(gate_to_calibrate.on(a, b)) + characterizations = [ + PhasedFSimCalibrationResult( + # Assume that the engine gate is perfect. + parameters={ + (a, b): cirq_google.PhasedFSimCharacterization( + theta=engine_gate.theta, phi=engine_gate.phi, zeta=0.0, chi=0.0, gamma=0.0 + ) + }, + gate=engine_gate, + options=ALL_ANGLES_FLOQUET_PHASED_FSIM_CHARACTERIZATION, + ) + ] calibrated_circuit = workflow.make_zeta_chi_gamma_compensation_for_moments( workflow.CircuitWithCalibration(circuit, moment_allocations), characterizations ) @@ -1252,6 +1273,26 @@ def test_make_zeta_chi_gamma_compensation_for_moments(): assert calibrated_circuit.moment_to_calibration == [None, 0, None] +def test_make_zeta_chi_gamma_compensation_for_moments_wrong_engine_gate_error(): + a, b = cirq.LineQubit.range(2) + circuit = cirq.Circuit(cirq.FSimGate(theta=np.pi / 4, phi=0.2).on(a, b)) + characterizations = [ + PhasedFSimCalibrationResult( + parameters={ + (a, b): cirq_google.PhasedFSimCharacterization( + theta=np.pi / 4, phi=0.2, zeta=0.0, chi=0.0, gamma=0.0 + ) + }, + gate=SQRT_ISWAP_INV_GATE, + options=ALL_ANGLES_FLOQUET_PHASED_FSIM_CHARACTERIZATION, + ) + ] + with pytest.raises(ValueError, match="Engine gate .+ doesn't match characterized gate .+"): + workflow.make_zeta_chi_gamma_compensation_for_moments( + workflow.CircuitWithCalibration(circuit, [0]), characterizations + ) + + def test_make_zeta_chi_gamma_compensation_for_moments_circuit(): a, b = cirq.LineQubit.range(2) @@ -1318,7 +1359,7 @@ def test_zmake_zeta_chi_gamma_compensation_for_moments_invalid_argument_fails() with pytest.raises(workflow.IncompatibleMomentError): circuit_with_calibration = workflow.CircuitWithCalibration( - cirq.Circuit(cirq.CZ.on(a, b)), [None] + cirq.Circuit(cirq.CX.on(a, b)), [None] ) workflow.make_zeta_chi_gamma_compensation_for_moments(circuit_with_calibration, []) @@ -1342,17 +1383,93 @@ def test_zmake_zeta_chi_gamma_compensation_for_moments_invalid_argument_fails() ) +def test_make_zeta_chi_gamma_compensation_for_moments_imperfect_gates(): + params_cz_ab = cirq_google.PhasedFSimCharacterization( + zeta=0.02, chi=0.05, gamma=0.04, theta=0.0, phi=np.pi + ) + params_cz_cd = cirq_google.PhasedFSimCharacterization( + zeta=0.03, chi=0.08, gamma=0.03, theta=0.0, phi=np.pi + ) + params_syc_ab = cirq_google.PhasedFSimCharacterization( + zeta=0.01, chi=0.09, gamma=0.02, theta=np.pi / 2, phi=np.pi / 6 + ) + params_sqrt_iswap_ac = cirq_google.PhasedFSimCharacterization( + zeta=0.05, chi=0.06, gamma=0.07, theta=np.pi / 4, phi=0.0 + ) + params_sqrt_iswap_bd = cirq_google.PhasedFSimCharacterization( + zeta=0.01, chi=0.02, gamma=0.03, theta=np.pi / 4, phi=0.0 + ) + + a, b, c, d = cirq.LineQubit.range(4) + engine_simulator = cirq_google.PhasedFSimEngineSimulator.create_from_dictionary( + parameters={ + (a, b): { + cirq.FSimGate(theta=0, phi=np.pi): params_cz_ab, + cirq_google.SYC: params_syc_ab, + }, + (c, d): {cirq.FSimGate(theta=0, phi=np.pi): params_cz_cd}, + (a, c): {SQRT_ISWAP_INV_GATE: params_sqrt_iswap_ac}, + (b, d): {SQRT_ISWAP_INV_GATE: params_sqrt_iswap_bd}, + } + ) + + circuit = cirq.Circuit( + [ + [cirq.X(a), cirq.H(c)], + [cirq.CZ.on(a, b), cirq.CZ.on(d, c)], + [cirq_google.SYC.on(a, b)], + [SQRT_ISWAP_GATE.on(a, c), SQRT_ISWAP_INV_GATE.on(b, d)], + ] + ) + + options = cirq_google.FloquetPhasedFSimCalibrationOptions( + characterize_theta=False, + characterize_zeta=True, + characterize_chi=True, + characterize_gamma=True, + characterize_phi=False, + ) + + characterizations = [ + cirq_google.PhasedFSimCalibrationResult( + gate=cirq.FSimGate(theta=0, phi=np.pi), + parameters={(a, b): params_cz_ab, (c, d): params_cz_cd}, + options=options, + ), + cirq_google.PhasedFSimCalibrationResult( + gate=cirq.FSimGate(theta=np.pi / 2, phi=np.pi / 6), + parameters={(a, b): params_syc_ab}, + options=options, + ), + cirq_google.PhasedFSimCalibrationResult( + gate=cirq.FSimGate(theta=np.pi / 4, phi=0), + parameters={(a, c): params_sqrt_iswap_ac, (b, d): params_sqrt_iswap_bd}, + options=options, + ), + ] + + circuit_with_calibration = workflow.make_zeta_chi_gamma_compensation_for_moments( + circuit, + characterizations, + ) + + assert cirq.allclose_up_to_global_phase( + engine_simulator.final_state_vector(circuit_with_calibration.circuit), + cirq.final_state_vector(circuit), + ) + + def test_run_zeta_chi_gamma_calibration_for_moments() -> None: parameters_ab = cirq_google.PhasedFSimCharacterization(zeta=0.5, chi=0.4, gamma=0.3) parameters_bc = cirq_google.PhasedFSimCharacterization(zeta=-0.5, chi=-0.4, gamma=-0.3) parameters_cd = cirq_google.PhasedFSimCharacterization(zeta=0.2, chi=0.3, gamma=0.4) a, b, c, d = cirq.LineQubit.range(4) - engine_simulator = cirq_google.PhasedFSimEngineSimulator.create_from_dictionary_sqrt_iswap( + engine_simulator = cirq_google.PhasedFSimEngineSimulator.create_from_dictionary( parameters={ - (a, b): parameters_ab.merge_with(SQRT_ISWAP_INV_PARAMETERS), - (b, c): parameters_bc.merge_with(SQRT_ISWAP_INV_PARAMETERS), - (c, d): parameters_cd.merge_with(SQRT_ISWAP_INV_PARAMETERS), + (a, b): {SQRT_ISWAP_INV_GATE: parameters_ab.merge_with(SQRT_ISWAP_INV_PARAMETERS)}, + (b, c): {cirq_google.ops.SYC: parameters_bc.merge_with(SYCAMORE_PARAMETERS)}, + (c, d): {SQRT_ISWAP_INV_GATE: parameters_cd.merge_with(SQRT_ISWAP_INV_PARAMETERS)}, } ) @@ -1360,7 +1477,7 @@ def test_run_zeta_chi_gamma_calibration_for_moments() -> None: [ [cirq.X(a), cirq.Y(c)], [SQRT_ISWAP_INV_GATE.on(a, b), SQRT_ISWAP_INV_GATE.on(c, d)], - [SQRT_ISWAP_INV_GATE.on(b, c)], + [cirq_google.ops.SYC.on(b, c)], ] ) @@ -1376,7 +1493,7 @@ def test_run_zeta_chi_gamma_calibration_for_moments() -> None: circuit, engine_simulator, processor_id=None, - gate_set=cirq_google.SQRT_ISWAP_GATESET, + gate_set=cirq_google.FSIM_GATESET, options=options, ) @@ -1391,7 +1508,7 @@ def test_run_zeta_chi_gamma_calibration_for_moments() -> None: options=options, ), cirq_google.PhasedFSimCalibrationResult( - gate=SQRT_ISWAP_INV_GATE, parameters={(b, c): parameters_bc}, options=options + gate=cirq_google.ops.SYC, parameters={(b, c): parameters_bc}, options=options ), ] assert calibrated_circuit.moment_to_calibration == [None, None, 0, None, None, 1, None] diff --git a/cirq-google/cirq_google/common_serializers.py b/cirq-google/cirq_google/common_serializers.py index 9bf0d48d806..6b34882170b 100644 --- a/cirq-google/cirq_google/common_serializers.py +++ b/cirq-google/cirq_google/common_serializers.py @@ -30,6 +30,7 @@ import cirq from cirq_google import op_deserializer, op_serializer from cirq_google.api import v2 +from cirq_google.experimental.ops import CouplerPulse from cirq_google.ops import PhysicalZTag # Type strings used in serialization for the two types of Z operations @@ -652,6 +653,66 @@ def _can_serialize_limited_iswap(exponent: float): # ############################################# +# +# Coupler Pulse serializer and deserializer +# + +COUPLER_PULSE_SERIALIZER = op_serializer.GateOpSerializer( + gate_type=CouplerPulse, + serialized_gate_id='coupler_pulse', + args=[ + op_serializer.SerializingArg( + serialized_name='coupling_mhz', serialized_type=float, op_getter='coupling_mhz' + ), + op_serializer.SerializingArg( + serialized_name='hold_time_ns', + serialized_type=float, + op_getter=lambda op: cast(CouplerPulse, op.gate).hold_time.total_nanos(), + ), + op_serializer.SerializingArg( + serialized_name='rise_time_ns', + serialized_type=float, + op_getter=lambda op: cast(CouplerPulse, op.gate).rise_time.total_nanos(), + ), + op_serializer.SerializingArg( + serialized_name='padding_time_ns', + serialized_type=float, + op_getter=lambda op: cast(CouplerPulse, op.gate).padding_time.total_nanos(), + ), + ], +) +COUPLER_PULSE_DESERIALIZER = op_deserializer.GateOpDeserializer( + serialized_gate_id='coupler_pulse', + gate_constructor=CouplerPulse, + args=[ + op_deserializer.DeserializingArg( + serialized_name='coupling_mhz', + constructor_arg_name='coupling_mhz', + ), + op_deserializer.DeserializingArg( + serialized_name='hold_time_ns', + constructor_arg_name='hold_time', + value_func=lambda nanos: cirq.Duration( + nanos=cast(Union[int, float, sympy.Basic], nanos) + ), + ), + op_deserializer.DeserializingArg( + serialized_name='rise_time_ns', + constructor_arg_name='rise_time', + value_func=lambda nanos: cirq.Duration( + nanos=cast(Union[int, float, sympy.Basic], nanos) + ), + ), + op_deserializer.DeserializingArg( + serialized_name='padding_time_ns', + constructor_arg_name='padding_time', + value_func=lambda nanos: cirq.Duration( + nanos=cast(Union[int, float, sympy.Basic], nanos) + ), + ), + ], +) + # # WaitGate serializer and deserializer # diff --git a/cirq-google/cirq_google/common_serializers_test.py b/cirq-google/cirq_google/common_serializers_test.py index 31276788cd7..2914bfdcda9 100644 --- a/cirq-google/cirq_google/common_serializers_test.py +++ b/cirq-google/cirq_google/common_serializers_test.py @@ -484,6 +484,34 @@ def test_cz_pow_non_integer_does_not_serialize(): gate_set.serialize_op(cirq.CZ(q1, q2) ** 0.5) +def test_coupler_pulse(): + gate_set = cg.SerializableGateSet( + 'test', [cgc.COUPLER_PULSE_SERIALIZER], [cgc.COUPLER_PULSE_DESERIALIZER] + ) + proto = op_proto( + { + 'gate': {'id': 'coupler_pulse'}, + 'args': { + 'hold_time_ns': {'arg_value': {'float_value': 16.0}}, + 'padding_time_ns': {'arg_value': {'float_value': 12.0}}, + 'rise_time_ns': {'arg_value': {'float_value': 32.0}}, + 'coupling_mhz': {'arg_value': {'float_value': 20.0}}, + }, + 'qubits': [{'id': '1_2'}, {'id': '2_2'}], + } + ) + q = cirq.GridQubit(1, 2) + q2 = cirq.GridQubit(2, 2) + op = cg.experimental.ops.CouplerPulse( + hold_time=cirq.Duration(nanos=16), + padding_time=cirq.Duration(nanos=12), + rise_time=cirq.Duration(nanos=32), + coupling_mhz=20, + )(q, q2) + assert gate_set.serialize_op(op) == proto + assert gate_set.deserialize_op(proto) == op + + def test_wait_gate(): gate_set = cg.SerializableGateSet( 'test', [cgc.WAIT_GATE_SERIALIZER], [cgc.WAIT_GATE_DESERIALIZER] diff --git a/cirq-google/cirq_google/conftest.py b/cirq-google/cirq_google/conftest.py new file mode 100644 index 00000000000..f7ba25dbfcd --- /dev/null +++ b/cirq-google/cirq_google/conftest.py @@ -0,0 +1,5 @@ +import os + + +def pytest_configure(config): + os.environ['CIRQ_TESTING'] = "true" diff --git a/cirq-google/cirq_google/devices/known_devices_test.py b/cirq-google/cirq_google/devices/known_devices_test.py index ca88568eb01..58dd392a262 100644 --- a/cirq-google/cirq_google/devices/known_devices_test.py +++ b/cirq-google/cirq_google/devices/known_devices_test.py @@ -484,22 +484,22 @@ def test_sycamore_devices(device): def test_sycamore_circuitop_device(): - circuitop_gateset = cirq.google.serializable_gate_set.SerializableGateSet( + circuitop_gateset = cirq_google.serializable_gate_set.SerializableGateSet( gate_set_name='circuitop_gateset', serializers=[cgc.CIRCUIT_OP_SERIALIZER], deserializers=[cgc.CIRCUIT_OP_DESERIALIZER], ) gateset_list = [ - cirq.google.gate_sets.SQRT_ISWAP_GATESET, - cirq.google.gate_sets.SYC_GATESET, + cirq_google.gate_sets.SQRT_ISWAP_GATESET, + cirq_google.gate_sets.SYC_GATESET, circuitop_gateset, ] - circuitop_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram( + circuitop_proto = cirq_google.devices.known_devices.create_device_proto_from_diagram( known_devices._SYCAMORE23_GRID, gateset_list, known_devices._SYCAMORE_DURATIONS_PICOS, ) - device = cirq.google.SerializableDevice.from_proto( + device = cirq_google.SerializableDevice.from_proto( proto=circuitop_proto, gate_sets=gateset_list, ) @@ -533,12 +533,12 @@ def test_sycamore_grid_layout(): def test_proto_with_circuitop(): - circuitop_gateset = cirq.google.serializable_gate_set.SerializableGateSet( + circuitop_gateset = cirq_google.serializable_gate_set.SerializableGateSet( gate_set_name='circuitop_gateset', serializers=[cgc.CIRCUIT_OP_SERIALIZER], deserializers=[cgc.CIRCUIT_OP_DESERIALIZER], ) - circuitop_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram( + circuitop_proto = cirq_google.devices.known_devices.create_device_proto_from_diagram( "aa\naa", [circuitop_gateset], ) diff --git a/cirq-google/cirq_google/devices/serializable_device.py b/cirq-google/cirq_google/devices/serializable_device.py index df4c1a8fbc7..56e76b2f257 100644 --- a/cirq-google/cirq_google/devices/serializable_device.py +++ b/cirq-google/cirq_google/devices/serializable_device.py @@ -180,28 +180,16 @@ def from_proto( gates_by_type[internal_type].append(gate_def) return SerializableDevice( - qubits=[cls._qid_from_str(q) for q in proto.valid_qubits], + qubits=[_qid_from_str(q) for q in proto.valid_qubits], gate_definitions=gates_by_type, ) - @staticmethod - def _qid_from_str(id_str: str) -> cirq.Qid: - """Translates a qubit id string info cirq.Qid objects. - - Tries to translate to GridQubit if possible (e.g. '4_3'), otherwise - falls back to using NamedQubit. - """ - try: - return v2.grid_qubit_from_proto_id(id_str) - except ValueError: - return v2.named_qubit_from_proto_id(id_str) - @classmethod def _create_target_set(cls, ts: v2.device_pb2.TargetSet) -> Set[Tuple[cirq.Qid, ...]]: """Transform a TargetSet proto into a set of qubit tuples""" target_set = set() for target in ts.targets: - qid_tuple = tuple(cls._qid_from_str(q) for q in target.ids) + qid_tuple = tuple(_qid_from_str(q) for q in target.ids) target_set.add(qid_tuple) if ts.target_ordering == v2.device_pb2.TargetSet.SYMMETRIC: target_set.add(qid_tuple[::-1]) @@ -335,3 +323,15 @@ def validate_operation(self, operation: cirq.Operation) -> None: if qubit_tuple not in gate_def.target_set: # Target is not within the target sets specified by the gate. raise ValueError(f'Operation does not use valid qubit target: {operation}.') + + +def _qid_from_str(id_str: str) -> cirq.Qid: + """Translates a qubit id string info cirq.Qid objects. + + Tries to translate to GridQubit if possible (e.g. '4_3'), otherwise + falls back to using NamedQubit. + """ + try: + return v2.grid_qubit_from_proto_id(id_str) + except ValueError: + return v2.named_qubit_from_proto_id(id_str) diff --git a/cirq-google/cirq_google/engine/engine.py b/cirq-google/cirq_google/engine/engine.py index 0d694b73e79..1b645c06afb 100644 --- a/cirq-google/cirq_google/engine/engine.py +++ b/cirq-google/cirq_google/engine/engine.py @@ -33,6 +33,7 @@ from google.protobuf import any_pb2 import cirq +from cirq_google.engine import engine_client from cirq_google.engine.client import quantum from cirq_google.engine.result_type import ResultType from cirq_google import serializable_gate_set as sgs @@ -658,8 +659,8 @@ def list_programs( ) return [ engine_program.EngineProgram( - project_id=client._ids_from_program_name(p.name)[0], - program_id=client._ids_from_program_name(p.name)[1], + project_id=engine_client._ids_from_program_name(p.name)[0], + program_id=engine_client._ids_from_program_name(p.name)[1], _program=p, context=self.context, ) @@ -709,9 +710,9 @@ def list_jobs( ) return [ engine_job.EngineJob( - project_id=client._ids_from_job_name(j.name)[0], - program_id=client._ids_from_job_name(j.name)[1], - job_id=client._ids_from_job_name(j.name)[2], + project_id=engine_client._ids_from_job_name(j.name)[0], + program_id=engine_client._ids_from_job_name(j.name)[1], + job_id=engine_client._ids_from_job_name(j.name)[2], context=self.context, _job=j, ) @@ -731,7 +732,7 @@ def list_processors(self) -> List[engine_processor.EngineProcessor]: return [ engine_processor.EngineProcessor( self.project_id, - self.context.client._ids_from_processor_name(p.name)[1], + engine_client._ids_from_processor_name(p.name)[1], self.context, p, ) diff --git a/cirq-google/cirq_google/engine/engine_client.py b/cirq-google/cirq_google/engine/engine_client.py index cf6ce4bc20f..477d97f1e31 100644 --- a/cirq-google/cirq_google/engine/engine_client.py +++ b/cirq-google/cirq_google/engine/engine_client.py @@ -72,81 +72,6 @@ def __init__( warnings.simplefilter('ignore') self.grpc_client = quantum.QuantumEngineServiceClient(**service_args) - @staticmethod - def _project_name(project_id: str) -> str: - return f'projects/{project_id}' - - @staticmethod - def _program_name_from_ids(project_id: str, program_id: str) -> str: - return f'projects/{project_id}/programs/{program_id}' - - @staticmethod - def _job_name_from_ids(project_id: str, program_id: str, job_id: str) -> str: - return f'projects/{project_id}/programs/{program_id}/jobs/{job_id}' - - @staticmethod - def _processor_name_from_ids(project_id: str, processor_id: str) -> str: - return f'projects/{project_id}/processors/{processor_id}' - - @staticmethod - def _calibration_name_from_ids( - project_id: str, processor_id: str, calibration_time_seconds: int - ) -> str: - return 'projects/%s/processors/%s/calibrations/%d' % ( - project_id, - processor_id, - calibration_time_seconds, - ) - - @staticmethod - def _reservation_name_from_ids(project_id: str, processor_id: str, reservation_id: str) -> str: - return 'projects/%s/processors/%s/reservations/%s' % ( - project_id, - processor_id, - reservation_id, - ) - - @staticmethod - def _ids_from_program_name(program_name: str) -> Tuple[str, str]: - parts = program_name.split('/') - return parts[1], parts[3] - - @staticmethod - def _ids_from_job_name(job_name: str) -> Tuple[str, str, str]: - parts = job_name.split('/') - return parts[1], parts[3], parts[5] - - @staticmethod - def _ids_from_processor_name(processor_name: str) -> Tuple[str, str]: - parts = processor_name.split('/') - return parts[1], parts[3] - - @staticmethod - def _ids_from_calibration_name(calibration_name: str) -> Tuple[str, str, int]: - parts = calibration_name.split('/') - return parts[1], parts[3], int(parts[5]) - - @staticmethod - def _date_or_time_to_filter_expr( - param_name: str, param: Union[datetime.datetime, datetime.date] - ): - """Formats datetime or date to filter expressions. - - Args: - arg_name: the name of the filter parameter (for error messaging) - param: the value of the paramter - """ - if isinstance(param, datetime.datetime): - return f"{int(param.timestamp())}" - elif isinstance(param, datetime.date): - return f"{param.isoformat()}" - - raise ValueError( - f"Unsupported date/time type for {param_name}: got {param} of " - f"type {type(param)}. Supported types: datetime.datetime and" - f"datetime.date" - ) - def _make_request(self, request: Callable[[], _R]) -> _R: # Start with a 100ms retry delay with exponential backoff to # max_retry_delay_seconds @@ -191,8 +116,8 @@ def create_program( Tuple of created program id and program """ - parent_name = self._project_name(project_id) - program_name = self._program_name_from_ids(project_id, program_id) if program_id else '' + parent_name = _project_name(project_id) + program_name = _program_name_from_ids(project_id, program_id) if program_id else '' request = qtypes.QuantumProgram(name=program_name, code=code) if description: request.description = description @@ -202,7 +127,7 @@ def create_program( program = self._make_request( lambda: self.grpc_client.create_quantum_program(parent_name, request, False) ) - return self._ids_from_program_name(program.name)[1], program + return _ids_from_program_name(program.name)[1], program def get_program( self, project_id: str, program_id: str, return_code: bool @@ -216,7 +141,7 @@ def get_program( """ return self._make_request( lambda: self.grpc_client.get_quantum_program( - self._program_name_from_ids(project_id, program_id), return_code + _program_name_from_ids(project_id, program_id), return_code ) ) @@ -246,17 +171,17 @@ def list_programs( filters = [] if created_after is not None: - val = self._date_or_time_to_filter_expr('created_after', created_after) + val = _date_or_time_to_filter_expr('created_after', created_after) filters.append(f"create_time >= {val}") if created_before is not None: - val = self._date_or_time_to_filter_expr('created_before', created_before) + val = _date_or_time_to_filter_expr('created_before', created_before) filters.append(f"create_time <= {val}") if has_labels is not None: for (k, v) in has_labels.items(): filters.append(f"labels.{k}:{v}") return self._make_request( lambda: self.grpc_client.list_quantum_programs( - self._project_name(project_id), filter_=" AND ".join(filters) + _project_name(project_id), filter_=" AND ".join(filters) ) ) @@ -273,7 +198,7 @@ def set_program_description( Returns: The updated quantum program. """ - program_resource_name = self._program_name_from_ids(project_id, program_id) + program_resource_name = _program_name_from_ids(project_id, program_id) return self._make_request( lambda: self.grpc_client.update_quantum_program( program_resource_name, @@ -285,7 +210,7 @@ def set_program_description( def _set_program_labels( self, project_id: str, program_id: str, labels: Dict[str, str], fingerprint: str ) -> qtypes.QuantumProgram: - program_resource_name = self._program_name_from_ids(project_id, program_id) + program_resource_name = _program_name_from_ids(project_id, program_id) return self._make_request( lambda: self.grpc_client.update_quantum_program( program_resource_name, @@ -370,7 +295,7 @@ def delete_program(self, project_id: str, program_id: str, delete_jobs: bool = F """ self._make_request( lambda: self.grpc_client.delete_quantum_program( - self._program_name_from_ids(project_id, program_id), delete_jobs + _program_name_from_ids(project_id, program_id), delete_jobs ) ) @@ -405,13 +330,13 @@ def create_job( raise ValueError('priority must be between 0 and 1000') # Create job. - job_name = self._job_name_from_ids(project_id, program_id, job_id) if job_id else '' + job_name = _job_name_from_ids(project_id, program_id, job_id) if job_id else '' request = qtypes.QuantumJob( name=job_name, scheduling_config=qtypes.SchedulingConfig( processor_selector=qtypes.SchedulingConfig.ProcessorSelector( processor_names=[ - self._processor_name_from_ids(project_id, processor_id) + _processor_name_from_ids(project_id, processor_id) for processor_id in processor_ids ] ) @@ -426,10 +351,10 @@ def create_job( request.labels.update(labels) job = self._make_request( lambda: self.grpc_client.create_quantum_job( - self._program_name_from_ids(project_id, program_id), request, False + _program_name_from_ids(project_id, program_id), request, False ) ) - return self._ids_from_job_name(job.name)[2], job + return _ids_from_job_name(job.name)[2], job def list_jobs( self, @@ -466,10 +391,10 @@ def list_jobs( filters = [] if created_after is not None: - val = self._date_or_time_to_filter_expr('created_after', created_after) + val = _date_or_time_to_filter_expr('created_after', created_after) filters.append(f"create_time >= {val}") if created_before is not None: - val = self._date_or_time_to_filter_expr('created_before', created_before) + val = _date_or_time_to_filter_expr('created_before', created_before) filters.append(f"create_time <= {val}") if has_labels is not None: for (k, v) in has_labels.items(): @@ -482,7 +407,7 @@ def list_jobs( if program_id is None: program_id = "-" - parent = self._program_name_from_ids(project_id, program_id) + parent = _program_name_from_ids(project_id, program_id) return self._make_request( lambda: self.grpc_client.list_quantum_jobs(parent, filter_=" AND ".join(filters)) ) @@ -502,7 +427,7 @@ def get_job( """ return self._make_request( lambda: self.grpc_client.get_quantum_job( - self._job_name_from_ids(project_id, program_id, job_id), return_run_context + _job_name_from_ids(project_id, program_id, job_id), return_run_context ) ) @@ -520,7 +445,7 @@ def set_job_description( Returns: The updated quantum job. """ - job_resource_name = self._job_name_from_ids(project_id, program_id, job_id) + job_resource_name = _job_name_from_ids(project_id, program_id, job_id) return self._make_request( lambda: self.grpc_client.update_quantum_job( job_resource_name, @@ -537,7 +462,7 @@ def _set_job_labels( labels: Dict[str, str], fingerprint: str, ) -> qtypes.QuantumJob: - job_resource_name = self._job_name_from_ids(project_id, program_id, job_id) + job_resource_name = _job_name_from_ids(project_id, program_id, job_id) return self._make_request( lambda: self.grpc_client.update_quantum_job( job_resource_name, @@ -623,7 +548,7 @@ def delete_job(self, project_id: str, program_id: str, job_id: str) -> None: """ self._make_request( lambda: self.grpc_client.delete_quantum_job( - self._job_name_from_ids(project_id, program_id, job_id) + _job_name_from_ids(project_id, program_id, job_id) ) ) @@ -637,7 +562,7 @@ def cancel_job(self, project_id: str, program_id: str, job_id: str) -> None: """ self._make_request( lambda: self.grpc_client.cancel_quantum_job( - self._job_name_from_ids(project_id, program_id, job_id) + _job_name_from_ids(project_id, program_id, job_id) ) ) @@ -656,7 +581,7 @@ def get_job_results( """ return self._make_request( lambda: self.grpc_client.get_quantum_result( - self._job_name_from_ids(project_id, program_id, job_id) + _job_name_from_ids(project_id, program_id, job_id) ) ) @@ -672,9 +597,7 @@ def list_processors(self, project_id: str) -> List[qtypes.QuantumProcessor]: A list of metadata of each processor. """ response = self._make_request( - lambda: self.grpc_client.list_quantum_processors( - self._project_name(project_id), filter_='' - ) + lambda: self.grpc_client.list_quantum_processors(_project_name(project_id), filter_='') ) return list(response) @@ -690,7 +613,7 @@ def get_processor(self, project_id: str, processor_id: str) -> qtypes.QuantumPro """ return self._make_request( lambda: self.grpc_client.get_quantum_processor( - self._processor_name_from_ids(project_id, processor_id) + _processor_name_from_ids(project_id, processor_id) ) ) @@ -712,7 +635,7 @@ def list_calibrations( """ response = self._make_request( lambda: self.grpc_client.list_quantum_calibrations( - self._processor_name_from_ids(project_id, processor_id), filter_=filter_str + _processor_name_from_ids(project_id, processor_id), filter_=filter_str ) ) return list(response) @@ -733,9 +656,7 @@ def get_calibration( """ return self._make_request( lambda: self.grpc_client.get_quantum_calibration( - self._calibration_name_from_ids( - project_id, processor_id, calibration_timestamp_seconds - ) + _calibration_name_from_ids(project_id, processor_id, calibration_timestamp_seconds) ) ) @@ -755,8 +676,7 @@ def get_current_calibration( try: return self._make_request( lambda: self.grpc_client.get_quantum_calibration( - self._processor_name_from_ids(project_id, processor_id) - + '/calibrations/current' + _processor_name_from_ids(project_id, processor_id) + '/calibrations/current' ) ) except EngineException as err: @@ -783,7 +703,7 @@ def create_reservation( end: the ending time of the reservation as a datetime object whitelisted_users: a list of emails that can use the reservation. """ - parent = self._processor_name_from_ids(project_id, processor_id) + parent = _processor_name_from_ids(project_id, processor_id) reservation = qtypes.QuantumReservation( name='', start_time=Timestamp(seconds=int(start.timestamp())), @@ -816,7 +736,7 @@ def cancel_reservation(self, project_id: str, processor_id: str, reservation_id: processor_id: The processor unique identifier. reservation_id: Unique ID of the reservation in the parent project, """ - name = self._reservation_name_from_ids(project_id, processor_id, reservation_id) + name = _reservation_name_from_ids(project_id, processor_id, reservation_id) return self._make_request(lambda: self.grpc_client.cancel_quantum_reservation(name=name)) def delete_reservation(self, project_id: str, processor_id: str, reservation_id: str): @@ -834,7 +754,7 @@ def delete_reservation(self, project_id: str, processor_id: str, reservation_id: processor_id: The processor unique identifier. reservation_id: Unique ID of the reservation in the parent project, """ - name = self._reservation_name_from_ids(project_id, processor_id, reservation_id) + name = _reservation_name_from_ids(project_id, processor_id, reservation_id) return self._make_request(lambda: self.grpc_client.delete_quantum_reservation(name=name)) def get_reservation(self, project_id: str, processor_id: str, reservation_id: str): @@ -846,7 +766,7 @@ def get_reservation(self, project_id: str, processor_id: str, reservation_id: st reservation_id: Unique ID of the reservation in the parent project, """ try: - name = self._reservation_name_from_ids(project_id, processor_id, reservation_id) + name = _reservation_name_from_ids(project_id, processor_id, reservation_id) return self._make_request(lambda: self.grpc_client.get_quantum_reservation(name=name)) except EngineException as err: if isinstance(err.__cause__, NotFound): @@ -876,7 +796,7 @@ def list_reservations( """ response = self._make_request( lambda: self.grpc_client.list_quantum_reservations( - self._processor_name_from_ids(project_id, processor_id), filter_=filter_str + _processor_name_from_ids(project_id, processor_id), filter_=filter_str ) ) @@ -908,7 +828,7 @@ def update_reservation( will leave the value unchanged. """ name = ( - self._reservation_name_from_ids(project_id, processor_id, reservation_id) + _reservation_name_from_ids(project_id, processor_id, reservation_id) if reservation_id else '' ) @@ -952,7 +872,80 @@ def list_time_slots( """ response = self._make_request( lambda: self.grpc_client.list_quantum_time_slots( - self._processor_name_from_ids(project_id, processor_id), filter_=filter_str + _processor_name_from_ids(project_id, processor_id), filter_=filter_str ) ) return list(response) + + +def _project_name(project_id: str) -> str: + return f'projects/{project_id}' + + +def _program_name_from_ids(project_id: str, program_id: str) -> str: + return f'projects/{project_id}/programs/{program_id}' + + +def _job_name_from_ids(project_id: str, program_id: str, job_id: str) -> str: + return f'projects/{project_id}/programs/{program_id}/jobs/{job_id}' + + +def _processor_name_from_ids(project_id: str, processor_id: str) -> str: + return f'projects/{project_id}/processors/{processor_id}' + + +def _calibration_name_from_ids( + project_id: str, processor_id: str, calibration_time_seconds: int +) -> str: + return 'projects/%s/processors/%s/calibrations/%d' % ( + project_id, + processor_id, + calibration_time_seconds, + ) + + +def _reservation_name_from_ids(project_id: str, processor_id: str, reservation_id: str) -> str: + return 'projects/%s/processors/%s/reservations/%s' % ( + project_id, + processor_id, + reservation_id, + ) + + +def _ids_from_program_name(program_name: str) -> Tuple[str, str]: + parts = program_name.split('/') + return parts[1], parts[3] + + +def _ids_from_job_name(job_name: str) -> Tuple[str, str, str]: + parts = job_name.split('/') + return parts[1], parts[3], parts[5] + + +def _ids_from_processor_name(processor_name: str) -> Tuple[str, str]: + parts = processor_name.split('/') + return parts[1], parts[3] + + +def _ids_from_calibration_name(calibration_name: str) -> Tuple[str, str, int]: + parts = calibration_name.split('/') + return parts[1], parts[3], int(parts[5]) + + +def _date_or_time_to_filter_expr(param_name: str, param: Union[datetime.datetime, datetime.date]): + """Formats datetime or date to filter expressions. + + Args: + arg_name: the name of the filter parameter (for error messaging) + param: the value of the paramter + """ + if isinstance(param, datetime.datetime): + return f"{int(param.timestamp())}" + elif isinstance(param, datetime.date): + return f"{param.isoformat()}" + + raise ValueError( + f"Unsupported date/time type for {param_name}: got {param} of " + f"type {type(param)}. Supported types: datetime.datetime and" + f"datetime.date" + ) diff --git a/cirq-google/cirq_google/engine/engine_job.py b/cirq-google/cirq_google/engine/engine_job.py index fcfafe6a7a1..6215f000b20 100644 --- a/cirq-google/cirq_google/engine/engine_job.py +++ b/cirq-google/cirq_google/engine/engine_job.py @@ -18,7 +18,7 @@ from typing import Dict, Iterator, List, Optional, overload, Tuple, TYPE_CHECKING import cirq -from cirq_google.engine import calibration +from cirq_google.engine import calibration, engine_client from cirq_google.engine.calibration_result import CalibrationResult from cirq_google.engine.client import quantum from cirq_google.engine.result_type import ResultType @@ -189,7 +189,7 @@ def remove_labels(self, keys: List[str]) -> 'EngineJob': def processor_ids(self) -> List[str]: """Returns the processor ids provided when the job was created.""" return [ - self.context.client._ids_from_processor_name(p)[1] + engine_client._ids_from_processor_name(p)[1] for p in self._inner_job().scheduling_config.processor_selector.processor_names ] @@ -218,29 +218,7 @@ def get_repetitions_and_sweeps(self) -> Tuple[int, List[cirq.Sweep]]: self.project_id, self.program_id, self.job_id, True ) - return self._deserialize_run_context(self._job.run_context) - - @staticmethod - def _deserialize_run_context( - run_context: quantum.types.any_pb2.Any, - ) -> Tuple[int, List[cirq.Sweep]]: - import cirq_google.engine.engine as engine_base - - run_context_type = run_context.type_url[len(engine_base.TYPE_PREFIX) :] - if ( - run_context_type == 'cirq.google.api.v1.RunContext' - or run_context_type == 'cirq.api.google.v1.RunContext' - ): - raise ValueError('deserializing a v1 RunContext is not supported') - if ( - run_context_type == 'cirq.google.api.v2.RunContext' - or run_context_type == 'cirq.api.google.v2.RunContext' - ): - v2_run_context = v2.run_context_pb2.RunContext.FromString(run_context.value) - return v2_run_context.parameter_sweeps[0].repetitions, [ - v2.sweep_from_proto(s.sweep) for s in v2_run_context.parameter_sweeps - ] - raise ValueError(f'unsupported run_context type: {run_context_type}') + return _deserialize_run_context(self._job.run_context) def get_processor(self) -> 'Optional[engine_processor.EngineProcessor]': """Returns the EngineProcessor for the processor the job is/was run on, @@ -250,7 +228,7 @@ def get_processor(self) -> 'Optional[engine_processor.EngineProcessor]': return None import cirq_google.engine.engine_processor as engine_processor - ids = self.context.client._ids_from_processor_name(status.processor_name) + ids = engine_client._ids_from_processor_name(status.processor_name) return engine_processor.EngineProcessor(ids[0], ids[1], self.context) def get_calibration(self) -> Optional[calibration.Calibration]: @@ -259,7 +237,7 @@ def get_calibration(self) -> Optional[calibration.Calibration]: status = self._inner_job().execution_status if not status.calibration_name: return None - ids = self.context.client._ids_from_calibration_name(status.calibration_name) + ids = engine_client._ids_from_calibration_name(status.calibration_name) response = self.context.client.get_calibration(*ids) metrics = v2.metrics_pb2.MetricsSnapshot.FromString(response.data.value) return calibration.Calibration(metrics) @@ -296,7 +274,7 @@ def _wait_for_result(self): time.sleep(0.5) total_seconds_waited += 0.5 job = self._refresh_job() - self._raise_on_failure(job) + _raise_on_failure(job) response = self.context.client.get_job_results( self.project_id, self.program_id, self.job_id ) @@ -314,13 +292,13 @@ def results(self) -> List[cirq.Result]: or result_type == 'cirq.api.google.v1.Result' ): v1_parsed_result = v1.program_pb2.Result.FromString(result.value) - self._results = self._get_job_results_v1(v1_parsed_result) + self._results = _get_job_results_v1(v1_parsed_result) elif ( result_type == 'cirq.google.api.v2.Result' or result_type == 'cirq.api.google.v2.Result' ): v2_parsed_result = v2.result_pb2.Result.FromString(result.value) - self._results = self._get_job_results_v2(v2_parsed_result) + self._results = _get_job_results_v2(v2_parsed_result) elif result.Is(v2.batch_pb2.BatchResult.DESCRIPTOR): v2_parsed_result = v2.batch_pb2.BatchResult.FromString(result.value) self._batched_results = self._get_batch_results_v2(v2_parsed_result) @@ -356,74 +334,18 @@ def calibration_results(self): self._calibration_results = cal_results return self._calibration_results - @staticmethod - def _get_job_results_v1(result: v1.program_pb2.Result) -> List[cirq.Result]: - trial_results = [] - for sweep_result in result.sweep_results: - sweep_repetitions = sweep_result.repetitions - key_sizes = [(m.key, len(m.qubits)) for m in sweep_result.measurement_keys] - for result in sweep_result.parameterized_results: - data = result.measurement_results - measurements = v1.unpack_results(data, sweep_repetitions, key_sizes) - - trial_results.append( - cirq.Result.from_single_parameter_set( - params=cirq.ParamResolver(result.params.assignments), - measurements=measurements, - ) - ) - return trial_results - @classmethod def _get_batch_results_v2(cls, results: v2.batch_pb2.BatchResult) -> List[List[cirq.Result]]: trial_results = [] for result in results.results: # Add a new list for the result - trial_results.append(cls._get_job_results_v2(result)) + trial_results.append(_get_job_results_v2(result)) return trial_results @classmethod def _flatten(cls, result) -> List[cirq.Result]: return [res for result_list in result for res in result_list] - @staticmethod - def _get_job_results_v2(result: v2.result_pb2.Result) -> List[cirq.Result]: - sweep_results = v2.results_from_proto(result) - # Flatten to single list to match to sampler api. - return [trial_result for sweep_result in sweep_results for trial_result in sweep_result] - - @staticmethod - def _raise_on_failure(job: quantum.types.QuantumJob) -> None: - execution_status = job.execution_status - state = execution_status.state - name = job.name - if state != quantum.enums.ExecutionStatus.State.SUCCESS: - if state == quantum.enums.ExecutionStatus.State.FAILURE: - processor = execution_status.processor_name or 'UNKNOWN' - error_code = execution_status.failure.error_code - error_message = execution_status.failure.error_message - raise RuntimeError( - "Job {} on processor {} failed. {}: {}".format( - name, - processor, - quantum.types.ExecutionStatus.Failure.Code.Name(error_code), - error_message, - ) - ) - elif state in TERMINAL_STATES: - raise RuntimeError( - 'Job {} failed in state {}.'.format( - name, - quantum.types.ExecutionStatus.State.Name(state), - ) - ) - else: - raise RuntimeError( - 'Timed out waiting for results. Job {} is in state {}'.format( - name, quantum.types.ExecutionStatus.State.Name(state) - ) - ) - def __iter__(self) -> Iterator[cirq.Result]: return iter(self.results()) @@ -449,3 +371,81 @@ def __str__(self) -> str: f'EngineJob(project_id=\'{self.project_id}\', ' f'program_id=\'{self.program_id}\', job_id=\'{self.job_id}\')' ) + + +def _deserialize_run_context( + run_context: quantum.types.any_pb2.Any, +) -> Tuple[int, List[cirq.Sweep]]: + import cirq_google.engine.engine as engine_base + + run_context_type = run_context.type_url[len(engine_base.TYPE_PREFIX) :] + if ( + run_context_type == 'cirq.google.api.v1.RunContext' + or run_context_type == 'cirq.api.google.v1.RunContext' + ): + raise ValueError('deserializing a v1 RunContext is not supported') + if ( + run_context_type == 'cirq.google.api.v2.RunContext' + or run_context_type == 'cirq.api.google.v2.RunContext' + ): + v2_run_context = v2.run_context_pb2.RunContext.FromString(run_context.value) + return v2_run_context.parameter_sweeps[0].repetitions, [ + v2.sweep_from_proto(s.sweep) for s in v2_run_context.parameter_sweeps + ] + raise ValueError(f'unsupported run_context type: {run_context_type}') + + +def _get_job_results_v1(result: v1.program_pb2.Result) -> List[cirq.Result]: + trial_results = [] + for sweep_result in result.sweep_results: + sweep_repetitions = sweep_result.repetitions + key_sizes = [(m.key, len(m.qubits)) for m in sweep_result.measurement_keys] + for result in sweep_result.parameterized_results: + data = result.measurement_results + measurements = v1.unpack_results(data, sweep_repetitions, key_sizes) + + trial_results.append( + cirq.Result.from_single_parameter_set( + params=cirq.ParamResolver(result.params.assignments), + measurements=measurements, + ) + ) + return trial_results + + +def _get_job_results_v2(result: v2.result_pb2.Result) -> List[cirq.Result]: + sweep_results = v2.results_from_proto(result) + # Flatten to single list to match to sampler api. + return [trial_result for sweep_result in sweep_results for trial_result in sweep_result] + + +def _raise_on_failure(job: quantum.types.QuantumJob) -> None: + execution_status = job.execution_status + state = execution_status.state + name = job.name + if state != quantum.enums.ExecutionStatus.State.SUCCESS: + if state == quantum.enums.ExecutionStatus.State.FAILURE: + processor = execution_status.processor_name or 'UNKNOWN' + error_code = execution_status.failure.error_code + error_message = execution_status.failure.error_message + raise RuntimeError( + "Job {} on processor {} failed. {}: {}".format( + name, + processor, + quantum.types.ExecutionStatus.Failure.Code.Name(error_code), + error_message, + ) + ) + elif state in TERMINAL_STATES: + raise RuntimeError( + 'Job {} failed in state {}.'.format( + name, + quantum.types.ExecutionStatus.State.Name(state), + ) + ) + else: + raise RuntimeError( + 'Timed out waiting for results. Job {} is in state {}'.format( + name, quantum.types.ExecutionStatus.State.Name(state) + ) + ) diff --git a/cirq-google/cirq_google/engine/engine_processor.py b/cirq-google/cirq_google/engine/engine_processor.py index 82243bb6d17..f0d1c2b1524 100644 --- a/cirq-google/cirq_google/engine/engine_processor.py +++ b/cirq-google/cirq_google/engine/engine_processor.py @@ -124,11 +124,6 @@ def get_device( raise ValueError('Processor does not have a device specification') return serializable_device.SerializableDevice.from_proto(spec, gate_sets) - @staticmethod - def _to_calibration(calibration_any: qtypes.any_pb2.Any) -> calibration.Calibration: - metrics = v2.metrics_pb2.MetricsSnapshot.FromString(calibration_any.value) - return calibration.Calibration(metrics) - def list_calibrations( self, earliest_timestamp_seconds: Optional[int] = None, @@ -159,7 +154,7 @@ def list_calibrations( response = self.context.client.list_calibrations( self.project_id, self.processor_id, filter_str ) - return [self._to_calibration(c.data) for c in list(response)] + return [_to_calibration(c.data) for c in list(response)] def get_calibration(self, calibration_timestamp_seconds: int) -> calibration.Calibration: """Retrieve metadata about a specific calibration run. @@ -174,7 +169,7 @@ def get_calibration(self, calibration_timestamp_seconds: int) -> calibration.Cal response = self.context.client.get_calibration( self.project_id, self.processor_id, calibration_timestamp_seconds ) - return self._to_calibration(response.data) + return _to_calibration(response.data) def get_current_calibration( self, @@ -186,7 +181,7 @@ def get_current_calibration( """ response = self.context.client.get_current_calibration(self.project_id, self.processor_id) if response: - return self._to_calibration(response.data) + return _to_calibration(response.data) else: return None @@ -352,6 +347,11 @@ def __str__(self): ) +def _to_calibration(calibration_any: qtypes.any_pb2.Any) -> calibration.Calibration: + metrics = v2.metrics_pb2.MetricsSnapshot.FromString(calibration_any.value) + return calibration.Calibration(metrics) + + def _to_date_time_filters( from_time: Union[None, datetime.datetime, datetime.timedelta], to_time: Union[None, datetime.datetime, datetime.timedelta], diff --git a/cirq-google/cirq_google/engine/engine_program.py b/cirq-google/cirq_google/engine/engine_program.py index 2e82c209f00..45a93f1c091 100644 --- a/cirq-google/cirq_google/engine/engine_program.py +++ b/cirq-google/cirq_google/engine/engine_program.py @@ -16,6 +16,7 @@ from typing import Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Union import cirq +from cirq_google.engine import engine_client from cirq_google.engine.client import quantum from cirq_google.engine.client.quantum import types as qtypes from cirq_google.engine.result_type import ResultType @@ -379,9 +380,9 @@ def list_jobs( ) return [ engine_job.EngineJob( - project_id=client._ids_from_job_name(j.name)[0], - program_id=client._ids_from_job_name(j.name)[1], - job_id=client._ids_from_job_name(j.name)[2], + project_id=engine_client._ids_from_job_name(j.name)[0], + program_id=engine_client._ids_from_job_name(j.name)[1], + job_id=engine_client._ids_from_job_name(j.name)[2], context=self.context, _job=j, ) @@ -482,7 +483,7 @@ def get_circuit(self, program_num: Optional[int] = None) -> cirq.Circuit: """ if not self._program or not self._program.HasField('code'): self._program = self.context.client.get_program(self.project_id, self.program_id, True) - return self._deserialize_program(self._program.code, program_num) + return _deserialize_program(self._program.code, program_num) def batch_size(self) -> int: """Returns the number of programs in a batch program. @@ -505,42 +506,6 @@ def batch_size(self) -> int: return len(batch.programs) raise ValueError(f'Program was not a batch program but instead was of type {code_type}.') - @staticmethod - def _deserialize_program( - code: qtypes.any_pb2.Any, program_num: Optional[int] = None - ) -> cirq.Circuit: - import cirq_google.engine.engine as engine_base - - code_type = code.type_url[len(engine_base.TYPE_PREFIX) :] - program = None - if code_type == 'cirq.google.api.v1.Program' or code_type == 'cirq.api.google.v1.Program': - raise ValueError('deserializing a v1 Program is not supported') - elif code_type == 'cirq.google.api.v2.Program' or code_type == 'cirq.api.google.v2.Program': - program = v2.program_pb2.Program.FromString(code.value) - elif code_type == 'cirq.google.api.v2.BatchProgram': - if program_num is None: - raise ValueError( - 'A program number must be specified when deserializing a Batch Program' - ) - batch = v2.batch_pb2.BatchProgram.FromString(code.value) - if abs(program_num) >= len(batch.programs): - raise ValueError( - f'Only {len(batch.programs)} in the batch but ' - f'index {program_num} was specified' - ) - - program = batch.programs[program_num] - if program: - gate_set_map = {g.gate_set_name: g for g in gate_sets.GOOGLE_GATESETS} - if program.language.gate_set not in gate_set_map: - raise ValueError( - f'Unknown gateset {program.language.gate_set}. ' - f'Supported gatesets: {list(gate_set_map.keys())}.' - ) - return gate_set_map[program.language.gate_set].deserialize(program) - - raise ValueError(f'unsupported program type: {code_type}') - def delete(self, delete_jobs: bool = False) -> None: """Deletes a previously created quantum program. @@ -554,3 +519,38 @@ def delete(self, delete_jobs: bool = False) -> None: def __str__(self) -> str: return f'EngineProgram(project_id=\'{self.project_id}\', program_id=\'{self.program_id}\')' + + +def _deserialize_program( + code: qtypes.any_pb2.Any, program_num: Optional[int] = None +) -> cirq.Circuit: + import cirq_google.engine.engine as engine_base + + code_type = code.type_url[len(engine_base.TYPE_PREFIX) :] + program = None + if code_type == 'cirq.google.api.v1.Program' or code_type == 'cirq.api.google.v1.Program': + raise ValueError('deserializing a v1 Program is not supported') + elif code_type == 'cirq.google.api.v2.Program' or code_type == 'cirq.api.google.v2.Program': + program = v2.program_pb2.Program.FromString(code.value) + elif code_type == 'cirq.google.api.v2.BatchProgram': + if program_num is None: + raise ValueError( + 'A program number must be specified when deserializing a Batch Program' + ) + batch = v2.batch_pb2.BatchProgram.FromString(code.value) + if abs(program_num) >= len(batch.programs): + raise ValueError( + f'Only {len(batch.programs)} in the batch but ' f'index {program_num} was specified' + ) + + program = batch.programs[program_num] + if program: + gate_set_map = {g.gate_set_name: g for g in gate_sets.GOOGLE_GATESETS} + if program.language.gate_set not in gate_set_map: + raise ValueError( + f'Unknown gateset {program.language.gate_set}. ' + f'Supported gatesets: {list(gate_set_map.keys())}.' + ) + return gate_set_map[program.language.gate_set].deserialize(program) + + raise ValueError(f'unsupported program type: {code_type}') diff --git a/cirq-google/cirq_google/experimental/__init__.py b/cirq-google/cirq_google/experimental/__init__.py index ef037fd1ca2..50eae1e3308 100644 --- a/cirq-google/cirq_google/experimental/__init__.py +++ b/cirq-google/cirq_google/experimental/__init__.py @@ -14,8 +14,11 @@ """Experimental features. Their API can be broken without any warning and might be in an work-in progress state""" - from cirq_google.experimental.noise_models import ( PerQubitDepolarizingWithDampedReadoutNoiseModel, simple_noise_from_calibration_metrics, ) + +from cirq_google.experimental.ops import ( + CouplerPulse, +) diff --git a/cirq-google/cirq_google/experimental/ops/__init__.py b/cirq-google/cirq_google/experimental/ops/__init__.py new file mode 100644 index 00000000000..0f4bb80d186 --- /dev/null +++ b/cirq-google/cirq_google/experimental/ops/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from cirq_google.experimental.ops.coupler_pulse import ( + CouplerPulse, +) diff --git a/cirq-google/cirq_google/experimental/ops/coupler_pulse.py b/cirq-google/cirq_google/experimental/ops/coupler_pulse.py new file mode 100644 index 00000000000..7f2cedcc183 --- /dev/null +++ b/cirq-google/cirq_google/experimental/ops/coupler_pulse.py @@ -0,0 +1,110 @@ +# Copyright 2020 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any, Optional, Tuple + +import numpy as np + +import cirq + + +_MIN_DURATION = cirq.Duration(nanos=0) +_MAX_DURATION = cirq.Duration(nanos=100) + + +@cirq.value_equality(approximate=True) +class CouplerPulse(cirq.ops.gate_features.TwoQubitGate): + """Tunable pulse for entangling adjacent qubits. + + For experimental usage only. + + This operation sends a trapezoidal pulse to the coupler between two + adjacent qubits placed in resonance. + + Note that this gate does not have a unitary matrix and must be + characterized by the user in order to determine its effects. + + Args: + hold_time: Length of the 'plateau' part of the coupler trajectory. + coupling_MHz: Target qubit-qubit coupling reached at the plateau. + rise_time: Full width of the smoothstep rise/fall. + padding_time: Symmetric padding around the coupler pulse. + """ + + def __init__( + self, + hold_time: cirq.Duration, + coupling_mhz: float, + rise_time: Optional[cirq.Duration] = cirq.Duration(nanos=8), + padding_time: Optional[cirq.Duration] = cirq.Duration(nanos=2.5), + ): + """ + Args: + hold_time: Length of the 'plateau' part of the coupler trajectory. + coupling_MHz: Target qubit-qubit coupling reached at the plateau. + rise_time: Full width of the smoothstep rise/fall. + padding_time: Symmetric padding around the coupler pulse. + """ + # Verification + if hold_time > rise_time: + raise ValueError( + f'Full rise time {rise_time} must be longer ' + 'than hold_time {hold_time} for CouplerPulse' + ) + if hold_time < _MIN_DURATION or hold_time > _MAX_DURATION: + raise ValueError(f'hold_time must be between {_MIN_DURATION} and {_MAX_DURATION}') + if padding_time < _MIN_DURATION or padding_time > _MAX_DURATION: + raise ValueError(f'padding_time must be between {_MIN_DURATION} and {_MAX_DURATION}') + if rise_time < _MIN_DURATION or rise_time > _MAX_DURATION: + raise ValueError(f'rise_time must be between {_MIN_DURATION} and {_MAX_DURATION}') + + self.hold_time = hold_time + self.coupling_mhz = coupling_mhz + self.rise_time = rise_time or cirq.Duration(nanos=8) + self.padding_time = padding_time or cirq.Duration(nanos=2.5) + + def num_qubits(self) -> int: + return 2 + + def _unitary_(self) -> np.ndarray: + return NotImplemented + + def __repr__(self) -> str: + return ( + 'cirq_google.experimental.ops.coupler_pulse.' + + f'CouplerPulse(hold_time={self.hold_time!r}, ' + + f'coupling_mhz={self.coupling_mhz}, ' + + f'rise_time={self.rise_time!r}, ' + + f'padding_time={self.padding_time!r})' + ) + + def __str__(self) -> str: + return ( + f'CouplerPulse(hold_time={self.hold_time}, ' + + f'coupling_mhz={self.coupling_mhz}, ' + + f'rise_time={self.rise_time}, ' + + f'padding_time={self.padding_time})' + ) + + def _value_equality_values_(self) -> Any: + return self.hold_time, self.coupling_mhz, self.rise_time, self.padding_time + + def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> Tuple[str, ...]: + s = f'/‾‾({self.hold_time}@{self.coupling_mhz}MHz)‾‾\\' + return (s, s) + + def _json_dict_(self): + return cirq.obj_to_dict_helper( + self, ['hold_time', 'coupling_mhz', 'rise_time', 'padding_time'] + ) diff --git a/cirq-google/cirq_google/experimental/ops/coupler_pulse_test.py b/cirq-google/cirq_google/experimental/ops/coupler_pulse_test.py new file mode 100644 index 00000000000..e23afa10cfb --- /dev/null +++ b/cirq-google/cirq_google/experimental/ops/coupler_pulse_test.py @@ -0,0 +1,151 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest + +import cirq +import cirq_google.experimental.ops.coupler_pulse as coupler_pulse + + +def test_consistent_protocols(): + gate = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), coupling_mhz=25.0, rise_time=cirq.Duration(nanos=18) + ) + cirq.testing.assert_implements_consistent_protocols( + gate, + setup_code='import cirq\nimport numpy as np\nimport sympy\nimport cirq_google', + qubit_count=2, + ) + assert gate.num_qubits() == 2 + + +def test_equality(): + eq = cirq.testing.EqualsTester() + eq.add_equality_group( + coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=18), + padding_time=cirq.Duration(nanos=4), + ), + coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=18), + padding_time=cirq.Duration(nanos=4), + ), + ) + eq.add_equality_group( + coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=12), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=18), + padding_time=cirq.Duration(nanos=4), + ) + ) + eq.add_equality_group( + coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=26.0, + rise_time=cirq.Duration(nanos=18), + padding_time=cirq.Duration(nanos=4), + ) + ) + eq.add_equality_group( + coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=28), + padding_time=cirq.Duration(nanos=4), + ) + ) + eq.add_equality_group( + coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=18), + padding_time=cirq.Duration(nanos=40), + ) + ) + + +def test_coupler_pulse_validation(): + with pytest.raises(ValueError, match='Full rise time'): + _ = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=20), coupling_mhz=25.0, rise_time=cirq.Duration(nanos=10) + ) + with pytest.raises(ValueError, match='hold_time must be between'): + _ = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=110), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=120), + ) + with pytest.raises(ValueError, match='hold_time must be between'): + _ = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=-10), coupling_mhz=25.0, rise_time=cirq.Duration(nanos=20) + ) + with pytest.raises(ValueError, match='padding_time must be between'): + _ = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=20), + padding_time=cirq.Duration(nanos=200), + ) + with pytest.raises(ValueError, match='padding_time must be between'): + _ = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=20), + padding_time=cirq.Duration(nanos=-20), + ) + with pytest.raises(ValueError, match='rise_time must be between'): + _ = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=102), + ) + + +def test_coupler_pulse_str_repr(): + gate = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), coupling_mhz=25.0, rise_time=cirq.Duration(nanos=18) + ) + assert ( + str(gate) + == 'CouplerPulse(hold_time=10 ns, coupling_mhz=25.0, ' + + 'rise_time=18 ns, padding_time=2500.0 ps)' + ) + assert ( + repr(gate) + == 'cirq_google.experimental.ops.coupler_pulse.CouplerPulse(' + + 'hold_time=cirq.Duration(nanos=10), ' + + 'coupling_mhz=25.0, ' + + 'rise_time=cirq.Duration(nanos=18), ' + + 'padding_time=cirq.Duration(picos=2500.0))' + ) + + +def test_coupler_pulse_circuit_diagram(): + a, b = cirq.LineQubit.range(2) + gate = coupler_pulse.CouplerPulse( + hold_time=cirq.Duration(nanos=10), coupling_mhz=25.0, rise_time=cirq.Duration(nanos=18) + ) + circuit = cirq.Circuit(gate(a, b)) + cirq.testing.assert_has_diagram( + circuit, + r""" +0: ───/‾‾(10 ns@25.0MHz)‾‾\─── + │ +1: ───/‾‾(10 ns@25.0MHz)‾‾\─── +""", + ) diff --git a/cirq-google/cirq_google/gate_sets.py b/cirq-google/cirq_google/gate_sets.py index 52aa52e3ffa..9cba6726421 100644 --- a/cirq-google/cirq_google/gate_sets.py +++ b/cirq-google/cirq_google/gate_sets.py @@ -29,6 +29,8 @@ SQRT_ISWAP_DESERIALIZERS, LIMITED_FSIM_SERIALIZERS, LIMITED_FSIM_DESERIALIZER, + COUPLER_PULSE_SERIALIZER, + COUPLER_PULSE_DESERIALIZER, WAIT_GATE_SERIALIZER, WAIT_GATE_DESERIALIZER, ) @@ -87,6 +89,30 @@ ) document(FSIM_GATESET, """Gate set that combines sqrt(iswap) and syc as one fsim id.""") + +EXPERIMENTAL_PULSE_GATESET = serializable_gate_set.SerializableGateSet( + gate_set_name='pulse', + serializers=[ + COUPLER_PULSE_SERIALIZER, + *LIMITED_FSIM_SERIALIZERS, + *SINGLE_QUBIT_SERIALIZERS, + MEASUREMENT_SERIALIZER, + WAIT_GATE_SERIALIZER, + ], + deserializers=[ + COUPLER_PULSE_DESERIALIZER, + LIMITED_FSIM_DESERIALIZER, + *SINGLE_QUBIT_DESERIALIZERS, + MEASUREMENT_DESERIALIZER, + WAIT_GATE_DESERIALIZER, + ], +) +document( + EXPERIMENTAL_PULSE_GATESET, + "Experimental only. Includes CouplerPulseGate with other fsim gates.", +) + + # The xmon gate set. XMON = serializable_gate_set.SerializableGateSet( gate_set_name='xmon', diff --git a/cirq-google/cirq_google/json_resolver_cache.py b/cirq-google/cirq_google/json_resolver_cache.py index e29cde55451..7c76d980931 100644 --- a/cirq-google/cirq_google/json_resolver_cache.py +++ b/cirq-google/cirq_google/json_resolver_cache.py @@ -29,6 +29,7 @@ def _class_resolver_dictionary() -> Dict[str, ObjectFactory]: 'CalibrationTag': cirq_google.CalibrationTag, 'CalibrationLayer': cirq_google.CalibrationLayer, 'CalibrationResult': cirq_google.CalibrationResult, + 'CouplerPulse': cirq_google.experimental.CouplerPulse, 'SycamoreGate': cirq_google.SycamoreGate, 'GateTabulation': cirq_google.GateTabulation, 'PhysicalZTag': cirq_google.PhysicalZTag, diff --git a/cirq-google/cirq_google/json_test_data/CouplerPulse.json b/cirq-google/cirq_google/json_test_data/CouplerPulse.json new file mode 100644 index 00000000000..41934b5fa34 --- /dev/null +++ b/cirq-google/cirq_google/json_test_data/CouplerPulse.json @@ -0,0 +1,16 @@ +{ + "cirq_type": "CouplerPulse", + "hold_time": { + "cirq_type": "Duration", + "picos": 10000 + }, + "coupling_mhz": 25.0, + "rise_time": { + "cirq_type": "Duration", + "picos": 18000 + }, + "padding_time": { + "cirq_type": "Duration", + "picos": 2500.0 + } +} diff --git a/cirq-google/cirq_google/json_test_data/CouplerPulse.repr b/cirq-google/cirq_google/json_test_data/CouplerPulse.repr new file mode 100644 index 00000000000..dcbb20988ec --- /dev/null +++ b/cirq-google/cirq_google/json_test_data/CouplerPulse.repr @@ -0,0 +1,5 @@ +cirq_google.experimental.CouplerPulse( + hold_time=cirq.Duration(nanos=10), + coupling_mhz=25.0, + rise_time=cirq.Duration(nanos=18), + padding_time=cirq.Duration(picos=2500.0)) diff --git a/cirq-ionq/cirq_ionq/conftest.py b/cirq-ionq/cirq_ionq/conftest.py new file mode 100644 index 00000000000..f7ba25dbfcd --- /dev/null +++ b/cirq-ionq/cirq_ionq/conftest.py @@ -0,0 +1,5 @@ +import os + + +def pytest_configure(config): + os.environ['CIRQ_TESTING'] = "true" diff --git a/cirq-pasqal/LICENSE b/cirq-pasqal/LICENSE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/cirq-pasqal/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cirq-pasqal/README.rst b/cirq-pasqal/README.rst new file mode 100644 index 00000000000..fc32146a693 --- /dev/null +++ b/cirq-pasqal/README.rst @@ -0,0 +1,27 @@ +.. image:: https://upload.wikimedia.org/wikipedia/en/thumb/d/d4/pasqal_corp_logo.svg/2560px-pasqal_corp_logo.svg.png + :target: https://github.com/quantumlib/cirq/ + :alt: cirq-pasqal + :width: 500px + +`Cirq `__ is a Python library for writing, manipulating, and optimizing quantum +circuits and running them against quantum computers and simulators. + +This module is **cirq-pasqal**, which provides everything you'll need to run Cirq quantum algorithms on pasqal quantum computers. + +Documentation +------------- + +To get started with pasqal quantum computing services, checkout the following guide and tutorial: + +- `Access and authentication `__ +- `Getting started guide `__ + +Installation +------------ + +To install the stable version of only **cirq-pasqal**, use `pip install cirq-pasqal`. +To install the pre-release version of only **cirq-pasqal**, use `pip install cirq-pasqal --pre`. + +Note, that this will install both **cirq-pasqal** and **cirq-core**. + +To get all the optional modules installed, you'll have to use `pip install cirq` or `pip install cirq --pre` for the pre-release version. diff --git a/cirq-pasqal/cirq_pasqal/__init__.py b/cirq-pasqal/cirq_pasqal/__init__.py new file mode 100644 index 00000000000..40aad252053 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/__init__.py @@ -0,0 +1,44 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Devices, qubits, and sampler for Pasqal's neutral atom device.""" + +from cirq_pasqal.pasqal_qubits import ( + ThreeDQubit, + TwoDQubit, +) + +from cirq_pasqal.pasqal_device import ( + PasqalDevice, + PasqalVirtualDevice, +) + +from cirq_pasqal.pasqal_noise_model import ( + PasqalNoiseModel, +) + +from cirq_pasqal.pasqal_sampler import ( + PasqalSampler, +) + + +def _register_resolver() -> None: + """Registers the cirq_google's public classes for JSON serialization.""" + from cirq.protocols.json_serialization import _internal_register_resolver + from cirq_pasqal.json_resolver_cache import _class_resolver_dictionary + + _internal_register_resolver(_class_resolver_dictionary) + + +_register_resolver() diff --git a/cirq-pasqal/cirq_pasqal/_version.py b/cirq-pasqal/cirq_pasqal/_version.py new file mode 100644 index 00000000000..941bf941f5d --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/_version.py @@ -0,0 +1,17 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Define version number here, read it from setup.py automatically""" + +__version__ = "0.12.0.dev" diff --git a/cirq-pasqal/cirq_pasqal/json_resolver_cache.py b/cirq-pasqal/cirq_pasqal/json_resolver_cache.py new file mode 100644 index 00000000000..6adb2678d7b --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_resolver_cache.py @@ -0,0 +1,29 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import functools +from typing import Dict + +import cirq_pasqal +from cirq.protocols.json_serialization import ObjectFactory + + +@functools.lru_cache(maxsize=1) +def _class_resolver_dictionary() -> Dict[str, ObjectFactory]: + return { + 'PasqalDevice': cirq_pasqal.PasqalDevice, + 'PasqalVirtualDevice': cirq_pasqal.PasqalVirtualDevice, + 'ThreeDQubit': cirq_pasqal.ThreeDQubit, + 'TwoDQubit': cirq_pasqal.TwoDQubit, + } diff --git a/cirq-core/cirq/protocols/json_test_data/PasqalDevice.json b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.json similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/PasqalDevice.json rename to cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.json diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.json_inward b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.json_inward new file mode 100644 index 00000000000..b348a2f68bc --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.json_inward @@ -0,0 +1,9 @@ +{ + "cirq_type": "PasqalDevice", + "qubits": [ + { + "cirq_type": "NamedQubit", + "name": "q0" + } + ] +} diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.repr b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.repr new file mode 100644 index 00000000000..e795593dbe5 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.repr @@ -0,0 +1,3 @@ +cirq_pasqal.PasqalDevice( + qubits=[cirq.NamedQubit('q0')] +) diff --git a/cirq-core/cirq/protocols/json_test_data/PasqalDevice.repr b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.repr_inward similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/PasqalDevice.repr rename to cirq-pasqal/cirq_pasqal/json_test_data/PasqalDevice.repr_inward diff --git a/cirq-core/cirq/protocols/json_test_data/PasqalVirtualDevice.json b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.json similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/PasqalVirtualDevice.json rename to cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.json diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.json_inward b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.json_inward new file mode 100644 index 00000000000..1dd6a50b940 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.json_inward @@ -0,0 +1,12 @@ +{ + "cirq_type": "PasqalVirtualDevice", + "control_radius": 3, + "qubits": [ + { + "cirq_type": "ThreeDQubit", + "x": 1, + "y": 1, + "z": 1 + } + ] +} diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.repr b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.repr new file mode 100644 index 00000000000..6d8cf729790 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.repr @@ -0,0 +1,4 @@ +cirq_pasqal.PasqalVirtualDevice( + control_radius=3, + qubits=[cirq_pasqal.ThreeDQubit(1, 1, 1)] +) diff --git a/cirq-core/cirq/protocols/json_test_data/PasqalVirtualDevice.repr b/cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.repr_inward similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/PasqalVirtualDevice.repr rename to cirq-pasqal/cirq_pasqal/json_test_data/PasqalVirtualDevice.repr_inward diff --git a/cirq-core/cirq/protocols/json_test_data/ThreeDQubit.json b/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.json similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/ThreeDQubit.json rename to cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.json diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.json_inward b/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.json_inward new file mode 100644 index 00000000000..4aad6ef521a --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.json_inward @@ -0,0 +1,6 @@ +{ + "cirq_type": "ThreeDQubit", + "x": 10, + "y": 11, + "z": 12 +} diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.repr b/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.repr new file mode 100644 index 00000000000..ae355ad98a4 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.repr @@ -0,0 +1 @@ +cirq_pasqal.ThreeDQubit(10, 11, 12) diff --git a/cirq-core/cirq/protocols/json_test_data/ThreeDQubit.repr b/cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.repr_inward similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/ThreeDQubit.repr rename to cirq-pasqal/cirq_pasqal/json_test_data/ThreeDQubit.repr_inward diff --git a/cirq-core/cirq/protocols/json_test_data/TwoDQubit.json b/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.json similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/TwoDQubit.json rename to cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.json diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.json_inward b/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.json_inward new file mode 100644 index 00000000000..9e6b06f3f3e --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.json_inward @@ -0,0 +1,5 @@ +{ + "cirq_type": "TwoDQubit", + "x": 10, + "y": 11 +} diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.repr b/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.repr new file mode 100644 index 00000000000..04837d811b7 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.repr @@ -0,0 +1 @@ +cirq_pasqal.TwoDQubit(10, 11) diff --git a/cirq-core/cirq/protocols/json_test_data/TwoDQubit.repr b/cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.repr_inward similarity index 100% rename from cirq-core/cirq/protocols/json_test_data/TwoDQubit.repr rename to cirq-pasqal/cirq_pasqal/json_test_data/TwoDQubit.repr_inward diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/__init__.py b/cirq-pasqal/cirq_pasqal/json_test_data/__init__.py new file mode 100644 index 00000000000..1377c9b5bbf --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# For the cirq_pasqal module, this file specifies test data for JSON +# serialization of public objects. It is used by +# cirq/protocols/json_serialization_test.py which checks for coverage of +# public classes and tests the correctness of repr and json results + +from cirq_pasqal.json_test_data.spec import TestSpec diff --git a/cirq-pasqal/cirq_pasqal/json_test_data/spec.py b/cirq-pasqal/cirq_pasqal/json_test_data/spec.py new file mode 100644 index 00000000000..2e8c7e48581 --- /dev/null +++ b/cirq-pasqal/cirq_pasqal/json_test_data/spec.py @@ -0,0 +1,39 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The cirq-pasqal test specification for JSON serialization tests. + +The actual tests live in cirq.protocols.json_serialization_test.py. +See cirq-core/cirq/testing/json.py for a description of the framework. +""" + +import pathlib + +import cirq_pasqal +from cirq_pasqal.json_resolver_cache import _class_resolver_dictionary + +from cirq.testing.json import ModuleJsonTestSpec + +TestSpec = ModuleJsonTestSpec( + name="cirq_pasqal", + packages=[cirq_pasqal], + test_data_path=pathlib.Path(__file__).parent, + not_yet_serializable=[], + should_not_be_serialized=[ + "PasqalNoiseModel", + "PasqalSampler", + ], + resolver_cache=_class_resolver_dictionary(), + deprecated={}, +) diff --git a/cirq-core/cirq/pasqal/pasqal_device.py b/cirq-pasqal/cirq_pasqal/pasqal_device.py similarity index 99% rename from cirq-core/cirq/pasqal/pasqal_device.py rename to cirq-pasqal/cirq_pasqal/pasqal_device.py index ff6de05f1b8..417e616cf4e 100644 --- a/cirq-core/cirq/pasqal/pasqal_device.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_device.py @@ -18,7 +18,7 @@ import cirq from cirq import GridQubit, LineQubit from cirq.ops import NamedQubit -from cirq.pasqal import ThreeDQubit, TwoDQubit +from cirq_pasqal import ThreeDQubit, TwoDQubit @cirq.value.value_equality diff --git a/cirq-core/cirq/pasqal/pasqal_device_test.py b/cirq-pasqal/cirq_pasqal/pasqal_device_test.py similarity index 98% rename from cirq-core/cirq/pasqal/pasqal_device_test.py rename to cirq-pasqal/cirq_pasqal/pasqal_device_test.py index 8c3c6e4ad36..9bcc13f031e 100644 --- a/cirq-core/cirq/pasqal/pasqal_device_test.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_device_test.py @@ -16,9 +16,10 @@ import numpy as np import cirq +import cirq_pasqal -from cirq.pasqal import PasqalDevice, PasqalVirtualDevice -from cirq.pasqal import TwoDQubit, ThreeDQubit +from cirq_pasqal import PasqalDevice, PasqalVirtualDevice +from cirq_pasqal import TwoDQubit, ThreeDQubit def generic_device(num_qubits) -> PasqalDevice: @@ -276,7 +277,7 @@ def test_to_json(): assert d == { "cirq_type": "PasqalVirtualDevice", "control_radius": 2, - "qubits": [cirq.pasqal.TwoDQubit(0, 0)], + "qubits": [cirq_pasqal.TwoDQubit(0, 0)], } diff --git a/cirq-core/cirq/pasqal/pasqal_noise_model.py b/cirq-pasqal/cirq_pasqal/pasqal_noise_model.py similarity index 97% rename from cirq-core/cirq/pasqal/pasqal_noise_model.py rename to cirq-pasqal/cirq_pasqal/pasqal_noise_model.py index 15a2b4ccafb..719a95656f8 100644 --- a/cirq-core/cirq/pasqal/pasqal_noise_model.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_noise_model.py @@ -14,6 +14,7 @@ from typing import List, Dict, Sequence, Any import cirq +import cirq_pasqal class PasqalNoiseModel(cirq.devices.NoiseModel): @@ -21,7 +22,7 @@ class PasqalNoiseModel(cirq.devices.NoiseModel): def __init__(self, device: cirq.devices.Device): self.noise_op_dict = self.get_default_noise_dict() - if not isinstance(device, cirq.pasqal.PasqalDevice): + if not isinstance(device, cirq_pasqal.PasqalDevice): raise TypeError( "The noise model varies between Pasqal's devices. " "Please specify the one you intend to execute the " diff --git a/cirq-core/cirq/pasqal/pasqal_noise_model_test.py b/cirq-pasqal/cirq_pasqal/pasqal_noise_model_test.py similarity index 97% rename from cirq-core/cirq/pasqal/pasqal_noise_model_test.py rename to cirq-pasqal/cirq_pasqal/pasqal_noise_model_test.py index 6dec946f5a9..d6668ab134c 100644 --- a/cirq-core/cirq/pasqal/pasqal_noise_model_test.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_noise_model_test.py @@ -14,11 +14,11 @@ import pytest import cirq -from cirq.pasqal import PasqalNoiseModel, PasqalDevice +from cirq_pasqal import PasqalNoiseModel, PasqalDevice from cirq.ops import NamedQubit -def test_NoiseModel_init(): +def test_noise_model_init(): noise_model = PasqalNoiseModel(PasqalDevice([])) assert noise_model.noise_op_dict == { str(cirq.ops.YPowGate()): cirq.ops.depolarize(1e-2), diff --git a/cirq-core/cirq/pasqal/pasqal_qubits.py b/cirq-pasqal/cirq_pasqal/pasqal_qubits.py similarity index 100% rename from cirq-core/cirq/pasqal/pasqal_qubits.py rename to cirq-pasqal/cirq_pasqal/pasqal_qubits.py diff --git a/cirq-core/cirq/pasqal/pasqal_qubits_test.py b/cirq-pasqal/cirq_pasqal/pasqal_qubits_test.py similarity index 92% rename from cirq-core/cirq/pasqal/pasqal_qubits_test.py rename to cirq-pasqal/cirq_pasqal/pasqal_qubits_test.py index bd0ad11b305..7d506cc4f30 100644 --- a/cirq-core/cirq/pasqal/pasqal_qubits_test.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_qubits_test.py @@ -15,23 +15,23 @@ import numpy as np import cirq -from cirq.pasqal import ThreeDQubit, TwoDQubit +from cirq_pasqal import ThreeDQubit, TwoDQubit -def test_pasqal_qubit_init_3D(): +def test_pasqal_qubit_init_3d(): q = ThreeDQubit(3, 4, 5) assert q.x == 3 assert q.y == 4 assert q.z == 5 -def test_comparison_key_3D(): +def test_comparison_key_3d(): assert ThreeDQubit(3, 4, 5)._comparison_key() == (5, 4, 3) coords = (np.cos(np.pi / 2), np.sin(np.pi / 2), 0) assert ThreeDQubit(*coords) == ThreeDQubit(0, 1, 0) -def test_pasqal_qubit_ordering_3D(): +def test_pasqal_qubit_ordering_3d(): assert ThreeDQubit(0, 0, 1) >= ThreeDQubit(1, 0, 0) assert ThreeDQubit(0, 0, 1) >= ThreeDQubit(0, 1, 0) assert ThreeDQubit(0, 1, 0) >= ThreeDQubit(1, 0, 0) @@ -47,7 +47,7 @@ def test_pasqal_qubit_ordering_3D(): assert ThreeDQubit(1, 1, 1) > ThreeDQubit(v[0], v[1], v[2]) -def test_distance_3D(): +def test_distance_3d(): with pytest.raises(TypeError): _ = ThreeDQubit(0, 0, 0).distance(cirq.GridQubit(0, 0)) @@ -59,7 +59,7 @@ def test_distance_3D(): ) -def test_grid_qubit_eq_3D(): +def test_grid_qubit_eq_3d(): eq = cirq.testing.EqualsTester() eq.make_equality_group(lambda: ThreeDQubit(0, 0, 0)) eq.make_equality_group(lambda: ThreeDQubit(1, 0, 0)) @@ -67,7 +67,7 @@ def test_grid_qubit_eq_3D(): eq.make_equality_group(lambda: ThreeDQubit(50, 25, 25)) -def test_cube_3D(): +def test_cube_3d(): assert ThreeDQubit.cube(2, x0=1, y0=1, z0=1) == [ ThreeDQubit(1, 1, 1), ThreeDQubit(2, 1, 1), @@ -90,7 +90,7 @@ def test_cube_3D(): ] -def test_parrallelep_3D(): +def test_parallelep_3d(): assert ThreeDQubit.parallelep(1, 2, 2, x0=5, y0=6, z0=7) == [ ThreeDQubit(5, 6, 7), ThreeDQubit(5, 7, 7), @@ -110,7 +110,7 @@ def test_parrallelep_3D(): ] -def test_square_2D(): +def test_square_2d(): assert TwoDQubit.square(2, x0=1, y0=1) == [ TwoDQubit(1, 1), TwoDQubit(2, 1), @@ -125,7 +125,7 @@ def test_square_2D(): ] -def test_rec_2D(): +def test_rec_2d(): assert TwoDQubit.rect(1, 2, x0=5, y0=6) == [TwoDQubit(5, 6), TwoDQubit(5, 7)] assert TwoDQubit.rect(2, 2) == [ TwoDQubit(0, 0), @@ -135,7 +135,7 @@ def test_rec_2D(): ] -def test_triangular_2D(): +def test_triangular_2d(): assert TwoDQubit.triangular_lattice(1) == [ TwoDQubit(0.0, 0.0), TwoDQubit(0.5, 0.8660254037844386), @@ -151,17 +151,17 @@ def test_triangular_2D(): ] -def test_repr_(): +def test_repr(): assert repr(ThreeDQubit(4, -25, 109)) == 'pasqal.ThreeDQubit(4, -25, 109)' assert repr(TwoDQubit(4, -25)) == 'pasqal.TwoDQubit(4, -25)' -def test_str_(): +def test_str(): assert str(ThreeDQubit(4, -25, 109)) == '(4, -25, 109)' assert str(TwoDQubit(4, -25)) == '(4, -25)' -def test_to_json_(): +def test_to_json(): q = ThreeDQubit(1.3, 1, 1) d = q._json_dict_() assert d == { diff --git a/cirq-core/cirq/pasqal/pasqal_sampler.py b/cirq-pasqal/cirq_pasqal/pasqal_sampler.py similarity index 98% rename from cirq-core/cirq/pasqal/pasqal_sampler.py rename to cirq-pasqal/cirq_pasqal/pasqal_sampler.py index 1bb56bc94ec..375430dd648 100644 --- a/cirq-core/cirq/pasqal/pasqal_sampler.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_sampler.py @@ -17,6 +17,7 @@ import requests import cirq +import cirq_pasqal class PasqalSampler(cirq.work.Sampler): @@ -110,7 +111,7 @@ def run_sweep( Result list for this run; one for each possible parameter resolver. """ - assert isinstance(program.device, cirq.pasqal.PasqalDevice) + assert isinstance(program.device, cirq_pasqal.PasqalDevice) program.device.validate_circuit(program) trial_results = [] diff --git a/cirq-core/cirq/pasqal/pasqal_sampler_test.py b/cirq-pasqal/cirq_pasqal/pasqal_sampler_test.py similarity index 87% rename from cirq-core/cirq/pasqal/pasqal_sampler_test.py rename to cirq-pasqal/cirq_pasqal/pasqal_sampler_test.py index f0b927de1b8..82af90c8213 100644 --- a/cirq-core/cirq/pasqal/pasqal_sampler_test.py +++ b/cirq-pasqal/cirq_pasqal/pasqal_sampler_test.py @@ -18,6 +18,7 @@ import pytest import cirq +import cirq_pasqal class MockGet: @@ -35,9 +36,9 @@ def text(self): return self.json -def _make_sampler() -> cirq.pasqal.PasqalSampler: +def _make_sampler() -> cirq_pasqal.PasqalSampler: - sampler = cirq.pasqal.PasqalSampler(remote_host='http://00.00.00/', access_token='N/A') + sampler = cirq_pasqal.PasqalSampler(remote_host='http://00.00.00/', access_token='N/A') return sampler @@ -45,7 +46,7 @@ def test_pasqal_circuit_init(): qs = cirq.NamedQubit.range(3, prefix='q') ex_circuit = cirq.Circuit() ex_circuit.append([[cirq.CZ(qs[i], qs[i + 1]), cirq.X(qs[i + 1])] for i in range(len(qs) - 1)]) - device = cirq.pasqal.PasqalDevice(qubits=qs) + device = cirq_pasqal.PasqalDevice(qubits=qs) test_circuit = cirq.Circuit(device=device) test_circuit.append( [[cirq.CZ(qs[i], qs[i + 1]), cirq.X(qs[i + 1])] for i in range(len(qs) - 1)] @@ -55,15 +56,15 @@ def test_pasqal_circuit_init(): assert moment1 == moment2 -@patch('cirq.pasqal.pasqal_sampler.requests.get') -@patch('cirq.pasqal.pasqal_sampler.requests.post') +@patch('cirq_pasqal.pasqal_sampler.requests.get') +@patch('cirq_pasqal.pasqal_sampler.requests.post') def test_run_sweep(mock_post, mock_get): """ Encodes a random binary number in the qubits, sweeps between odd and even without noise and checks if the results match. """ - qs = [cirq.pasqal.ThreeDQubit(i, j, 0) for i in range(3) for j in range(3)] + qs = [cirq_pasqal.ThreeDQubit(i, j, 0) for i in range(3) for j in range(3)] par = sympy.Symbol('par') sweep = cirq.Linspace(key='par', start=0.0, stop=1.0, length=2) @@ -71,7 +72,7 @@ def test_run_sweep(mock_post, mock_get): num = np.random.randint(0, 2 ** 9) binary = bin(num)[2:].zfill(9) - device = cirq.pasqal.PasqalVirtualDevice(control_radius=1, qubits=qs) + device = cirq_pasqal.PasqalVirtualDevice(control_radius=1, qubits=qs) ex_circuit = cirq.Circuit(device=device) for i, b in enumerate(binary[:-1]): diff --git a/cirq-pasqal/requirements.txt b/cirq-pasqal/requirements.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cirq-pasqal/setup.cfg b/cirq-pasqal/setup.cfg new file mode 100644 index 00000000000..0c9e0fc1447 --- /dev/null +++ b/cirq-pasqal/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +license_file = LICENSE diff --git a/cirq-pasqal/setup.py b/cirq-pasqal/setup.py new file mode 100644 index 00000000000..56b11c628ca --- /dev/null +++ b/cirq-pasqal/setup.py @@ -0,0 +1,70 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +import os +from setuptools import find_packages, setup + +# This reads the __version__ variable from cirq/_version.py +__version__ = '' +exec(open('cirq_pasqal/_version.py').read()) + +name = 'cirq-pasqal' + +description = 'A Cirq package to simulate and connect to Pasqal quantum computers' + +# README file as long_description. +long_description = io.open('README.rst', encoding='utf-8').read() + +# If CIRQ_PRE_RELEASE_VERSION is set then we update the version to this value. +# It is assumed that it ends with one of `.devN`, `.aN`, `.bN`, `.rcN` and hence +# it will be a pre-release version on PyPi. See +# https://packaging.python.org/guides/distributing-packages-using-setuptools/#pre-release-versioning +# for more details. +if 'CIRQ_PRE_RELEASE_VERSION' in os.environ: + __version__ = os.environ['CIRQ_PRE_RELEASE_VERSION'] + long_description = ( + "**This is a development version of Cirq-pasqal and may be " + "unstable.**\n\n**For the latest stable release of Cirq-pasqal " + "see**\n`here `__.\n\n" + long_description + ) + +# Read in requirements +requirements = open('requirements.txt').readlines() +requirements = [r.strip() for r in requirements] + +cirq_packages = ['cirq_pasqal'] + [ + 'cirq_pasqal.' + package for package in find_packages(where='cirq_pasqal') +] + +# Sanity check +assert __version__, 'Version string cannot be empty' + +setup( + name=name, + version=__version__, + url='http://github.com/quantumlib/cirq', + author='The Cirq Developers', + author_email='cirq-dev@googlegroups.com', + python_requires='>=3.6.0', + install_requires=requirements, + license='Apache 2', + description=description, + long_description=long_description, + packages=cirq_packages, + package_data={ + 'cirq_pasqal': ['py.typed'], + 'cirq_pasqal.json_test_data': ['*'], + }, +) diff --git a/cirq-web/.gitignore b/cirq-web/.gitignore new file mode 100644 index 00000000000..9919c51a1e8 --- /dev/null +++ b/cirq-web/.gitignore @@ -0,0 +1,12 @@ +# Ignore node modules +node_modules/ + +# Pytest files +.pytest_cache/ + +# Coverage testing information +.nyc_output/ + +# Extras +build/ + diff --git a/cirq-web/LICENSE b/cirq-web/LICENSE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/cirq-web/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cirq-web/README.rst b/cirq-web/README.rst new file mode 100644 index 00000000000..c64942ad11a --- /dev/null +++ b/cirq-web/README.rst @@ -0,0 +1,60 @@ +.. image:: https://raw.githubusercontent.com/quantumlib/Cirq/master/docs/images/Cirq_logo_color.png + :target: https://github.com/quantumlib/cirq + :alt: Cirq + :width: 500px + +`Cirq `__ is a Python library for writing, manipulating, and optimizing quantum +circuits and running them against quantum computers and simulators. + +This module is **cirq-web**, which allows users to take advantage of browser based 3D visualization tools +and features in Cirq. cirq-web also provides a development environment for contributors to create and add +their own visualizations to the module. + +Documentation +------------- +Documentation for cirq-web can be found in the README files located in this module's subdirectories. + +Below is a quick example of how to generate a portable 3D rendering of the Bloch sphere using cirq-web: + +.. code-block:: python + + from cirq_web import BlochSphere + + # Prepare a state + zero_state = [1+0j, 0+0j] + state_vector = cirq.to_valid_state_vector(zero_state) + + # Create and display the Bloch sphere + sphere = BlochSphere(state_vector=state_vector) + sphere.generate_html_file() + +This will create the file in the current working directory. There are additional options to specify the +output directory or to open the visualization in a browser for example. + +You can also view and interact with a Bloch sphere in a Colab or Jupyter notebook setting +with the following: + +.. code-block:: python + + from cirq_web import BlochSphere + + # Prepare a state + zero_state = [1+0j, 0+0j] + state_vector = cirq.to_valid_state_vector(zero_state) + + # Create and display the Bloch sphere + sphere = BlochSphere(state_vector=state_vector) + display(sphere) + +See the example Jupyter notebook in this directory for more examples on how to use cirq-web. + +Installation +------------ + +Cirq-web is currently in development, and therefore is only available via pre-release. + +To install the pre-release version of only **cirq-web**, use `pip install cirq-web --pre`. + +Note, that this will install both cirq-web and cirq-core. + +To get all the optional modules installed as well, you'll have to use `pip install cirq` or `pip install cirq --pre` for the pre-release version. \ No newline at end of file diff --git a/cirq-web/cirq_ts/.eslintignore b/cirq-web/cirq_ts/.eslintignore new file mode 100644 index 00000000000..44a6b377c00 --- /dev/null +++ b/cirq-web/cirq_ts/.eslintignore @@ -0,0 +1,3 @@ +build/ +**/node_modules +**/dist \ No newline at end of file diff --git a/cirq-web/cirq_ts/.eslintrc.json b/cirq-web/cirq_ts/.eslintrc.json new file mode 100644 index 00000000000..0de68a65b7f --- /dev/null +++ b/cirq-web/cirq_ts/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "extends": "./node_modules/gts/", + "overrides": [ + { + "files": ["*.ts"], + "rules": { + "node/no-unpublished-import": "off", + "max-len": [2, 100] + } + } + ] +} diff --git a/cirq-web/cirq_ts/.nycrc.json b/cirq-web/cirq_ts/.nycrc.json new file mode 100644 index 00000000000..a3a48bbfee7 --- /dev/null +++ b/cirq-web/cirq_ts/.nycrc.json @@ -0,0 +1,25 @@ +{ + "cache": false, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100, + "extension": [ + ".ts" + ], + "include": [ + "**/*.ts" + ], + "exclude": [ + "coverage/**", + "node_modules/**", + "**/*_test.ts", + "dist/**", + "build/**", + "utils/**", + "e2e/**" + ], + "all": true, + "instrument": true +} \ No newline at end of file diff --git a/cirq-web/cirq_ts/.prettierrc.js b/cirq-web/cirq_ts/.prettierrc.js new file mode 100644 index 00000000000..ff154833921 --- /dev/null +++ b/cirq-web/cirq_ts/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('gts/.prettierrc.json') +} diff --git a/cirq-web/cirq_ts/README.md b/cirq-web/cirq_ts/README.md new file mode 100644 index 00000000000..101464f6350 --- /dev/null +++ b/cirq-web/cirq_ts/README.md @@ -0,0 +1,110 @@ +## Cirq Typescript Development + +*This directory contains the code and instructions for visualization tools in a web browser or Colab/Juptyer notebooks. We do this using Typescript. While necessary, this is only half of the code needed to run the `cirq-web` project. For information on how to integrate projects here with Python and the wider Cirq package, [see the `cirq_web` directory](../cirq_web/).* + +Visualizations run on [NodeJS](https://nodejs.org/en/), and we use [npm](https://www.npmjs.com/) for package management. To start developing, clone the repository and run `npm install` within this directory, or `check/npm install` from the top level directory, to install the necessary packages and begin development. You will need to install Node and npm if you haven't already. + + +For developing 3D visualizations, we rely on the [three.js](https://threejs.org/) framework. + +For bundling the Typescript into Javascript that can be run in the browser, and for overall ease of development, we use [Webpack](https://webpack.js.org/). + +As an additional note, all `npm` and `npx` commands can be ran from the top-level of Cirq like so: +```bash +# check/npm runs npm --prefix 'cirq-web/cirq_ts` and forwards arguments +check/npm [YOUR_COMMAND] + +# check/npx navigates to this directory and runs from there +check/npx [YOUR_COMMAND] +``` + +### Visualization build structure + +The reference example for the build structure of a visualization is the Bloch sphere. Reference the `src/bloch_sphere/main.ts` file and the `src/bloch_sphere/bloch_sphere.ts` file to see the code. The `src/bloch_sphere/` directory should serve as a guide for how Typescript visualizations in Cirq should be structured. Visualizations should have: + - A "root" folder within the `src/` directory labeled according to the title of the visualization. All files and directories for a particular visualization will live here. In the case of the Bloch Sphere, this is `bloch_sphere/`. + - A `components/` directory which contains classes representing different components of the larger visualization, following typical object oriented programming techniques. In the case of the Bloch sphere, you can see that we have different classes for `Axes`, `Meridians`, `Text` etc. + - Any `assets/` directory with information necessary for the visualization (fonts, images, etc.). In the case of the Bloch Sphere, we can see a `fonts/` subdirectory which holds necessary font data, within the `assets/` directory, but for instances where there isn't a lot of extra information necessary subdirectories may not be needed. + - A class within the visualization's "root" folder which brings the individual components of the visualization together. In the case of the Bloch Sphere, this is `bloch_sphere.ts`. + - A `main.ts` consisting of functions which will be called from the bundled library. These function should handle: + - Receiving any input data which could affect the visualization. + - Sending final visualization output to the development environment, notebook, or HTML files. + - Combining aspects of the visualization that need to be added separately. + +This `main.ts` file will also need to be added as an entry point in the `webpack.config.js` file in order for your visualization to be bundled accordingly. +```javascript +module.exports = { + entry: { + bloch_sphere: './src/bloch_sphere/main.ts', + ... + YOUR_VIZ_NAME: './src/YOUR_VIZ_NAME/main.ts', + }, + ... +}; +``` +You can learn more about Webpack entry points here: [Webpack Entry Points](https://webpack.js.org/concepts/entry-points/) + +### Creating visualization bundle files + +Following this structure, you will be able to bundle your visualization by running the command `npx webpack --mode production` in this directory, or `check/ts-build` from the top-level directory. This will build the bundled Javascript file(s) within the `dist/` directory, where you can access and reference them in HTML. + +### Developing visualizations + +There are two main ways to develop visualizations you are creating in Cirq. The first, and recommended way, is to spin up a Webpack development server and view your visualizations in the browser. You can also develop using Jupyter notebook if you want to easily test integration with Python code. + +#### Hot reloading development server (Recommended) + +Using `webpack-dev-server`, we are able to develop and test visualizations in the browser and have changes update as we're writing the code. You can start this server by running `npm run start` in this directory, and view your work on the port specified by Webpack. This method also requires an `index.html` file placed within the `dist` folder. You can also manually determine where your index file is served from by modifying where `webpack-dev-server` searches for files in the `webpack.config.js` file: +```javascript +module.exports = { + ... + devServer: { + static: path.join(__dirname, 'dist'), + public: 'localhost:8080', + }, + ... +}; +``` +Note that the bundled files that `webpack-dev-server` creates live in memory, so you won't be able to find them on the file system. +```html + +``` + +Note that you can also inspect the bundle Javascript from the browser by navigating to `http://localhost:8080/YOUR_VIZ_NAME.bundle.js`. + +### Developing in a Jupyter Notebook + +An alternative to developing using `webpack-dev-server` is to bundle the Typescript and reference the Javascript output. You can spin up a notebook server with `jupyter notebook`, and bundle (while watching for live changes and updating accordingly) with `npx webpack --mode production --watch`. These processes must run simultaneously. This is especially useful for if you want to work with integrating Python code into your visualization. There's an example notebook `example.ipynb` that provides an example on how to do this. + +**NOTE:** In order to access the bundled javascript, you need to include the full path to it: `cirq_ts/dist/YOUR_VIZ_NAME.bundle.js`. If you make any changes to the directory structure, take into account that the path may change as well. + +### Developing in Google Colaboratory + +We currently do not support developing visualizations in Google Colaboratory notebooks. However, visualization ran from the PyPI package are able to be viewed in Colab. + +### Formatting and linting + +All Typescript files need to be formatted/linted according to [Google's public Typescript style guide](https://google.github.io/styleguide/tsguide.html). We use (Google's open source tool GTS)[https://github.com/google/gts] to handle this for you. Run `npm run fix` to handle fixing changes automatically, or refer to the `package.json` file for more options. + +### Testing + +We expect developed visualizations to be well tested. The Cirq typescript development environment requires two types of tests for any created visualization, unit testing and visualization testing. Unit testing ensures that the Typescript you wrote compiles correctly and generates the appropriate Three.js objects without breaking the rest of your code. Visualization testing actually compares the visualizations by building the visualization, taking a PNG screenshot, and comparing it to an expected PNG. + +We use [Mocha](https://mochajs.org) and [Chai](https://www.chaijs.com/) as our main testing tools. For comparing image diffs, we use [Pixelmatch](https://github.com/mapbox/pixelmatch) and [pngjs](https://github.com/lukeapage/pngjs). + +#### Unit testing + +Run unit tests using `npm run test`. We expect 100% code coverage for unit tests. You can check coverage by running `npm run coverage`. + +Unit tests must live adjacent to their source file with the `_test.ts` suffix. So for the file `dir/MyFile.ts`, the corresponding testfile will be `dir/MyFile_test.ts`. + +#### Visualization testing + +We take the following steps for visualization testing in our development environment: + 1. We generate an generic HTML file with the specified visualization's current JS bundle + 2. We run the HTML file in a headless browser (Using [Puppeteer](https://github.com/puppeteer/puppeteer)) + 3. We take a screenshot of the HTML output in the "browser" (Using Puppeteer) + 4. We compare the result of the screenshot with a pre-generated PNG file. + + The screenshot of the HTML browser output must live in a temporary directory; we use the [temp](https://github.com/bruce/node-temp) package to handle that for us. Reference the test at `e2e/bloch_sphere/test_bloch_sphere.ts` to see how to easily generate the screenshot in a temporary directory. + + The pre-generated PNG file is a screenshot of the developer's choice that represents what the visualization should look like. Each visualization is required to have at least one expected PNG screenshot. For more complex visualizations, multiple screenshots may be needed. diff --git a/cirq-web/cirq_ts/__init__.py b/cirq-web/cirq_ts/__init__.py new file mode 100644 index 00000000000..ab56a6f87b8 --- /dev/null +++ b/cirq-web/cirq_ts/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/cirq-web/cirq_ts/dist/bloch_sphere.bundle.js b/cirq-web/cirq_ts/dist/bloch_sphere.bundle.js new file mode 100644 index 00000000000..0cc3dc2a9d9 --- /dev/null +++ b/cirq-web/cirq_ts/dist/bloch_sphere.bundle.js @@ -0,0 +1,2 @@ +/*! For license information please see bloch_sphere.bundle.js.LICENSE.txt */ +(()=>{"use strict";var t={212:(t,e,n)=>{n.r(e),n.d(e,{ACESFilmicToneMapping:()=>nt,AddEquation:()=>T,AddOperation:()=>Q,AdditiveAnimationBlendMode:()=>qe,AdditiveBlending:()=>b,AlphaFormat:()=>Bt,AlwaysDepth:()=>V,AlwaysStencilFunc:()=>Sn,AmbientLight:()=>Pu,AmbientLightProbe:()=>$u,AnimationClip:()=>Dh,AnimationLoader:()=>Gh,AnimationMixer:()=>Cd,AnimationObjectGroup:()=>Ld,AnimationUtils:()=>xh,ArcCurve:()=>Yh,ArrayCamera:()=>Xo,ArrowHelper:()=>_p,Audio:()=>hd,AudioAnalyser:()=>gd,AudioContext:()=>Ju,AudioListener:()=>cd,AudioLoader:()=>Qu,AxesHelper:()=>bp,AxisHelper:()=>ym,BackSide:()=>f,BasicDepthPacking:()=>sn,BasicShadowMap:()=>h,BinaryTextureLoader:()=>Mm,Bone:()=>Dl,BooleanKeyframeTrack:()=>Th,BoundingBoxHelper:()=>xm,Box2:()=>Gd,Box3:()=>ci,Box3Helper:()=>fp,BoxBufferGeometry:()=>Ms,BoxGeometry:()=>Ms,BoxHelper:()=>mp,BufferAttribute:()=>Br,BufferGeometry:()=>is,BufferGeometryLoader:()=>Hu,ByteType:()=>Tt,Cache:()=>Nh,Camera:()=>Ls,CameraHelper:()=>up,CanvasRenderer:()=>Tm,CanvasTexture:()=>oc,CatmullRomCurve3:()=>tu,CineonToneMapping:()=>et,CircleBufferGeometry:()=>cc,CircleGeometry:()=>cc,ClampToEdgeWrapping:()=>dt,Clock:()=>id,Color:()=>Dr,ColorKeyframeTrack:()=>Eh,CompressedTexture:()=>ac,CompressedTextureLoader:()=>Uh,ConeBufferGeometry:()=>uc,ConeGeometry:()=>uc,CubeCamera:()=>Ps,CubeReflectionMapping:()=>st,CubeRefractionMapping:()=>at,CubeTexture:()=>Ds,CubeTextureLoader:()=>kh,CubeUVReflectionMapping:()=>ct,CubeUVRefractionMapping:()=>ht,CubicBezierCurve:()=>ru,CubicBezierCurve3:()=>su,CubicInterpolant:()=>bh,CullFaceBack:()=>o,CullFaceFront:()=>l,CullFaceFrontBack:()=>c,CullFaceNone:()=>a,Curve:()=>qh,CurvePath:()=>du,CustomBlending:()=>S,CustomToneMapping:()=>it,CylinderBufferGeometry:()=>hc,CylinderGeometry:()=>hc,Cylindrical:()=>Fd,DataTexture:()=>Ns,DataTexture2DArray:()=>ra,DataTexture3D:()=>sa,DataTextureLoader:()=>Wh,DataUtils:()=>Sp,DecrementStencilOp:()=>pn,DecrementWrapStencilOp:()=>fn,DefaultLoadingManager:()=>Bh,DepthFormat:()=>Vt,DepthStencilFormat:()=>kt,DepthTexture:()=>lc,DirectionalLight:()=>Cu,DirectionalLightHelper:()=>lp,DiscreteInterpolant:()=>Mh,DodecahedronBufferGeometry:()=>pc,DodecahedronGeometry:()=>pc,DoubleSide:()=>g,DstAlphaFactor:()=>B,DstColorFactor:()=>F,DynamicBufferAttribute:()=>lm,DynamicCopyUsage:()=>Dn,DynamicDrawUsage:()=>En,DynamicReadUsage:()=>Rn,EdgesGeometry:()=>yc,EdgesHelper:()=>_m,EllipseCurve:()=>Xh,EqualDepth:()=>j,EqualStencilFunc:()=>xn,EquirectangularReflectionMapping:()=>ot,EquirectangularRefractionMapping:()=>lt,Euler:()=>Xi,EventDispatcher:()=>Bn,ExtrudeBufferGeometry:()=>qc,ExtrudeGeometry:()=>qc,FaceColors:()=>Qp,FileLoader:()=>Hh,FlatShading:()=>v,Float16BufferAttribute:()=>Wr,Float32Attribute:()=>gm,Float32BufferAttribute:()=>jr,Float64Attribute:()=>vm,Float64BufferAttribute:()=>qr,FloatType:()=>Ct,Fog:()=>nl,FogExp2:()=>el,Font:()=>qu,FontLoader:()=>Yu,FrontSide:()=>m,Frustum:()=>zs,GLBufferAttribute:()=>Id,GLSL1:()=>Nn,GLSL3:()=>On,GammaEncoding:()=>Ke,GreaterDepth:()=>X,GreaterEqualDepth:()=>q,GreaterEqualStencilFunc:()=>Mn,GreaterStencilFunc:()=>bn,GridHelper:()=>ip,Group:()=>Yo,HalfFloatType:()=>Pt,HemisphereLight:()=>gu,HemisphereLightHelper:()=>np,HemisphereLightProbe:()=>Ku,IcosahedronBufferGeometry:()=>Yc,IcosahedronGeometry:()=>Yc,ImageBitmapLoader:()=>Wu,ImageLoader:()=>Vh,ImageUtils:()=>Kn,ImmediateRenderObject:()=>Wd,IncrementStencilOp:()=>dn,IncrementWrapStencilOp:()=>mn,InstancedBufferAttribute:()=>Fu,InstancedBufferGeometry:()=>zu,InstancedInterleavedBuffer:()=>Dd,InstancedMesh:()=>Gl,Int16Attribute:()=>dm,Int16BufferAttribute:()=>Gr,Int32Attribute:()=>mm,Int32BufferAttribute:()=>Vr,Int8Attribute:()=>cm,Int8BufferAttribute:()=>zr,IntType:()=>Lt,InterleavedBuffer:()=>rl,InterleavedBufferAttribute:()=>al,Interpolant:()=>_h,InterpolateDiscrete:()=>He,InterpolateLinear:()=>Ge,InterpolateSmooth:()=>Ue,InvertStencilOp:()=>gn,JSONLoader:()=>Em,KeepStencilOp:()=>hn,KeyframeTrack:()=>Sh,LOD:()=>Tl,LatheBufferGeometry:()=>Zc,LatheGeometry:()=>Zc,Layers:()=>Yi,LensFlare:()=>Lm,LessDepth:()=>k,LessEqualDepth:()=>W,LessEqualStencilFunc:()=>_n,LessStencilFunc:()=>yn,Light:()=>fu,LightProbe:()=>Nu,Line:()=>Xl,Line3:()=>kd,LineBasicMaterial:()=>Ul,LineCurve:()=>au,LineCurve3:()=>ou,LineDashedMaterial:()=>vh,LineLoop:()=>Ql,LinePieces:()=>Zp,LineSegments:()=>Jl,LineStrip:()=>Yp,LinearEncoding:()=>Je,LinearFilter:()=>xt,LinearInterpolant:()=>wh,LinearMipMapLinearFilter:()=>Mt,LinearMipMapNearestFilter:()=>bt,LinearMipmapLinearFilter:()=>wt,LinearMipmapNearestFilter:()=>_t,LinearToneMapping:()=>$,Loader:()=>zh,LoaderUtils:()=>Bu,LoadingManager:()=>Oh,LogLuvEncoding:()=>tn,LoopOnce:()=>Be,LoopPingPong:()=>Fe,LoopRepeat:()=>ze,LuminanceAlphaFormat:()=>Gt,LuminanceFormat:()=>Ht,MOUSE:()=>r,Material:()=>Tr,MaterialLoader:()=>Ou,Math:()=>Yn,MathUtils:()=>Yn,Matrix3:()=>Jn,Matrix4:()=>zi,MaxEquation:()=>R,Mesh:()=>bs,MeshBasicMaterial:()=>Ir,MeshDepthMaterial:()=>Uo,MeshDistanceMaterial:()=>Vo,MeshFaceMaterial:()=>$p,MeshLambertMaterial:()=>fh,MeshMatcapMaterial:()=>gh,MeshNormalMaterial:()=>mh,MeshPhongMaterial:()=>dh,MeshPhysicalMaterial:()=>uh,MeshStandardMaterial:()=>hh,MeshToonMaterial:()=>ph,MinEquation:()=>L,MirroredRepeatWrapping:()=>pt,MixOperation:()=>J,MultiMaterial:()=>tm,MultiplyBlending:()=>M,MultiplyOperation:()=>Z,NearestFilter:()=>mt,NearestMipMapLinearFilter:()=>yt,NearestMipMapNearestFilter:()=>gt,NearestMipmapLinearFilter:()=>vt,NearestMipmapNearestFilter:()=>ft,NeverDepth:()=>U,NeverStencilFunc:()=>vn,NoBlending:()=>x,NoColors:()=>Jp,NoToneMapping:()=>K,NormalAnimationBlendMode:()=>je,NormalBlending:()=>_,NotEqualDepth:()=>Y,NotEqualStencilFunc:()=>wn,NumberKeyframeTrack:()=>Ah,Object3D:()=>lr,ObjectLoader:()=>Gu,ObjectSpaceNormalMap:()=>ln,OctahedronBufferGeometry:()=>Jc,OctahedronGeometry:()=>Jc,OneFactor:()=>P,OneMinusDstAlphaFactor:()=>z,OneMinusDstColorFactor:()=>H,OneMinusSrcAlphaFactor:()=>O,OneMinusSrcColorFactor:()=>I,OrthographicCamera:()=>Lu,PCFShadowMap:()=>u,PCFSoftShadowMap:()=>d,PMREMGenerator:()=>Up,ParametricBufferGeometry:()=>Qc,ParametricGeometry:()=>Qc,Particle:()=>nm,ParticleBasicMaterial:()=>sm,ParticleSystem:()=>im,ParticleSystemMaterial:()=>am,Path:()=>pu,PerspectiveCamera:()=>Rs,Plane:()=>dr,PlaneBufferGeometry:()=>Gs,PlaneGeometry:()=>Gs,PlaneHelper:()=>gp,PointCloud:()=>em,PointCloudMaterial:()=>rm,PointLight:()=>Au,PointLightHelper:()=>Kd,Points:()=>ic,PointsMaterial:()=>Kl,PolarGridHelper:()=>rp,PolyhedronBufferGeometry:()=>dc,PolyhedronGeometry:()=>dc,PositionalAudio:()=>fd,PropertyBinding:()=>Ad,PropertyMixer:()=>vd,QuadraticBezierCurve:()=>lu,QuadraticBezierCurve3:()=>cu,Quaternion:()=>si,QuaternionKeyframeTrack:()=>Rh,QuaternionLinearInterpolant:()=>Lh,REVISION:()=>i,RGBADepthPacking:()=>an,RGBAFormat:()=>Ft,RGBAIntegerFormat:()=>Zt,RGBA_ASTC_10x10_Format:()=>ye,RGBA_ASTC_10x5_Format:()=>fe,RGBA_ASTC_10x6_Format:()=>ge,RGBA_ASTC_10x8_Format:()=>ve,RGBA_ASTC_12x10_Format:()=>xe,RGBA_ASTC_12x12_Format:()=>_e,RGBA_ASTC_4x4_Format:()=>oe,RGBA_ASTC_5x4_Format:()=>le,RGBA_ASTC_5x5_Format:()=>ce,RGBA_ASTC_6x5_Format:()=>he,RGBA_ASTC_6x6_Format:()=>ue,RGBA_ASTC_8x5_Format:()=>de,RGBA_ASTC_8x6_Format:()=>pe,RGBA_ASTC_8x8_Format:()=>me,RGBA_BPTC_Format:()=>be,RGBA_ETC2_EAC_Format:()=>ae,RGBA_PVRTC_2BPPV1_Format:()=>ie,RGBA_PVRTC_4BPPV1_Format:()=>ne,RGBA_S3TC_DXT1_Format:()=>Qt,RGBA_S3TC_DXT3_Format:()=>Kt,RGBA_S3TC_DXT5_Format:()=>$t,RGBDEncoding:()=>rn,RGBEEncoding:()=>$e,RGBEFormat:()=>Ut,RGBFormat:()=>zt,RGBIntegerFormat:()=>Yt,RGBM16Encoding:()=>nn,RGBM7Encoding:()=>en,RGB_ETC1_Format:()=>re,RGB_ETC2_Format:()=>se,RGB_PVRTC_2BPPV1_Format:()=>ee,RGB_PVRTC_4BPPV1_Format:()=>te,RGB_S3TC_DXT1_Format:()=>Jt,RGFormat:()=>qt,RGIntegerFormat:()=>Xt,RawShaderMaterial:()=>ch,Ray:()=>Bi,Raycaster:()=>Nd,RectAreaLight:()=>Du,RedFormat:()=>Wt,RedIntegerFormat:()=>jt,ReinhardToneMapping:()=>tt,RepeatWrapping:()=>ut,ReplaceStencilOp:()=>un,ReverseSubtractEquation:()=>A,RingBufferGeometry:()=>Kc,RingGeometry:()=>Kc,SRGB8_ALPHA8_ASTC_10x10_Format:()=>Ie,SRGB8_ALPHA8_ASTC_10x5_Format:()=>Ce,SRGB8_ALPHA8_ASTC_10x6_Format:()=>Pe,SRGB8_ALPHA8_ASTC_10x8_Format:()=>De,SRGB8_ALPHA8_ASTC_12x10_Format:()=>Ne,SRGB8_ALPHA8_ASTC_12x12_Format:()=>Oe,SRGB8_ALPHA8_ASTC_4x4_Format:()=>we,SRGB8_ALPHA8_ASTC_5x4_Format:()=>Me,SRGB8_ALPHA8_ASTC_5x5_Format:()=>Se,SRGB8_ALPHA8_ASTC_6x5_Format:()=>Te,SRGB8_ALPHA8_ASTC_6x6_Format:()=>Ee,SRGB8_ALPHA8_ASTC_8x5_Format:()=>Ae,SRGB8_ALPHA8_ASTC_8x6_Format:()=>Le,SRGB8_ALPHA8_ASTC_8x8_Format:()=>Re,Scene:()=>il,SceneUtils:()=>Am,ShaderChunk:()=>Us,ShaderLib:()=>ks,ShaderMaterial:()=>As,ShadowMaterial:()=>lh,Shape:()=>mu,ShapeBufferGeometry:()=>$c,ShapeGeometry:()=>$c,ShapePath:()=>ju,ShapeUtils:()=>kc,ShortType:()=>Et,Skeleton:()=>Ol,SkeletonHelper:()=>Jd,SkinnedMesh:()=>Pl,SmoothShading:()=>y,Sphere:()=>Li,SphereBufferGeometry:()=>th,SphereGeometry:()=>th,Spherical:()=>zd,SphericalHarmonics3:()=>Iu,SplineCurve:()=>hu,SpotLight:()=>wu,SpotLightHelper:()=>qd,Sprite:()=>bl,SpriteMaterial:()=>ol,SrcAlphaFactor:()=>N,SrcAlphaSaturateFactor:()=>G,SrcColorFactor:()=>D,StaticCopyUsage:()=>Pn,StaticDrawUsage:()=>Tn,StaticReadUsage:()=>Ln,StereoCamera:()=>nd,StreamCopyUsage:()=>In,StreamDrawUsage:()=>An,StreamReadUsage:()=>Cn,StringKeyframeTrack:()=>Ch,SubtractEquation:()=>E,SubtractiveBlending:()=>w,TOUCH:()=>s,TangentSpaceNormalMap:()=>on,TetrahedronBufferGeometry:()=>eh,TetrahedronGeometry:()=>eh,TextBufferGeometry:()=>nh,TextGeometry:()=>nh,Texture:()=>ti,TextureLoader:()=>jh,TorusBufferGeometry:()=>ih,TorusGeometry:()=>ih,TorusKnotBufferGeometry:()=>rh,TorusKnotGeometry:()=>rh,Triangle:()=>Mr,TriangleFanDrawMode:()=>Ze,TriangleStripDrawMode:()=>Ye,TrianglesDrawMode:()=>Xe,TubeBufferGeometry:()=>sh,TubeGeometry:()=>sh,UVMapping:()=>rt,Uint16Attribute:()=>pm,Uint16BufferAttribute:()=>Ur,Uint32Attribute:()=>fm,Uint32BufferAttribute:()=>kr,Uint8Attribute:()=>hm,Uint8BufferAttribute:()=>Fr,Uint8ClampedAttribute:()=>um,Uint8ClampedBufferAttribute:()=>Hr,Uniform:()=>Pd,UniformsLib:()=>Vs,UniformsUtils:()=>Es,UnsignedByteType:()=>St,UnsignedInt248Type:()=>Ot,UnsignedIntType:()=>Rt,UnsignedShort4444Type:()=>Dt,UnsignedShort5551Type:()=>It,UnsignedShort565Type:()=>Nt,UnsignedShortType:()=>At,VSMShadowMap:()=>p,Vector2:()=>Zn,Vector3:()=>ai,Vector4:()=>ni,VectorKeyframeTrack:()=>Ph,Vertex:()=>om,VertexColors:()=>Kp,VideoTexture:()=>sc,WebGL1Renderer:()=>tl,WebGLCubeRenderTarget:()=>Is,WebGLMultisampleRenderTarget:()=>ri,WebGLRenderTarget:()=>ii,WebGLRenderTargetCube:()=>Sm,WebGLRenderer:()=>$o,WebGLUtils:()=>qo,WireframeGeometry:()=>ah,WireframeHelper:()=>bm,WrapAroundEnding:()=>We,XHRLoader:()=>wm,ZeroCurvatureEnding:()=>Ve,ZeroFactor:()=>C,ZeroSlopeEnding:()=>ke,ZeroStencilOp:()=>cn,sRGBEncoding:()=>Qe});const i="128",r={LEFT:0,MIDDLE:1,RIGHT:2,ROTATE:0,DOLLY:1,PAN:2},s={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3},a=0,o=1,l=2,c=3,h=0,u=1,d=2,p=3,m=0,f=1,g=2,v=1,y=2,x=0,_=1,b=2,w=3,M=4,S=5,T=100,E=101,A=102,L=103,R=104,C=200,P=201,D=202,I=203,N=204,O=205,B=206,z=207,F=208,H=209,G=210,U=0,V=1,k=2,W=3,j=4,q=5,X=6,Y=7,Z=0,J=1,Q=2,K=0,$=1,tt=2,et=3,nt=4,it=5,rt=300,st=301,at=302,ot=303,lt=304,ct=306,ht=307,ut=1e3,dt=1001,pt=1002,mt=1003,ft=1004,gt=1004,vt=1005,yt=1005,xt=1006,_t=1007,bt=1007,wt=1008,Mt=1008,St=1009,Tt=1010,Et=1011,At=1012,Lt=1013,Rt=1014,Ct=1015,Pt=1016,Dt=1017,It=1018,Nt=1019,Ot=1020,Bt=1021,zt=1022,Ft=1023,Ht=1024,Gt=1025,Ut=Ft,Vt=1026,kt=1027,Wt=1028,jt=1029,qt=1030,Xt=1031,Yt=1032,Zt=1033,Jt=33776,Qt=33777,Kt=33778,$t=33779,te=35840,ee=35841,ne=35842,ie=35843,re=36196,se=37492,ae=37496,oe=37808,le=37809,ce=37810,he=37811,ue=37812,de=37813,pe=37814,me=37815,fe=37816,ge=37817,ve=37818,ye=37819,xe=37820,_e=37821,be=36492,we=37840,Me=37841,Se=37842,Te=37843,Ee=37844,Ae=37845,Le=37846,Re=37847,Ce=37848,Pe=37849,De=37850,Ie=37851,Ne=37852,Oe=37853,Be=2200,ze=2201,Fe=2202,He=2300,Ge=2301,Ue=2302,Ve=2400,ke=2401,We=2402,je=2500,qe=2501,Xe=0,Ye=1,Ze=2,Je=3e3,Qe=3001,Ke=3007,$e=3002,tn=3003,en=3004,nn=3005,rn=3006,sn=3200,an=3201,on=0,ln=1,cn=0,hn=7680,un=7681,dn=7682,pn=7683,mn=34055,fn=34056,gn=5386,vn=512,yn=513,xn=514,_n=515,bn=516,wn=517,Mn=518,Sn=519,Tn=35044,En=35048,An=35040,Ln=35045,Rn=35049,Cn=35041,Pn=35046,Dn=35050,In=35042,Nn="100",On="300 es";class Bn{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const n=this._listeners;void 0===n[t]&&(n[t]=[]),-1===n[t].indexOf(e)&&n[t].push(e)}hasEventListener(t,e){if(void 0===this._listeners)return!1;const n=this._listeners;return void 0!==n[t]&&-1!==n[t].indexOf(e)}removeEventListener(t,e){if(void 0===this._listeners)return;const n=this._listeners[t];if(void 0!==n){const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}dispatchEvent(t){if(void 0===this._listeners)return;const e=this._listeners[t.type];if(void 0!==e){t.target=this;const n=e.slice(0);for(let e=0,i=n.length;e>8&255]+zn[t>>16&255]+zn[t>>24&255]+"-"+zn[255&e]+zn[e>>8&255]+"-"+zn[e>>16&15|64]+zn[e>>24&255]+"-"+zn[63&n|128]+zn[n>>8&255]+"-"+zn[n>>16&255]+zn[n>>24&255]+zn[255&i]+zn[i>>8&255]+zn[i>>16&255]+zn[i>>24&255]).toUpperCase()}function Vn(t,e,n){return Math.max(e,Math.min(n,t))}function kn(t,e){return(t%e+e)%e}function Wn(t,e,n){return(1-n)*t+n*e}function jn(t){return 0==(t&t-1)&&0!==t}function qn(t){return Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))}function Xn(t){return Math.pow(2,Math.floor(Math.log(t)/Math.LN2))}var Yn=Object.freeze({__proto__:null,DEG2RAD:Hn,RAD2DEG:Gn,generateUUID:Un,clamp:Vn,euclideanModulo:kn,mapLinear:function(t,e,n,i,r){return i+(t-e)*(r-i)/(n-e)},inverseLerp:function(t,e,n){return t!==e?(n-t)/(e-t):0},lerp:Wn,damp:function(t,e,n,i){return Wn(t,e,1-Math.exp(-n*i))},pingpong:function(t,e=1){return e-Math.abs(kn(t,2*e)-e)},smoothstep:function(t,e,n){return t<=e?0:t>=n?1:(t=(t-e)/(n-e))*t*(3-2*t)},smootherstep:function(t,e,n){return t<=e?0:t>=n?1:(t=(t-e)/(n-e))*t*t*(t*(6*t-15)+10)},randInt:function(t,e){return t+Math.floor(Math.random()*(e-t+1))},randFloat:function(t,e){return t+Math.random()*(e-t)},randFloatSpread:function(t){return t*(.5-Math.random())},seededRandom:function(t){return void 0!==t&&(Fn=t%2147483647),Fn=16807*Fn%2147483647,(Fn-1)/2147483646},degToRad:function(t){return t*Hn},radToDeg:function(t){return t*Gn},isPowerOfTwo:jn,ceilPowerOfTwo:qn,floorPowerOfTwo:Xn,setQuaternionFromProperEuler:function(t,e,n,i,r){const s=Math.cos,a=Math.sin,o=s(n/2),l=a(n/2),c=s((e+i)/2),h=a((e+i)/2),u=s((e-i)/2),d=a((e-i)/2),p=s((i-e)/2),m=a((i-e)/2);switch(r){case"XYX":t.set(o*h,l*u,l*d,o*c);break;case"YZY":t.set(l*d,o*h,l*u,o*c);break;case"ZXZ":t.set(l*u,l*d,o*h,o*c);break;case"XZX":t.set(o*h,l*m,l*p,o*c);break;case"YXY":t.set(l*p,o*h,l*m,o*c);break;case"ZYZ":t.set(l*m,l*p,o*h,o*c);break;default:console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+r)}}});class Zn{constructor(t=0,e=0){this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t,e){return void 0!==e?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(t,e)):(this.x+=t.x,this.y+=t.y,this)}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t,e){return void 0!==e?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(t,e)):(this.x-=t.x,this.y-=t.y,this)}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,n=this.y,i=t.elements;return this.x=i[0]*e+i[3]*n+i[6],this.y=i[1]*e+i[4]*n+i[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this}clamp(t,e){return this.x=Math.max(t.x,Math.min(e.x,this.x)),this.y=Math.max(t.y,Math.min(e.y,this.y)),this}clampScalar(t,e){return this.x=Math.max(t,Math.min(e,this.x)),this.y=Math.max(t,Math.min(e,this.y)),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(Math.max(t,Math.min(e,n)))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y;return e*e+n*n}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this}equals(t){return t.x===this.x&&t.y===this.y}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t}fromBufferAttribute(t,e,n){return void 0!==n&&console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."),this.x=t.getX(e),this.y=t.getY(e),this}rotateAround(t,e){const n=Math.cos(e),i=Math.sin(e),r=this.x-t.x,s=this.y-t.y;return this.x=r*n-s*i+t.x,this.y=r*i+s*n+t.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}}Zn.prototype.isVector2=!0;class Jn{constructor(){this.elements=[1,0,0,0,1,0,0,0,1],arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")}set(t,e,n,i,r,s,a,o,l){const c=this.elements;return c[0]=t,c[1]=i,c[2]=a,c[3]=e,c[4]=r,c[5]=o,c[6]=n,c[7]=s,c[8]=l,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],this}extractBasis(t,e,n){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),n.setFromMatrix3Column(this,2),this}setFromMatrix4(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const n=t.elements,i=e.elements,r=this.elements,s=n[0],a=n[3],o=n[6],l=n[1],c=n[4],h=n[7],u=n[2],d=n[5],p=n[8],m=i[0],f=i[3],g=i[6],v=i[1],y=i[4],x=i[7],_=i[2],b=i[5],w=i[8];return r[0]=s*m+a*v+o*_,r[3]=s*f+a*y+o*b,r[6]=s*g+a*x+o*w,r[1]=l*m+c*v+h*_,r[4]=l*f+c*y+h*b,r[7]=l*g+c*x+h*w,r[2]=u*m+d*v+p*_,r[5]=u*f+d*y+p*b,r[8]=u*g+d*x+p*w,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}determinant(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],s=t[4],a=t[5],o=t[6],l=t[7],c=t[8];return e*s*c-e*a*l-n*r*c+n*a*o+i*r*l-i*s*o}invert(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],s=t[4],a=t[5],o=t[6],l=t[7],c=t[8],h=c*s-a*l,u=a*o-c*r,d=l*r-s*o,p=e*h+n*u+i*d;if(0===p)return this.set(0,0,0,0,0,0,0,0,0);const m=1/p;return t[0]=h*m,t[1]=(i*l-c*n)*m,t[2]=(a*n-i*s)*m,t[3]=u*m,t[4]=(c*e-i*o)*m,t[5]=(i*r-a*e)*m,t[6]=d*m,t[7]=(n*o-l*e)*m,t[8]=(s*e-n*r)*m,this}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}getNormalMatrix(t){return this.setFromMatrix4(t).invert().transpose()}transposeIntoArray(t){const e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this}setUvTransform(t,e,n,i,r,s,a){const o=Math.cos(r),l=Math.sin(r);return this.set(n*o,n*l,-n*(o*s+l*a)+s+t,-i*l,i*o,-i*(-l*s+o*a)+a+e,0,0,1),this}scale(t,e){const n=this.elements;return n[0]*=t,n[3]*=t,n[6]*=t,n[1]*=e,n[4]*=e,n[7]*=e,this}rotate(t){const e=Math.cos(t),n=Math.sin(t),i=this.elements,r=i[0],s=i[3],a=i[6],o=i[1],l=i[4],c=i[7];return i[0]=e*r+n*o,i[3]=e*s+n*l,i[6]=e*a+n*c,i[1]=-n*r+e*o,i[4]=-n*s+e*l,i[7]=-n*a+e*c,this}translate(t,e){const n=this.elements;return n[0]+=t*n[2],n[3]+=t*n[5],n[6]+=t*n[8],n[1]+=e*n[2],n[4]+=e*n[5],n[7]+=e*n[8],this}equals(t){const e=this.elements,n=t.elements;for(let t=0;t<9;t++)if(e[t]!==n[t])return!1;return!0}fromArray(t,e=0){for(let n=0;n<9;n++)this.elements[n]=t[n+e];return this}toArray(t=[],e=0){const n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t}clone(){return(new this.constructor).fromArray(this.elements)}}let Qn;Jn.prototype.isMatrix3=!0;class Kn{static getDataURL(t){if(/^data:/i.test(t.src))return t.src;if("undefined"==typeof HTMLCanvasElement)return t.src;let e;if(t instanceof HTMLCanvasElement)e=t;else{void 0===Qn&&(Qn=document.createElementNS("http://www.w3.org/1999/xhtml","canvas")),Qn.width=t.width,Qn.height=t.height;const n=Qn.getContext("2d");t instanceof ImageData?n.putImageData(t,0,0):n.drawImage(t,0,0,t.width,t.height),e=Qn}return e.width>2048||e.height>2048?(console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons",t),e.toDataURL("image/jpeg",.6)):e.toDataURL("image/png")}}let $n=0;class ti extends Bn{constructor(t=ti.DEFAULT_IMAGE,e=ti.DEFAULT_MAPPING,n=dt,i=dt,r=xt,s=wt,a=Ft,o=St,l=1,c=Je){super(),Object.defineProperty(this,"id",{value:$n++}),this.uuid=Un(),this.name="",this.image=t,this.mipmaps=[],this.mapping=e,this.wrapS=n,this.wrapT=i,this.magFilter=r,this.minFilter=s,this.anisotropy=l,this.format=a,this.internalFormat=null,this.type=o,this.offset=new Zn(0,0),this.repeat=new Zn(1,1),this.center=new Zn(0,0),this.rotation=0,this.matrixAutoUpdate=!0,this.matrix=new Jn,this.generateMipmaps=!0,this.premultiplyAlpha=!1,this.flipY=!0,this.unpackAlignment=4,this.encoding=c,this.version=0,this.onUpdate=null}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}clone(){return(new this.constructor).copy(this)}copy(t){return this.name=t.name,this.image=t.image,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.encoding=t.encoding,this}toJSON(t){const e=void 0===t||"string"==typeof t;if(!e&&void 0!==t.textures[this.uuid])return t.textures[this.uuid];const n={metadata:{version:4.5,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,type:this.type,encoding:this.encoding,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};if(void 0!==this.image){const i=this.image;if(void 0===i.uuid&&(i.uuid=Un()),!e&&void 0===t.images[i.uuid]){let e;if(Array.isArray(i)){e=[];for(let t=0,n=i.length;t1)switch(this.wrapS){case ut:t.x=t.x-Math.floor(t.x);break;case dt:t.x=t.x<0?0:1;break;case pt:1===Math.abs(Math.floor(t.x)%2)?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x)}if(t.y<0||t.y>1)switch(this.wrapT){case ut:t.y=t.y-Math.floor(t.y);break;case dt:t.y=t.y<0?0:1;break;case pt:1===Math.abs(Math.floor(t.y)%2)?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y)}return this.flipY&&(t.y=1-t.y),t}set needsUpdate(t){!0===t&&this.version++}}function ei(t){return"undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap?Kn.getDataURL(t):t.data?{data:Array.prototype.slice.call(t.data),width:t.width,height:t.height,type:t.data.constructor.name}:(console.warn("THREE.Texture: Unable to serialize Texture."),{})}ti.DEFAULT_IMAGE=void 0,ti.DEFAULT_MAPPING=rt,ti.prototype.isTexture=!0;class ni{constructor(t=0,e=0,n=0,i=1){this.x=t,this.y=e,this.z=n,this.w=i}get width(){return this.z}set width(t){this.z=t}get height(){return this.w}set height(t){this.w=t}set(t,e,n,i){return this.x=t,this.y=e,this.z=n,this.w=i,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this.w=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setW(t){return this.w=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=void 0!==t.w?t.w:1,this}add(t,e){return void 0!==e?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(t,e)):(this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this)}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this}sub(t,e){return void 0!==e?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(t,e)):(this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this)}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}applyMatrix4(t){const e=this.x,n=this.y,i=this.z,r=this.w,s=t.elements;return this.x=s[0]*e+s[4]*n+s[8]*i+s[12]*r,this.y=s[1]*e+s[5]*n+s[9]*i+s[13]*r,this.z=s[2]*e+s[6]*n+s[10]*i+s[14]*r,this.w=s[3]*e+s[7]*n+s[11]*i+s[15]*r,this}divideScalar(t){return this.multiplyScalar(1/t)}setAxisAngleFromQuaternion(t){this.w=2*Math.acos(t.w);const e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this}setAxisAngleFromRotationMatrix(t){let e,n,i,r;const s=.01,a=.1,o=t.elements,l=o[0],c=o[4],h=o[8],u=o[1],d=o[5],p=o[9],m=o[2],f=o[6],g=o[10];if(Math.abs(c-u)o&&t>v?tv?o=0?1:-1,i=1-e*e;if(i>Number.EPSILON){const r=Math.sqrt(i),s=Math.atan2(r,e*n);t=Math.sin(t*s)/r,a=Math.sin(a*s)/r}const r=a*n;if(o=o*t+u*r,l=l*t+d*r,c=c*t+p*r,h=h*t+m*r,t===1-a){const t=1/Math.sqrt(o*o+l*l+c*c+h*h);o*=t,l*=t,c*=t,h*=t}}t[e]=o,t[e+1]=l,t[e+2]=c,t[e+3]=h}static multiplyQuaternionsFlat(t,e,n,i,r,s){const a=n[i],o=n[i+1],l=n[i+2],c=n[i+3],h=r[s],u=r[s+1],d=r[s+2],p=r[s+3];return t[e]=a*p+c*h+o*d-l*u,t[e+1]=o*p+c*u+l*h-a*d,t[e+2]=l*p+c*d+a*u-o*h,t[e+3]=c*p-a*h-o*u-l*d,t}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get w(){return this._w}set w(t){this._w=t,this._onChangeCallback()}set(t,e,n,i){return this._x=t,this._y=e,this._z=n,this._w=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this}setFromEuler(t,e){if(!t||!t.isEuler)throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.");const n=t._x,i=t._y,r=t._z,s=t._order,a=Math.cos,o=Math.sin,l=a(n/2),c=a(i/2),h=a(r/2),u=o(n/2),d=o(i/2),p=o(r/2);switch(s){case"XYZ":this._x=u*c*h+l*d*p,this._y=l*d*h-u*c*p,this._z=l*c*p+u*d*h,this._w=l*c*h-u*d*p;break;case"YXZ":this._x=u*c*h+l*d*p,this._y=l*d*h-u*c*p,this._z=l*c*p-u*d*h,this._w=l*c*h+u*d*p;break;case"ZXY":this._x=u*c*h-l*d*p,this._y=l*d*h+u*c*p,this._z=l*c*p+u*d*h,this._w=l*c*h-u*d*p;break;case"ZYX":this._x=u*c*h-l*d*p,this._y=l*d*h+u*c*p,this._z=l*c*p-u*d*h,this._w=l*c*h+u*d*p;break;case"YZX":this._x=u*c*h+l*d*p,this._y=l*d*h+u*c*p,this._z=l*c*p-u*d*h,this._w=l*c*h-u*d*p;break;case"XZY":this._x=u*c*h-l*d*p,this._y=l*d*h-u*c*p,this._z=l*c*p+u*d*h,this._w=l*c*h+u*d*p;break;default:console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: "+s)}return!1!==e&&this._onChangeCallback(),this}setFromAxisAngle(t,e){const n=e/2,i=Math.sin(n);return this._x=t.x*i,this._y=t.y*i,this._z=t.z*i,this._w=Math.cos(n),this._onChangeCallback(),this}setFromRotationMatrix(t){const e=t.elements,n=e[0],i=e[4],r=e[8],s=e[1],a=e[5],o=e[9],l=e[2],c=e[6],h=e[10],u=n+a+h;if(u>0){const t=.5/Math.sqrt(u+1);this._w=.25/t,this._x=(c-o)*t,this._y=(r-l)*t,this._z=(s-i)*t}else if(n>a&&n>h){const t=2*Math.sqrt(1+n-a-h);this._w=(c-o)/t,this._x=.25*t,this._y=(i+s)/t,this._z=(r+l)/t}else if(a>h){const t=2*Math.sqrt(1+a-n-h);this._w=(r-l)/t,this._x=(i+s)/t,this._y=.25*t,this._z=(o+c)/t}else{const t=2*Math.sqrt(1+h-n-a);this._w=(s-i)/t,this._x=(r+l)/t,this._y=(o+c)/t,this._z=.25*t}return this._onChangeCallback(),this}setFromUnitVectors(t,e){let n=t.dot(e)+1;return nMath.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=n):(this._x=0,this._y=-t.z,this._z=t.y,this._w=n)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=n),this.normalize()}angleTo(t){return 2*Math.acos(Math.abs(Vn(this.dot(t),-1,1)))}rotateTowards(t,e){const n=this.angleTo(t);if(0===n)return this;const i=Math.min(1,e/n);return this.slerp(t,i),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let t=this.length();return 0===t?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this}multiply(t,e){return void 0!==e?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(t,e)):this.multiplyQuaternions(this,t)}premultiply(t){return this.multiplyQuaternions(t,this)}multiplyQuaternions(t,e){const n=t._x,i=t._y,r=t._z,s=t._w,a=e._x,o=e._y,l=e._z,c=e._w;return this._x=n*c+s*a+i*l-r*o,this._y=i*c+s*o+r*a-n*l,this._z=r*c+s*l+n*o-i*a,this._w=s*c-n*a-i*o-r*l,this._onChangeCallback(),this}slerp(t,e){if(0===e)return this;if(1===e)return this.copy(t);const n=this._x,i=this._y,r=this._z,s=this._w;let a=s*t._w+n*t._x+i*t._y+r*t._z;if(a<0?(this._w=-t._w,this._x=-t._x,this._y=-t._y,this._z=-t._z,a=-a):this.copy(t),a>=1)return this._w=s,this._x=n,this._y=i,this._z=r,this;const o=1-a*a;if(o<=Number.EPSILON){const t=1-e;return this._w=t*s+e*this._w,this._x=t*n+e*this._x,this._y=t*i+e*this._y,this._z=t*r+e*this._z,this.normalize(),this._onChangeCallback(),this}const l=Math.sqrt(o),c=Math.atan2(l,a),h=Math.sin((1-e)*c)/l,u=Math.sin(e*c)/l;return this._w=s*h+this._w*u,this._x=n*h+this._x*u,this._y=i*h+this._y*u,this._z=r*h+this._z*u,this._onChangeCallback(),this}slerpQuaternions(t,e,n){this.copy(t).slerp(e,n)}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w}fromArray(t,e=0){return this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t}fromBufferAttribute(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}}si.prototype.isQuaternion=!0;class ai{constructor(t=0,e=0,n=0){this.x=t,this.y=e,this.z=n}set(t,e,n){return void 0===n&&(n=this.z),this.x=t,this.y=e,this.z=n,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t,e){return void 0!==e?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(t,e)):(this.x+=t.x,this.y+=t.y,this.z+=t.z,this)}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this}sub(t,e){return void 0!==e?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(t,e)):(this.x-=t.x,this.y-=t.y,this.z-=t.z,this)}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiply(t,e){return void 0!==e?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(t,e)):(this.x*=t.x,this.y*=t.y,this.z*=t.z,this)}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}applyEuler(t){return t&&t.isEuler||console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."),this.applyQuaternion(li.setFromEuler(t))}applyAxisAngle(t,e){return this.applyQuaternion(li.setFromAxisAngle(t,e))}applyMatrix3(t){const e=this.x,n=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[3]*n+r[6]*i,this.y=r[1]*e+r[4]*n+r[7]*i,this.z=r[2]*e+r[5]*n+r[8]*i,this}applyNormalMatrix(t){return this.applyMatrix3(t).normalize()}applyMatrix4(t){const e=this.x,n=this.y,i=this.z,r=t.elements,s=1/(r[3]*e+r[7]*n+r[11]*i+r[15]);return this.x=(r[0]*e+r[4]*n+r[8]*i+r[12])*s,this.y=(r[1]*e+r[5]*n+r[9]*i+r[13])*s,this.z=(r[2]*e+r[6]*n+r[10]*i+r[14])*s,this}applyQuaternion(t){const e=this.x,n=this.y,i=this.z,r=t.x,s=t.y,a=t.z,o=t.w,l=o*e+s*i-a*n,c=o*n+a*e-r*i,h=o*i+r*n-s*e,u=-r*e-s*n-a*i;return this.x=l*o+u*-r+c*-a-h*-s,this.y=c*o+u*-s+h*-r-l*-a,this.z=h*o+u*-a+l*-s-c*-r,this}project(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)}unproject(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)}transformDirection(t){const e=this.x,n=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[4]*n+r[8]*i,this.y=r[1]*e+r[5]*n+r[9]*i,this.z=r[2]*e+r[6]*n+r[10]*i,this.normalize()}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}divideScalar(t){return this.multiplyScalar(1/t)}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}clamp(t,e){return this.x=Math.max(t.x,Math.min(e.x,this.x)),this.y=Math.max(t.y,Math.min(e.y,this.y)),this.z=Math.max(t.z,Math.min(e.z,this.z)),this}clampScalar(t,e){return this.x=Math.max(t,Math.min(e,this.x)),this.y=Math.max(t,Math.min(e,this.y)),this.z=Math.max(t,Math.min(e,this.z)),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(Math.max(t,Math.min(e,n)))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this}cross(t,e){return void 0!==e?(console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(t,e)):this.crossVectors(this,t)}crossVectors(t,e){const n=t.x,i=t.y,r=t.z,s=e.x,a=e.y,o=e.z;return this.x=i*o-r*a,this.y=r*s-n*o,this.z=n*a-i*s,this}projectOnVector(t){const e=t.lengthSq();if(0===e)return this.set(0,0,0);const n=t.dot(this)/e;return this.copy(t).multiplyScalar(n)}projectOnPlane(t){return oi.copy(this).projectOnVector(t),this.sub(oi)}reflect(t){return this.sub(oi.copy(t).multiplyScalar(2*this.dot(t)))}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(0===e)return Math.PI/2;const n=this.dot(t)/e;return Math.acos(Vn(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y,i=this.z-t.z;return e*e+n*n+i*i}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)}setFromSpherical(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)}setFromSphericalCoords(t,e,n){const i=Math.sin(e)*t;return this.x=i*Math.sin(n),this.y=Math.cos(e)*t,this.z=i*Math.cos(n),this}setFromCylindrical(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)}setFromCylindricalCoords(t,e,n){return this.x=t*Math.sin(e),this.y=n,this.z=t*Math.cos(e),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}setFromMatrixScale(t){const e=this.setFromMatrixColumn(t,0).length(),n=this.setFromMatrixColumn(t,1).length(),i=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=n,this.z=i,this}setFromMatrixColumn(t,e){return this.fromArray(t.elements,4*e)}setFromMatrix3Column(t,e){return this.fromArray(t.elements,3*e)}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t}fromBufferAttribute(t,e,n){return void 0!==n&&console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."),this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}}ai.prototype.isVector3=!0;const oi=new ai,li=new si;class ci{constructor(t=new ai(1/0,1/0,1/0),e=new ai(-1/0,-1/0,-1/0)){this.min=t,this.max=e}set(t,e){return this.min.copy(t),this.max.copy(e),this}setFromArray(t){let e=1/0,n=1/0,i=1/0,r=-1/0,s=-1/0,a=-1/0;for(let o=0,l=t.length;or&&(r=l),c>s&&(s=c),h>a&&(a=h)}return this.min.set(e,n,i),this.max.set(r,s,a),this}setFromBufferAttribute(t){let e=1/0,n=1/0,i=1/0,r=-1/0,s=-1/0,a=-1/0;for(let o=0,l=t.count;or&&(r=l),c>s&&(s=c),h>a&&(a=h)}return this.min.set(e,n,i),this.max.set(r,s,a),this}setFromPoints(t){this.makeEmpty();for(let e=0,n=t.length;ethis.max.x||t.ythis.max.y||t.zthis.max.z)}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z}getParameter(t,e){return void 0===e&&(console.warn("THREE.Box3: .getParameter() target is now required"),e=new ai),e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(t){return!(t.max.xthis.max.x||t.max.ythis.max.y||t.max.zthis.max.z)}intersectsSphere(t){return this.clampPoint(t.center,ui),ui.distanceToSquared(t.center)<=t.radius*t.radius}intersectsPlane(t){let e,n;return t.normal.x>0?(e=t.normal.x*this.min.x,n=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,n=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,n+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,n+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,n+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,n+=t.normal.z*this.min.z),e<=-t.constant&&n>=-t.constant}intersectsTriangle(t){if(this.isEmpty())return!1;this.getCenter(xi),_i.subVectors(this.max,xi),pi.subVectors(t.a,xi),mi.subVectors(t.b,xi),fi.subVectors(t.c,xi),gi.subVectors(mi,pi),vi.subVectors(fi,mi),yi.subVectors(pi,fi);let e=[0,-gi.z,gi.y,0,-vi.z,vi.y,0,-yi.z,yi.y,gi.z,0,-gi.x,vi.z,0,-vi.x,yi.z,0,-yi.x,-gi.y,gi.x,0,-vi.y,vi.x,0,-yi.y,yi.x,0];return!!Mi(e,pi,mi,fi,_i)&&(e=[1,0,0,0,1,0,0,0,1],!!Mi(e,pi,mi,fi,_i)&&(bi.crossVectors(gi,vi),e=[bi.x,bi.y,bi.z],Mi(e,pi,mi,fi,_i)))}clampPoint(t,e){return void 0===e&&(console.warn("THREE.Box3: .clampPoint() target is now required"),e=new ai),e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return ui.copy(t).clamp(this.min,this.max).sub(t).length()}getBoundingSphere(t){return void 0===t&&console.error("THREE.Box3: .getBoundingSphere() target is now required"),this.getCenter(t.center),t.radius=.5*this.getSize(ui).length(),t}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}applyMatrix4(t){return this.isEmpty()||(hi[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),hi[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),hi[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),hi[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),hi[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),hi[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),hi[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),hi[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(hi)),this}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}}ci.prototype.isBox3=!0;const hi=[new ai,new ai,new ai,new ai,new ai,new ai,new ai,new ai],ui=new ai,di=new ci,pi=new ai,mi=new ai,fi=new ai,gi=new ai,vi=new ai,yi=new ai,xi=new ai,_i=new ai,bi=new ai,wi=new ai;function Mi(t,e,n,i,r){for(let s=0,a=t.length-3;s<=a;s+=3){wi.fromArray(t,s);const a=r.x*Math.abs(wi.x)+r.y*Math.abs(wi.y)+r.z*Math.abs(wi.z),o=e.dot(wi),l=n.dot(wi),c=i.dot(wi);if(Math.max(-Math.max(o,l,c),Math.min(o,l,c))>a)return!1}return!0}const Si=new ci,Ti=new ai,Ei=new ai,Ai=new ai;class Li{constructor(t=new ai,e=-1){this.center=t,this.radius=e}set(t,e){return this.center.copy(t),this.radius=e,this}setFromPoints(t,e){const n=this.center;void 0!==e?n.copy(e):Si.setFromPoints(t).getCenter(n);let i=0;for(let e=0,r=t.length;ethis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e}getBoundingBox(t){return void 0===t&&(console.warn("THREE.Sphere: .getBoundingBox() target is now required"),t=new ci),this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}expandByPoint(t){Ai.subVectors(t,this.center);const e=Ai.lengthSq();if(e>this.radius*this.radius){const t=Math.sqrt(e),n=.5*(t-this.radius);this.center.add(Ai.multiplyScalar(n/t)),this.radius+=n}return this}union(t){return Ei.subVectors(t.center,this.center).normalize().multiplyScalar(t.radius),this.expandByPoint(Ti.copy(t.center).add(Ei)),this.expandByPoint(Ti.copy(t.center).sub(Ei)),this}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return(new this.constructor).copy(this)}}const Ri=new ai,Ci=new ai,Pi=new ai,Di=new ai,Ii=new ai,Ni=new ai,Oi=new ai;class Bi{constructor(t=new ai,e=new ai(0,0,-1)){this.origin=t,this.direction=e}set(t,e){return this.origin.copy(t),this.direction.copy(e),this}copy(t){return this.origin.copy(t.origin),this.direction.copy(t.direction),this}at(t,e){return void 0===e&&(console.warn("THREE.Ray: .at() target is now required"),e=new ai),e.copy(this.direction).multiplyScalar(t).add(this.origin)}lookAt(t){return this.direction.copy(t).sub(this.origin).normalize(),this}recast(t){return this.origin.copy(this.at(t,Ri)),this}closestPointToPoint(t,e){void 0===e&&(console.warn("THREE.Ray: .closestPointToPoint() target is now required"),e=new ai),e.subVectors(t,this.origin);const n=e.dot(this.direction);return n<0?e.copy(this.origin):e.copy(this.direction).multiplyScalar(n).add(this.origin)}distanceToPoint(t){return Math.sqrt(this.distanceSqToPoint(t))}distanceSqToPoint(t){const e=Ri.subVectors(t,this.origin).dot(this.direction);return e<0?this.origin.distanceToSquared(t):(Ri.copy(this.direction).multiplyScalar(e).add(this.origin),Ri.distanceToSquared(t))}distanceSqToSegment(t,e,n,i){Ci.copy(t).add(e).multiplyScalar(.5),Pi.copy(e).sub(t).normalize(),Di.copy(this.origin).sub(Ci);const r=.5*t.distanceTo(e),s=-this.direction.dot(Pi),a=Di.dot(this.direction),o=-Di.dot(Pi),l=Di.lengthSq(),c=Math.abs(1-s*s);let h,u,d,p;if(c>0)if(h=s*o-a,u=s*a-o,p=r*c,h>=0)if(u>=-p)if(u<=p){const t=1/c;h*=t,u*=t,d=h*(h+s*u+2*a)+u*(s*h+u+2*o)+l}else u=r,h=Math.max(0,-(s*u+a)),d=-h*h+u*(u+2*o)+l;else u=-r,h=Math.max(0,-(s*u+a)),d=-h*h+u*(u+2*o)+l;else u<=-p?(h=Math.max(0,-(-s*r+a)),u=h>0?-r:Math.min(Math.max(-r,-o),r),d=-h*h+u*(u+2*o)+l):u<=p?(h=0,u=Math.min(Math.max(-r,-o),r),d=u*(u+2*o)+l):(h=Math.max(0,-(s*r+a)),u=h>0?r:Math.min(Math.max(-r,-o),r),d=-h*h+u*(u+2*o)+l);else u=s>0?-r:r,h=Math.max(0,-(s*u+a)),d=-h*h+u*(u+2*o)+l;return n&&n.copy(this.direction).multiplyScalar(h).add(this.origin),i&&i.copy(Pi).multiplyScalar(u).add(Ci),d}intersectSphere(t,e){Ri.subVectors(t.center,this.origin);const n=Ri.dot(this.direction),i=Ri.dot(Ri)-n*n,r=t.radius*t.radius;if(i>r)return null;const s=Math.sqrt(r-i),a=n-s,o=n+s;return a<0&&o<0?null:a<0?this.at(o,e):this.at(a,e)}intersectsSphere(t){return this.distanceSqToPoint(t.center)<=t.radius*t.radius}distanceToPlane(t){const e=t.normal.dot(this.direction);if(0===e)return 0===t.distanceToPoint(this.origin)?0:null;const n=-(this.origin.dot(t.normal)+t.constant)/e;return n>=0?n:null}intersectPlane(t,e){const n=this.distanceToPlane(t);return null===n?null:this.at(n,e)}intersectsPlane(t){const e=t.distanceToPoint(this.origin);return 0===e||t.normal.dot(this.direction)*e<0}intersectBox(t,e){let n,i,r,s,a,o;const l=1/this.direction.x,c=1/this.direction.y,h=1/this.direction.z,u=this.origin;return l>=0?(n=(t.min.x-u.x)*l,i=(t.max.x-u.x)*l):(n=(t.max.x-u.x)*l,i=(t.min.x-u.x)*l),c>=0?(r=(t.min.y-u.y)*c,s=(t.max.y-u.y)*c):(r=(t.max.y-u.y)*c,s=(t.min.y-u.y)*c),n>s||r>i?null:((r>n||n!=n)&&(n=r),(s=0?(a=(t.min.z-u.z)*h,o=(t.max.z-u.z)*h):(a=(t.max.z-u.z)*h,o=(t.min.z-u.z)*h),n>o||a>i?null:((a>n||n!=n)&&(n=a),(o=0?n:i,e)))}intersectsBox(t){return null!==this.intersectBox(t,Ri)}intersectTriangle(t,e,n,i,r){Ii.subVectors(e,t),Ni.subVectors(n,t),Oi.crossVectors(Ii,Ni);let s,a=this.direction.dot(Oi);if(a>0){if(i)return null;s=1}else{if(!(a<0))return null;s=-1,a=-a}Di.subVectors(this.origin,t);const o=s*this.direction.dot(Ni.crossVectors(Di,Ni));if(o<0)return null;const l=s*this.direction.dot(Ii.cross(Di));if(l<0)return null;if(o+l>a)return null;const c=-s*Di.dot(Oi);return c<0?null:this.at(c/a,r)}applyMatrix4(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this}equals(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)}clone(){return(new this.constructor).copy(this)}}class zi{constructor(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")}set(t,e,n,i,r,s,a,o,l,c,h,u,d,p,m,f){const g=this.elements;return g[0]=t,g[4]=e,g[8]=n,g[12]=i,g[1]=r,g[5]=s,g[9]=a,g[13]=o,g[2]=l,g[6]=c,g[10]=h,g[14]=u,g[3]=d,g[7]=p,g[11]=m,g[15]=f,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}clone(){return(new zi).fromArray(this.elements)}copy(t){const e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],e[9]=n[9],e[10]=n[10],e[11]=n[11],e[12]=n[12],e[13]=n[13],e[14]=n[14],e[15]=n[15],this}copyPosition(t){const e=this.elements,n=t.elements;return e[12]=n[12],e[13]=n[13],e[14]=n[14],this}setFromMatrix3(t){const e=t.elements;return this.set(e[0],e[3],e[6],0,e[1],e[4],e[7],0,e[2],e[5],e[8],0,0,0,0,1),this}extractBasis(t,e,n){return t.setFromMatrixColumn(this,0),e.setFromMatrixColumn(this,1),n.setFromMatrixColumn(this,2),this}makeBasis(t,e,n){return this.set(t.x,e.x,n.x,0,t.y,e.y,n.y,0,t.z,e.z,n.z,0,0,0,0,1),this}extractRotation(t){const e=this.elements,n=t.elements,i=1/Fi.setFromMatrixColumn(t,0).length(),r=1/Fi.setFromMatrixColumn(t,1).length(),s=1/Fi.setFromMatrixColumn(t,2).length();return e[0]=n[0]*i,e[1]=n[1]*i,e[2]=n[2]*i,e[3]=0,e[4]=n[4]*r,e[5]=n[5]*r,e[6]=n[6]*r,e[7]=0,e[8]=n[8]*s,e[9]=n[9]*s,e[10]=n[10]*s,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,this}makeRotationFromEuler(t){t&&t.isEuler||console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");const e=this.elements,n=t.x,i=t.y,r=t.z,s=Math.cos(n),a=Math.sin(n),o=Math.cos(i),l=Math.sin(i),c=Math.cos(r),h=Math.sin(r);if("XYZ"===t.order){const t=s*c,n=s*h,i=a*c,r=a*h;e[0]=o*c,e[4]=-o*h,e[8]=l,e[1]=n+i*l,e[5]=t-r*l,e[9]=-a*o,e[2]=r-t*l,e[6]=i+n*l,e[10]=s*o}else if("YXZ"===t.order){const t=o*c,n=o*h,i=l*c,r=l*h;e[0]=t+r*a,e[4]=i*a-n,e[8]=s*l,e[1]=s*h,e[5]=s*c,e[9]=-a,e[2]=n*a-i,e[6]=r+t*a,e[10]=s*o}else if("ZXY"===t.order){const t=o*c,n=o*h,i=l*c,r=l*h;e[0]=t-r*a,e[4]=-s*h,e[8]=i+n*a,e[1]=n+i*a,e[5]=s*c,e[9]=r-t*a,e[2]=-s*l,e[6]=a,e[10]=s*o}else if("ZYX"===t.order){const t=s*c,n=s*h,i=a*c,r=a*h;e[0]=o*c,e[4]=i*l-n,e[8]=t*l+r,e[1]=o*h,e[5]=r*l+t,e[9]=n*l-i,e[2]=-l,e[6]=a*o,e[10]=s*o}else if("YZX"===t.order){const t=s*o,n=s*l,i=a*o,r=a*l;e[0]=o*c,e[4]=r-t*h,e[8]=i*h+n,e[1]=h,e[5]=s*c,e[9]=-a*c,e[2]=-l*c,e[6]=n*h+i,e[10]=t-r*h}else if("XZY"===t.order){const t=s*o,n=s*l,i=a*o,r=a*l;e[0]=o*c,e[4]=-h,e[8]=l*c,e[1]=t*h+r,e[5]=s*c,e[9]=n*h-i,e[2]=i*h-n,e[6]=a*c,e[10]=r*h+t}return e[3]=0,e[7]=0,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,this}makeRotationFromQuaternion(t){return this.compose(Gi,t,Ui)}lookAt(t,e,n){const i=this.elements;return Wi.subVectors(t,e),0===Wi.lengthSq()&&(Wi.z=1),Wi.normalize(),Vi.crossVectors(n,Wi),0===Vi.lengthSq()&&(1===Math.abs(n.z)?Wi.x+=1e-4:Wi.z+=1e-4,Wi.normalize(),Vi.crossVectors(n,Wi)),Vi.normalize(),ki.crossVectors(Wi,Vi),i[0]=Vi.x,i[4]=ki.x,i[8]=Wi.x,i[1]=Vi.y,i[5]=ki.y,i[9]=Wi.y,i[2]=Vi.z,i[6]=ki.z,i[10]=Wi.z,this}multiply(t,e){return void 0!==e?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(t,e)):this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const n=t.elements,i=e.elements,r=this.elements,s=n[0],a=n[4],o=n[8],l=n[12],c=n[1],h=n[5],u=n[9],d=n[13],p=n[2],m=n[6],f=n[10],g=n[14],v=n[3],y=n[7],x=n[11],_=n[15],b=i[0],w=i[4],M=i[8],S=i[12],T=i[1],E=i[5],A=i[9],L=i[13],R=i[2],C=i[6],P=i[10],D=i[14],I=i[3],N=i[7],O=i[11],B=i[15];return r[0]=s*b+a*T+o*R+l*I,r[4]=s*w+a*E+o*C+l*N,r[8]=s*M+a*A+o*P+l*O,r[12]=s*S+a*L+o*D+l*B,r[1]=c*b+h*T+u*R+d*I,r[5]=c*w+h*E+u*C+d*N,r[9]=c*M+h*A+u*P+d*O,r[13]=c*S+h*L+u*D+d*B,r[2]=p*b+m*T+f*R+g*I,r[6]=p*w+m*E+f*C+g*N,r[10]=p*M+m*A+f*P+g*O,r[14]=p*S+m*L+f*D+g*B,r[3]=v*b+y*T+x*R+_*I,r[7]=v*w+y*E+x*C+_*N,r[11]=v*M+y*A+x*P+_*O,r[15]=v*S+y*L+x*D+_*B,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[4]*=t,e[8]*=t,e[12]*=t,e[1]*=t,e[5]*=t,e[9]*=t,e[13]*=t,e[2]*=t,e[6]*=t,e[10]*=t,e[14]*=t,e[3]*=t,e[7]*=t,e[11]*=t,e[15]*=t,this}determinant(){const t=this.elements,e=t[0],n=t[4],i=t[8],r=t[12],s=t[1],a=t[5],o=t[9],l=t[13],c=t[2],h=t[6],u=t[10],d=t[14];return t[3]*(+r*o*h-i*l*h-r*a*u+n*l*u+i*a*d-n*o*d)+t[7]*(+e*o*d-e*l*u+r*s*u-i*s*d+i*l*c-r*o*c)+t[11]*(+e*l*h-e*a*d-r*s*h+n*s*d+r*a*c-n*l*c)+t[15]*(-i*a*c-e*o*h+e*a*u+i*s*h-n*s*u+n*o*c)}transpose(){const t=this.elements;let e;return e=t[1],t[1]=t[4],t[4]=e,e=t[2],t[2]=t[8],t[8]=e,e=t[6],t[6]=t[9],t[9]=e,e=t[3],t[3]=t[12],t[12]=e,e=t[7],t[7]=t[13],t[13]=e,e=t[11],t[11]=t[14],t[14]=e,this}setPosition(t,e,n){const i=this.elements;return t.isVector3?(i[12]=t.x,i[13]=t.y,i[14]=t.z):(i[12]=t,i[13]=e,i[14]=n),this}invert(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],s=t[4],a=t[5],o=t[6],l=t[7],c=t[8],h=t[9],u=t[10],d=t[11],p=t[12],m=t[13],f=t[14],g=t[15],v=h*f*l-m*u*l+m*o*d-a*f*d-h*o*g+a*u*g,y=p*u*l-c*f*l-p*o*d+s*f*d+c*o*g-s*u*g,x=c*m*l-p*h*l+p*a*d-s*m*d-c*a*g+s*h*g,_=p*h*o-c*m*o-p*a*u+s*m*u+c*a*f-s*h*f,b=e*v+n*y+i*x+r*_;if(0===b)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const w=1/b;return t[0]=v*w,t[1]=(m*u*r-h*f*r-m*i*d+n*f*d+h*i*g-n*u*g)*w,t[2]=(a*f*r-m*o*r+m*i*l-n*f*l-a*i*g+n*o*g)*w,t[3]=(h*o*r-a*u*r-h*i*l+n*u*l+a*i*d-n*o*d)*w,t[4]=y*w,t[5]=(c*f*r-p*u*r+p*i*d-e*f*d-c*i*g+e*u*g)*w,t[6]=(p*o*r-s*f*r-p*i*l+e*f*l+s*i*g-e*o*g)*w,t[7]=(s*u*r-c*o*r+c*i*l-e*u*l-s*i*d+e*o*d)*w,t[8]=x*w,t[9]=(p*h*r-c*m*r-p*n*d+e*m*d+c*n*g-e*h*g)*w,t[10]=(s*m*r-p*a*r+p*n*l-e*m*l-s*n*g+e*a*g)*w,t[11]=(c*a*r-s*h*r-c*n*l+e*h*l+s*n*d-e*a*d)*w,t[12]=_*w,t[13]=(c*m*i-p*h*i+p*n*u-e*m*u-c*n*f+e*h*f)*w,t[14]=(p*a*i-s*m*i-p*n*o+e*m*o+s*n*f-e*a*f)*w,t[15]=(s*h*i-c*a*i+c*n*o-e*h*o-s*n*u+e*a*u)*w,this}scale(t){const e=this.elements,n=t.x,i=t.y,r=t.z;return e[0]*=n,e[4]*=i,e[8]*=r,e[1]*=n,e[5]*=i,e[9]*=r,e[2]*=n,e[6]*=i,e[10]*=r,e[3]*=n,e[7]*=i,e[11]*=r,this}getMaxScaleOnAxis(){const t=this.elements,e=t[0]*t[0]+t[1]*t[1]+t[2]*t[2],n=t[4]*t[4]+t[5]*t[5]+t[6]*t[6],i=t[8]*t[8]+t[9]*t[9]+t[10]*t[10];return Math.sqrt(Math.max(e,n,i))}makeTranslation(t,e,n){return this.set(1,0,0,t,0,1,0,e,0,0,1,n,0,0,0,1),this}makeRotationX(t){const e=Math.cos(t),n=Math.sin(t);return this.set(1,0,0,0,0,e,-n,0,0,n,e,0,0,0,0,1),this}makeRotationY(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,0,n,0,0,1,0,0,-n,0,e,0,0,0,0,1),this}makeRotationZ(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,-n,0,0,n,e,0,0,0,0,1,0,0,0,0,1),this}makeRotationAxis(t,e){const n=Math.cos(e),i=Math.sin(e),r=1-n,s=t.x,a=t.y,o=t.z,l=r*s,c=r*a;return this.set(l*s+n,l*a-i*o,l*o+i*a,0,l*a+i*o,c*a+n,c*o-i*s,0,l*o-i*a,c*o+i*s,r*o*o+n,0,0,0,0,1),this}makeScale(t,e,n){return this.set(t,0,0,0,0,e,0,0,0,0,n,0,0,0,0,1),this}makeShear(t,e,n){return this.set(1,e,n,0,t,1,n,0,t,e,1,0,0,0,0,1),this}compose(t,e,n){const i=this.elements,r=e._x,s=e._y,a=e._z,o=e._w,l=r+r,c=s+s,h=a+a,u=r*l,d=r*c,p=r*h,m=s*c,f=s*h,g=a*h,v=o*l,y=o*c,x=o*h,_=n.x,b=n.y,w=n.z;return i[0]=(1-(m+g))*_,i[1]=(d+x)*_,i[2]=(p-y)*_,i[3]=0,i[4]=(d-x)*b,i[5]=(1-(u+g))*b,i[6]=(f+v)*b,i[7]=0,i[8]=(p+y)*w,i[9]=(f-v)*w,i[10]=(1-(u+m))*w,i[11]=0,i[12]=t.x,i[13]=t.y,i[14]=t.z,i[15]=1,this}decompose(t,e,n){const i=this.elements;let r=Fi.set(i[0],i[1],i[2]).length();const s=Fi.set(i[4],i[5],i[6]).length(),a=Fi.set(i[8],i[9],i[10]).length();this.determinant()<0&&(r=-r),t.x=i[12],t.y=i[13],t.z=i[14],Hi.copy(this);const o=1/r,l=1/s,c=1/a;return Hi.elements[0]*=o,Hi.elements[1]*=o,Hi.elements[2]*=o,Hi.elements[4]*=l,Hi.elements[5]*=l,Hi.elements[6]*=l,Hi.elements[8]*=c,Hi.elements[9]*=c,Hi.elements[10]*=c,e.setFromRotationMatrix(Hi),n.x=r,n.y=s,n.z=a,this}makePerspective(t,e,n,i,r,s){void 0===s&&console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.");const a=this.elements,o=2*r/(e-t),l=2*r/(n-i),c=(e+t)/(e-t),h=(n+i)/(n-i),u=-(s+r)/(s-r),d=-2*s*r/(s-r);return a[0]=o,a[4]=0,a[8]=c,a[12]=0,a[1]=0,a[5]=l,a[9]=h,a[13]=0,a[2]=0,a[6]=0,a[10]=u,a[14]=d,a[3]=0,a[7]=0,a[11]=-1,a[15]=0,this}makeOrthographic(t,e,n,i,r,s){const a=this.elements,o=1/(e-t),l=1/(n-i),c=1/(s-r),h=(e+t)*o,u=(n+i)*l,d=(s+r)*c;return a[0]=2*o,a[4]=0,a[8]=0,a[12]=-h,a[1]=0,a[5]=2*l,a[9]=0,a[13]=-u,a[2]=0,a[6]=0,a[10]=-2*c,a[14]=-d,a[3]=0,a[7]=0,a[11]=0,a[15]=1,this}equals(t){const e=this.elements,n=t.elements;for(let t=0;t<16;t++)if(e[t]!==n[t])return!1;return!0}fromArray(t,e=0){for(let n=0;n<16;n++)this.elements[n]=t[n+e];return this}toArray(t=[],e=0){const n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t[e+9]=n[9],t[e+10]=n[10],t[e+11]=n[11],t[e+12]=n[12],t[e+13]=n[13],t[e+14]=n[14],t[e+15]=n[15],t}}zi.prototype.isMatrix4=!0;const Fi=new ai,Hi=new zi,Gi=new ai(0,0,0),Ui=new ai(1,1,1),Vi=new ai,ki=new ai,Wi=new ai,ji=new zi,qi=new si;class Xi{constructor(t=0,e=0,n=0,i=Xi.DefaultOrder){this._x=t,this._y=e,this._z=n,this._order=i}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get order(){return this._order}set order(t){this._order=t,this._onChangeCallback()}set(t,e,n,i){return this._x=t,this._y=e,this._z=n,this._order=i||this._order,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(t){return this._x=t._x,this._y=t._y,this._z=t._z,this._order=t._order,this._onChangeCallback(),this}setFromRotationMatrix(t,e,n){const i=t.elements,r=i[0],s=i[4],a=i[8],o=i[1],l=i[5],c=i[9],h=i[2],u=i[6],d=i[10];switch(e=e||this._order){case"XYZ":this._y=Math.asin(Vn(a,-1,1)),Math.abs(a)<.9999999?(this._x=Math.atan2(-c,d),this._z=Math.atan2(-s,r)):(this._x=Math.atan2(u,l),this._z=0);break;case"YXZ":this._x=Math.asin(-Vn(c,-1,1)),Math.abs(c)<.9999999?(this._y=Math.atan2(a,d),this._z=Math.atan2(o,l)):(this._y=Math.atan2(-h,r),this._z=0);break;case"ZXY":this._x=Math.asin(Vn(u,-1,1)),Math.abs(u)<.9999999?(this._y=Math.atan2(-h,d),this._z=Math.atan2(-s,l)):(this._y=0,this._z=Math.atan2(o,r));break;case"ZYX":this._y=Math.asin(-Vn(h,-1,1)),Math.abs(h)<.9999999?(this._x=Math.atan2(u,d),this._z=Math.atan2(o,r)):(this._x=0,this._z=Math.atan2(-s,l));break;case"YZX":this._z=Math.asin(Vn(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(-c,l),this._y=Math.atan2(-h,r)):(this._x=0,this._y=Math.atan2(a,d));break;case"XZY":this._z=Math.asin(-Vn(s,-1,1)),Math.abs(s)<.9999999?(this._x=Math.atan2(u,l),this._y=Math.atan2(a,r)):(this._x=Math.atan2(-c,d),this._y=0);break;default:console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: "+e)}return this._order=e,!1!==n&&this._onChangeCallback(),this}setFromQuaternion(t,e,n){return ji.makeRotationFromQuaternion(t),this.setFromRotationMatrix(ji,e,n)}setFromVector3(t,e){return this.set(t.x,t.y,t.z,e||this._order)}reorder(t){return qi.setFromEuler(this),this.setFromQuaternion(qi,t)}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._order===this._order}fromArray(t){return this._x=t[0],this._y=t[1],this._z=t[2],void 0!==t[3]&&(this._order=t[3]),this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._order,t}toVector3(t){return t?t.set(this._x,this._y,this._z):new ai(this._x,this._y,this._z)}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}}Xi.prototype.isEuler=!0,Xi.DefaultOrder="XYZ",Xi.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"];class Yi{constructor(){this.mask=1}set(t){this.mask=1<1){for(let t=0;t1){for(let t=0;t0){i.children=[];for(let e=0;e0){i.animations=[];for(let e=0;e0&&(n.geometries=e),i.length>0&&(n.materials=i),r.length>0&&(n.textures=r),a.length>0&&(n.images=a),o.length>0&&(n.shapes=o),l.length>0&&(n.skeletons=l),c.length>0&&(n.animations=c)}return n.object=i,n;function s(t){const e=[];for(const n in t){const i=t[n];delete i.metadata,e.push(i)}return e}}clone(t){return(new this.constructor).copy(this,t)}copy(t,e=!0){if(this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.userData=JSON.parse(JSON.stringify(t.userData)),!0===e)for(let e=0;e1?null:e.copy(n).multiplyScalar(r).add(t.start)}intersectsLine(t){const e=this.distanceToPoint(t.start),n=this.distanceToPoint(t.end);return e<0&&n>0||n<0&&e>0}intersectsBox(t){return t.intersectsPlane(this)}intersectsSphere(t){return t.intersectsPlane(this)}coplanarPoint(t){return void 0===t&&(console.warn("THREE.Plane: .coplanarPoint() target is now required"),t=new ai),t.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(t,e){const n=e||ur.getNormalMatrix(t),i=this.coplanarPoint(cr).applyMatrix4(t),r=this.normal.applyMatrix3(n).normalize();return this.constant=-i.dot(r),this}translate(t){return this.constant-=t.dot(this.normal),this}equals(t){return t.normal.equals(this.normal)&&t.constant===this.constant}clone(){return(new this.constructor).copy(this)}}dr.prototype.isPlane=!0;const pr=new ai,mr=new ai,fr=new ai,gr=new ai,vr=new ai,yr=new ai,xr=new ai,_r=new ai,br=new ai,wr=new ai;class Mr{constructor(t=new ai,e=new ai,n=new ai){this.a=t,this.b=e,this.c=n}static getNormal(t,e,n,i){void 0===i&&(console.warn("THREE.Triangle: .getNormal() target is now required"),i=new ai),i.subVectors(n,e),pr.subVectors(t,e),i.cross(pr);const r=i.lengthSq();return r>0?i.multiplyScalar(1/Math.sqrt(r)):i.set(0,0,0)}static getBarycoord(t,e,n,i,r){pr.subVectors(i,e),mr.subVectors(n,e),fr.subVectors(t,e);const s=pr.dot(pr),a=pr.dot(mr),o=pr.dot(fr),l=mr.dot(mr),c=mr.dot(fr),h=s*l-a*a;if(void 0===r&&(console.warn("THREE.Triangle: .getBarycoord() target is now required"),r=new ai),0===h)return r.set(-2,-1,-1);const u=1/h,d=(l*o-a*c)*u,p=(s*c-a*o)*u;return r.set(1-d-p,p,d)}static containsPoint(t,e,n,i){return this.getBarycoord(t,e,n,i,gr),gr.x>=0&&gr.y>=0&&gr.x+gr.y<=1}static getUV(t,e,n,i,r,s,a,o){return this.getBarycoord(t,e,n,i,gr),o.set(0,0),o.addScaledVector(r,gr.x),o.addScaledVector(s,gr.y),o.addScaledVector(a,gr.z),o}static isFrontFacing(t,e,n,i){return pr.subVectors(n,e),mr.subVectors(t,e),pr.cross(mr).dot(i)<0}set(t,e,n){return this.a.copy(t),this.b.copy(e),this.c.copy(n),this}setFromPointsAndIndices(t,e,n,i){return this.a.copy(t[e]),this.b.copy(t[n]),this.c.copy(t[i]),this}clone(){return(new this.constructor).copy(this)}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}getArea(){return pr.subVectors(this.c,this.b),mr.subVectors(this.a,this.b),.5*pr.cross(mr).length()}getMidpoint(t){return void 0===t&&(console.warn("THREE.Triangle: .getMidpoint() target is now required"),t=new ai),t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(t){return Mr.getNormal(this.a,this.b,this.c,t)}getPlane(t){return void 0===t&&(console.warn("THREE.Triangle: .getPlane() target is now required"),t=new dr),t.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(t,e){return Mr.getBarycoord(t,this.a,this.b,this.c,e)}getUV(t,e,n,i,r){return Mr.getUV(t,this.a,this.b,this.c,e,n,i,r)}containsPoint(t){return Mr.containsPoint(t,this.a,this.b,this.c)}isFrontFacing(t){return Mr.isFrontFacing(this.a,this.b,this.c,t)}intersectsBox(t){return t.intersectsTriangle(this)}closestPointToPoint(t,e){void 0===e&&(console.warn("THREE.Triangle: .closestPointToPoint() target is now required"),e=new ai);const n=this.a,i=this.b,r=this.c;let s,a;vr.subVectors(i,n),yr.subVectors(r,n),_r.subVectors(t,n);const o=vr.dot(_r),l=yr.dot(_r);if(o<=0&&l<=0)return e.copy(n);br.subVectors(t,i);const c=vr.dot(br),h=yr.dot(br);if(c>=0&&h<=c)return e.copy(i);const u=o*h-c*l;if(u<=0&&o>=0&&c<=0)return s=o/(o-c),e.copy(n).addScaledVector(vr,s);wr.subVectors(t,r);const d=vr.dot(wr),p=yr.dot(wr);if(p>=0&&d<=p)return e.copy(r);const m=d*l-o*p;if(m<=0&&l>=0&&p<=0)return a=l/(l-p),e.copy(n).addScaledVector(yr,a);const f=c*p-d*h;if(f<=0&&h-c>=0&&d-p>=0)return xr.subVectors(r,i),a=(h-c)/(h-c+(d-p)),e.copy(i).addScaledVector(xr,a);const g=1/(f+m+u);return s=m*g,a=u*g,e.copy(n).addScaledVector(vr,s).addScaledVector(yr,a)}equals(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)}}let Sr=0;function Tr(){Object.defineProperty(this,"id",{value:Sr++}),this.uuid=Un(),this.name="",this.type="Material",this.fog=!0,this.blending=_,this.side=m,this.vertexColors=!1,this.opacity=1,this.transparent=!1,this.blendSrc=N,this.blendDst=O,this.blendEquation=T,this.blendSrcAlpha=null,this.blendDstAlpha=null,this.blendEquationAlpha=null,this.depthFunc=W,this.depthTest=!0,this.depthWrite=!0,this.stencilWriteMask=255,this.stencilFunc=Sn,this.stencilRef=0,this.stencilFuncMask=255,this.stencilFail=hn,this.stencilZFail=hn,this.stencilZPass=hn,this.stencilWrite=!1,this.clippingPlanes=null,this.clipIntersection=!1,this.clipShadows=!1,this.shadowSide=null,this.colorWrite=!0,this.precision=null,this.polygonOffset=!1,this.polygonOffsetFactor=0,this.polygonOffsetUnits=0,this.dithering=!1,this.alphaTest=0,this.alphaToCoverage=!1,this.premultipliedAlpha=!1,this.visible=!0,this.toneMapped=!0,this.userData={},this.version=0}Tr.prototype=Object.assign(Object.create(Bn.prototype),{constructor:Tr,isMaterial:!0,onBuild:function(){},onBeforeCompile:function(){},customProgramCacheKey:function(){return this.onBeforeCompile.toString()},setValues:function(t){if(void 0!==t)for(const e in t){const n=t[e];if(void 0===n){console.warn("THREE.Material: '"+e+"' parameter is undefined.");continue}if("shading"===e){console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead."),this.flatShading=n===v;continue}const i=this[e];void 0!==i?i&&i.isColor?i.set(n):i&&i.isVector3&&n&&n.isVector3?i.copy(n):this[e]=n:console.warn("THREE."+this.type+": '"+e+"' is not a property of this material.")}},toJSON:function(t){const e=void 0===t||"string"==typeof t;e&&(t={textures:{},images:{}});const n={metadata:{version:4.5,type:"Material",generator:"Material.toJSON"}};function i(t){const e=[];for(const n in t){const i=t[n];delete i.metadata,e.push(i)}return e}if(n.uuid=this.uuid,n.type=this.type,""!==this.name&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),void 0!==this.roughness&&(n.roughness=this.roughness),void 0!==this.metalness&&(n.metalness=this.metalness),this.sheen&&this.sheen.isColor&&(n.sheen=this.sheen.getHex()),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.emissiveIntensity&&1!==this.emissiveIntensity&&(n.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),void 0!==this.shininess&&(n.shininess=this.shininess),void 0!==this.clearcoat&&(n.clearcoat=this.clearcoat),void 0!==this.clearcoatRoughness&&(n.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(n.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(n.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(n.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,n.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(n.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(t).uuid,n.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(n.aoMap=this.aoMap.toJSON(t).uuid,n.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(t).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(t).uuid,n.normalMapType=this.normalMapType,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(t).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(t).uuid,void 0!==this.combine&&(n.combine=this.combine)),void 0!==this.envMapIntensity&&(n.envMapIntensity=this.envMapIntensity),void 0!==this.reflectivity&&(n.reflectivity=this.reflectivity),void 0!==this.refractionRatio&&(n.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(t).uuid),void 0!==this.size&&(n.size=this.size),null!==this.shadowSide&&(n.shadowSide=this.shadowSide),void 0!==this.sizeAttenuation&&(n.sizeAttenuation=this.sizeAttenuation),this.blending!==_&&(n.blending=this.blending),this.side!==m&&(n.side=this.side),this.vertexColors&&(n.vertexColors=!0),this.opacity<1&&(n.opacity=this.opacity),!0===this.transparent&&(n.transparent=this.transparent),n.depthFunc=this.depthFunc,n.depthTest=this.depthTest,n.depthWrite=this.depthWrite,n.colorWrite=this.colorWrite,n.stencilWrite=this.stencilWrite,n.stencilWriteMask=this.stencilWriteMask,n.stencilFunc=this.stencilFunc,n.stencilRef=this.stencilRef,n.stencilFuncMask=this.stencilFuncMask,n.stencilFail=this.stencilFail,n.stencilZFail=this.stencilZFail,n.stencilZPass=this.stencilZPass,this.rotation&&0!==this.rotation&&(n.rotation=this.rotation),!0===this.polygonOffset&&(n.polygonOffset=!0),0!==this.polygonOffsetFactor&&(n.polygonOffsetFactor=this.polygonOffsetFactor),0!==this.polygonOffsetUnits&&(n.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth&&1!==this.linewidth&&(n.linewidth=this.linewidth),void 0!==this.dashSize&&(n.dashSize=this.dashSize),void 0!==this.gapSize&&(n.gapSize=this.gapSize),void 0!==this.scale&&(n.scale=this.scale),!0===this.dithering&&(n.dithering=!0),this.alphaTest>0&&(n.alphaTest=this.alphaTest),!0===this.alphaToCoverage&&(n.alphaToCoverage=this.alphaToCoverage),!0===this.premultipliedAlpha&&(n.premultipliedAlpha=this.premultipliedAlpha),!0===this.wireframe&&(n.wireframe=this.wireframe),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),"round"!==this.wireframeLinecap&&(n.wireframeLinecap=this.wireframeLinecap),"round"!==this.wireframeLinejoin&&(n.wireframeLinejoin=this.wireframeLinejoin),!0===this.morphTargets&&(n.morphTargets=!0),!0===this.morphNormals&&(n.morphNormals=!0),!0===this.skinning&&(n.skinning=!0),!0===this.flatShading&&(n.flatShading=this.flatShading),!1===this.visible&&(n.visible=!1),!1===this.toneMapped&&(n.toneMapped=!1),"{}"!==JSON.stringify(this.userData)&&(n.userData=this.userData),e){const e=i(t.textures),r=i(t.images);e.length>0&&(n.textures=e),r.length>0&&(n.images=r)}return n},clone:function(){return(new this.constructor).copy(this)},copy:function(t){this.name=t.name,this.fog=t.fog,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;const e=t.clippingPlanes;let n=null;if(null!==e){const t=e.length;n=new Array(t);for(let i=0;i!==t;++i)n[i]=e[i].clone()}return this.clippingPlanes=n,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.alphaToCoverage=t.alphaToCoverage,this.premultipliedAlpha=t.premultipliedAlpha,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this},dispose:function(){this.dispatchEvent({type:"dispose"})}}),Object.defineProperty(Tr.prototype,"needsUpdate",{set:function(t){!0===t&&this.version++}});const Er={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Ar={h:0,s:0,l:0},Lr={h:0,s:0,l:0};function Rr(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+6*(e-t)*(2/3-n):t}function Cr(t){return t<.04045?.0773993808*t:Math.pow(.9478672986*t+.0521327014,2.4)}function Pr(t){return t<.0031308?12.92*t:1.055*Math.pow(t,.41666)-.055}class Dr{constructor(t,e,n){return void 0===e&&void 0===n?this.set(t):this.setRGB(t,e,n)}set(t){return t&&t.isColor?this.copy(t):"number"==typeof t?this.setHex(t):"string"==typeof t&&this.setStyle(t),this}setScalar(t){return this.r=t,this.g=t,this.b=t,this}setHex(t){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(255&t)/255,this}setRGB(t,e,n){return this.r=t,this.g=e,this.b=n,this}setHSL(t,e,n){if(t=kn(t,1),e=Vn(e,0,1),n=Vn(n,0,1),0===e)this.r=this.g=this.b=n;else{const i=n<=.5?n*(1+e):n+e-n*e,r=2*n-i;this.r=Rr(r,i,t+1/3),this.g=Rr(r,i,t),this.b=Rr(r,i,t-1/3)}return this}setStyle(t){function e(e){void 0!==e&&parseFloat(e)<1&&console.warn("THREE.Color: Alpha component of "+t+" will be ignored.")}let n;if(n=/^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec(t)){let t;const i=n[1],r=n[2];switch(i){case"rgb":case"rgba":if(t=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(r))return this.r=Math.min(255,parseInt(t[1],10))/255,this.g=Math.min(255,parseInt(t[2],10))/255,this.b=Math.min(255,parseInt(t[3],10))/255,e(t[4]),this;if(t=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(r))return this.r=Math.min(100,parseInt(t[1],10))/100,this.g=Math.min(100,parseInt(t[2],10))/100,this.b=Math.min(100,parseInt(t[3],10))/100,e(t[4]),this;break;case"hsl":case"hsla":if(t=/^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(r)){const n=parseFloat(t[1])/360,i=parseInt(t[2],10)/100,r=parseInt(t[3],10)/100;return e(t[4]),this.setHSL(n,i,r)}}}else if(n=/^\#([A-Fa-f\d]+)$/.exec(t)){const t=n[1],e=t.length;if(3===e)return this.r=parseInt(t.charAt(0)+t.charAt(0),16)/255,this.g=parseInt(t.charAt(1)+t.charAt(1),16)/255,this.b=parseInt(t.charAt(2)+t.charAt(2),16)/255,this;if(6===e)return this.r=parseInt(t.charAt(0)+t.charAt(1),16)/255,this.g=parseInt(t.charAt(2)+t.charAt(3),16)/255,this.b=parseInt(t.charAt(4)+t.charAt(5),16)/255,this}return t&&t.length>0?this.setColorName(t):this}setColorName(t){const e=Er[t.toLowerCase()];return void 0!==e?this.setHex(e):console.warn("THREE.Color: Unknown color "+t),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}copyGammaToLinear(t,e=2){return this.r=Math.pow(t.r,e),this.g=Math.pow(t.g,e),this.b=Math.pow(t.b,e),this}copyLinearToGamma(t,e=2){const n=e>0?1/e:1;return this.r=Math.pow(t.r,n),this.g=Math.pow(t.g,n),this.b=Math.pow(t.b,n),this}convertGammaToLinear(t){return this.copyGammaToLinear(this,t),this}convertLinearToGamma(t){return this.copyLinearToGamma(this,t),this}copySRGBToLinear(t){return this.r=Cr(t.r),this.g=Cr(t.g),this.b=Cr(t.b),this}copyLinearToSRGB(t){return this.r=Pr(t.r),this.g=Pr(t.g),this.b=Pr(t.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0}getHexString(){return("000000"+this.getHex().toString(16)).slice(-6)}getHSL(t){void 0===t&&(console.warn("THREE.Color: .getHSL() target is now required"),t={h:0,s:0,l:0});const e=this.r,n=this.g,i=this.b,r=Math.max(e,n,i),s=Math.min(e,n,i);let a,o;const l=(s+r)/2;if(s===r)a=0,o=0;else{const t=r-s;switch(o=l<=.5?t/(r+s):t/(2-r-s),r){case e:a=(n-i)/t+(ne&&(e=t[n]);return e}const Yr={Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array};function Zr(t,e){return new Yr[t](e)}let Jr=0;const Qr=new zi,Kr=new lr,$r=new ai,ts=new ci,es=new ci,ns=new ai;class is extends Bn{constructor(){super(),Object.defineProperty(this,"id",{value:Jr++}),this.uuid=Un(),this.name="",this.type="BufferGeometry",this.index=null,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={}}getIndex(){return this.index}setIndex(t){return Array.isArray(t)?this.index=new(Xr(t)>65535?kr:Ur)(t,1):this.index=t,this}getAttribute(t){return this.attributes[t]}setAttribute(t,e){return this.attributes[t]=e,this}deleteAttribute(t){return delete this.attributes[t],this}hasAttribute(t){return void 0!==this.attributes[t]}addGroup(t,e,n=0){this.groups.push({start:t,count:e,materialIndex:n})}clearGroups(){this.groups=[]}setDrawRange(t,e){this.drawRange.start=t,this.drawRange.count=e}applyMatrix4(t){const e=this.attributes.position;void 0!==e&&(e.applyMatrix4(t),e.needsUpdate=!0);const n=this.attributes.normal;if(void 0!==n){const e=(new Jn).getNormalMatrix(t);n.applyNormalMatrix(e),n.needsUpdate=!0}const i=this.attributes.tangent;return void 0!==i&&(i.transformDirection(t),i.needsUpdate=!0),null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this}rotateX(t){return Qr.makeRotationX(t),this.applyMatrix4(Qr),this}rotateY(t){return Qr.makeRotationY(t),this.applyMatrix4(Qr),this}rotateZ(t){return Qr.makeRotationZ(t),this.applyMatrix4(Qr),this}translate(t,e,n){return Qr.makeTranslation(t,e,n),this.applyMatrix4(Qr),this}scale(t,e,n){return Qr.makeScale(t,e,n),this.applyMatrix4(Qr),this}lookAt(t){return Kr.lookAt(t),Kr.updateMatrix(),this.applyMatrix4(Kr.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter($r).negate(),this.translate($r.x,$r.y,$r.z),this}setFromPoints(t){const e=[];for(let n=0,i=t.length;n0&&(t.userData=this.userData),void 0!==this.parameters){const e=this.parameters;for(const n in e)void 0!==e[n]&&(t[n]=e[n]);return t}t.data={attributes:{}};const e=this.index;null!==e&&(t.data.index={type:e.array.constructor.name,array:Array.prototype.slice.call(e.array)});const n=this.attributes;for(const e in n){const i=n[e];t.data.attributes[e]=i.toJSON(t.data)}const i={};let r=!1;for(const e in this.morphAttributes){const n=this.morphAttributes[e],s=[];for(let e=0,i=n.length;e0&&(i[e]=s,r=!0)}r&&(t.data.morphAttributes=i,t.data.morphTargetsRelative=this.morphTargetsRelative);const s=this.groups;s.length>0&&(t.data.groups=JSON.parse(JSON.stringify(s)));const a=this.boundingSphere;return null!==a&&(t.data.boundingSphere={center:a.center.toArray(),radius:a.radius}),t}clone(){return(new is).copy(this)}copy(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const e={};this.name=t.name;const n=t.index;null!==n&&this.setIndex(n.clone(e));const i=t.attributes;for(const t in i){const n=i[t];this.setAttribute(t,n.clone(e))}const r=t.morphAttributes;for(const t in r){const n=[],i=r[t];for(let t=0,r=i.length;t0){const t=e[n[0]];if(void 0!==t){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let e=0,n=t.length;e0&&console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}}raycast(t,e){const n=this.geometry,i=this.material,r=this.matrixWorld;if(void 0===i)return;if(null===n.boundingSphere&&n.computeBoundingSphere(),as.copy(n.boundingSphere),as.applyMatrix4(r),!1===t.ray.intersectsSphere(as))return;if(rs.copy(r).invert(),ss.copy(t.ray).applyMatrix4(rs),null!==n.boundingBox&&!1===ss.intersectsBox(n.boundingBox))return;let s;if(n.isBufferGeometry){const r=n.index,a=n.attributes.position,o=n.morphAttributes.position,l=n.morphTargetsRelative,c=n.attributes.uv,h=n.attributes.uv2,u=n.groups,d=n.drawRange;if(null!==r)if(Array.isArray(i))for(let n=0,p=u.length;nn.far?null:{distance:c,point:_s.clone(),object:t}}(t,e,n,i,os,ls,cs,xs);if(p){o&&(gs.fromBufferAttribute(o,c),vs.fromBufferAttribute(o,h),ys.fromBufferAttribute(o,u),p.uv=Mr.getUV(xs,os,ls,cs,gs,vs,ys,new Zn)),l&&(gs.fromBufferAttribute(l,c),vs.fromBufferAttribute(l,h),ys.fromBufferAttribute(l,u),p.uv2=Mr.getUV(xs,os,ls,cs,gs,vs,ys,new Zn));const t={a:c,b:h,c:u,normal:new ai,materialIndex:0};Mr.getNormal(os,ls,cs,t.normal),p.face=t}return p}bs.prototype.isMesh=!0;class Ms extends is{constructor(t=1,e=1,n=1,i=1,r=1,s=1){super(),this.type="BoxGeometry",this.parameters={width:t,height:e,depth:n,widthSegments:i,heightSegments:r,depthSegments:s};const a=this;i=Math.floor(i),r=Math.floor(r),s=Math.floor(s);const o=[],l=[],c=[],h=[];let u=0,d=0;function p(t,e,n,i,r,s,p,m,f,g,v){const y=s/f,x=p/g,_=s/2,b=p/2,w=m/2,M=f+1,S=g+1;let T=0,E=0;const A=new ai;for(let s=0;s0?1:-1,c.push(A.x,A.y,A.z),h.push(o/f),h.push(1-s/g),T+=1}}for(let t=0;t0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader;const n={};for(const t in this.extensions)!0===this.extensions[t]&&(n[t]=!0);return Object.keys(n).length>0&&(e.extensions=n),e}}As.prototype.isShaderMaterial=!0;class Ls extends lr{constructor(){super(),this.type="Camera",this.matrixWorldInverse=new zi,this.projectionMatrix=new zi,this.projectionMatrixInverse=new zi}copy(t,e){return super.copy(t,e),this.matrixWorldInverse.copy(t.matrixWorldInverse),this.projectionMatrix.copy(t.projectionMatrix),this.projectionMatrixInverse.copy(t.projectionMatrixInverse),this}getWorldDirection(t){void 0===t&&(console.warn("THREE.Camera: .getWorldDirection() target is now required"),t=new ai),this.updateWorldMatrix(!0,!1);const e=this.matrixWorld.elements;return t.set(-e[8],-e[9],-e[10]).normalize()}updateMatrixWorld(t){super.updateMatrixWorld(t),this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(t,e){super.updateWorldMatrix(t,e),this.matrixWorldInverse.copy(this.matrixWorld).invert()}clone(){return(new this.constructor).copy(this)}}Ls.prototype.isCamera=!0;class Rs extends Ls{constructor(t=50,e=1,n=.1,i=2e3){super(),this.type="PerspectiveCamera",this.fov=t,this.zoom=1,this.near=n,this.far=i,this.focus=10,this.aspect=e,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.fov=t.fov,this.zoom=t.zoom,this.near=t.near,this.far=t.far,this.focus=t.focus,this.aspect=t.aspect,this.view=null===t.view?null:Object.assign({},t.view),this.filmGauge=t.filmGauge,this.filmOffset=t.filmOffset,this}setFocalLength(t){const e=.5*this.getFilmHeight()/t;this.fov=2*Gn*Math.atan(e),this.updateProjectionMatrix()}getFocalLength(){const t=Math.tan(.5*Hn*this.fov);return.5*this.getFilmHeight()/t}getEffectiveFOV(){return 2*Gn*Math.atan(Math.tan(.5*Hn*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}setViewOffset(t,e,n,i,r,s){this.aspect=t/e,null===this.view&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=i,this.view.width=r,this.view.height=s,this.updateProjectionMatrix()}clearViewOffset(){null!==this.view&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=this.near;let e=t*Math.tan(.5*Hn*this.fov)/this.zoom,n=2*e,i=this.aspect*n,r=-.5*i;const s=this.view;if(null!==this.view&&this.view.enabled){const t=s.fullWidth,a=s.fullHeight;r+=s.offsetX*i/t,e-=s.offsetY*n/a,i*=s.width/t,n*=s.height/a}const a=this.filmOffset;0!==a&&(r+=t*a/this.getFilmWidth()),this.projectionMatrix.makePerspective(r,r+i,e,e-n,t,this.far),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.fov=this.fov,e.object.zoom=this.zoom,e.object.near=this.near,e.object.far=this.far,e.object.focus=this.focus,e.object.aspect=this.aspect,null!==this.view&&(e.object.view=Object.assign({},this.view)),e.object.filmGauge=this.filmGauge,e.object.filmOffset=this.filmOffset,e}}Rs.prototype.isPerspectiveCamera=!0;const Cs=90;class Ps extends lr{constructor(t,e,n){if(super(),this.type="CubeCamera",!0!==n.isWebGLCubeRenderTarget)return void console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.");this.renderTarget=n;const i=new Rs(Cs,1,t,e);i.layers=this.layers,i.up.set(0,-1,0),i.lookAt(new ai(1,0,0)),this.add(i);const r=new Rs(Cs,1,t,e);r.layers=this.layers,r.up.set(0,-1,0),r.lookAt(new ai(-1,0,0)),this.add(r);const s=new Rs(Cs,1,t,e);s.layers=this.layers,s.up.set(0,0,1),s.lookAt(new ai(0,1,0)),this.add(s);const a=new Rs(Cs,1,t,e);a.layers=this.layers,a.up.set(0,0,-1),a.lookAt(new ai(0,-1,0)),this.add(a);const o=new Rs(Cs,1,t,e);o.layers=this.layers,o.up.set(0,-1,0),o.lookAt(new ai(0,0,1)),this.add(o);const l=new Rs(Cs,1,t,e);l.layers=this.layers,l.up.set(0,-1,0),l.lookAt(new ai(0,0,-1)),this.add(l)}update(t,e){null===this.parent&&this.updateMatrixWorld();const n=this.renderTarget,[i,r,s,a,o,l]=this.children,c=t.xr.enabled,h=t.getRenderTarget();t.xr.enabled=!1;const u=n.texture.generateMipmaps;n.texture.generateMipmaps=!1,t.setRenderTarget(n,0),t.render(e,i),t.setRenderTarget(n,1),t.render(e,r),t.setRenderTarget(n,2),t.render(e,s),t.setRenderTarget(n,3),t.render(e,a),t.setRenderTarget(n,4),t.render(e,o),n.texture.generateMipmaps=u,t.setRenderTarget(n,5),t.render(e,l),t.setRenderTarget(h),t.xr.enabled=c}}class Ds extends ti{constructor(t,e,n,i,r,s,a,o,l,c){super(t=void 0!==t?t:[],e=void 0!==e?e:st,n,i,r,s,a=void 0!==a?a:zt,o,l,c),this._needsFlipEnvMap=!0,this.flipY=!1}get images(){return this.image}set images(t){this.image=t}}Ds.prototype.isCubeTexture=!0;class Is extends ii{constructor(t,e,n){Number.isInteger(e)&&(console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )"),e=n),super(t,t,e),e=e||{},this.texture=new Ds(void 0,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.encoding),this.texture.generateMipmaps=void 0!==e.generateMipmaps&&e.generateMipmaps,this.texture.minFilter=void 0!==e.minFilter?e.minFilter:xt,this.texture._needsFlipEnvMap=!1}fromEquirectangularTexture(t,e){this.texture.type=e.type,this.texture.format=Ft,this.texture.encoding=e.encoding,this.texture.generateMipmaps=e.generateMipmaps,this.texture.minFilter=e.minFilter,this.texture.magFilter=e.magFilter;const n={tEquirect:{value:null}},i="\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\n\t\t\t\t}\n\t\t\t",r="\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include \n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t",s=new Ms(5,5,5),a=new As({name:"CubemapFromEquirect",uniforms:Ss(n),vertexShader:i,fragmentShader:r,side:f,blending:x});a.uniforms.tEquirect.value=e;const o=new bs(s,a),l=e.minFilter;return e.minFilter===wt&&(e.minFilter=xt),new Ps(1,10,this).update(t,o),e.minFilter=l,o.geometry.dispose(),o.material.dispose(),this}clear(t,e,n,i){const r=t.getRenderTarget();for(let r=0;r<6;r++)t.setRenderTarget(this,r),t.clear(e,n,i);t.setRenderTarget(r)}}Is.prototype.isWebGLCubeRenderTarget=!0;class Ns extends ti{constructor(t,e,n,i,r,s,a,o,l,c,h,u){super(null,s,a,o,l,c,i,r,h,u),this.image={data:t||null,width:e||1,height:n||1},this.magFilter=void 0!==l?l:mt,this.minFilter=void 0!==c?c:mt,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1,this.needsUpdate=!0}}Ns.prototype.isDataTexture=!0;const Os=new Li,Bs=new ai;class zs{constructor(t=new dr,e=new dr,n=new dr,i=new dr,r=new dr,s=new dr){this.planes=[t,e,n,i,r,s]}set(t,e,n,i,r,s){const a=this.planes;return a[0].copy(t),a[1].copy(e),a[2].copy(n),a[3].copy(i),a[4].copy(r),a[5].copy(s),this}copy(t){const e=this.planes;for(let n=0;n<6;n++)e[n].copy(t.planes[n]);return this}setFromProjectionMatrix(t){const e=this.planes,n=t.elements,i=n[0],r=n[1],s=n[2],a=n[3],o=n[4],l=n[5],c=n[6],h=n[7],u=n[8],d=n[9],p=n[10],m=n[11],f=n[12],g=n[13],v=n[14],y=n[15];return e[0].setComponents(a-i,h-o,m-u,y-f).normalize(),e[1].setComponents(a+i,h+o,m+u,y+f).normalize(),e[2].setComponents(a+r,h+l,m+d,y+g).normalize(),e[3].setComponents(a-r,h-l,m-d,y-g).normalize(),e[4].setComponents(a-s,h-c,m-p,y-v).normalize(),e[5].setComponents(a+s,h+c,m+p,y+v).normalize(),this}intersectsObject(t){const e=t.geometry;return null===e.boundingSphere&&e.computeBoundingSphere(),Os.copy(e.boundingSphere).applyMatrix4(t.matrixWorld),this.intersectsSphere(Os)}intersectsSprite(t){return Os.center.set(0,0,0),Os.radius=.7071067811865476,Os.applyMatrix4(t.matrixWorld),this.intersectsSphere(Os)}intersectsSphere(t){const e=this.planes,n=t.center,i=-t.radius;for(let t=0;t<6;t++)if(e[t].distanceToPoint(n)0?t.max.x:t.min.x,Bs.y=i.normal.y>0?t.max.y:t.min.y,Bs.z=i.normal.z>0?t.max.z:t.min.z,i.distanceToPoint(Bs)<0)return!1}return!0}containsPoint(t){const e=this.planes;for(let n=0;n<6;n++)if(e[n].distanceToPoint(t)<0)return!1;return!0}clone(){return(new this.constructor).copy(this)}}function Fs(){let t=null,e=!1,n=null,i=null;function r(e,s){n(e,s),i=t.requestAnimationFrame(r)}return{start:function(){!0!==e&&null!==n&&(i=t.requestAnimationFrame(r),e=!0)},stop:function(){t.cancelAnimationFrame(i),e=!1},setAnimationLoop:function(t){n=t},setContext:function(e){t=e}}}function Hs(t,e){const n=e.isWebGL2,i=new WeakMap;return{get:function(t){return t.isInterleavedBufferAttribute&&(t=t.data),i.get(t)},remove:function(e){e.isInterleavedBufferAttribute&&(e=e.data);const n=i.get(e);n&&(t.deleteBuffer(n.buffer),i.delete(e))},update:function(e,r){if(e.isGLBufferAttribute){const t=i.get(e);return void((!t||t.version 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif",bumpmap_pars_fragment:"#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif",clipping_planes_fragment:"#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif",clipping_planes_pars_fragment:"#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif",clipping_planes_pars_vertex:"#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif",clipping_planes_vertex:"#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif",color_fragment:"#if defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#elif defined( USE_COLOR )\n\tdiffuseColor.rgb *= vColor;\n#endif",color_pars_fragment:"#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR )\n\tvarying vec3 vColor;\n#endif",color_pars_vertex:"#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif",color_vertex:"#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif",common:"#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}",cube_uv_reflection_fragment:"#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif",defaultnormal_vertex:"vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif",displacementmap_pars_vertex:"#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif",displacementmap_vertex:"#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif",emissivemap_fragment:"#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif",emissivemap_pars_fragment:"#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif",encodings_fragment:"gl_FragColor = linearToOutputTexel( gl_FragColor );",encodings_pars_fragment:"\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}",envmap_fragment:"#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif",envmap_common_pars_fragment:"#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif",envmap_pars_fragment:"#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif",envmap_pars_vertex:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif",envmap_physical_pars_fragment:"#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif",envmap_vertex:"#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif",fog_vertex:"#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif",fog_pars_vertex:"#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif",fog_fragment:"#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif",fog_pars_fragment:"#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif",gradientmap_pars_fragment:"#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}",lightmap_fragment:"#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif",lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif",lights_lambert_vertex:"vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif",lights_pars_begin:"uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif",lights_toon_fragment:"ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;",lights_toon_pars_fragment:"varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)",lights_phong_fragment:"BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;",lights_phong_pars_fragment:"varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)",lights_physical_fragment:"PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif",lights_physical_pars_fragment:"struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}",lights_fragment_begin:"\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif",lights_fragment_maps:"#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif",lights_fragment_end:"#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif",logdepthbuf_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif",logdepthbuf_pars_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif",logdepthbuf_pars_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif",logdepthbuf_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif",map_fragment:"#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif",map_pars_fragment:"#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif",map_particle_fragment:"#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif",map_particle_pars_fragment:"#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif",metalnessmap_fragment:"float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif",metalnessmap_pars_fragment:"#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif",morphnormal_vertex:"#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif",morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif",morphtarget_vertex:"#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif",normal_fragment_begin:"float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;",normal_fragment_maps:"#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif",normalmap_pars_fragment:"#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif",clearcoat_normal_fragment_begin:"#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif",clearcoat_normal_fragment_maps:"#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif",clearcoat_pars_fragment:"#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif",packing:"vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}",premultiplied_alpha_fragment:"#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif",project_vertex:"vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;",dithering_fragment:"#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif",dithering_pars_fragment:"#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif",roughnessmap_fragment:"float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif",roughnessmap_pars_fragment:"#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif",shadowmap_pars_fragment:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif",shadowmap_pars_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif",shadowmap_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif",shadowmask_pars_fragment:"float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}",skinbase_vertex:"#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif",skinning_vertex:"#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif",skinnormal_vertex:"#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif",specularmap_fragment:"float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif",specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif",tonemapping_fragment:"#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif",tonemapping_pars_fragment:"#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }",transmissionmap_fragment:"#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif",transmissionmap_pars_fragment:"#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif",uv_pars_fragment:"#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif",uv_pars_vertex:"#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif",uv_vertex:"#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif",uv2_pars_fragment:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif",uv2_pars_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif",uv2_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif",background_frag:"uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}",background_vert:"varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}",cube_frag:"#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}",cube_vert:"varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}",depth_frag:"#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}",depth_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}",distanceRGBA_frag:"#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}",distanceRGBA_vert:"#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}",equirect_frag:"uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}",equirect_vert:"varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}",linedashed_frag:"uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}",linedashed_vert:"uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshbasic_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshbasic_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshlambert_frag:"uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshlambert_vert:"#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshmatcap_frag:"#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshmatcap_vert:"#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}",meshtoon_frag:"#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshtoon_vert:"#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}",meshphong_frag:"#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshphong_vert:"#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}",meshphysical_frag:"#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshphysical_vert:"#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}",normal_frag:"#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}",normal_vert:"#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}",points_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}",points_vert:"uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}",shadow_frag:"uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}",shadow_vert:"#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",sprite_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}",sprite_vert:"uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"},Vs={common:{diffuse:{value:new Dr(15658734)},opacity:{value:1},map:{value:null},uvTransform:{value:new Jn},uv2Transform:{value:new Jn},alphaMap:{value:null}},specularmap:{specularMap:{value:null}},envmap:{envMap:{value:null},flipEnvMap:{value:-1},reflectivity:{value:1},refractionRatio:{value:.98},maxMipLevel:{value:0}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1}},emissivemap:{emissiveMap:{value:null}},bumpmap:{bumpMap:{value:null},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalScale:{value:new Zn(1,1)}},displacementmap:{displacementMap:{value:null},displacementScale:{value:1},displacementBias:{value:0}},roughnessmap:{roughnessMap:{value:null}},metalnessmap:{metalnessMap:{value:null}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new Dr(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotShadowMap:{value:[]},spotShadowMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null}},points:{diffuse:{value:new Dr(15658734)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},uvTransform:{value:new Jn}},sprite:{diffuse:{value:new Dr(15658734)},opacity:{value:1},center:{value:new Zn(.5,.5)},rotation:{value:0},map:{value:null},alphaMap:{value:null},uvTransform:{value:new Jn}}},ks={basic:{uniforms:Ts([Vs.common,Vs.specularmap,Vs.envmap,Vs.aomap,Vs.lightmap,Vs.fog]),vertexShader:Us.meshbasic_vert,fragmentShader:Us.meshbasic_frag},lambert:{uniforms:Ts([Vs.common,Vs.specularmap,Vs.envmap,Vs.aomap,Vs.lightmap,Vs.emissivemap,Vs.fog,Vs.lights,{emissive:{value:new Dr(0)}}]),vertexShader:Us.meshlambert_vert,fragmentShader:Us.meshlambert_frag},phong:{uniforms:Ts([Vs.common,Vs.specularmap,Vs.envmap,Vs.aomap,Vs.lightmap,Vs.emissivemap,Vs.bumpmap,Vs.normalmap,Vs.displacementmap,Vs.fog,Vs.lights,{emissive:{value:new Dr(0)},specular:{value:new Dr(1118481)},shininess:{value:30}}]),vertexShader:Us.meshphong_vert,fragmentShader:Us.meshphong_frag},standard:{uniforms:Ts([Vs.common,Vs.envmap,Vs.aomap,Vs.lightmap,Vs.emissivemap,Vs.bumpmap,Vs.normalmap,Vs.displacementmap,Vs.roughnessmap,Vs.metalnessmap,Vs.fog,Vs.lights,{emissive:{value:new Dr(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:Us.meshphysical_vert,fragmentShader:Us.meshphysical_frag},toon:{uniforms:Ts([Vs.common,Vs.aomap,Vs.lightmap,Vs.emissivemap,Vs.bumpmap,Vs.normalmap,Vs.displacementmap,Vs.gradientmap,Vs.fog,Vs.lights,{emissive:{value:new Dr(0)}}]),vertexShader:Us.meshtoon_vert,fragmentShader:Us.meshtoon_frag},matcap:{uniforms:Ts([Vs.common,Vs.bumpmap,Vs.normalmap,Vs.displacementmap,Vs.fog,{matcap:{value:null}}]),vertexShader:Us.meshmatcap_vert,fragmentShader:Us.meshmatcap_frag},points:{uniforms:Ts([Vs.points,Vs.fog]),vertexShader:Us.points_vert,fragmentShader:Us.points_frag},dashed:{uniforms:Ts([Vs.common,Vs.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:Us.linedashed_vert,fragmentShader:Us.linedashed_frag},depth:{uniforms:Ts([Vs.common,Vs.displacementmap]),vertexShader:Us.depth_vert,fragmentShader:Us.depth_frag},normal:{uniforms:Ts([Vs.common,Vs.bumpmap,Vs.normalmap,Vs.displacementmap,{opacity:{value:1}}]),vertexShader:Us.normal_vert,fragmentShader:Us.normal_frag},sprite:{uniforms:Ts([Vs.sprite,Vs.fog]),vertexShader:Us.sprite_vert,fragmentShader:Us.sprite_frag},background:{uniforms:{uvTransform:{value:new Jn},t2D:{value:null}},vertexShader:Us.background_vert,fragmentShader:Us.background_frag},cube:{uniforms:Ts([Vs.envmap,{opacity:{value:1}}]),vertexShader:Us.cube_vert,fragmentShader:Us.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:Us.equirect_vert,fragmentShader:Us.equirect_frag},distanceRGBA:{uniforms:Ts([Vs.common,Vs.displacementmap,{referencePosition:{value:new ai},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:Us.distanceRGBA_vert,fragmentShader:Us.distanceRGBA_frag},shadow:{uniforms:Ts([Vs.lights,Vs.fog,{color:{value:new Dr(0)},opacity:{value:1}}]),vertexShader:Us.shadow_vert,fragmentShader:Us.shadow_frag}};function Ws(t,e,n,i,r){const s=new Dr(0);let a,o,l=0,c=null,h=0,u=null;function d(t,e){n.buffers.color.setClear(t.r,t.g,t.b,e,r)}return{getClearColor:function(){return s},setClearColor:function(t,e=1){s.set(t),l=e,d(s,l)},getClearAlpha:function(){return l},setClearAlpha:function(t){l=t,d(s,l)},render:function(n,r,p,g){let v=!0===r.isScene?r.background:null;v&&v.isTexture&&(v=e.get(v));const y=t.xr,x=y.getSession&&y.getSession();x&&"additive"===x.environmentBlendMode&&(v=null),null===v?d(s,l):v&&v.isColor&&(d(v,1),g=!0),(t.autoClear||g)&&t.clear(t.autoClearColor,t.autoClearDepth,t.autoClearStencil),v&&(v.isCubeTexture||v.mapping===ct)?(void 0===o&&(o=new bs(new Ms(1,1,1),new As({name:"BackgroundCubeMaterial",uniforms:Ss(ks.cube.uniforms),vertexShader:ks.cube.vertexShader,fragmentShader:ks.cube.fragmentShader,side:f,depthTest:!1,depthWrite:!1,fog:!1})),o.geometry.deleteAttribute("normal"),o.geometry.deleteAttribute("uv"),o.onBeforeRender=function(t,e,n){this.matrixWorld.copyPosition(n.matrixWorld)},Object.defineProperty(o.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),i.update(o)),o.material.uniforms.envMap.value=v,o.material.uniforms.flipEnvMap.value=v.isCubeTexture&&v._needsFlipEnvMap?-1:1,c===v&&h===v.version&&u===t.toneMapping||(o.material.needsUpdate=!0,c=v,h=v.version,u=t.toneMapping),n.unshift(o,o.geometry,o.material,0,0,null)):v&&v.isTexture&&(void 0===a&&(a=new bs(new Gs(2,2),new As({name:"BackgroundMaterial",uniforms:Ss(ks.background.uniforms),vertexShader:ks.background.vertexShader,fragmentShader:ks.background.fragmentShader,side:m,depthTest:!1,depthWrite:!1,fog:!1})),a.geometry.deleteAttribute("normal"),Object.defineProperty(a.material,"map",{get:function(){return this.uniforms.t2D.value}}),i.update(a)),a.material.uniforms.t2D.value=v,!0===v.matrixAutoUpdate&&v.updateMatrix(),a.material.uniforms.uvTransform.value.copy(v.matrix),c===v&&h===v.version&&u===t.toneMapping||(a.material.needsUpdate=!0,c=v,h=v.version,u=t.toneMapping),n.unshift(a,a.geometry,a.material,0,0,null))}}}function js(t,e,n,i){const r=t.getParameter(34921),s=i.isWebGL2?null:e.get("OES_vertex_array_object"),a=i.isWebGL2||null!==s,o={},l=d(null);let c=l;function h(e){return i.isWebGL2?t.bindVertexArray(e):s.bindVertexArrayOES(e)}function u(e){return i.isWebGL2?t.deleteVertexArray(e):s.deleteVertexArrayOES(e)}function d(t){const e=[],n=[],i=[];for(let t=0;t=0){const s=l[e];if(void 0!==s){const e=s.normalized,r=s.itemSize,a=n.get(s);if(void 0===a)continue;const l=a.buffer,c=a.type,h=a.bytesPerElement;if(s.isInterleavedBufferAttribute){const n=s.data,a=n.stride,u=s.offset;n&&n.isInstancedInterleavedBuffer?(f(i,n.meshPerAttribute),void 0===o._maxInstanceCount&&(o._maxInstanceCount=n.meshPerAttribute*n.count)):m(i),t.bindBuffer(34962,l),v(i,r,c,e,a*h,u*h)}else s.isInstancedBufferAttribute?(f(i,s.meshPerAttribute),void 0===o._maxInstanceCount&&(o._maxInstanceCount=s.meshPerAttribute*s.count)):m(i),t.bindBuffer(34962,l),v(i,r,c,e,0,0)}else if("instanceMatrix"===e){const e=n.get(r.instanceMatrix);if(void 0===e)continue;const s=e.buffer,a=e.type;f(i+0,1),f(i+1,1),f(i+2,1),f(i+3,1),t.bindBuffer(34962,s),t.vertexAttribPointer(i+0,4,a,!1,64,0),t.vertexAttribPointer(i+1,4,a,!1,64,16),t.vertexAttribPointer(i+2,4,a,!1,64,32),t.vertexAttribPointer(i+3,4,a,!1,64,48)}else if("instanceColor"===e){const e=n.get(r.instanceColor);if(void 0===e)continue;const s=e.buffer,a=e.type;f(i,1),t.bindBuffer(34962,s),t.vertexAttribPointer(i,3,a,!1,12,0)}else if(void 0!==h){const n=h[e];if(void 0!==n)switch(n.length){case 2:t.vertexAttrib2fv(i,n);break;case 3:t.vertexAttrib3fv(i,n);break;case 4:t.vertexAttrib4fv(i,n);break;default:t.vertexAttrib1fv(i,n)}}}}g()}(r,l,u,y),null!==x&&t.bindBuffer(34963,n.get(x).buffer))},reset:y,resetDefaultState:x,dispose:function(){y();for(const t in o){const e=o[t];for(const t in e){const n=e[t];for(const t in n)u(n[t].object),delete n[t];delete e[t]}delete o[t]}},releaseStatesOfGeometry:function(t){if(void 0===o[t.id])return;const e=o[t.id];for(const t in e){const n=e[t];for(const t in n)u(n[t].object),delete n[t];delete e[t]}delete o[t.id]},releaseStatesOfProgram:function(t){for(const e in o){const n=o[e];if(void 0===n[t.id])continue;const i=n[t.id];for(const t in i)u(i[t].object),delete i[t];delete n[t.id]}},initAttributes:p,enableAttribute:m,disableUnusedAttributes:g}}function qs(t,e,n,i){const r=i.isWebGL2;let s;this.setMode=function(t){s=t},this.render=function(e,i){t.drawArrays(s,e,i),n.update(i,s,1)},this.renderInstances=function(i,a,o){if(0===o)return;let l,c;if(r)l=t,c="drawArraysInstanced";else if(l=e.get("ANGLE_instanced_arrays"),c="drawArraysInstancedANGLE",null===l)return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");l[c](s,i,a,o),n.update(a,s,o)}}function Xs(t,e,n){let i;function r(e){if("highp"===e){if(t.getShaderPrecisionFormat(35633,36338).precision>0&&t.getShaderPrecisionFormat(35632,36338).precision>0)return"highp";e="mediump"}return"mediump"===e&&t.getShaderPrecisionFormat(35633,36337).precision>0&&t.getShaderPrecisionFormat(35632,36337).precision>0?"mediump":"lowp"}const s="undefined"!=typeof WebGL2RenderingContext&&t instanceof WebGL2RenderingContext||"undefined"!=typeof WebGL2ComputeRenderingContext&&t instanceof WebGL2ComputeRenderingContext;let a=void 0!==n.precision?n.precision:"highp";const o=r(a);o!==a&&(console.warn("THREE.WebGLRenderer:",a,"not supported, using",o,"instead."),a=o);const l=!0===n.logarithmicDepthBuffer,c=t.getParameter(34930),h=t.getParameter(35660),u=t.getParameter(3379),d=t.getParameter(34076),p=t.getParameter(34921),m=t.getParameter(36347),f=t.getParameter(36348),g=t.getParameter(36349),v=h>0,y=s||e.has("OES_texture_float");return{isWebGL2:s,getMaxAnisotropy:function(){if(void 0!==i)return i;if(!0===e.has("EXT_texture_filter_anisotropic")){const n=e.get("EXT_texture_filter_anisotropic");i=t.getParameter(n.MAX_TEXTURE_MAX_ANISOTROPY_EXT)}else i=0;return i},getMaxPrecision:r,precision:a,logarithmicDepthBuffer:l,maxTextures:c,maxVertexTextures:h,maxTextureSize:u,maxCubemapSize:d,maxAttributes:p,maxVertexUniforms:m,maxVaryings:f,maxFragmentUniforms:g,vertexTextures:v,floatFragmentTextures:y,floatVertexTextures:v&&y,maxSamples:s?t.getParameter(36183):0}}function Ys(t){const e=this;let n=null,i=0,r=!1,s=!1;const a=new dr,o=new Jn,l={value:null,needsUpdate:!1};function c(){l.value!==n&&(l.value=n,l.needsUpdate=i>0),e.numPlanes=i,e.numIntersection=0}function h(t,n,i,r){const s=null!==t?t.length:0;let c=null;if(0!==s){if(c=l.value,!0!==r||null===c){const e=i+4*s,r=n.matrixWorldInverse;o.getNormalMatrix(r),(null===c||c.length0){const a=t.getRenderTarget(),o=new Is(s.height/2);return o.fromEquirectangularTexture(t,r),e.set(r,o),t.setRenderTarget(a),r.addEventListener("dispose",i),n(o.texture,r.mapping)}return null}}}return r},dispose:function(){e=new WeakMap}}}function Js(t){const e={};function n(n){if(void 0!==e[n])return e[n];let i;switch(n){case"WEBGL_depth_texture":i=t.getExtension("WEBGL_depth_texture")||t.getExtension("MOZ_WEBGL_depth_texture")||t.getExtension("WEBKIT_WEBGL_depth_texture");break;case"EXT_texture_filter_anisotropic":i=t.getExtension("EXT_texture_filter_anisotropic")||t.getExtension("MOZ_EXT_texture_filter_anisotropic")||t.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":i=t.getExtension("WEBGL_compressed_texture_s3tc")||t.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||t.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":i=t.getExtension("WEBGL_compressed_texture_pvrtc")||t.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:i=t.getExtension(n)}return e[n]=i,i}return{has:function(t){return null!==n(t)},init:function(t){t.isWebGL2?n("EXT_color_buffer_float"):(n("WEBGL_depth_texture"),n("OES_texture_float"),n("OES_texture_half_float"),n("OES_texture_half_float_linear"),n("OES_standard_derivatives"),n("OES_element_index_uint"),n("OES_vertex_array_object"),n("ANGLE_instanced_arrays")),n("OES_texture_float_linear"),n("EXT_color_buffer_half_float")},get:function(t){const e=n(t);return null===e&&console.warn("THREE.WebGLRenderer: "+t+" extension not supported."),e}}}function Qs(t,e,n,i){const r={},s=new WeakMap;function a(t){const o=t.target;null!==o.index&&e.remove(o.index);for(const t in o.attributes)e.remove(o.attributes[t]);o.removeEventListener("dispose",a),delete r[o.id];const l=s.get(o);l&&(e.remove(l),s.delete(o)),i.releaseStatesOfGeometry(o),!0===o.isInstancedBufferGeometry&&delete o._maxInstanceCount,n.memory.geometries--}function o(t){const n=[],i=t.index,r=t.attributes.position;let a=0;if(null!==i){const t=i.array;a=i.version;for(let e=0,i=t.length;e65535?kr:Ur)(n,1);o.version=a;const l=s.get(t);l&&e.remove(l),s.set(t,o)}return{get:function(t,e){return!0===r[e.id]||(e.addEventListener("dispose",a),r[e.id]=!0,n.memory.geometries++),e},update:function(t){const n=t.attributes;for(const t in n)e.update(n[t],34962);const i=t.morphAttributes;for(const t in i){const n=i[t];for(let t=0,i=n.length;t0)return t;const r=e*n;let s=ha[r];if(void 0===s&&(s=new Float32Array(r),ha[r]=s),0!==e){i.toArray(s,0);for(let i=1,r=0;i!==e;++i)r+=n,t[i].toArray(s,r)}return s}function ga(t,e){if(t.length!==e.length)return!1;for(let n=0,i=t.length;n/gm;function _o(t){return t.replace(xo,bo)}function bo(t,e){const n=Us[e];if(void 0===n)throw new Error("Can not resolve #include <"+e+">");return _o(n)}const wo=/#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g,Mo=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function So(t){return t.replace(Mo,Eo).replace(wo,To)}function To(t,e,n,i){return console.warn("WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead."),Eo(0,e,n,i)}function Eo(t,e,n,i){let r="";for(let t=parseInt(e);t0?t.gammaFactor:1,g=n.isWebGL2?"":function(t){return[t.extensionDerivatives||t.envMapCubeUV||t.bumpMap||t.tangentSpaceNormalMap||t.clearcoatNormalMap||t.flatShading||"physical"===t.shaderID?"#extension GL_OES_standard_derivatives : enable":"",(t.extensionFragDepth||t.logarithmicDepthBuffer)&&t.rendererExtensionFragDepth?"#extension GL_EXT_frag_depth : enable":"",t.extensionDrawBuffers&&t.rendererExtensionDrawBuffers?"#extension GL_EXT_draw_buffers : require":"",(t.extensionShaderTextureLOD||t.envMap)&&t.rendererExtensionShaderTextureLod?"#extension GL_EXT_shader_texture_lod : enable":""].filter(go).join("\n")}(n),v=function(t){const e=[];for(const n in t){const i=t[n];!1!==i&&e.push("#define "+n+" "+i)}return e.join("\n")}(s),y=r.createProgram();let x,_,b=n.glslVersion?"#version "+n.glslVersion+"\n":"";n.isRawShaderMaterial?(x=[v].filter(go).join("\n"),x.length>0&&(x+="\n"),_=[g,v].filter(go).join("\n"),_.length>0&&(_+="\n")):(x=[Ao(n),"#define SHADER_NAME "+n.shaderName,v,n.instancing?"#define USE_INSTANCING":"",n.instancingColor?"#define USE_INSTANCING_COLOR":"",n.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define GAMMA_FACTOR "+f,"#define MAX_BONES "+n.maxBones,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+h:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMap&&n.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",n.normalMap&&n.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.displacementMap&&n.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.vertexTangents?"#define USE_TANGENT":"",n.vertexColors?"#define USE_COLOR":"",n.vertexAlphas?"#define USE_COLOR_ALPHA":"",n.vertexUvs?"#define USE_UV":"",n.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",n.flatShading?"#define FLAT_SHADED":"",n.skinning?"#define USE_SKINNING":"",n.useVertexTexture?"#define BONE_TEXTURE":"",n.morphTargets?"#define USE_MORPHTARGETS":"",n.morphNormals&&!1===n.flatShading?"#define USE_MORPHNORMALS":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+l:"",n.sizeAttenuation?"#define USE_SIZEATTENUATION":"",n.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",n.logarithmicDepthBuffer&&n.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING","\tattribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR","\tattribute vec3 instanceColor;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_TANGENT","\tattribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )","\tattribute vec4 color;","#elif defined( USE_COLOR )","\tattribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS","\tattribute vec3 morphTarget0;","\tattribute vec3 morphTarget1;","\tattribute vec3 morphTarget2;","\tattribute vec3 morphTarget3;","\t#ifdef USE_MORPHNORMALS","\t\tattribute vec3 morphNormal0;","\t\tattribute vec3 morphNormal1;","\t\tattribute vec3 morphNormal2;","\t\tattribute vec3 morphNormal3;","\t#else","\t\tattribute vec3 morphTarget4;","\t\tattribute vec3 morphTarget5;","\t\tattribute vec3 morphTarget6;","\t\tattribute vec3 morphTarget7;","\t#endif","#endif","#ifdef USE_SKINNING","\tattribute vec4 skinIndex;","\tattribute vec4 skinWeight;","#endif","\n"].filter(go).join("\n"),_=[g,Ao(n),"#define SHADER_NAME "+n.shaderName,v,n.alphaTest?"#define ALPHATEST "+n.alphaTest+(n.alphaTest%1?"":".0"):"","#define GAMMA_FACTOR "+f,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.matcap?"#define USE_MATCAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+c:"",n.envMap?"#define "+h:"",n.envMap?"#define "+m:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMap&&n.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",n.normalMap&&n.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.sheen?"#define USE_SHEEN":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.vertexTangents?"#define USE_TANGENT":"",n.vertexColors||n.instancingColor?"#define USE_COLOR":"",n.vertexAlphas?"#define USE_COLOR_ALPHA":"",n.vertexUvs?"#define USE_UV":"",n.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",n.gradientMap?"#define USE_GRADIENTMAP":"",n.flatShading?"#define FLAT_SHADED":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+l:"",n.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",n.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",n.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",n.logarithmicDepthBuffer&&n.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"",(n.extensionShaderTextureLOD||n.envMap)&&n.rendererExtensionShaderTextureLod?"#define TEXTURE_LOD_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",n.toneMapping!==K?"#define TONE_MAPPING":"",n.toneMapping!==K?Us.tonemapping_pars_fragment:"",n.toneMapping!==K?fo("toneMapping",n.toneMapping):"",n.dithering?"#define DITHERING":"",Us.encodings_pars_fragment,n.map?po("mapTexelToLinear",n.mapEncoding):"",n.matcap?po("matcapTexelToLinear",n.matcapEncoding):"",n.envMap?po("envMapTexelToLinear",n.envMapEncoding):"",n.emissiveMap?po("emissiveMapTexelToLinear",n.emissiveMapEncoding):"",n.lightMap?po("lightMapTexelToLinear",n.lightMapEncoding):"",mo("linearToOutputTexel",n.outputEncoding),n.depthPacking?"#define DEPTH_PACKING "+n.depthPacking:"","\n"].filter(go).join("\n")),a=_o(a),a=vo(a,n),a=yo(a,n),o=_o(o),o=vo(o,n),o=yo(o,n),a=So(a),o=So(o),n.isWebGL2&&!0!==n.isRawShaderMaterial&&(b="#version 300 es\n",x=["#define attribute in","#define varying out","#define texture2D texture"].join("\n")+"\n"+x,_=["#define varying in",n.glslVersion===On?"":"out highp vec4 pc_fragColor;",n.glslVersion===On?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join("\n")+"\n"+_);const w=b+_+o,M=lo(r,35633,b+x+a),S=lo(r,35632,w);if(r.attachShader(y,M),r.attachShader(y,S),void 0!==n.index0AttributeName?r.bindAttribLocation(y,0,n.index0AttributeName):!0===n.morphTargets&&r.bindAttribLocation(y,0,"position"),r.linkProgram(y),t.debug.checkShaderErrors){const t=r.getProgramInfoLog(y).trim(),e=r.getShaderInfoLog(M).trim(),n=r.getShaderInfoLog(S).trim();let i=!0,s=!0;if(!1===r.getProgramParameter(y,35714)){i=!1;const e=uo(r,M,"vertex"),n=uo(r,S,"fragment");console.error("THREE.WebGLProgram: shader error: ",r.getError(),"35715",r.getProgramParameter(y,35715),"gl.getProgramInfoLog",t,e,n)}else""!==t?console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",t):""!==e&&""!==n||(s=!1);s&&(this.diagnostics={runnable:i,programLog:t,vertexShader:{log:e,prefix:x},fragmentShader:{log:n,prefix:_}})}let T,E;return r.deleteShader(M),r.deleteShader(S),this.getUniforms=function(){return void 0===T&&(T=new oo(r,y)),T},this.getAttributes=function(){return void 0===E&&(E=function(t,e){const n={},i=t.getProgramParameter(e,35721);for(let r=0;r0,maxBones:S,useVertexTexture:c,morphTargets:r.morphTargets,morphNormals:r.morphNormals,numDirLights:a.directional.length,numPointLights:a.point.length,numSpotLights:a.spot.length,numRectAreaLights:a.rectArea.length,numHemiLights:a.hemi.length,numDirLightShadows:a.directionalShadowMap.length,numPointLightShadows:a.pointShadowMap.length,numSpotLightShadows:a.spotShadowMap.length,numClippingPlanes:s.numPlanes,numClipIntersection:s.numIntersection,dithering:r.dithering,shadowMapEnabled:t.shadowMap.enabled&&m.length>0,shadowMapType:t.shadowMap.type,toneMapping:r.toneMapped?t.toneMapping:K,physicallyCorrectLights:t.physicallyCorrectLights,premultipliedAlpha:r.premultipliedAlpha,alphaTest:r.alphaTest,doubleSided:r.side===g,flipSided:r.side===f,depthPacking:void 0!==r.depthPacking&&r.depthPacking,index0AttributeName:r.index0AttributeName,extensionDerivatives:r.extensions&&r.extensions.derivatives,extensionFragDepth:r.extensions&&r.extensions.fragDepth,extensionDrawBuffers:r.extensions&&r.extensions.drawBuffers,extensionShaderTextureLOD:r.extensions&&r.extensions.shaderTextureLOD,rendererExtensionFragDepth:o||n.has("EXT_frag_depth"),rendererExtensionDrawBuffers:o||n.has("WEBGL_draw_buffers"),rendererExtensionShaderTextureLod:o||n.has("EXT_shader_texture_lod"),customProgramCacheKey:r.customProgramCacheKey()}},getProgramCacheKey:function(e){const n=[];if(e.shaderID?n.push(e.shaderID):(n.push(e.fragmentShader),n.push(e.vertexShader)),void 0!==e.defines)for(const t in e.defines)n.push(t),n.push(e.defines[t]);if(!1===e.isRawShaderMaterial){for(let t=0;t1&&i.sort(t||Po),r.length>1&&r.sort(e||Do)}}}function No(t){let e=new WeakMap;return{get:function(n,i){let r;return!1===e.has(n)?(r=new Io(t),e.set(n,[r])):i>=e.get(n).length?(r=new Io(t),e.get(n).push(r)):r=e.get(n)[i],r},dispose:function(){e=new WeakMap}}}function Oo(){const t={};return{get:function(e){if(void 0!==t[e.id])return t[e.id];let n;switch(e.type){case"DirectionalLight":n={direction:new ai,color:new Dr};break;case"SpotLight":n={position:new ai,direction:new ai,color:new Dr,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":n={position:new ai,color:new Dr,distance:0,decay:0};break;case"HemisphereLight":n={direction:new ai,skyColor:new Dr,groundColor:new Dr};break;case"RectAreaLight":n={color:new Dr,position:new ai,halfWidth:new ai,halfHeight:new ai}}return t[e.id]=n,n}}}let Bo=0;function zo(t,e){return(e.castShadow?1:0)-(t.castShadow?1:0)}function Fo(t,e){const n=new Oo,i=function(){const t={};return{get:function(e){if(void 0!==t[e.id])return t[e.id];let n;switch(e.type){case"DirectionalLight":case"SpotLight":n={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Zn};break;case"PointLight":n={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Zn,shadowCameraNear:1,shadowCameraFar:1e3}}return t[e.id]=n,n}}}(),r={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotShadow:[],spotShadowMap:[],spotShadowMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[]};for(let t=0;t<9;t++)r.probe.push(new ai);const s=new ai,a=new zi,o=new zi;return{setup:function(s){let a=0,o=0,l=0;for(let t=0;t<9;t++)r.probe[t].set(0,0,0);let c=0,h=0,u=0,d=0,p=0,m=0,f=0,g=0;s.sort(zo);for(let t=0,e=s.length;t0&&(e.isWebGL2||!0===t.has("OES_texture_float_linear")?(r.rectAreaLTC1=Vs.LTC_FLOAT_1,r.rectAreaLTC2=Vs.LTC_FLOAT_2):!0===t.has("OES_texture_half_float_linear")?(r.rectAreaLTC1=Vs.LTC_HALF_1,r.rectAreaLTC2=Vs.LTC_HALF_2):console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.")),r.ambient[0]=a,r.ambient[1]=o,r.ambient[2]=l;const v=r.hash;v.directionalLength===c&&v.pointLength===h&&v.spotLength===u&&v.rectAreaLength===d&&v.hemiLength===p&&v.numDirectionalShadows===m&&v.numPointShadows===f&&v.numSpotShadows===g||(r.directional.length=c,r.spot.length=u,r.rectArea.length=d,r.point.length=h,r.hemi.length=p,r.directionalShadow.length=m,r.directionalShadowMap.length=m,r.pointShadow.length=f,r.pointShadowMap.length=f,r.spotShadow.length=g,r.spotShadowMap.length=g,r.directionalShadowMatrix.length=m,r.pointShadowMatrix.length=f,r.spotShadowMatrix.length=g,v.directionalLength=c,v.pointLength=h,v.spotLength=u,v.rectAreaLength=d,v.hemiLength=p,v.numDirectionalShadows=m,v.numPointShadows=f,v.numSpotShadows=g,r.version=Bo++)},setupView:function(t,e){let n=0,i=0,l=0,c=0,h=0;const u=e.matrixWorldInverse;for(let e=0,d=t.length;e=n.get(i).length?(s=new Ho(t,e),n.get(i).push(s)):s=n.get(i)[r],s},dispose:function(){n=new WeakMap}}}class Uo extends Tr{constructor(t){super(),this.type="MeshDepthMaterial",this.depthPacking=sn,this.skinning=!1,this.morphTargets=!1,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.fog=!1,this.setValues(t)}copy(t){return super.copy(t),this.depthPacking=t.depthPacking,this.skinning=t.skinning,this.morphTargets=t.morphTargets,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this}}Uo.prototype.isMeshDepthMaterial=!0;class Vo extends Tr{constructor(t){super(),this.type="MeshDistanceMaterial",this.referencePosition=new ai,this.nearDistance=1,this.farDistance=1e3,this.skinning=!1,this.morphTargets=!1,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.fog=!1,this.setValues(t)}copy(t){return super.copy(t),this.referencePosition.copy(t.referencePosition),this.nearDistance=t.nearDistance,this.farDistance=t.farDistance,this.skinning=t.skinning,this.morphTargets=t.morphTargets,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this}}function ko(t,e,n){let i=new zs;const r=new Zn,s=new Zn,a=new ni,o=[],l=[],c={},h=n.maxTextureSize,d={0:f,1:m,2:g},v=new As({defines:{SAMPLE_RATE:2/8,HALF_SAMPLE_RATE:1/8},uniforms:{shadow_pass:{value:null},resolution:{value:new Zn},radius:{value:4}},vertexShader:"void main() {\n\tgl_Position = vec4( position, 1.0 );\n}",fragmentShader:"uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"}),y=v.clone();y.defines.HORIZONTAL_PASS=1;const _=new is;_.setAttribute("position",new Br(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const b=new bs(_,v),w=this;function M(n,i){const r=e.update(b);v.uniforms.shadow_pass.value=n.map.texture,v.uniforms.resolution.value=n.mapSize,v.uniforms.radius.value=n.radius,t.setRenderTarget(n.mapPass),t.clear(),t.renderBufferDirect(i,null,r,v,b,null),y.uniforms.shadow_pass.value=n.mapPass.texture,y.uniforms.resolution.value=n.mapSize,y.uniforms.radius.value=n.radius,t.setRenderTarget(n.map),t.clear(),t.renderBufferDirect(i,null,r,y,b,null)}function S(t,e,n){const i=t<<0|e<<1|n<<2;let r=o[i];return void 0===r&&(r=new Uo({depthPacking:an,morphTargets:t,skinning:e}),o[i]=r),r}function T(t,e,n){const i=t<<0|e<<1|n<<2;let r=l[i];return void 0===r&&(r=new Vo({morphTargets:t,skinning:e}),l[i]=r),r}function E(e,n,i,r,s,a,o){let l=null,h=S,u=e.customDepthMaterial;if(!0===r.isPointLight&&(h=T,u=e.customDistanceMaterial),void 0===u){let t=!1;!0===i.morphTargets&&(t=n.morphAttributes&&n.morphAttributes.position&&n.morphAttributes.position.length>0);let r=!1;!0===e.isSkinnedMesh&&(!0===i.skinning?r=!0:console.warn("THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:",e)),l=h(t,r,!0===e.isInstancedMesh)}else l=u;if(t.localClippingEnabled&&!0===i.clipShadows&&0!==i.clippingPlanes.length){const t=l.uuid,e=i.uuid;let n=c[t];void 0===n&&(n={},c[t]=n);let r=n[e];void 0===r&&(r=l.clone(),n[e]=r),l=r}return l.visible=i.visible,l.wireframe=i.wireframe,l.side=o===p?null!==i.shadowSide?i.shadowSide:i.side:null!==i.shadowSide?i.shadowSide:d[i.side],l.clipShadows=i.clipShadows,l.clippingPlanes=i.clippingPlanes,l.clipIntersection=i.clipIntersection,l.wireframeLinewidth=i.wireframeLinewidth,l.linewidth=i.linewidth,!0===r.isPointLight&&!0===l.isMeshDistanceMaterial&&(l.referencePosition.setFromMatrixPosition(r.matrixWorld),l.nearDistance=s,l.farDistance=a),l}function A(n,r,s,a,o){if(!1===n.visible)return;if(n.layers.test(r.layers)&&(n.isMesh||n.isLine||n.isPoints)&&(n.castShadow||n.receiveShadow&&o===p)&&(!n.frustumCulled||i.intersectsObject(n))){n.modelViewMatrix.multiplyMatrices(s.matrixWorldInverse,n.matrixWorld);const i=e.update(n),r=n.material;if(Array.isArray(r)){const e=i.groups;for(let l=0,c=e.length;lh||r.y>h)&&(r.x>h&&(s.x=Math.floor(h/m.x),r.x=s.x*m.x,u.mapSize.x=s.x),r.y>h&&(s.y=Math.floor(h/m.y),r.y=s.y*m.y,u.mapSize.y=s.y)),null===u.map&&!u.isPointLightShadow&&this.type===p){const t={minFilter:xt,magFilter:xt,format:Ft};u.map=new ii(r.x,r.y,t),u.map.texture.name=c.name+".shadowMap",u.mapPass=new ii(r.x,r.y,t),u.camera.updateProjectionMatrix()}if(null===u.map){const t={minFilter:mt,magFilter:mt,format:Ft};u.map=new ii(r.x,r.y,t),u.map.texture.name=c.name+".shadowMap",u.camera.updateProjectionMatrix()}t.setRenderTarget(u.map),t.clear();const f=u.getViewportCount();for(let t=0;t=1):-1!==ct.indexOf("OpenGL ES")&&(lt=parseFloat(/^OpenGL ES (\d)/.exec(ct)[1]),ot=lt>=2);let ht=null,ut={};const dt=new ni(0,0,t.canvas.width,t.canvas.height),pt=new ni(0,0,t.canvas.width,t.canvas.height);function mt(e,n,i){const r=new Uint8Array(4),s=t.createTexture();t.bindTexture(e,s),t.texParameteri(e,10241,9728),t.texParameteri(e,10240,9728);for(let e=0;ei||t.height>i)&&(r=i/Math.max(t.width,t.height)),r<1||!0===e){if("undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap){const i=e?Xn:Math.floor,s=i(r*t.width),a=i(r*t.height);void 0===p&&(p=f(s,a));const o=n?f(s,a):p;return o.width=s,o.height=a,o.getContext("2d").drawImage(t,0,0,s,a),console.warn("THREE.WebGLRenderer: Texture has been resized from ("+t.width+"x"+t.height+") to ("+s+"x"+a+")."),o}return"data"in t&&console.warn("THREE.WebGLRenderer: Image in DataTexture is too big ("+t.width+"x"+t.height+")."),t}return t}function v(t){return jn(t.width)&&jn(t.height)}function y(t,e){return t.generateMipmaps&&e&&t.minFilter!==mt&&t.minFilter!==xt}function x(e,n,r,s){t.generateMipmap(e),i.get(n).__maxMipLevel=Math.log2(Math.max(r,s))}function _(n,i,r){if(!1===o)return i;if(null!==n){if(void 0!==t[n])return t[n];console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format '"+n+"'")}let s=i;return 6403===i&&(5126===r&&(s=33326),5131===r&&(s=33325),5121===r&&(s=33321)),6407===i&&(5126===r&&(s=34837),5131===r&&(s=34843),5121===r&&(s=32849)),6408===i&&(5126===r&&(s=34836),5131===r&&(s=34842),5121===r&&(s=32856)),33325!==s&&33326!==s&&34842!==s&&34836!==s||e.get("EXT_color_buffer_float"),s}function b(t){return t===mt||t===ft||t===vt?9728:9729}function w(e){const n=e.target;n.removeEventListener("dispose",w),function(e){const n=i.get(e);void 0!==n.__webglInit&&(t.deleteTexture(n.__webglTexture),i.remove(e))}(n),n.isVideoTexture&&d.delete(n),a.memory.textures--}function M(e){const n=e.target;n.removeEventListener("dispose",M),function(e){const n=e.texture,r=i.get(e),s=i.get(n);if(e){if(void 0!==s.__webglTexture&&t.deleteTexture(s.__webglTexture),e.depthTexture&&e.depthTexture.dispose(),e.isWebGLCubeRenderTarget)for(let e=0;e<6;e++)t.deleteFramebuffer(r.__webglFramebuffer[e]),r.__webglDepthbuffer&&t.deleteRenderbuffer(r.__webglDepthbuffer[e]);else t.deleteFramebuffer(r.__webglFramebuffer),r.__webglDepthbuffer&&t.deleteRenderbuffer(r.__webglDepthbuffer),r.__webglMultisampledFramebuffer&&t.deleteFramebuffer(r.__webglMultisampledFramebuffer),r.__webglColorRenderbuffer&&t.deleteRenderbuffer(r.__webglColorRenderbuffer),r.__webglDepthRenderbuffer&&t.deleteRenderbuffer(r.__webglDepthRenderbuffer);i.remove(n),i.remove(e)}}(n),a.memory.textures--}let S=0;function T(t,e){const r=i.get(t);if(t.isVideoTexture&&function(t){const e=a.render.frame;d.get(t)!==e&&(d.set(t,e),t.update())}(t),t.version>0&&r.__version!==t.version){const n=t.image;if(void 0===n)console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined");else{if(!1!==n.complete)return void P(r,t,e);console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete")}}n.activeTexture(33984+e),n.bindTexture(3553,r.__webglTexture)}function E(e,r){const a=i.get(e);e.version>0&&a.__version!==e.version?function(e,i,r){if(6!==i.image.length)return;C(e,i),n.activeTexture(33984+r),n.bindTexture(34067,e.__webglTexture),t.pixelStorei(37440,i.flipY),t.pixelStorei(37441,i.premultiplyAlpha),t.pixelStorei(3317,i.unpackAlignment),t.pixelStorei(37443,0);const a=i&&(i.isCompressedTexture||i.image[0].isCompressedTexture),l=i.image[0]&&i.image[0].isDataTexture,h=[];for(let t=0;t<6;t++)h[t]=a||l?l?i.image[t].image:i.image[t]:g(i.image[t],!1,!0,c);const u=h[0],d=v(u)||o,p=s.convert(i.format),m=s.convert(i.type),f=_(i.internalFormat,p,m);let b;if(R(34067,i,d),a){for(let t=0;t<6;t++){b=h[t].mipmaps;for(let e=0;e1||i.get(s).__currentAnisotropy)&&(t.texParameterf(n,a.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(s.anisotropy,r.getMaxAnisotropy())),i.get(s).__currentAnisotropy=s.anisotropy)}}function C(e,n){void 0===e.__webglInit&&(e.__webglInit=!0,n.addEventListener("dispose",w),e.__webglTexture=t.createTexture(),a.memory.textures++)}function P(e,i,r){let a=3553;i.isDataTexture2DArray&&(a=35866),i.isDataTexture3D&&(a=32879),C(e,i),n.activeTexture(33984+r),n.bindTexture(a,e.__webglTexture),t.pixelStorei(37440,i.flipY),t.pixelStorei(37441,i.premultiplyAlpha),t.pixelStorei(3317,i.unpackAlignment),t.pixelStorei(37443,0);const l=function(t){return!o&&(t.wrapS!==dt||t.wrapT!==dt||t.minFilter!==mt&&t.minFilter!==xt)}(i)&&!1===v(i.image),c=g(i.image,l,!1,h),u=v(c)||o,d=s.convert(i.format);let p,m=s.convert(i.type),f=_(i.internalFormat,d,m);R(a,i,u);const b=i.mipmaps;if(i.isDepthTexture)f=6402,o?f=i.type===Ct?36012:i.type===Rt?33190:i.type===Ot?35056:33189:i.type===Ct&&console.error("WebGLRenderer: Floating point depth texture requires WebGL2."),i.format===Vt&&6402===f&&i.type!==At&&i.type!==Rt&&(console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."),i.type=At,m=s.convert(i.type)),i.format===kt&&6402===f&&(f=34041,i.type!==Ot&&(console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."),i.type=Ot,m=s.convert(i.type))),n.texImage2D(3553,0,f,c.width,c.height,0,d,m,null);else if(i.isDataTexture)if(b.length>0&&u){for(let t=0,e=b.length;t0&&u){for(let t=0,e=b.length;t=l&&console.warn("THREE.WebGLTextures: Trying to use "+t+" texture units while this GPU supports only "+l),S+=1,t},this.resetTextureUnits=function(){S=0},this.setTexture2D=T,this.setTexture2DArray=function(t,e){const r=i.get(t);t.version>0&&r.__version!==t.version?P(r,t,e):(n.activeTexture(33984+e),n.bindTexture(35866,r.__webglTexture))},this.setTexture3D=function(t,e){const r=i.get(t);t.version>0&&r.__version!==t.version?P(r,t,e):(n.activeTexture(33984+e),n.bindTexture(32879,r.__webglTexture))},this.setTextureCube=E,this.setupRenderTarget=function(e){const r=e.texture,l=i.get(e),c=i.get(r);e.addEventListener("dispose",M),c.__webglTexture=t.createTexture(),c.__version=r.version,a.memory.textures++;const h=!0===e.isWebGLCubeRenderTarget,u=!0===e.isWebGLMultisampleRenderTarget,d=r.isDataTexture3D||r.isDataTexture2DArray,p=v(e)||o;if(!o||r.format!==zt||r.type!==Ct&&r.type!==Pt||(r.format=Ft,console.warn("THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.")),h){l.__webglFramebuffer=[];for(let e=0;e<6;e++)l.__webglFramebuffer[e]=t.createFramebuffer()}else if(l.__webglFramebuffer=t.createFramebuffer(),u)if(o){l.__webglMultisampledFramebuffer=t.createFramebuffer(),l.__webglColorRenderbuffer=t.createRenderbuffer(),t.bindRenderbuffer(36161,l.__webglColorRenderbuffer);const i=s.convert(r.format),a=s.convert(r.type),o=_(r.internalFormat,i,a),c=N(e);t.renderbufferStorageMultisample(36161,c,o,e.width,e.height),n.bindFramebuffer(36160,l.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(36160,36064,36161,l.__webglColorRenderbuffer),t.bindRenderbuffer(36161,null),e.depthBuffer&&(l.__webglDepthRenderbuffer=t.createRenderbuffer(),I(l.__webglDepthRenderbuffer,e,!0)),n.bindFramebuffer(36160,null)}else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.");if(h){n.bindTexture(34067,c.__webglTexture),R(34067,r,p);for(let t=0;t<6;t++)D(l.__webglFramebuffer[t],e,36064,34069+t);y(r,p)&&x(34067,r,e.width,e.height),n.bindTexture(34067,null)}else{let t=3553;d&&(o?t=r.isDataTexture3D?32879:35866:console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.")),n.bindTexture(t,c.__webglTexture),R(t,r,p),D(l.__webglFramebuffer,e,36064,t),y(r,p)&&x(3553,r,e.width,e.height),n.bindTexture(3553,null)}e.depthBuffer&&function(e){const r=i.get(e),s=!0===e.isWebGLCubeRenderTarget;if(e.depthTexture){if(s)throw new Error("target.depthTexture not supported in Cube render targets");!function(e,r){if(r&&r.isWebGLCubeRenderTarget)throw new Error("Depth Texture with cube render targets is not supported");if(n.bindFramebuffer(36160,e),!r.depthTexture||!r.depthTexture.isDepthTexture)throw new Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture");i.get(r.depthTexture).__webglTexture&&r.depthTexture.image.width===r.width&&r.depthTexture.image.height===r.height||(r.depthTexture.image.width=r.width,r.depthTexture.image.height=r.height,r.depthTexture.needsUpdate=!0),T(r.depthTexture,0);const s=i.get(r.depthTexture).__webglTexture;if(r.depthTexture.format===Vt)t.framebufferTexture2D(36160,36096,3553,s,0);else{if(r.depthTexture.format!==kt)throw new Error("Unknown depthTexture format");t.framebufferTexture2D(36160,33306,3553,s,0)}}(r.__webglFramebuffer,e)}else if(s){r.__webglDepthbuffer=[];for(let i=0;i<6;i++)n.bindFramebuffer(36160,r.__webglFramebuffer[i]),r.__webglDepthbuffer[i]=t.createRenderbuffer(),I(r.__webglDepthbuffer[i],e,!1)}else n.bindFramebuffer(36160,r.__webglFramebuffer),r.__webglDepthbuffer=t.createRenderbuffer(),I(r.__webglDepthbuffer,e,!1);n.bindFramebuffer(36160,null)}(e)},this.updateRenderTargetMipmap=function(t){const e=t.texture;if(y(e,v(t)||o)){const r=t.isWebGLCubeRenderTarget?34067:3553,s=i.get(e).__webglTexture;n.bindTexture(r,s),x(r,e,t.width,t.height),n.bindTexture(r,null)}},this.updateMultisampleRenderTarget=function(e){if(e.isWebGLMultisampleRenderTarget)if(o){const r=e.width,s=e.height;let a=16384;e.depthBuffer&&(a|=256),e.stencilBuffer&&(a|=1024);const o=i.get(e);n.bindFramebuffer(36008,o.__webglMultisampledFramebuffer),n.bindFramebuffer(36009,o.__webglFramebuffer),t.blitFramebuffer(0,0,r,s,0,0,r,s,a,9728),n.bindFramebuffer(36008,null),n.bindFramebuffer(36009,o.__webglMultisampledFramebuffer)}else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.")},this.safeSetTexture2D=function(t,e){t&&t.isWebGLRenderTarget&&(!1===O&&(console.warn("THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead."),O=!0),t=t.texture),T(t,e)},this.safeSetTextureCube=function(t,e){t&&t.isWebGLCubeRenderTarget&&(!1===B&&(console.warn("THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead."),B=!0),t=t.texture),E(t,e)}}function qo(t,e,n){const i=n.isWebGL2;return{convert:function(t){let n;if(t===St)return 5121;if(t===Dt)return 32819;if(t===It)return 32820;if(t===Nt)return 33635;if(t===Tt)return 5120;if(t===Et)return 5122;if(t===At)return 5123;if(t===Lt)return 5124;if(t===Rt)return 5125;if(t===Ct)return 5126;if(t===Pt)return i?5131:(n=e.get("OES_texture_half_float"),null!==n?n.HALF_FLOAT_OES:null);if(t===Bt)return 6406;if(t===zt)return 6407;if(t===Ft)return 6408;if(t===Ht)return 6409;if(t===Gt)return 6410;if(t===Vt)return 6402;if(t===kt)return 34041;if(t===Wt)return 6403;if(t===jt)return 36244;if(t===qt)return 33319;if(t===Xt)return 33320;if(t===Yt)return 36248;if(t===Zt)return 36249;if(t===Jt||t===Qt||t===Kt||t===$t){if(n=e.get("WEBGL_compressed_texture_s3tc"),null===n)return null;if(t===Jt)return n.COMPRESSED_RGB_S3TC_DXT1_EXT;if(t===Qt)return n.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(t===Kt)return n.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(t===$t)return n.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(t===te||t===ee||t===ne||t===ie){if(n=e.get("WEBGL_compressed_texture_pvrtc"),null===n)return null;if(t===te)return n.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(t===ee)return n.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(t===ne)return n.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(t===ie)return n.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(t===re)return n=e.get("WEBGL_compressed_texture_etc1"),null!==n?n.COMPRESSED_RGB_ETC1_WEBGL:null;if((t===se||t===ae)&&(n=e.get("WEBGL_compressed_texture_etc"),null!==n)){if(t===se)return n.COMPRESSED_RGB8_ETC2;if(t===ae)return n.COMPRESSED_RGBA8_ETC2_EAC}return t===oe||t===le||t===ce||t===he||t===ue||t===de||t===pe||t===me||t===fe||t===ge||t===ve||t===ye||t===xe||t===_e||t===we||t===Me||t===Se||t===Te||t===Ee||t===Ae||t===Le||t===Re||t===Ce||t===Pe||t===De||t===Ie||t===Ne||t===Oe?(n=e.get("WEBGL_compressed_texture_astc"),null!==n?t:null):t===be?(n=e.get("EXT_texture_compression_bptc"),null!==n?t:null):t===Ot?i?34042:(n=e.get("WEBGL_depth_texture"),null!==n?n.UNSIGNED_INT_24_8_WEBGL:null):void 0}}}Vo.prototype.isMeshDistanceMaterial=!0;class Xo extends Rs{constructor(t=[]){super(),this.cameras=t}}Xo.prototype.isArrayCamera=!0;class Yo extends lr{constructor(){super(),this.type="Group"}}Yo.prototype.isGroup=!0;const Zo={type:"move"};class Jo{constructor(){this._targetRay=null,this._grip=null,this._hand=null}getHandSpace(){return null===this._hand&&(this._hand=new Yo,this._hand.matrixAutoUpdate=!1,this._hand.visible=!1,this._hand.joints={},this._hand.inputState={pinching:!1}),this._hand}getTargetRaySpace(){return null===this._targetRay&&(this._targetRay=new Yo,this._targetRay.matrixAutoUpdate=!1,this._targetRay.visible=!1,this._targetRay.hasLinearVelocity=!1,this._targetRay.linearVelocity=new ai,this._targetRay.hasAngularVelocity=!1,this._targetRay.angularVelocity=new ai),this._targetRay}getGripSpace(){return null===this._grip&&(this._grip=new Yo,this._grip.matrixAutoUpdate=!1,this._grip.visible=!1,this._grip.hasLinearVelocity=!1,this._grip.linearVelocity=new ai,this._grip.hasAngularVelocity=!1,this._grip.angularVelocity=new ai),this._grip}dispatchEvent(t){return null!==this._targetRay&&this._targetRay.dispatchEvent(t),null!==this._grip&&this._grip.dispatchEvent(t),null!==this._hand&&this._hand.dispatchEvent(t),this}disconnect(t){return this.dispatchEvent({type:"disconnected",data:t}),null!==this._targetRay&&(this._targetRay.visible=!1),null!==this._grip&&(this._grip.visible=!1),null!==this._hand&&(this._hand.visible=!1),this}update(t,e,n){let i=null,r=null,s=null;const a=this._targetRay,o=this._grip,l=this._hand;if(t&&"visible-blurred"!==e.session.visibilityState)if(null!==a&&(i=e.getPose(t.targetRaySpace,n),null!==i&&(a.matrix.fromArray(i.transform.matrix),a.matrix.decompose(a.position,a.rotation,a.scale),i.linearVelocity?(a.hasLinearVelocity=!0,a.linearVelocity.copy(i.linearVelocity)):a.hasLinearVelocity=!1,i.angularVelocity?(a.hasAngularVelocity=!0,a.angularVelocity.copy(i.angularVelocity)):a.hasAngularVelocity=!1,this.dispatchEvent(Zo))),l&&t.hand){s=!0;for(const i of t.hand.values()){const t=e.getJointPose(i,n);if(void 0===l.joints[i.jointName]){const t=new Yo;t.matrixAutoUpdate=!1,t.visible=!1,l.joints[i.jointName]=t,l.add(t)}const r=l.joints[i.jointName];null!==t&&(r.matrix.fromArray(t.transform.matrix),r.matrix.decompose(r.position,r.rotation,r.scale),r.jointRadius=t.radius),r.visible=null!==t}const i=l.joints["index-finger-tip"],r=l.joints["thumb-tip"],a=i.position.distanceTo(r.position),o=.02,c=.005;l.inputState.pinching&&a>o+c?(l.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!l.inputState.pinching&&a<=o-c&&(l.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else null!==o&&t.gripSpace&&(r=e.getPose(t.gripSpace,n),null!==r&&(o.matrix.fromArray(r.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),r.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(r.linearVelocity)):o.hasLinearVelocity=!1,r.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(r.angularVelocity)):o.hasAngularVelocity=!1));return null!==a&&(a.visible=null!==i),null!==o&&(o.visible=null!==r),null!==l&&(l.visible=null!==s),this}}class Qo extends Bn{constructor(t,e){super();const n=this,i=t.state;let r=null,s=1,a=null,o="local-floor",l=null;const c=[],h=new Map,u=new Rs;u.layers.enable(1),u.viewport=new ni;const d=new Rs;d.layers.enable(2),d.viewport=new ni;const p=[u,d],m=new Xo;m.layers.enable(1),m.layers.enable(2);let f=null,g=null;function v(t){const e=h.get(t.inputSource);e&&e.dispatchEvent({type:t.type,data:t.inputSource})}function y(){h.forEach((function(t,e){t.disconnect(e)})),h.clear(),f=null,g=null,i.bindXRFramebuffer(null),t.setRenderTarget(t.getRenderTarget()),S.stop(),n.isPresenting=!1,n.dispatchEvent({type:"sessionend"})}function x(t){const e=r.inputSources;for(let t=0;t0&&bt(s,t,e),a.length>0&&bt(a,t,e),null!==x&&(j.updateRenderTargetMipmap(x),j.updateMultisampleRenderTarget(x)),!0===t.isScene&&t.onAfterRender(f,t,e),V.buffers.depth.setTest(!0),V.buffers.depth.setMask(!0),V.buffers.color.setMask(!0),V.setPolygonOffset(!1),lt.resetDefaultState(),_=-1,b=null,m.pop(),d=m.length>0?m[m.length-1]:null,p.pop(),u=p.length>0?p[p.length-1]:null},this.getActiveCubeFace=function(){return v},this.getActiveMipmapLevel=function(){return y},this.getRenderTarget=function(){return x},this.setRenderTarget=function(t,e=0,n=0){x=t,v=e,y=n,t&&void 0===W.get(t).__webglFramebuffer&&j.setupRenderTarget(t);let i=null,r=!1,s=!1;if(t){const n=t.texture;(n.isDataTexture3D||n.isDataTexture2DArray)&&(s=!0);const a=W.get(t).__webglFramebuffer;t.isWebGLCubeRenderTarget?(i=a[e],r=!0):i=t.isWebGLMultisampleRenderTarget?W.get(t).__webglMultisampledFramebuffer:a,w.copy(t.viewport),M.copy(t.scissor),S=t.scissorTest}else w.copy(C).multiplyScalar(A).floor(),M.copy(P).multiplyScalar(A).floor(),S=D;if(V.bindFramebuffer(36160,i),V.viewport(w),V.scissor(M),V.setScissorTest(S),r){const i=W.get(t.texture);ct.framebufferTexture2D(36160,36064,34069+e,i.__webglTexture,n)}else if(s){const i=W.get(t.texture),r=e||0;ct.framebufferTextureLayer(36160,36064,i.__webglTexture,n||0,r)}},this.readRenderTargetPixels=function(t,e,n,i,r,s,a){if(!t||!t.isWebGLRenderTarget)return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let o=W.get(t).__webglFramebuffer;if(t.isWebGLCubeRenderTarget&&void 0!==a&&(o=o[a]),o){V.bindFramebuffer(36160,o);try{const a=t.texture,o=a.format,l=a.type;if(o!==Ft&&ot.convert(o)!==ct.getParameter(35739))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");const c=l===Pt&&(G.has("EXT_color_buffer_half_float")||U.isWebGL2&&G.has("EXT_color_buffer_float"));if(!(l===St||ot.convert(l)===ct.getParameter(35738)||l===Ct&&(U.isWebGL2||G.has("OES_texture_float")||G.has("WEBGL_color_buffer_float"))||c))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");36053===ct.checkFramebufferStatus(36160)?e>=0&&e<=t.width-i&&n>=0&&n<=t.height-r&&ct.readPixels(e,n,i,r,ot.convert(o),ot.convert(l),s):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}finally{const t=null!==x?W.get(x).__webglFramebuffer:null;V.bindFramebuffer(36160,t)}}},this.copyFramebufferToTexture=function(t,e,n=0){const i=Math.pow(2,-n),r=Math.floor(e.image.width*i),s=Math.floor(e.image.height*i),a=ot.convert(e.format);j.setTexture2D(e,0),ct.copyTexImage2D(3553,n,a,t.x,t.y,r,s,0),V.unbindTexture()},this.copyTextureToTexture=function(t,e,n,i=0){const r=e.image.width,s=e.image.height,a=ot.convert(n.format),o=ot.convert(n.type);j.setTexture2D(n,0),ct.pixelStorei(37440,n.flipY),ct.pixelStorei(37441,n.premultiplyAlpha),ct.pixelStorei(3317,n.unpackAlignment),e.isDataTexture?ct.texSubImage2D(3553,i,t.x,t.y,r,s,a,o,e.image.data):e.isCompressedTexture?ct.compressedTexSubImage2D(3553,i,t.x,t.y,e.mipmaps[0].width,e.mipmaps[0].height,a,e.mipmaps[0].data):ct.texSubImage2D(3553,i,t.x,t.y,a,o,e.image),0===i&&n.generateMipmaps&&ct.generateMipmap(3553),V.unbindTexture()},this.copyTextureToTexture3D=function(t,e,n,i,r=0){if(f.isWebGL1Renderer)return void console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.");const{width:s,height:a,data:o}=n.image,l=ot.convert(i.format),c=ot.convert(i.type);let h;if(i.isDataTexture3D)j.setTexture3D(i,0),h=32879;else{if(!i.isDataTexture2DArray)return void console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.");j.setTexture2DArray(i,0),h=35866}ct.pixelStorei(37440,i.flipY),ct.pixelStorei(37441,i.premultiplyAlpha),ct.pixelStorei(3317,i.unpackAlignment);const u=ct.getParameter(3314),d=ct.getParameter(32878),p=ct.getParameter(3316),m=ct.getParameter(3315),g=ct.getParameter(32877);ct.pixelStorei(3314,s),ct.pixelStorei(32878,a),ct.pixelStorei(3316,t.min.x),ct.pixelStorei(3315,t.min.y),ct.pixelStorei(32877,t.min.z),ct.texSubImage3D(h,r,e.x,e.y,e.z,t.max.x-t.min.x+1,t.max.y-t.min.y+1,t.max.z-t.min.z+1,l,c,o),ct.pixelStorei(3314,u),ct.pixelStorei(32878,d),ct.pixelStorei(3316,p),ct.pixelStorei(3315,m),ct.pixelStorei(32877,g),0===r&&i.generateMipmaps&&ct.generateMipmap(h),V.unbindTexture()},this.initTexture=function(t){j.setTexture2D(t,0),V.unbindTexture()},this.resetState=function(){v=0,y=0,x=null,V.reset(),lt.reset()},"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}class tl extends $o{}tl.prototype.isWebGL1Renderer=!0;class el{constructor(t,e=25e-5){this.name="",this.color=new Dr(t),this.density=e}clone(){return new el(this.color,this.density)}toJSON(){return{type:"FogExp2",color:this.color.getHex(),density:this.density}}}el.prototype.isFogExp2=!0;class nl{constructor(t,e=1,n=1e3){this.name="",this.color=new Dr(t),this.near=e,this.far=n}clone(){return new nl(this.color,this.near,this.far)}toJSON(){return{type:"Fog",color:this.color.getHex(),near:this.near,far:this.far}}}nl.prototype.isFog=!0;class il extends lr{constructor(){super(),this.type="Scene",this.background=null,this.environment=null,this.fog=null,this.overrideMaterial=null,this.autoUpdate=!0,"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}copy(t,e){return super.copy(t,e),null!==t.background&&(this.background=t.background.clone()),null!==t.environment&&(this.environment=t.environment.clone()),null!==t.fog&&(this.fog=t.fog.clone()),null!==t.overrideMaterial&&(this.overrideMaterial=t.overrideMaterial.clone()),this.autoUpdate=t.autoUpdate,this.matrixAutoUpdate=t.matrixAutoUpdate,this}toJSON(t){const e=super.toJSON(t);return null!==this.background&&(e.object.background=this.background.toJSON(t)),null!==this.environment&&(e.object.environment=this.environment.toJSON(t)),null!==this.fog&&(e.object.fog=this.fog.toJSON()),e}}il.prototype.isScene=!0;class rl{constructor(t,e){this.array=t,this.stride=e,this.count=void 0!==t?t.length/e:0,this.usage=Tn,this.updateRange={offset:0,count:-1},this.version=0,this.uuid=Un(),this.onUploadCallback=function(){}}set needsUpdate(t){!0===t&&this.version++}setUsage(t){return this.usage=t,this}copy(t){return this.array=new t.array.constructor(t.array),this.count=t.count,this.stride=t.stride,this.usage=t.usage,this}copyAt(t,e,n){t*=this.stride,n*=e.stride;for(let i=0,r=this.stride;it.far||e.push({distance:o,point:cl.clone(),uv:Mr.getUV(cl,fl,gl,vl,yl,xl,_l,new Zn),face:null,object:this})}copy(t){return super.copy(t),void 0!==t.center&&this.center.copy(t.center),this.material=t.material,this}}function wl(t,e,n,i,r,s){dl.subVectors(t,n).addScalar(.5).multiply(i),void 0!==r?(pl.x=s*dl.x-r*dl.y,pl.y=r*dl.x+s*dl.y):pl.copy(dl),t.copy(e),t.x+=pl.x,t.y+=pl.y,t.applyMatrix4(ml)}bl.prototype.isSprite=!0;const Ml=new ai,Sl=new ai;class Tl extends lr{constructor(){super(),this._currentLevel=0,this.type="LOD",Object.defineProperties(this,{levels:{enumerable:!0,value:[]},isLOD:{value:!0}}),this.autoUpdate=!0}copy(t){super.copy(t,!1);const e=t.levels;for(let t=0,n=e.length;t0){let n,i;for(n=1,i=e.length;n0){Ml.setFromMatrixPosition(this.matrixWorld);const n=t.ray.origin.distanceTo(Ml);this.getObjectForDistance(n).raycast(t,e)}}update(t){const e=this.levels;if(e.length>1){Ml.setFromMatrixPosition(t.matrixWorld),Sl.setFromMatrixPosition(this.matrixWorld);const n=Ml.distanceTo(Sl)/t.zoom;let i,r;for(e[0].object.visible=!0,i=1,r=e.length;i=e[i].distance;i++)e[i-1].object.visible=!1,e[i].object.visible=!0;for(this._currentLevel=i-1;io)continue;u.applyMatrix4(this.matrixWorld);const d=t.ray.origin.distanceTo(u);dt.far||e.push({distance:d,point:h.clone().applyMatrix4(this.matrixWorld),index:n,face:null,faceIndex:null,object:this})}else for(let n=Math.max(0,s.start),i=Math.min(r.count,s.start+s.count)-1;no)continue;u.applyMatrix4(this.matrixWorld);const i=t.ray.origin.distanceTo(u);it.far||e.push({distance:i,point:h.clone().applyMatrix4(this.matrixWorld),index:n,face:null,faceIndex:null,object:this})}}else n.isGeometry&&console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}updateMorphTargets(){const t=this.geometry;if(t.isBufferGeometry){const e=t.morphAttributes,n=Object.keys(e);if(n.length>0){const t=e[n[0]];if(void 0!==t){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let e=0,n=t.length;e0&&console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}}Xl.prototype.isLine=!0;const Yl=new ai,Zl=new ai;class Jl extends Xl{constructor(t,e){super(t,e),this.type="LineSegments"}computeLineDistances(){const t=this.geometry;if(t.isBufferGeometry)if(null===t.index){const e=t.attributes.position,n=[];for(let t=0,i=e.count;t0){const t=e[n[0]];if(void 0!==t){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let e=0,n=t.length;e0&&console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}}function rc(t,e,n,i,r,s,a){const o=tc.distanceSqToPoint(t);if(or.far)return;s.push({distance:l,distanceToRay:Math.sqrt(o),point:n,index:e,face:null,object:a})}}ic.prototype.isPoints=!0;class sc extends ti{constructor(t,e,n,i,r,s,a,o,l){super(t,e,n,i,r,s,a,o,l),this.format=void 0!==a?a:zt,this.minFilter=void 0!==s?s:xt,this.magFilter=void 0!==r?r:xt,this.generateMipmaps=!1;const c=this;"requestVideoFrameCallback"in t&&t.requestVideoFrameCallback((function e(){c.needsUpdate=!0,t.requestVideoFrameCallback(e)}))}clone(){return new this.constructor(this.image).copy(this)}update(){const t=this.image;!1=="requestVideoFrameCallback"in t&&t.readyState>=t.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}}sc.prototype.isVideoTexture=!0;class ac extends ti{constructor(t,e,n,i,r,s,a,o,l,c,h,u){super(null,s,a,o,l,c,i,r,h,u),this.image={width:e,height:n},this.mipmaps=t,this.flipY=!1,this.generateMipmaps=!1}}ac.prototype.isCompressedTexture=!0;class oc extends ti{constructor(t,e,n,i,r,s,a,o,l){super(t,e,n,i,r,s,a,o,l),this.needsUpdate=!0}}oc.prototype.isCanvasTexture=!0;class lc extends ti{constructor(t,e,n,i,r,s,a,o,l,c){if((c=void 0!==c?c:Vt)!==Vt&&c!==kt)throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");void 0===n&&c===Vt&&(n=At),void 0===n&&c===kt&&(n=Ot),super(null,i,r,s,a,o,c,n,l),this.image={width:t,height:e},this.magFilter=void 0!==a?a:mt,this.minFilter=void 0!==o?o:mt,this.flipY=!1,this.generateMipmaps=!1}}lc.prototype.isDepthTexture=!0;class cc extends is{constructor(t=1,e=8,n=0,i=2*Math.PI){super(),this.type="CircleGeometry",this.parameters={radius:t,segments:e,thetaStart:n,thetaLength:i},e=Math.max(3,e);const r=[],s=[],a=[],o=[],l=new ai,c=new Zn;s.push(0,0,0),a.push(0,0,1),o.push(.5,.5);for(let r=0,h=3;r<=e;r++,h+=3){const u=n+r/e*i;l.x=t*Math.cos(u),l.y=t*Math.sin(u),s.push(l.x,l.y,l.z),a.push(0,0,1),c.x=(s[h]/t+1)/2,c.y=(s[h+1]/t+1)/2,o.push(c.x,c.y)}for(let t=1;t<=e;t++)r.push(t,t+1,0);this.setIndex(r),this.setAttribute("position",new jr(s,3)),this.setAttribute("normal",new jr(a,3)),this.setAttribute("uv",new jr(o,2))}}class hc extends is{constructor(t=1,e=1,n=1,i=8,r=1,s=!1,a=0,o=2*Math.PI){super(),this.type="CylinderGeometry",this.parameters={radiusTop:t,radiusBottom:e,height:n,radialSegments:i,heightSegments:r,openEnded:s,thetaStart:a,thetaLength:o};const l=this;i=Math.floor(i),r=Math.floor(r);const c=[],h=[],u=[],d=[];let p=0;const m=[],f=n/2;let g=0;function v(n){const r=p,s=new Zn,m=new ai;let v=0;const y=!0===n?t:e,x=!0===n?1:-1;for(let t=1;t<=i;t++)h.push(0,f*x,0),u.push(0,x,0),d.push(.5,.5),p++;const _=p;for(let t=0;t<=i;t++){const e=t/i*o+a,n=Math.cos(e),r=Math.sin(e);m.x=y*r,m.y=f*x,m.z=y*n,h.push(m.x,m.y,m.z),u.push(0,x,0),s.x=.5*n+.5,s.y=.5*r*x+.5,d.push(s.x,s.y),p++}for(let t=0;t0&&v(!0),e>0&&v(!1)),this.setIndex(c),this.setAttribute("position",new jr(h,3)),this.setAttribute("normal",new jr(u,3)),this.setAttribute("uv",new jr(d,2))}}class uc extends hc{constructor(t=1,e=1,n=8,i=1,r=!1,s=0,a=2*Math.PI){super(0,t,e,n,i,r,s,a),this.type="ConeGeometry",this.parameters={radius:t,height:e,radialSegments:n,heightSegments:i,openEnded:r,thetaStart:s,thetaLength:a}}}class dc extends is{constructor(t,e,n=1,i=0){super(),this.type="PolyhedronGeometry",this.parameters={vertices:t,indices:e,radius:n,detail:i};const r=[],s=[];function a(t,e,n,i){const r=i+1,s=[];for(let i=0;i<=r;i++){s[i]=[];const a=t.clone().lerp(n,i/r),o=e.clone().lerp(n,i/r),l=r-i;for(let t=0;t<=l;t++)s[i][t]=0===t&&i===r?a:a.clone().lerp(o,t/l)}for(let t=0;t.9&&a<.1&&(e<.2&&(s[t+0]+=1),n<.2&&(s[t+2]+=1),i<.2&&(s[t+4]+=1))}}()}(),this.setAttribute("position",new jr(r,3)),this.setAttribute("normal",new jr(r.slice(),3)),this.setAttribute("uv",new jr(s,2)),0===i?this.computeVertexNormals():this.normalizeNormals()}}class pc extends dc{constructor(t=1,e=0){const n=(1+Math.sqrt(5))/2,i=1/n;super([-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-i,-n,0,-i,n,0,i,-n,0,i,n,-i,-n,0,-i,n,0,i,-n,0,i,n,0,-n,0,-i,n,0,-i,-n,0,i,n,0,i],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],t,e),this.type="DodecahedronGeometry",this.parameters={radius:t,detail:e}}}const mc=new ai,fc=new ai,gc=new ai,vc=new Mr;class yc extends is{constructor(t,e){if(super(),this.type="EdgesGeometry",this.parameters={thresholdAngle:e},e=void 0!==e?e:1,!0===t.isGeometry)return void console.error("THREE.EdgesGeometry no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.");const n=Math.pow(10,4),i=Math.cos(Hn*e),r=t.getIndex(),s=t.getAttribute("position"),a=r?r.count:s.count,o=[0,0,0],l=["a","b","c"],c=new Array(3),h={},u=[];for(let t=0;t0)for(s=e;s=e;s-=i)a=Gc(s,t[s],t[s+1],a);return a&&Nc(a,a.next)&&(Uc(a),a=a.next),a}function _c(t,e){if(!t)return t;e||(e=t);let n,i=t;do{if(n=!1,i.steiner||!Nc(i,i.next)&&0!==Ic(i.prev,i,i.next))i=i.next;else{if(Uc(i),i=e=i.prev,i===i.next)break;n=!0}}while(n||i!==e);return e}function bc(t,e,n,i,r,s,a){if(!t)return;!a&&s&&function(t,e,n,i){let r=t;do{null===r.z&&(r.z=Rc(r.x,r.y,e,n,i)),r.prevZ=r.prev,r.nextZ=r.next,r=r.next}while(r!==t);r.prevZ.nextZ=null,r.prevZ=null,function(t){let e,n,i,r,s,a,o,l,c=1;do{for(n=t,t=null,s=null,a=0;n;){for(a++,i=n,o=0,e=0;e0||l>0&&i;)0!==o&&(0===l||!i||n.z<=i.z)?(r=n,n=n.nextZ,o--):(r=i,i=i.nextZ,l--),s?s.nextZ=r:t=r,r.prevZ=s,s=r;n=i}s.nextZ=null,c*=2}while(a>1)}(r)}(t,i,r,s);let o,l,c=t;for(;t.prev!==t.next;)if(o=t.prev,l=t.next,s?Mc(t,i,r,s):wc(t))e.push(o.i/n),e.push(t.i/n),e.push(l.i/n),Uc(t),t=l.next,c=l.next;else if((t=l)===c){a?1===a?bc(t=Sc(_c(t),e,n),e,n,i,r,s,2):2===a&&Tc(t,e,n,i,r,s):bc(_c(t),e,n,i,r,s,1);break}}function wc(t){const e=t.prev,n=t,i=t.next;if(Ic(e,n,i)>=0)return!1;let r=t.next.next;for(;r!==t.prev;){if(Pc(e.x,e.y,n.x,n.y,i.x,i.y,r.x,r.y)&&Ic(r.prev,r,r.next)>=0)return!1;r=r.next}return!0}function Mc(t,e,n,i){const r=t.prev,s=t,a=t.next;if(Ic(r,s,a)>=0)return!1;const o=r.xs.x?r.x>a.x?r.x:a.x:s.x>a.x?s.x:a.x,h=r.y>s.y?r.y>a.y?r.y:a.y:s.y>a.y?s.y:a.y,u=Rc(o,l,e,n,i),d=Rc(c,h,e,n,i);let p=t.prevZ,m=t.nextZ;for(;p&&p.z>=u&&m&&m.z<=d;){if(p!==t.prev&&p!==t.next&&Pc(r.x,r.y,s.x,s.y,a.x,a.y,p.x,p.y)&&Ic(p.prev,p,p.next)>=0)return!1;if(p=p.prevZ,m!==t.prev&&m!==t.next&&Pc(r.x,r.y,s.x,s.y,a.x,a.y,m.x,m.y)&&Ic(m.prev,m,m.next)>=0)return!1;m=m.nextZ}for(;p&&p.z>=u;){if(p!==t.prev&&p!==t.next&&Pc(r.x,r.y,s.x,s.y,a.x,a.y,p.x,p.y)&&Ic(p.prev,p,p.next)>=0)return!1;p=p.prevZ}for(;m&&m.z<=d;){if(m!==t.prev&&m!==t.next&&Pc(r.x,r.y,s.x,s.y,a.x,a.y,m.x,m.y)&&Ic(m.prev,m,m.next)>=0)return!1;m=m.nextZ}return!0}function Sc(t,e,n){let i=t;do{const r=i.prev,s=i.next.next;!Nc(r,s)&&Oc(r,i,i.next,s)&&Fc(r,s)&&Fc(s,r)&&(e.push(r.i/n),e.push(i.i/n),e.push(s.i/n),Uc(i),Uc(i.next),i=t=s),i=i.next}while(i!==t);return _c(i)}function Tc(t,e,n,i,r,s){let a=t;do{let t=a.next.next;for(;t!==a.prev;){if(a.i!==t.i&&Dc(a,t)){let o=Hc(a,t);return a=_c(a,a.next),o=_c(o,o.next),bc(a,e,n,i,r,s),void bc(o,e,n,i,r,s)}t=t.next}a=a.next}while(a!==t)}function Ec(t,e){return t.x-e.x}function Ac(t,e){if(e=function(t,e){let n=e;const i=t.x,r=t.y;let s,a=-1/0;do{if(r<=n.y&&r>=n.next.y&&n.next.y!==n.y){const t=n.x+(r-n.y)*(n.next.x-n.x)/(n.next.y-n.y);if(t<=i&&t>a){if(a=t,t===i){if(r===n.y)return n;if(r===n.next.y)return n.next}s=n.x=n.x&&n.x>=l&&i!==n.x&&Pc(rs.x||n.x===s.x&&Lc(s,n)))&&(s=n,u=h)),n=n.next}while(n!==o);return s}(t,e)){const n=Hc(e,t);_c(e,e.next),_c(n,n.next)}}function Lc(t,e){return Ic(t.prev,t,e.prev)<0&&Ic(e.next,t,t.next)<0}function Rc(t,e,n,i,r){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-n)*r)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-i)*r)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function Cc(t){let e=t,n=t;do{(e.x=0&&(t-a)*(i-o)-(n-a)*(e-o)>=0&&(n-a)*(s-o)-(r-a)*(i-o)>=0}function Dc(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){let n=t;do{if(n.i!==t.i&&n.next.i!==t.i&&n.i!==e.i&&n.next.i!==e.i&&Oc(n,n.next,t,e))return!0;n=n.next}while(n!==t);return!1}(t,e)&&(Fc(t,e)&&Fc(e,t)&&function(t,e){let n=t,i=!1;const r=(t.x+e.x)/2,s=(t.y+e.y)/2;do{n.y>s!=n.next.y>s&&n.next.y!==n.y&&r<(n.next.x-n.x)*(s-n.y)/(n.next.y-n.y)+n.x&&(i=!i),n=n.next}while(n!==t);return i}(t,e)&&(Ic(t.prev,t,e.prev)||Ic(t,e.prev,e))||Nc(t,e)&&Ic(t.prev,t,t.next)>0&&Ic(e.prev,e,e.next)>0)}function Ic(t,e,n){return(e.y-t.y)*(n.x-e.x)-(e.x-t.x)*(n.y-e.y)}function Nc(t,e){return t.x===e.x&&t.y===e.y}function Oc(t,e,n,i){const r=zc(Ic(t,e,n)),s=zc(Ic(t,e,i)),a=zc(Ic(n,i,t)),o=zc(Ic(n,i,e));return r!==s&&a!==o||!(0!==r||!Bc(t,n,e))||!(0!==s||!Bc(t,i,e))||!(0!==a||!Bc(n,t,i))||!(0!==o||!Bc(n,e,i))}function Bc(t,e,n){return e.x<=Math.max(t.x,n.x)&&e.x>=Math.min(t.x,n.x)&&e.y<=Math.max(t.y,n.y)&&e.y>=Math.min(t.y,n.y)}function zc(t){return t>0?1:t<0?-1:0}function Fc(t,e){return Ic(t.prev,t,t.next)<0?Ic(t,e,t.next)>=0&&Ic(t,t.prev,e)>=0:Ic(t,e,t.prev)<0||Ic(t,t.next,e)<0}function Hc(t,e){const n=new Vc(t.i,t.x,t.y),i=new Vc(e.i,e.x,e.y),r=t.next,s=e.prev;return t.next=e,e.prev=t,n.next=r,r.prev=n,i.next=n,n.prev=i,s.next=i,i.prev=s,i}function Gc(t,e,n,i){const r=new Vc(t,e,n);return i?(r.next=i.next,r.prev=i,i.next.prev=r,i.next=r):(r.prev=r,r.next=r),r}function Uc(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function Vc(t,e,n){this.i=t,this.x=e,this.y=n,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}class kc{static area(t){const e=t.length;let n=0;for(let i=e-1,r=0;r80*n){o=c=t[0],l=h=t[1];for(let e=n;ec&&(c=u),d>h&&(h=d);p=Math.max(c-o,h-l),p=0!==p?1/p:0}return bc(s,a,n,o,l,p),a}(n,i);for(let t=0;t2&&t[e-1].equals(t[0])&&t.pop()}function jc(t,e){for(let n=0;nNumber.EPSILON){const u=Math.sqrt(h),d=Math.sqrt(l*l+c*c),p=e.x-o/u,m=e.y+a/u,f=((n.x-c/d-p)*c-(n.y+l/d-m)*l)/(a*c-o*l);i=p+a*f-t.x,r=m+o*f-t.y;const g=i*i+r*r;if(g<=2)return new Zn(i,r);s=Math.sqrt(g/2)}else{let t=!1;a>Number.EPSILON?l>Number.EPSILON&&(t=!0):a<-Number.EPSILON?l<-Number.EPSILON&&(t=!0):Math.sign(o)===Math.sign(c)&&(t=!0),t?(i=-o,r=a,s=Math.sqrt(h)):(i=a,r=o,s=Math.sqrt(h/2))}return new Zn(i/s,r/s)}const P=[];for(let t=0,e=E.length,n=e-1,i=t+1;t=0;t--){const e=t/p,n=h*Math.cos(e*Math.PI/2),i=u*Math.sin(e*Math.PI/2)+d;for(let t=0,e=E.length;t=0;){const i=n;let r=n-1;r<0&&(r=t.length-1);for(let t=0,n=o+2*p;t=0?(t(i-o,p,h),u.subVectors(c,h)):(t(i+o,p,h),u.subVectors(h,c)),p-o>=0?(t(i,p-o,h),d.subVectors(c,h)):(t(i,p+o,h),d.subVectors(h,c)),l.crossVectors(u,d).normalize(),s.push(l.x,l.y,l.z),a.push(i,p)}}for(let t=0;t0)&&d.push(e,r,l),(t!==n-1||o=i)){l.push(e.times[t]);for(let n=0;ns.tracks[t].times[0]&&(o=s.tracks[t].times[0]);for(let t=0;t=i.times[u]){const t=u*l+o,e=t+l-o;d=xh.arraySlice(i.values,t,e)}else{const t=i.createInterpolant(),e=o,n=l-o;t.evaluate(s),d=xh.arraySlice(t.resultBuffer,e,n)}"quaternion"===r&&(new si).fromArray(d).normalize().conjugate().toArray(d);const p=a.times.length;for(let t=0;t=r)break t;{const a=e[1];t=r)break e}s=n,n=0}}for(;n>>1;te;)--s;if(++s,0!==r||s!==i){r>=s&&(s=Math.max(s,1),r=s-1);const t=this.getValueSize();this.times=xh.arraySlice(n,r,s),this.values=xh.arraySlice(this.values,r*t,s*t)}return this}validate(){let t=!0;const e=this.getValueSize();e-Math.floor(e)!=0&&(console.error("THREE.KeyframeTrack: Invalid value size in track.",this),t=!1);const n=this.times,i=this.values,r=n.length;0===r&&(console.error("THREE.KeyframeTrack: Track is empty.",this),t=!1);let s=null;for(let e=0;e!==r;e++){const i=n[e];if("number"==typeof i&&isNaN(i)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,e,i),t=!1;break}if(null!==s&&s>i){console.error("THREE.KeyframeTrack: Out of order keys.",this,e,i,s),t=!1;break}s=i}if(void 0!==i&&xh.isTypedArray(i))for(let e=0,n=i.length;e!==n;++e){const n=i[e];if(isNaN(n)){console.error("THREE.KeyframeTrack: Value is not a valid number.",this,e,n),t=!1;break}}return t}optimize(){const t=xh.arraySlice(this.times),e=xh.arraySlice(this.values),n=this.getValueSize(),i=this.getInterpolation()===Ue,r=t.length-1;let s=1;for(let a=1;a0){t[s]=t[r];for(let t=r*n,i=s*n,a=0;a!==n;++a)e[i+a]=e[t+a];++s}return s!==t.length?(this.times=xh.arraySlice(t,0,s),this.values=xh.arraySlice(e,0,s*n)):(this.times=t,this.values=e),this}clone(){const t=xh.arraySlice(this.times,0),e=xh.arraySlice(this.values,0),n=new(0,this.constructor)(this.name,t,e);return n.createInterpolant=this.createInterpolant,n}}Sh.prototype.TimeBufferType=Float32Array,Sh.prototype.ValueBufferType=Float32Array,Sh.prototype.DefaultInterpolation=Ge;class Th extends Sh{}Th.prototype.ValueTypeName="bool",Th.prototype.ValueBufferType=Array,Th.prototype.DefaultInterpolation=He,Th.prototype.InterpolantFactoryMethodLinear=void 0,Th.prototype.InterpolantFactoryMethodSmooth=void 0;class Eh extends Sh{}Eh.prototype.ValueTypeName="color";class Ah extends Sh{}Ah.prototype.ValueTypeName="number";class Lh extends _h{constructor(t,e,n,i){super(t,e,n,i)}interpolate_(t,e,n,i){const r=this.resultBuffer,s=this.sampleValues,a=this.valueSize,o=(n-e)/(i-e);let l=t*a;for(let t=l+a;l!==t;l+=4)si.slerpFlat(r,0,s,l-a,s,l,o);return r}}class Rh extends Sh{InterpolantFactoryMethodLinear(t){return new Lh(this.times,this.values,this.getValueSize(),t)}}Rh.prototype.ValueTypeName="quaternion",Rh.prototype.DefaultInterpolation=Ge,Rh.prototype.InterpolantFactoryMethodSmooth=void 0;class Ch extends Sh{}Ch.prototype.ValueTypeName="string",Ch.prototype.ValueBufferType=Array,Ch.prototype.DefaultInterpolation=He,Ch.prototype.InterpolantFactoryMethodLinear=void 0,Ch.prototype.InterpolantFactoryMethodSmooth=void 0;class Ph extends Sh{}Ph.prototype.ValueTypeName="vector";class Dh{constructor(t,e=-1,n,i=je){this.name=t,this.tracks=n,this.duration=e,this.blendMode=i,this.uuid=Un(),this.duration<0&&this.resetDuration()}static parse(t){const e=[],n=t.tracks,i=1/(t.fps||1);for(let t=0,r=n.length;t!==r;++t)e.push(Ih(n[t]).scale(i));const r=new this(t.name,t.duration,e,t.blendMode);return r.uuid=t.uuid,r}static toJSON(t){const e=[],n=t.tracks,i={name:t.name,duration:t.duration,tracks:e,uuid:t.uuid,blendMode:t.blendMode};for(let t=0,i=n.length;t!==i;++t)e.push(Sh.toJSON(n[t]));return i}static CreateFromMorphTargetSequence(t,e,n,i){const r=e.length,s=[];for(let t=0;t1){const t=s[1];let e=i[t];e||(i[t]=e=[]),e.push(n)}}const s=[];for(const t in i)s.push(this.CreateFromMorphTargetSequence(t,i[t],e,n));return s}static parseAnimation(t,e){if(!t)return console.error("THREE.AnimationClip: No animation in JSONLoader data."),null;const n=function(t,e,n,i,r){if(0!==n.length){const s=[],a=[];xh.flattenJSON(n,s,a,i),0!==s.length&&r.push(new t(e,s,a))}},i=[],r=t.name||"default",s=t.fps||30,a=t.blendMode;let o=t.length||-1;const l=t.hierarchy||[];for(let t=0;t0||0===t.search(/^data\:image\/jpeg/);r.format=i?zt:Ft,r.needsUpdate=!0,void 0!==e&&e(r)}),n,i),r}}class qh{constructor(){this.type="Curve",this.arcLengthDivisions=200}getPoint(){return console.warn("THREE.Curve: .getPoint() not implemented."),null}getPointAt(t,e){const n=this.getUtoTmapping(t);return this.getPoint(n,e)}getPoints(t=5){const e=[];for(let n=0;n<=t;n++)e.push(this.getPoint(n/t));return e}getSpacedPoints(t=5){const e=[];for(let n=0;n<=t;n++)e.push(this.getPointAt(n/t));return e}getLength(){const t=this.getLengths();return t[t.length-1]}getLengths(t=this.arcLengthDivisions){if(this.cacheArcLengths&&this.cacheArcLengths.length===t+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;const e=[];let n,i=this.getPoint(0),r=0;e.push(0);for(let s=1;s<=t;s++)n=this.getPoint(s/t),r+=n.distanceTo(i),e.push(r),i=n;return this.cacheArcLengths=e,e}updateArcLengths(){this.needsUpdate=!0,this.getLengths()}getUtoTmapping(t,e){const n=this.getLengths();let i=0;const r=n.length;let s;s=e||t*n[r-1];let a,o=0,l=r-1;for(;o<=l;)if(i=Math.floor(o+(l-o)/2),a=n[i]-s,a<0)o=i+1;else{if(!(a>0)){l=i;break}l=i-1}if(i=l,n[i]===s)return i/(r-1);const c=n[i];return(i+(s-c)/(n[i+1]-c))/(r-1)}getTangent(t,e){const n=1e-4;let i=t-n,r=t+n;i<0&&(i=0),r>1&&(r=1);const s=this.getPoint(i),a=this.getPoint(r),o=e||(s.isVector2?new Zn:new ai);return o.copy(a).sub(s).normalize(),o}getTangentAt(t,e){const n=this.getUtoTmapping(t);return this.getTangent(n,e)}computeFrenetFrames(t,e){const n=new ai,i=[],r=[],s=[],a=new ai,o=new zi;for(let e=0;e<=t;e++){const n=e/t;i[e]=this.getTangentAt(n,new ai),i[e].normalize()}r[0]=new ai,s[0]=new ai;let l=Number.MAX_VALUE;const c=Math.abs(i[0].x),h=Math.abs(i[0].y),u=Math.abs(i[0].z);c<=l&&(l=c,n.set(1,0,0)),h<=l&&(l=h,n.set(0,1,0)),u<=l&&n.set(0,0,1),a.crossVectors(i[0],n).normalize(),r[0].crossVectors(i[0],a),s[0].crossVectors(i[0],r[0]);for(let e=1;e<=t;e++){if(r[e]=r[e-1].clone(),s[e]=s[e-1].clone(),a.crossVectors(i[e-1],i[e]),a.length()>Number.EPSILON){a.normalize();const t=Math.acos(Vn(i[e-1].dot(i[e]),-1,1));r[e].applyMatrix4(o.makeRotationAxis(a,t))}s[e].crossVectors(i[e],r[e])}if(!0===e){let e=Math.acos(Vn(r[0].dot(r[t]),-1,1));e/=t,i[0].dot(a.crossVectors(r[0],r[t]))>0&&(e=-e);for(let n=1;n<=t;n++)r[n].applyMatrix4(o.makeRotationAxis(i[n],e*n)),s[n].crossVectors(i[n],r[n])}return{tangents:i,normals:r,binormals:s}}clone(){return(new this.constructor).copy(this)}copy(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}toJSON(){const t={metadata:{version:4.5,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t}fromJSON(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}}class Xh extends qh{constructor(t=0,e=0,n=1,i=1,r=0,s=2*Math.PI,a=!1,o=0){super(),this.type="EllipseCurve",this.aX=t,this.aY=e,this.xRadius=n,this.yRadius=i,this.aStartAngle=r,this.aEndAngle=s,this.aClockwise=a,this.aRotation=o}getPoint(t,e){const n=e||new Zn,i=2*Math.PI;let r=this.aEndAngle-this.aStartAngle;const s=Math.abs(r)i;)r-=i;r0?0:(Math.floor(Math.abs(l)/r)+1)*r:0===c&&l===r-1&&(l=r-2,c=1),this.closed||l>0?a=i[(l-1)%r]:(Jh.subVectors(i[0],i[1]).add(i[0]),a=Jh);const h=i[l%r],u=i[(l+1)%r];if(this.closed||l+2i.length-2?i.length-1:s+1],h=i[s>i.length-3?i.length-1:s+2];return n.set(eu(a,o.x,l.x,c.x,h.x),eu(a,o.y,l.y,c.y,h.y)),n}copy(t){super.copy(t),this.points=[];for(let e=0,n=t.points.length;e=e){const t=n[i]-e,r=this.curves[i],s=r.getLength(),a=0===s?0:1-t/s;return r.getPointAt(a)}i++}return null}getLength(){const t=this.getCurveLengths();return t[t.length-1]}updateArcLengths(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;const t=[];let e=0;for(let n=0,i=this.curves.length;n1&&!e[e.length-1].equals(e[0])&&e.push(e[0]),e}copy(t){super.copy(t),this.curves=[];for(let e=0,n=t.curves.length;e0){const t=l.getPoint(0);t.equals(this.currentPoint)||this.lineTo(t.x,t.y)}this.curves.push(l);const c=l.getPoint(1);return this.currentPoint.copy(c),this}copy(t){return super.copy(t),this.currentPoint.copy(t.currentPoint),this}toJSON(){const t=super.toJSON();return t.currentPoint=this.currentPoint.toArray(),t}fromJSON(t){return super.fromJSON(t),this.currentPoint.fromArray(t.currentPoint),this}}class mu extends pu{constructor(t){super(t),this.uuid=Un(),this.type="Shape",this.holes=[]}getPointsHoles(t){const e=[];for(let n=0,i=this.holes.length;n0:i.vertexColors=t.vertexColors),void 0!==t.uniforms)for(const e in t.uniforms){const r=t.uniforms[e];switch(i.uniforms[e]={},r.type){case"t":i.uniforms[e].value=n(r.value);break;case"c":i.uniforms[e].value=(new Dr).setHex(r.value);break;case"v2":i.uniforms[e].value=(new Zn).fromArray(r.value);break;case"v3":i.uniforms[e].value=(new ai).fromArray(r.value);break;case"v4":i.uniforms[e].value=(new ni).fromArray(r.value);break;case"m3":i.uniforms[e].value=(new Jn).fromArray(r.value);break;case"m4":i.uniforms[e].value=(new zi).fromArray(r.value);break;default:i.uniforms[e].value=r.value}}if(void 0!==t.defines&&(i.defines=t.defines),void 0!==t.vertexShader&&(i.vertexShader=t.vertexShader),void 0!==t.fragmentShader&&(i.fragmentShader=t.fragmentShader),void 0!==t.extensions)for(const e in t.extensions)i.extensions[e]=t.extensions[e];if(void 0!==t.shading&&(i.flatShading=1===t.shading),void 0!==t.size&&(i.size=t.size),void 0!==t.sizeAttenuation&&(i.sizeAttenuation=t.sizeAttenuation),void 0!==t.map&&(i.map=n(t.map)),void 0!==t.matcap&&(i.matcap=n(t.matcap)),void 0!==t.alphaMap&&(i.alphaMap=n(t.alphaMap)),void 0!==t.bumpMap&&(i.bumpMap=n(t.bumpMap)),void 0!==t.bumpScale&&(i.bumpScale=t.bumpScale),void 0!==t.normalMap&&(i.normalMap=n(t.normalMap)),void 0!==t.normalMapType&&(i.normalMapType=t.normalMapType),void 0!==t.normalScale){let e=t.normalScale;!1===Array.isArray(e)&&(e=[e,e]),i.normalScale=(new Zn).fromArray(e)}return void 0!==t.displacementMap&&(i.displacementMap=n(t.displacementMap)),void 0!==t.displacementScale&&(i.displacementScale=t.displacementScale),void 0!==t.displacementBias&&(i.displacementBias=t.displacementBias),void 0!==t.roughnessMap&&(i.roughnessMap=n(t.roughnessMap)),void 0!==t.metalnessMap&&(i.metalnessMap=n(t.metalnessMap)),void 0!==t.emissiveMap&&(i.emissiveMap=n(t.emissiveMap)),void 0!==t.emissiveIntensity&&(i.emissiveIntensity=t.emissiveIntensity),void 0!==t.specularMap&&(i.specularMap=n(t.specularMap)),void 0!==t.envMap&&(i.envMap=n(t.envMap)),void 0!==t.envMapIntensity&&(i.envMapIntensity=t.envMapIntensity),void 0!==t.reflectivity&&(i.reflectivity=t.reflectivity),void 0!==t.refractionRatio&&(i.refractionRatio=t.refractionRatio),void 0!==t.lightMap&&(i.lightMap=n(t.lightMap)),void 0!==t.lightMapIntensity&&(i.lightMapIntensity=t.lightMapIntensity),void 0!==t.aoMap&&(i.aoMap=n(t.aoMap)),void 0!==t.aoMapIntensity&&(i.aoMapIntensity=t.aoMapIntensity),void 0!==t.gradientMap&&(i.gradientMap=n(t.gradientMap)),void 0!==t.clearcoatMap&&(i.clearcoatMap=n(t.clearcoatMap)),void 0!==t.clearcoatRoughnessMap&&(i.clearcoatRoughnessMap=n(t.clearcoatRoughnessMap)),void 0!==t.clearcoatNormalMap&&(i.clearcoatNormalMap=n(t.clearcoatNormalMap)),void 0!==t.clearcoatNormalScale&&(i.clearcoatNormalScale=(new Zn).fromArray(t.clearcoatNormalScale)),void 0!==t.transmission&&(i.transmission=t.transmission),void 0!==t.transmissionMap&&(i.transmissionMap=n(t.transmissionMap)),i}setTextures(t){return this.textures=t,this}}class Bu{static decodeText(t){if("undefined"!=typeof TextDecoder)return(new TextDecoder).decode(t);let e="";for(let n=0,i=t.length;n0){const n=new Oh(e);r=new Vh(n),r.setCrossOrigin(this.crossOrigin);for(let e=0,n=t.length;eNumber.EPSILON){if(l<0&&(n=e[s],o=-o,a=e[r],l=-l),t.ya.y)continue;if(t.y===n.y){if(t.x===n.x)return!0}else{const e=l*(t.x-n.x)-o*(t.y-n.y);if(0===e)return!0;if(e<0)continue;i=!i}}else{if(t.y!==n.y)continue;if(a.x<=t.x&&t.x<=n.x||n.x<=t.x&&t.x<=a.x)return!0}}return i}const r=kc.isClockWise,s=this.subPaths;if(0===s.length)return[];if(!0===e)return n(s);let a,o,l;const c=[];if(1===s.length)return o=s[0],l=new mu,l.curves=o.curves,c.push(l),c;let h=!r(s[0].getPoints());h=t?!h:h;const u=[],d=[];let p,m,f=[],g=0;d[g]=void 0,f[g]=[];for(let e=0,n=s.length;e1){let t=!1;const e=[];for(let t=0,e=d.length;t0&&(t||(f=u))}for(let t=0,e=d.length;t0){this.source.connect(this.filters[0]);for(let t=1,e=this.filters.length;t0){this.source.disconnect(this.filters[0]);for(let t=1,e=this.filters.length;t0&&this._mixBufferRegionAdditive(n,i,this._addIndex*e,1,e);for(let t=e,r=e+e;t!==r;++t)if(n[t]!==n[t+e]){a.setValue(n,i);break}}saveOriginalState(){const t=this.binding,e=this.buffer,n=this.valueSize,i=n*this._origIndex;t.getValue(e,i);for(let t=n,r=i;t!==r;++t)e[t]=e[i+t%n];this._setIdentity(),this.cumulativeWeight=0,this.cumulativeWeightAdditive=0}restoreOriginalState(){const t=3*this.valueSize;this.binding.setValue(this.buffer,t)}_setAdditiveIdentityNumeric(){const t=this._addIndex*this.valueSize,e=t+this.valueSize;for(let n=t;n=.5)for(let i=0;i!==r;++i)t[e+i]=t[n+i]}_slerp(t,e,n,i){si.slerpFlat(t,e,t,e,t,n,i)}_slerpAdditive(t,e,n,i,r){const s=this._workIndex*r;si.multiplyQuaternionsFlat(t,s,t,e,t,n),si.slerpFlat(t,e,t,e,t,s,i)}_lerp(t,e,n,i,r){const s=1-i;for(let a=0;a!==r;++a){const r=e+a;t[r]=t[r]*s+t[n+a]*i}}_lerpAdditive(t,e,n,i,r){for(let s=0;s!==r;++s){const r=e+s;t[r]=t[r]+t[n+s]*i}}}const yd=new RegExp("[\\[\\]\\.:\\/]","g"),xd="[^\\[\\]\\.:\\/]",_d="[^"+"\\[\\]\\.:\\/".replace("\\.","")+"]",bd=/((?:WC+[\/:])*)/.source.replace("WC",xd),wd=/(WCOD+)?/.source.replace("WCOD",_d),Md=/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC",xd),Sd=/\.(WC+)(?:\[(.+)\])?/.source.replace("WC",xd),Td=new RegExp("^"+bd+wd+Md+Sd+"$"),Ed=["material","materials","bones"];class Ad{constructor(t,e,n){this.path=e,this.parsedPath=n||Ad.parseTrackName(e),this.node=Ad.findNode(t,this.parsedPath.nodeName)||t,this.rootNode=t,this.getValue=this._getValue_unbound,this.setValue=this._setValue_unbound}static create(t,e,n){return t&&t.isAnimationObjectGroup?new Ad.Composite(t,e,n):new Ad(t,e,n)}static sanitizeNodeName(t){return t.replace(/\s/g,"_").replace(yd,"")}static parseTrackName(t){const e=Td.exec(t);if(!e)throw new Error("PropertyBinding: Cannot parse trackName: "+t);const n={nodeName:e[2],objectName:e[3],objectIndex:e[4],propertyName:e[5],propertyIndex:e[6]},i=n.nodeName&&n.nodeName.lastIndexOf(".");if(void 0!==i&&-1!==i){const t=n.nodeName.substring(i+1);-1!==Ed.indexOf(t)&&(n.nodeName=n.nodeName.substring(0,i),n.objectName=t)}if(null===n.propertyName||0===n.propertyName.length)throw new Error("PropertyBinding: can not parse propertyName from trackName: "+t);return n}static findNode(t,e){if(!e||""===e||"."===e||-1===e||e===t.name||e===t.uuid)return t;if(t.skeleton){const n=t.skeleton.getBoneByName(e);if(void 0!==n)return n}if(t.children){const n=function(t){for(let i=0;i=r){const s=r++,c=t[s];e[c.uuid]=l,t[l]=c,e[o]=s,t[s]=a;for(let t=0,e=i;t!==e;++t){const e=n[t],i=e[s],r=e[l];e[l]=i,e[s]=r}}}this.nCachedObjects_=r}uncache(){const t=this._objects,e=this._indicesByUUID,n=this._bindings,i=n.length;let r=this.nCachedObjects_,s=t.length;for(let a=0,o=arguments.length;a!==o;++a){const o=arguments[a].uuid,l=e[o];if(void 0!==l)if(delete e[o],l0&&(e[a.uuid]=l),t[l]=a,t.pop();for(let t=0,e=i;t!==e;++t){const e=n[t];e[l]=e[r],e.pop()}}}this.nCachedObjects_=r}subscribe_(t,e){const n=this._bindingsIndicesByPath;let i=n[t];const r=this._bindings;if(void 0!==i)return r[i];const s=this._paths,a=this._parsedPaths,o=this._objects,l=o.length,c=this.nCachedObjects_,h=new Array(l);i=r.length,n[t]=i,s.push(t),a.push(e),r.push(h);for(let n=c,i=o.length;n!==i;++n){const i=o[n];h[n]=new Ad(i,t,e)}return h}unsubscribe_(t){const e=this._bindingsIndicesByPath,n=e[t];if(void 0!==n){const i=this._paths,r=this._parsedPaths,s=this._bindings,a=s.length-1,o=s[a];e[t[a]]=n,s[n]=o,s.pop(),r[n]=r[a],r.pop(),i[n]=i[a],i.pop()}}}Ld.prototype.isAnimationObjectGroup=!0;class Rd{constructor(t,e,n=null,i=e.blendMode){this._mixer=t,this._clip=e,this._localRoot=n,this.blendMode=i;const r=e.tracks,s=r.length,a=new Array(s),o={endingStart:Ve,endingEnd:Ve};for(let t=0;t!==s;++t){const e=r[t].createInterpolant(null);a[t]=e,e.settings=o}this._interpolantSettings=o,this._interpolants=a,this._propertyBindings=new Array(s),this._cacheIndex=null,this._byClipCacheIndex=null,this._timeScaleInterpolant=null,this._weightInterpolant=null,this.loop=ze,this._loopCount=-1,this._startTime=null,this.time=0,this.timeScale=1,this._effectiveTimeScale=1,this.weight=1,this._effectiveWeight=1,this.repetitions=1/0,this.paused=!1,this.enabled=!0,this.clampWhenFinished=!1,this.zeroSlopeAtStart=!0,this.zeroSlopeAtEnd=!0}play(){return this._mixer._activateAction(this),this}stop(){return this._mixer._deactivateAction(this),this.reset()}reset(){return this.paused=!1,this.enabled=!0,this.time=0,this._loopCount=-1,this._startTime=null,this.stopFading().stopWarping()}isRunning(){return this.enabled&&!this.paused&&0!==this.timeScale&&null===this._startTime&&this._mixer._isActiveAction(this)}isScheduled(){return this._mixer._isActiveAction(this)}startAt(t){return this._startTime=t,this}setLoop(t,e){return this.loop=t,this.repetitions=e,this}setEffectiveWeight(t){return this.weight=t,this._effectiveWeight=this.enabled?t:0,this.stopFading()}getEffectiveWeight(){return this._effectiveWeight}fadeIn(t){return this._scheduleFading(t,0,1)}fadeOut(t){return this._scheduleFading(t,1,0)}crossFadeFrom(t,e,n){if(t.fadeOut(e),this.fadeIn(e),n){const n=this._clip.duration,i=t._clip.duration,r=i/n,s=n/i;t.warp(1,r,e),this.warp(s,1,e)}return this}crossFadeTo(t,e,n){return t.crossFadeFrom(this,e,n)}stopFading(){const t=this._weightInterpolant;return null!==t&&(this._weightInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this}setEffectiveTimeScale(t){return this.timeScale=t,this._effectiveTimeScale=this.paused?0:t,this.stopWarping()}getEffectiveTimeScale(){return this._effectiveTimeScale}setDuration(t){return this.timeScale=this._clip.duration/t,this.stopWarping()}syncWith(t){return this.time=t.time,this.timeScale=t.timeScale,this.stopWarping()}halt(t){return this.warp(this._effectiveTimeScale,0,t)}warp(t,e,n){const i=this._mixer,r=i.time,s=this.timeScale;let a=this._timeScaleInterpolant;null===a&&(a=i._lendControlInterpolant(),this._timeScaleInterpolant=a);const o=a.parameterPositions,l=a.sampleValues;return o[0]=r,o[1]=r+n,l[0]=t/s,l[1]=e/s,this}stopWarping(){const t=this._timeScaleInterpolant;return null!==t&&(this._timeScaleInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this}getMixer(){return this._mixer}getClip(){return this._clip}getRoot(){return this._localRoot||this._mixer._root}_update(t,e,n,i){if(!this.enabled)return void this._updateWeight(t);const r=this._startTime;if(null!==r){const i=(t-r)*n;if(i<0||0===n)return;this._startTime=null,e=n*i}e*=this._updateTimeScale(t);const s=this._updateTime(e),a=this._updateWeight(t);if(a>0){const t=this._interpolants,e=this._propertyBindings;switch(this.blendMode){case qe:for(let n=0,i=t.length;n!==i;++n)t[n].evaluate(s),e[n].accumulateAdditive(a);break;case je:default:for(let n=0,r=t.length;n!==r;++n)t[n].evaluate(s),e[n].accumulate(i,a)}}}_updateWeight(t){let e=0;if(this.enabled){e=this.weight;const n=this._weightInterpolant;if(null!==n){const i=n.evaluate(t)[0];e*=i,t>n.parameterPositions[1]&&(this.stopFading(),0===i&&(this.enabled=!1))}}return this._effectiveWeight=e,e}_updateTimeScale(t){let e=0;if(!this.paused){e=this.timeScale;const n=this._timeScaleInterpolant;null!==n&&(e*=n.evaluate(t)[0],t>n.parameterPositions[1]&&(this.stopWarping(),0===e?this.paused=!0:this.timeScale=e))}return this._effectiveTimeScale=e,e}_updateTime(t){const e=this._clip.duration,n=this.loop;let i=this.time+t,r=this._loopCount;const s=n===Fe;if(0===t)return-1===r?i:s&&1==(1&r)?e-i:i;if(n===Be){-1===r&&(this._loopCount=0,this._setEndings(!0,!0,!1));t:{if(i>=e)i=e;else{if(!(i<0)){this.time=i;break t}i=0}this.clampWhenFinished?this.paused=!0:this.enabled=!1,this.time=i,this._mixer.dispatchEvent({type:"finished",action:this,direction:t<0?-1:1})}}else{if(-1===r&&(t>=0?(r=0,this._setEndings(!0,0===this.repetitions,s)):this._setEndings(0===this.repetitions,!0,s)),i>=e||i<0){const n=Math.floor(i/e);i-=e*n,r+=Math.abs(n);const a=this.repetitions-r;if(a<=0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,i=t>0?e:0,this.time=i,this._mixer.dispatchEvent({type:"finished",action:this,direction:t>0?1:-1});else{if(1===a){const e=t<0;this._setEndings(e,!e,s)}else this._setEndings(!1,!1,s);this._loopCount=r,this.time=i,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:n})}}else this.time=i;if(s&&1==(1&r))return e-i}return i}_setEndings(t,e,n){const i=this._interpolantSettings;n?(i.endingStart=ke,i.endingEnd=ke):(i.endingStart=t?this.zeroSlopeAtStart?ke:Ve:We,i.endingEnd=e?this.zeroSlopeAtEnd?ke:Ve:We)}_scheduleFading(t,e,n){const i=this._mixer,r=i.time;let s=this._weightInterpolant;null===s&&(s=i._lendControlInterpolant(),this._weightInterpolant=s);const a=s.parameterPositions,o=s.sampleValues;return a[0]=r,o[0]=e,a[1]=r+t,o[1]=n,this}}class Cd extends Bn{constructor(t){super(),this._root=t,this._initMemoryManager(),this._accuIndex=0,this.time=0,this.timeScale=1}_bindAction(t,e){const n=t._localRoot||this._root,i=t._clip.tracks,r=i.length,s=t._propertyBindings,a=t._interpolants,o=n.uuid,l=this._bindingsByRootAndName;let c=l[o];void 0===c&&(c={},l[o]=c);for(let t=0;t!==r;++t){const r=i[t],l=r.name;let h=c[l];if(void 0!==h)s[t]=h;else{if(h=s[t],void 0!==h){null===h._cacheIndex&&(++h.referenceCount,this._addInactiveBinding(h,o,l));continue}const i=e&&e._propertyBindings[t].binding.parsedPath;h=new vd(Ad.create(n,l,i),r.ValueTypeName,r.getValueSize()),++h.referenceCount,this._addInactiveBinding(h,o,l),s[t]=h}a[t].resultBuffer=h.buffer}}_activateAction(t){if(!this._isActiveAction(t)){if(null===t._cacheIndex){const e=(t._localRoot||this._root).uuid,n=t._clip.uuid,i=this._actionsByClip[n];this._bindAction(t,i&&i.knownActions[0]),this._addInactiveAction(t,n,e)}const e=t._propertyBindings;for(let t=0,n=e.length;t!==n;++t){const n=e[t];0==n.useCount++&&(this._lendBinding(n),n.saveOriginalState())}this._lendAction(t)}}_deactivateAction(t){if(this._isActiveAction(t)){const e=t._propertyBindings;for(let t=0,n=e.length;t!==n;++t){const n=e[t];0==--n.useCount&&(n.restoreOriginalState(),this._takeBackBinding(n))}this._takeBackAction(t)}}_initMemoryManager(){this._actions=[],this._nActiveActions=0,this._actionsByClip={},this._bindings=[],this._nActiveBindings=0,this._bindingsByRootAndName={},this._controlInterpolants=[],this._nActiveControlInterpolants=0;const t=this;this.stats={actions:{get total(){return t._actions.length},get inUse(){return t._nActiveActions}},bindings:{get total(){return t._bindings.length},get inUse(){return t._nActiveBindings}},controlInterpolants:{get total(){return t._controlInterpolants.length},get inUse(){return t._nActiveControlInterpolants}}}}_isActiveAction(t){const e=t._cacheIndex;return null!==e&&e=0;--e)t[e].stop();return this}update(t){t*=this.timeScale;const e=this._actions,n=this._nActiveActions,i=this.time+=t,r=Math.sign(t),s=this._accuIndex^=1;for(let a=0;a!==n;++a)e[a]._update(i,t,r,s);const a=this._bindings,o=this._nActiveBindings;for(let t=0;t!==o;++t)a[t].apply(s);return this}setTime(t){this.time=0;for(let t=0;tthis.max.x||t.ythis.max.y)}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y}getParameter(t,e){return void 0===e&&(console.warn("THREE.Box2: .getParameter() target is now required"),e=new Zn),e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y))}intersectsBox(t){return!(t.max.xthis.max.x||t.max.ythis.max.y)}clampPoint(t,e){return void 0===e&&(console.warn("THREE.Box2: .clampPoint() target is now required"),e=new Zn),e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return Hd.copy(t).clamp(this.min,this.max).sub(t).length()}intersect(t){return this.min.max(t.min),this.max.min(t.max),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}}Gd.prototype.isBox2=!0;const Ud=new ai,Vd=new ai;class kd{constructor(t=new ai,e=new ai){this.start=t,this.end=e}set(t,e){return this.start.copy(t),this.end.copy(e),this}copy(t){return this.start.copy(t.start),this.end.copy(t.end),this}getCenter(t){return void 0===t&&(console.warn("THREE.Line3: .getCenter() target is now required"),t=new ai),t.addVectors(this.start,this.end).multiplyScalar(.5)}delta(t){return void 0===t&&(console.warn("THREE.Line3: .delta() target is now required"),t=new ai),t.subVectors(this.end,this.start)}distanceSq(){return this.start.distanceToSquared(this.end)}distance(){return this.start.distanceTo(this.end)}at(t,e){return void 0===e&&(console.warn("THREE.Line3: .at() target is now required"),e=new ai),this.delta(e).multiplyScalar(t).add(this.start)}closestPointToPointParameter(t,e){Ud.subVectors(t,this.start),Vd.subVectors(this.end,this.start);const n=Vd.dot(Vd);let i=Vd.dot(Ud)/n;return e&&(i=Vn(i,0,1)),i}closestPointToPoint(t,e,n){const i=this.closestPointToPointParameter(t,e);return void 0===n&&(console.warn("THREE.Line3: .closestPointToPoint() target is now required"),n=new ai),this.delta(n).multiplyScalar(i).add(this.start)}applyMatrix4(t){return this.start.applyMatrix4(t),this.end.applyMatrix4(t),this}equals(t){return t.start.equals(this.start)&&t.end.equals(this.end)}clone(){return(new this.constructor).copy(this)}}class Wd extends lr{constructor(t){super(),this.material=t,this.render=function(){},this.hasPositions=!1,this.hasNormals=!1,this.hasColors=!1,this.hasUvs=!1,this.positionArray=null,this.normalArray=null,this.colorArray=null,this.uvArray=null,this.count=0}}Wd.prototype.isImmediateRenderObject=!0;const jd=new ai;class qd extends lr{constructor(t,e){super(),this.light=t,this.light.updateMatrixWorld(),this.matrix=t.matrixWorld,this.matrixAutoUpdate=!1,this.color=e;const n=new is,i=[0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,-1,0,1,0,0,0,0,1,1,0,0,0,0,-1,1];for(let t=0,e=1,n=32;t.99999)this.quaternion.set(0,0,0,1);else if(t.y<-.99999)this.quaternion.set(1,0,0,0);else{vp.set(t.z,0,-t.x).normalize();const e=Math.acos(t.y);this.quaternion.setFromAxisAngle(vp,e)}}setLength(t,e=.2*t,n=.2*e){this.line.scale.set(1,Math.max(1e-4,t-e),1),this.line.updateMatrix(),this.cone.scale.set(n,e,n),this.cone.position.y=t,this.cone.updateMatrix()}setColor(t){this.line.material.color.set(t),this.cone.material.color.set(t)}copy(t){return super.copy(t,!1),this.line.copy(t.line),this.cone.copy(t.cone),this}}class bp extends Jl{constructor(t=1){const e=[0,0,0,t,0,0,0,0,0,0,t,0,0,0,0,0,0,t],n=new is;n.setAttribute("position",new jr(e,3)),n.setAttribute("color",new jr([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1],3)),super(n,new Ul({vertexColors:!0,toneMapped:!1})),this.type="AxesHelper"}dispose(){this.geometry.dispose(),this.material.dispose()}}const wp=new Float32Array(1),Mp=new Int32Array(wp.buffer);class Sp{static toHalfFloat(t){wp[0]=t;const e=Mp[0];let n=e>>16&32768,i=e>>12&2047;const r=e>>23&255;return r<103?n:r>142?(n|=31744,n|=(255==r?0:1)&&8388607&e,n):r<113?(i|=2048,n|=(i>>114-r)+(i>>113-r&1),n):(n|=r-112<<10|i>>1,n+=1&i,n)}}const Tp=Math.pow(2,8),Ep=[.125,.215,.35,.446,.526,.582],Ap=5+Ep.length,Lp={[Je]:0,[Qe]:1,[$e]:2,[en]:3,[nn]:4,[rn]:5,[Ke]:6},Rp=new Ir({side:f,depthWrite:!1,depthTest:!1}),Cp=new bs(new Ms,Rp),Pp=new Lu,{_lodPlanes:Dp,_sizeLods:Ip,_sigmas:Np}=kp(),Op=new Dr;let Bp=null;const zp=(1+Math.sqrt(5))/2,Fp=1/zp,Hp=[new ai(1,1,1),new ai(-1,1,1),new ai(1,1,-1),new ai(-1,1,-1),new ai(0,zp,Fp),new ai(0,zp,-Fp),new ai(Fp,0,zp),new ai(-Fp,0,zp),new ai(zp,Fp,0),new ai(-zp,Fp,0)];function Gp(t){const e=Math.max(t.r,t.g,t.b),n=Math.min(Math.max(Math.ceil(Math.log2(e)),-128),127);return t.multiplyScalar(Math.pow(2,-n)),(n+128)/255}class Up{constructor(t){this._renderer=t,this._pingPongRenderTarget=null,this._blurMaterial=function(t){const e=new Float32Array(20),n=new ai(0,1,0);return new ch({name:"SphericalGaussianBlur",defines:{n:20},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:e},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:n},inputEncoding:{value:Lp[Je]},outputEncoding:{value:Lp[Je]}},vertexShader:"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t",fragmentShader:"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include \n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t",blending:x,depthTest:!1,depthWrite:!1})}(),this._equirectShader=null,this._cubemapShader=null,this._compileMaterial(this._blurMaterial)}fromScene(t,e=0,n=.1,i=100){Bp=this._renderer.getRenderTarget();const r=this._allocateTargets();return this._sceneToCubeUV(t,n,i,r),e>0&&this._blur(r,0,0,e),this._applyPMREM(r),this._cleanup(r),r}fromEquirectangular(t){return this._fromTexture(t)}fromCubemap(t){return this._fromTexture(t)}compileCubemapShader(){null===this._cubemapShader&&(this._cubemapShader=Xp(),this._compileMaterial(this._cubemapShader))}compileEquirectangularShader(){null===this._equirectShader&&(this._equirectShader=qp(),this._compileMaterial(this._equirectShader))}dispose(){this._blurMaterial.dispose(),null!==this._cubemapShader&&this._cubemapShader.dispose(),null!==this._equirectShader&&this._equirectShader.dispose();for(let t=0;t2?Tp:0,Tp,Tp),o.setRenderTarget(i),u&&o.render(Cp,r),o.render(t,r)}o.toneMapping=h,o.outputEncoding=c,o.autoClear=l}_textureToCubeUV(t,e){const n=this._renderer;t.isCubeTexture?null==this._cubemapShader&&(this._cubemapShader=Xp()):null==this._equirectShader&&(this._equirectShader=qp());const i=t.isCubeTexture?this._cubemapShader:this._equirectShader,r=new bs(Dp[0],i),s=i.uniforms;s.envMap.value=t,t.isCubeTexture||s.texelSize.value.set(1/t.image.width,1/t.image.height),s.inputEncoding.value=Lp[t.encoding],s.outputEncoding.value=Lp[e.texture.encoding],jp(e,0,0,3*Tp,2*Tp),n.setRenderTarget(e),n.render(r,Pp)}_applyPMREM(t){const e=this._renderer,n=e.autoClear;e.autoClear=!1;for(let e=1;e20&&console.warn(`sigmaRadians, ${r}, is too large and will clip, as it requested ${m} samples when the maximum is set to 20`);const f=[];let g=0;for(let t=0;t<20;++t){const e=t/p,n=Math.exp(-e*e/2);f.push(n),0==t?g+=n:t4?i-8+4:0),3*v,2*v),o.setRenderTarget(e),o.render(c,Pp)}}function Vp(t){return void 0!==t&&t.type===St&&(t.encoding===Je||t.encoding===Qe||t.encoding===Ke)}function kp(){const t=[],e=[],n=[];let i=8;for(let r=0;r4?a=Ep[r-8+4-1]:0==r&&(a=0),n.push(a);const o=1/(s-1),l=-o/2,c=1+o/2,h=[l,l,c,l,c,c,l,l,c,c,l,c],u=6,d=6,p=3,m=2,f=1,g=new Float32Array(p*d*u),v=new Float32Array(m*d*u),y=new Float32Array(f*d*u);for(let t=0;t2?0:-1,i=[e,n,0,e+2/3,n,0,e+2/3,n+1,0,e,n,0,e+2/3,n+1,0,e,n+1,0];g.set(i,p*d*t),v.set(h,m*d*t);const r=[t,t,t,t,t,t];y.set(r,f*d*t)}const x=new is;x.setAttribute("position",new Br(g,p)),x.setAttribute("uv",new Br(v,m)),x.setAttribute("faceIndex",new Br(y,f)),t.push(x),i>4&&i--}return{_lodPlanes:t,_sizeLods:e,_sigmas:n}}function Wp(t){const e=new ii(3*Tp,3*Tp,t);return e.texture.mapping=ct,e.texture.name="PMREM.cubeUv",e.scissorTest=!0,e}function jp(t,e,n,i,r){t.viewport.set(e,n,i,r),t.scissor.set(e,n,i,r)}function qp(){const t=new Zn(1,1);return new ch({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null},texelSize:{value:t},inputEncoding:{value:Lp[Je]},outputEncoding:{value:Lp[Je]}},vertexShader:"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t",fragmentShader:"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform vec2 texelSize;\n\n\t\t\t\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tvec2 f = fract( uv / texelSize - 0.5 );\n\t\t\t\tuv -= f * texelSize;\n\t\t\t\tvec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x += texelSize.x;\n\t\t\t\tvec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.y += texelSize.y;\n\t\t\t\tvec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x -= texelSize.x;\n\t\t\t\tvec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\n\t\t\t\tvec3 tm = mix( tl, tr, f.x );\n\t\t\t\tvec3 bm = mix( bl, br, f.x );\n\t\t\t\tgl_FragColor.rgb = mix( tm, bm, f.y );\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t",blending:x,depthTest:!1,depthWrite:!1})}function Xp(){return new ch({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},inputEncoding:{value:Lp[Je]},outputEncoding:{value:Lp[Je]}},vertexShader:"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t",fragmentShader:"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\t\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb;\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t",blending:x,depthTest:!1,depthWrite:!1})}const Yp=0,Zp=1,Jp=0,Qp=1,Kp=2;function $p(t){return console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."),t}function tm(t=[]){return console.warn("THREE.MultiMaterial has been removed. Use an Array instead."),t.isMultiMaterial=!0,t.materials=t,t.clone=function(){return t.slice()},t}function em(t,e){return console.warn("THREE.PointCloud has been renamed to THREE.Points."),new ic(t,e)}function nm(t){return console.warn("THREE.Particle has been renamed to THREE.Sprite."),new bl(t)}function im(t,e){return console.warn("THREE.ParticleSystem has been renamed to THREE.Points."),new ic(t,e)}function rm(t){return console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."),new Kl(t)}function sm(t){return console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."),new Kl(t)}function am(t){return console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."),new Kl(t)}function om(t,e,n){return console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."),new ai(t,e,n)}function lm(t,e){return console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."),new Br(t,e).setUsage(En)}function cm(t,e){return console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."),new zr(t,e)}function hm(t,e){return console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."),new Fr(t,e)}function um(t,e){return console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."),new Hr(t,e)}function dm(t,e){return console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."),new Gr(t,e)}function pm(t,e){return console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."),new Ur(t,e)}function mm(t,e){return console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."),new Vr(t,e)}function fm(t,e){return console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."),new kr(t,e)}function gm(t,e){return console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."),new jr(t,e)}function vm(t,e){return console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."),new qr(t,e)}function ym(t){return console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."),new bp(t)}function xm(t,e){return console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."),new mp(t,e)}function _m(t,e){return console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."),new Jl(new yc(t.geometry),new Ul({color:void 0!==e?e:16777215}))}function bm(t,e){return console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."),new Jl(new ah(t.geometry),new Ul({color:void 0!==e?e:16777215}))}function wm(t){return console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."),new Hh(t)}function Mm(t){return console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."),new Wh(t)}function Sm(t,e,n){return console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."),new Is(t,n)}function Tm(){console.error("THREE.CanvasRenderer has been removed")}function Em(){console.error("THREE.JSONLoader has been removed.")}qh.create=function(t,e){return console.log("THREE.Curve.create() has been deprecated"),t.prototype=Object.create(qh.prototype),t.prototype.constructor=t,t.prototype.getPoint=e,t},pu.prototype.fromPoints=function(t){return console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."),this.setFromPoints(t)},ip.prototype.setColors=function(){console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.")},Jd.prototype.update=function(){console.error("THREE.SkeletonHelper: update() no longer needs to be called.")},zh.prototype.extractUrlBase=function(t){return console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."),Bu.extractUrlBase(t)},zh.Handlers={add:function(){console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.")},get:function(){console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.")}},Gd.prototype.center=function(t){return console.warn("THREE.Box2: .center() has been renamed to .getCenter()."),this.getCenter(t)},Gd.prototype.empty=function(){return console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."),this.isEmpty()},Gd.prototype.isIntersectionBox=function(t){return console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."),this.intersectsBox(t)},Gd.prototype.size=function(t){return console.warn("THREE.Box2: .size() has been renamed to .getSize()."),this.getSize(t)},ci.prototype.center=function(t){return console.warn("THREE.Box3: .center() has been renamed to .getCenter()."),this.getCenter(t)},ci.prototype.empty=function(){return console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."),this.isEmpty()},ci.prototype.isIntersectionBox=function(t){return console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."),this.intersectsBox(t)},ci.prototype.isIntersectionSphere=function(t){return console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."),this.intersectsSphere(t)},ci.prototype.size=function(t){return console.warn("THREE.Box3: .size() has been renamed to .getSize()."),this.getSize(t)},Li.prototype.empty=function(){return console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."),this.isEmpty()},zs.prototype.setFromMatrix=function(t){return console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."),this.setFromProjectionMatrix(t)},kd.prototype.center=function(t){return console.warn("THREE.Line3: .center() has been renamed to .getCenter()."),this.getCenter(t)},Jn.prototype.flattenToArrayOffset=function(t,e){return console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."),this.toArray(t,e)},Jn.prototype.multiplyVector3=function(t){return console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."),t.applyMatrix3(this)},Jn.prototype.multiplyVector3Array=function(){console.error("THREE.Matrix3: .multiplyVector3Array() has been removed.")},Jn.prototype.applyToBufferAttribute=function(t){return console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."),t.applyMatrix3(this)},Jn.prototype.applyToVector3Array=function(){console.error("THREE.Matrix3: .applyToVector3Array() has been removed.")},Jn.prototype.getInverse=function(t){return console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."),this.copy(t).invert()},zi.prototype.extractPosition=function(t){return console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."),this.copyPosition(t)},zi.prototype.flattenToArrayOffset=function(t,e){return console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."),this.toArray(t,e)},zi.prototype.getPosition=function(){return console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."),(new ai).setFromMatrixColumn(this,3)},zi.prototype.setRotationFromQuaternion=function(t){return console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."),this.makeRotationFromQuaternion(t)},zi.prototype.multiplyToArray=function(){console.warn("THREE.Matrix4: .multiplyToArray() has been removed.")},zi.prototype.multiplyVector3=function(t){return console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},zi.prototype.multiplyVector4=function(t){return console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},zi.prototype.multiplyVector3Array=function(){console.error("THREE.Matrix4: .multiplyVector3Array() has been removed.")},zi.prototype.rotateAxis=function(t){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."),t.transformDirection(this)},zi.prototype.crossVector=function(t){return console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},zi.prototype.translate=function(){console.error("THREE.Matrix4: .translate() has been removed.")},zi.prototype.rotateX=function(){console.error("THREE.Matrix4: .rotateX() has been removed.")},zi.prototype.rotateY=function(){console.error("THREE.Matrix4: .rotateY() has been removed.")},zi.prototype.rotateZ=function(){console.error("THREE.Matrix4: .rotateZ() has been removed.")},zi.prototype.rotateByAxis=function(){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")},zi.prototype.applyToBufferAttribute=function(t){return console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},zi.prototype.applyToVector3Array=function(){console.error("THREE.Matrix4: .applyToVector3Array() has been removed.")},zi.prototype.makeFrustum=function(t,e,n,i,r,s){return console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."),this.makePerspective(t,e,i,n,r,s)},zi.prototype.getInverse=function(t){return console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."),this.copy(t).invert()},dr.prototype.isIntersectionLine=function(t){return console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."),this.intersectsLine(t)},si.prototype.multiplyVector3=function(t){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),t.applyQuaternion(this)},si.prototype.inverse=function(){return console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."),this.invert()},Bi.prototype.isIntersectionBox=function(t){return console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."),this.intersectsBox(t)},Bi.prototype.isIntersectionPlane=function(t){return console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."),this.intersectsPlane(t)},Bi.prototype.isIntersectionSphere=function(t){return console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."),this.intersectsSphere(t)},Mr.prototype.area=function(){return console.warn("THREE.Triangle: .area() has been renamed to .getArea()."),this.getArea()},Mr.prototype.barycoordFromPoint=function(t,e){return console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."),this.getBarycoord(t,e)},Mr.prototype.midpoint=function(t){return console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."),this.getMidpoint(t)},Mr.prototypenormal=function(t){return console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."),this.getNormal(t)},Mr.prototype.plane=function(t){return console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."),this.getPlane(t)},Mr.barycoordFromPoint=function(t,e,n,i,r){return console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."),Mr.getBarycoord(t,e,n,i,r)},Mr.normal=function(t,e,n,i){return console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."),Mr.getNormal(t,e,n,i)},mu.prototype.extractAllPoints=function(t){return console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."),this.extractPoints(t)},mu.prototype.extrude=function(t){return console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."),new qc(this,t)},mu.prototype.makeGeometry=function(t){return console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."),new $c(this,t)},Zn.prototype.fromAttribute=function(t,e,n){return console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."),this.fromBufferAttribute(t,e,n)},Zn.prototype.distanceToManhattan=function(t){return console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."),this.manhattanDistanceTo(t)},Zn.prototype.lengthManhattan=function(){return console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."),this.manhattanLength()},ai.prototype.setEulerFromRotationMatrix=function(){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},ai.prototype.setEulerFromQuaternion=function(){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},ai.prototype.getPositionFromMatrix=function(t){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(t)},ai.prototype.getScaleFromMatrix=function(t){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(t)},ai.prototype.getColumnFromMatrix=function(t,e){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(e,t)},ai.prototype.applyProjection=function(t){return console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."),this.applyMatrix4(t)},ai.prototype.fromAttribute=function(t,e,n){return console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."),this.fromBufferAttribute(t,e,n)},ai.prototype.distanceToManhattan=function(t){return console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."),this.manhattanDistanceTo(t)},ai.prototype.lengthManhattan=function(){return console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."),this.manhattanLength()},ni.prototype.fromAttribute=function(t,e,n){return console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."),this.fromBufferAttribute(t,e,n)},ni.prototype.lengthManhattan=function(){return console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."),this.manhattanLength()},lr.prototype.getChildByName=function(t){return console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."),this.getObjectByName(t)},lr.prototype.renderDepth=function(){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")},lr.prototype.translate=function(t,e){return console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."),this.translateOnAxis(e,t)},lr.prototype.getWorldRotation=function(){console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.")},lr.prototype.applyMatrix=function(t){return console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."),this.applyMatrix4(t)},Object.defineProperties(lr.prototype,{eulerOrder:{get:function(){return console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order},set:function(t){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order=t}},useQuaternion:{get:function(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set:function(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")}}}),bs.prototype.setDrawMode=function(){console.error("THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.")},Object.defineProperties(bs.prototype,{drawMode:{get:function(){return console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."),Xe},set:function(){console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.")}}}),Pl.prototype.initBones=function(){console.error("THREE.SkinnedMesh: initBones() has been removed.")},Rs.prototype.setLens=function(t,e){console.warn("THREE.PerspectiveCamera.setLens is deprecated. Use .setFocalLength and .filmGauge for a photographic setup."),void 0!==e&&(this.filmGauge=e),this.setFocalLength(t)},Object.defineProperties(fu.prototype,{onlyShadow:{set:function(){console.warn("THREE.Light: .onlyShadow has been removed.")}},shadowCameraFov:{set:function(t){console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."),this.shadow.camera.fov=t}},shadowCameraLeft:{set:function(t){console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."),this.shadow.camera.left=t}},shadowCameraRight:{set:function(t){console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."),this.shadow.camera.right=t}},shadowCameraTop:{set:function(t){console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."),this.shadow.camera.top=t}},shadowCameraBottom:{set:function(t){console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."),this.shadow.camera.bottom=t}},shadowCameraNear:{set:function(t){console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."),this.shadow.camera.near=t}},shadowCameraFar:{set:function(t){console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."),this.shadow.camera.far=t}},shadowCameraVisible:{set:function(){console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.")}},shadowBias:{set:function(t){console.warn("THREE.Light: .shadowBias is now .shadow.bias."),this.shadow.bias=t}},shadowDarkness:{set:function(){console.warn("THREE.Light: .shadowDarkness has been removed.")}},shadowMapWidth:{set:function(t){console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."),this.shadow.mapSize.width=t}},shadowMapHeight:{set:function(t){console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."),this.shadow.mapSize.height=t}}}),Object.defineProperties(Br.prototype,{length:{get:function(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."),this.array.length}},dynamic:{get:function(){return console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."),this.usage===En},set:function(){console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."),this.setUsage(En)}}}),Br.prototype.setDynamic=function(t){return console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."),this.setUsage(!0===t?En:Tn),this},Br.prototype.copyIndicesArray=function(){console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed.")},Br.prototype.setArray=function(){console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers")},is.prototype.addIndex=function(t){console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."),this.setIndex(t)},is.prototype.addAttribute=function(t,e){return console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."),e&&e.isBufferAttribute||e&&e.isInterleavedBufferAttribute?"index"===t?(console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."),this.setIndex(e),this):this.setAttribute(t,e):(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),this.setAttribute(t,new Br(arguments[1],arguments[2])))},is.prototype.addDrawCall=function(t,e,n){void 0!==n&&console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."),console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."),this.addGroup(t,e)},is.prototype.clearDrawCalls=function(){console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."),this.clearGroups()},is.prototype.computeOffsets=function(){console.warn("THREE.BufferGeometry: .computeOffsets() has been removed.")},is.prototype.removeAttribute=function(t){return console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."),this.deleteAttribute(t)},is.prototype.applyMatrix=function(t){return console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."),this.applyMatrix4(t)},Object.defineProperties(is.prototype,{drawcalls:{get:function(){return console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."),this.groups}},offsets:{get:function(){return console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."),this.groups}}}),rl.prototype.setDynamic=function(t){return console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."),this.setUsage(!0===t?En:Tn),this},rl.prototype.setArray=function(){console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers")},qc.prototype.getArrays=function(){console.error("THREE.ExtrudeGeometry: .getArrays() has been removed.")},qc.prototype.addShapeList=function(){console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed.")},qc.prototype.addShape=function(){console.error("THREE.ExtrudeGeometry: .addShape() has been removed.")},il.prototype.dispose=function(){console.error("THREE.Scene: .dispose() has been removed.")},Pd.prototype.onUpdate=function(){return console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."),this},Object.defineProperties(Tr.prototype,{wrapAround:{get:function(){console.warn("THREE.Material: .wrapAround has been removed.")},set:function(){console.warn("THREE.Material: .wrapAround has been removed.")}},overdraw:{get:function(){console.warn("THREE.Material: .overdraw has been removed.")},set:function(){console.warn("THREE.Material: .overdraw has been removed.")}},wrapRGB:{get:function(){return console.warn("THREE.Material: .wrapRGB has been removed."),new Dr}},shading:{get:function(){console.error("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead.")},set:function(t){console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead."),this.flatShading=t===v}},stencilMask:{get:function(){return console.warn("THREE."+this.type+": .stencilMask has been removed. Use .stencilFuncMask instead."),this.stencilFuncMask},set:function(t){console.warn("THREE."+this.type+": .stencilMask has been removed. Use .stencilFuncMask instead."),this.stencilFuncMask=t}}}),Object.defineProperties(As.prototype,{derivatives:{get:function(){return console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."),this.extensions.derivatives},set:function(t){console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."),this.extensions.derivatives=t}}}),$o.prototype.clearTarget=function(t,e,n,i){console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."),this.setRenderTarget(t),this.clear(e,n,i)},$o.prototype.animate=function(t){console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."),this.setAnimationLoop(t)},$o.prototype.getCurrentRenderTarget=function(){return console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."),this.getRenderTarget()},$o.prototype.getMaxAnisotropy=function(){return console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."),this.capabilities.getMaxAnisotropy()},$o.prototype.getPrecision=function(){return console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."),this.capabilities.precision},$o.prototype.resetGLState=function(){return console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."),this.state.reset()},$o.prototype.supportsFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' )."),this.extensions.get("OES_texture_float")},$o.prototype.supportsHalfFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' )."),this.extensions.get("OES_texture_half_float")},$o.prototype.supportsStandardDerivatives=function(){return console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' )."),this.extensions.get("OES_standard_derivatives")},$o.prototype.supportsCompressedTextureS3TC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' )."),this.extensions.get("WEBGL_compressed_texture_s3tc")},$o.prototype.supportsCompressedTexturePVRTC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' )."),this.extensions.get("WEBGL_compressed_texture_pvrtc")},$o.prototype.supportsBlendMinMax=function(){return console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' )."),this.extensions.get("EXT_blend_minmax")},$o.prototype.supportsVertexTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."),this.capabilities.vertexTextures},$o.prototype.supportsInstancedArrays=function(){return console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' )."),this.extensions.get("ANGLE_instanced_arrays")},$o.prototype.enableScissorTest=function(t){console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."),this.setScissorTest(t)},$o.prototype.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")},$o.prototype.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")},$o.prototype.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")},$o.prototype.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")},$o.prototype.setFaceCulling=function(){console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed.")},$o.prototype.allocTextureUnit=function(){console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed.")},$o.prototype.setTexture=function(){console.warn("THREE.WebGLRenderer: .setTexture() has been removed.")},$o.prototype.setTexture2D=function(){console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed.")},$o.prototype.setTextureCube=function(){console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed.")},$o.prototype.getActiveMipMapLevel=function(){return console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."),this.getActiveMipmapLevel()},Object.defineProperties($o.prototype,{shadowMapEnabled:{get:function(){return this.shadowMap.enabled},set:function(t){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."),this.shadowMap.enabled=t}},shadowMapType:{get:function(){return this.shadowMap.type},set:function(t){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."),this.shadowMap.type=t}},shadowMapCullFace:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.")}},context:{get:function(){return console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."),this.getContext()}},vr:{get:function(){return console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"),this.xr}},gammaInput:{get:function(){return console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."),!1},set:function(){console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.")}},gammaOutput:{get:function(){return console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."),!1},set:function(t){console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."),this.outputEncoding=!0===t?Qe:Je}},toneMappingWhitePoint:{get:function(){return console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."),1},set:function(){console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.")}}}),Object.defineProperties(ko.prototype,{cullFace:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.")}},renderReverseSided:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.")}},renderSingleSided:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.")}}}),Object.defineProperties(ii.prototype,{wrapS:{get:function(){return console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS},set:function(t){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS=t}},wrapT:{get:function(){return console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT},set:function(t){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT=t}},magFilter:{get:function(){return console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter},set:function(t){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter=t}},minFilter:{get:function(){return console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter},set:function(t){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter=t}},anisotropy:{get:function(){return console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy},set:function(t){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy=t}},offset:{get:function(){return console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset},set:function(t){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset=t}},repeat:{get:function(){return console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat},set:function(t){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat=t}},format:{get:function(){return console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format},set:function(t){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format=t}},type:{get:function(){return console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type},set:function(t){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type=t}},generateMipmaps:{get:function(){return console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps},set:function(t){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps=t}}}),hd.prototype.load=function(t){console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.");const e=this;return(new Qu).load(t,(function(t){e.setBuffer(t)})),this},gd.prototype.getData=function(){return console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."),this.getFrequencyData()},Ps.prototype.updateCubeMap=function(t,e){return console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."),this.update(t,e)},Ps.prototype.clear=function(t,e,n,i){return console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."),this.renderTarget.clear(t,e,n,i)},Kn.crossOrigin=void 0,Kn.loadTexture=function(t,e,n,i){console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.");const r=new jh;r.setCrossOrigin(this.crossOrigin);const s=r.load(t,n,void 0,i);return e&&(s.mapping=e),s},Kn.loadTextureCube=function(t,e,n,i){console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.");const r=new kh;r.setCrossOrigin(this.crossOrigin);const s=r.load(t,n,void 0,i);return e&&(s.mapping=e),s},Kn.loadCompressedTexture=function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},Kn.loadCompressedTextureCube=function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")};const Am={createMultiMaterialObject:function(){console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js")},detach:function(){console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js")},attach:function(){console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js")}};function Lm(){console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js")}"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:i}})),"undefined"!=typeof window&&(window.__THREE__?console.warn("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=i)},886:(t,e,n)=>{n.r(e),n.d(e,{OrbitControls:()=>o,MapControls:()=>l});var i=n(212);const r={type:"change"},s={type:"start"},a={type:"end"};class o extends i.EventDispatcher{constructor(t,e){super(),void 0===e&&console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.'),e===document&&console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.'),this.object=t,this.domElement=e,this.enabled=!0,this.target=new i.Vector3,this.minDistance=0,this.maxDistance=1/0,this.minZoom=0,this.maxZoom=1/0,this.minPolarAngle=0,this.maxPolarAngle=Math.PI,this.minAzimuthAngle=-1/0,this.maxAzimuthAngle=1/0,this.enableDamping=!1,this.dampingFactor=.05,this.enableZoom=!0,this.zoomSpeed=1,this.enableRotate=!0,this.rotateSpeed=1,this.enablePan=!0,this.panSpeed=1,this.screenSpacePanning=!0,this.keyPanSpeed=7,this.autoRotate=!1,this.autoRotateSpeed=2,this.keys={LEFT:"ArrowLeft",UP:"ArrowUp",RIGHT:"ArrowRight",BOTTOM:"ArrowDown"},this.mouseButtons={LEFT:i.MOUSE.ROTATE,MIDDLE:i.MOUSE.DOLLY,RIGHT:i.MOUSE.PAN},this.touches={ONE:i.TOUCH.ROTATE,TWO:i.TOUCH.DOLLY_PAN},this.target0=this.target.clone(),this.position0=this.object.position.clone(),this.zoom0=this.object.zoom,this._domElementKeyEvents=null,this.getPolarAngle=function(){return h.phi},this.getAzimuthalAngle=function(){return h.theta},this.listenToKeyEvents=function(t){t.addEventListener("keydown",W),this._domElementKeyEvents=t},this.saveState=function(){n.target0.copy(n.target),n.position0.copy(n.object.position),n.zoom0=n.object.zoom},this.reset=function(){n.target.copy(n.target0),n.object.position.copy(n.position0),n.object.zoom=n.zoom0,n.object.updateProjectionMatrix(),n.dispatchEvent(r),n.update(),l=o.NONE},this.update=function(){const e=new i.Vector3,s=(new i.Quaternion).setFromUnitVectors(t.up,new i.Vector3(0,1,0)),a=s.clone().invert(),f=new i.Vector3,g=new i.Quaternion,v=2*Math.PI;return function(){const t=n.object.position;e.copy(t).sub(n.target),e.applyQuaternion(s),h.setFromVector3(e),n.autoRotate&&l===o.NONE&&T(2*Math.PI/60/60*n.autoRotateSpeed),n.enableDamping?(h.theta+=u.theta*n.dampingFactor,h.phi+=u.phi*n.dampingFactor):(h.theta+=u.theta,h.phi+=u.phi);let i=n.minAzimuthAngle,y=n.maxAzimuthAngle;return isFinite(i)&&isFinite(y)&&(i<-Math.PI?i+=v:i>Math.PI&&(i-=v),y<-Math.PI?y+=v:y>Math.PI&&(y-=v),h.theta=i<=y?Math.max(i,Math.min(y,h.theta)):h.theta>(i+y)/2?Math.max(i,h.theta):Math.min(y,h.theta)),h.phi=Math.max(n.minPolarAngle,Math.min(n.maxPolarAngle,h.phi)),h.makeSafe(),h.radius*=d,h.radius=Math.max(n.minDistance,Math.min(n.maxDistance,h.radius)),!0===n.enableDamping?n.target.addScaledVector(p,n.dampingFactor):n.target.add(p),e.setFromSpherical(h),e.applyQuaternion(a),t.copy(n.target).add(e),n.object.lookAt(n.target),!0===n.enableDamping?(u.theta*=1-n.dampingFactor,u.phi*=1-n.dampingFactor,p.multiplyScalar(1-n.dampingFactor)):(u.set(0,0,0),p.set(0,0,0)),d=1,!!(m||f.distanceToSquared(n.object.position)>c||8*(1-g.dot(n.object.quaternion))>c)&&(n.dispatchEvent(r),f.copy(n.object.position),g.copy(n.object.quaternion),m=!1,!0)}}(),this.dispose=function(){n.domElement.removeEventListener("contextmenu",Y),n.domElement.removeEventListener("pointerdown",G),n.domElement.removeEventListener("wheel",k),n.domElement.removeEventListener("touchstart",j),n.domElement.removeEventListener("touchend",X),n.domElement.removeEventListener("touchmove",q),n.domElement.ownerDocument.removeEventListener("pointermove",U),n.domElement.ownerDocument.removeEventListener("pointerup",V),null!==n._domElementKeyEvents&&n._domElementKeyEvents.removeEventListener("keydown",W)};const n=this,o={NONE:-1,ROTATE:0,DOLLY:1,PAN:2,TOUCH_ROTATE:3,TOUCH_PAN:4,TOUCH_DOLLY_PAN:5,TOUCH_DOLLY_ROTATE:6};let l=o.NONE;const c=1e-6,h=new i.Spherical,u=new i.Spherical;let d=1;const p=new i.Vector3;let m=!1;const f=new i.Vector2,g=new i.Vector2,v=new i.Vector2,y=new i.Vector2,x=new i.Vector2,_=new i.Vector2,b=new i.Vector2,w=new i.Vector2,M=new i.Vector2;function S(){return Math.pow(.95,n.zoomSpeed)}function T(t){u.theta-=t}function E(t){u.phi-=t}const A=function(){const t=new i.Vector3;return function(e,n){t.setFromMatrixColumn(n,0),t.multiplyScalar(-e),p.add(t)}}(),L=function(){const t=new i.Vector3;return function(e,i){!0===n.screenSpacePanning?t.setFromMatrixColumn(i,1):(t.setFromMatrixColumn(i,0),t.crossVectors(n.object.up,t)),t.multiplyScalar(e),p.add(t)}}(),R=function(){const t=new i.Vector3;return function(e,i){const r=n.domElement;if(n.object.isPerspectiveCamera){const s=n.object.position;t.copy(s).sub(n.target);let a=t.length();a*=Math.tan(n.object.fov/2*Math.PI/180),A(2*e*a/r.clientHeight,n.object.matrix),L(2*i*a/r.clientHeight,n.object.matrix)}else n.object.isOrthographicCamera?(A(e*(n.object.right-n.object.left)/n.object.zoom/r.clientWidth,n.object.matrix),L(i*(n.object.top-n.object.bottom)/n.object.zoom/r.clientHeight,n.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),n.enablePan=!1)}}();function C(t){n.object.isPerspectiveCamera?d/=t:n.object.isOrthographicCamera?(n.object.zoom=Math.max(n.minZoom,Math.min(n.maxZoom,n.object.zoom*t)),n.object.updateProjectionMatrix(),m=!0):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),n.enableZoom=!1)}function P(t){n.object.isPerspectiveCamera?d*=t:n.object.isOrthographicCamera?(n.object.zoom=Math.max(n.minZoom,Math.min(n.maxZoom,n.object.zoom/t)),n.object.updateProjectionMatrix(),m=!0):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),n.enableZoom=!1)}function D(t){f.set(t.clientX,t.clientY)}function I(t){y.set(t.clientX,t.clientY)}function N(t){if(1==t.touches.length)f.set(t.touches[0].pageX,t.touches[0].pageY);else{const e=.5*(t.touches[0].pageX+t.touches[1].pageX),n=.5*(t.touches[0].pageY+t.touches[1].pageY);f.set(e,n)}}function O(t){if(1==t.touches.length)y.set(t.touches[0].pageX,t.touches[0].pageY);else{const e=.5*(t.touches[0].pageX+t.touches[1].pageX),n=.5*(t.touches[0].pageY+t.touches[1].pageY);y.set(e,n)}}function B(t){const e=t.touches[0].pageX-t.touches[1].pageX,n=t.touches[0].pageY-t.touches[1].pageY,i=Math.sqrt(e*e+n*n);b.set(0,i)}function z(t){if(1==t.touches.length)g.set(t.touches[0].pageX,t.touches[0].pageY);else{const e=.5*(t.touches[0].pageX+t.touches[1].pageX),n=.5*(t.touches[0].pageY+t.touches[1].pageY);g.set(e,n)}v.subVectors(g,f).multiplyScalar(n.rotateSpeed);const e=n.domElement;T(2*Math.PI*v.x/e.clientHeight),E(2*Math.PI*v.y/e.clientHeight),f.copy(g)}function F(t){if(1==t.touches.length)x.set(t.touches[0].pageX,t.touches[0].pageY);else{const e=.5*(t.touches[0].pageX+t.touches[1].pageX),n=.5*(t.touches[0].pageY+t.touches[1].pageY);x.set(e,n)}_.subVectors(x,y).multiplyScalar(n.panSpeed),R(_.x,_.y),y.copy(x)}function H(t){const e=t.touches[0].pageX-t.touches[1].pageX,i=t.touches[0].pageY-t.touches[1].pageY,r=Math.sqrt(e*e+i*i);w.set(0,r),M.set(0,Math.pow(w.y/b.y,n.zoomSpeed)),C(M.y),b.copy(w)}function G(t){if(!1!==n.enabled)switch(t.pointerType){case"mouse":case"pen":!function(t){let e;switch(t.preventDefault(),n.domElement.focus?n.domElement.focus():window.focus(),t.button){case 0:e=n.mouseButtons.LEFT;break;case 1:e=n.mouseButtons.MIDDLE;break;case 2:e=n.mouseButtons.RIGHT;break;default:e=-1}switch(e){case i.MOUSE.DOLLY:if(!1===n.enableZoom)return;!function(t){b.set(t.clientX,t.clientY)}(t),l=o.DOLLY;break;case i.MOUSE.ROTATE:if(t.ctrlKey||t.metaKey||t.shiftKey){if(!1===n.enablePan)return;I(t),l=o.PAN}else{if(!1===n.enableRotate)return;D(t),l=o.ROTATE}break;case i.MOUSE.PAN:if(t.ctrlKey||t.metaKey||t.shiftKey){if(!1===n.enableRotate)return;D(t),l=o.ROTATE}else{if(!1===n.enablePan)return;I(t),l=o.PAN}break;default:l=o.NONE}l!==o.NONE&&(n.domElement.ownerDocument.addEventListener("pointermove",U),n.domElement.ownerDocument.addEventListener("pointerup",V),n.dispatchEvent(s))}(t)}}function U(t){if(!1!==n.enabled)switch(t.pointerType){case"mouse":case"pen":!function(t){if(!1!==n.enabled)switch(t.preventDefault(),l){case o.ROTATE:if(!1===n.enableRotate)return;!function(t){g.set(t.clientX,t.clientY),v.subVectors(g,f).multiplyScalar(n.rotateSpeed);const e=n.domElement;T(2*Math.PI*v.x/e.clientHeight),E(2*Math.PI*v.y/e.clientHeight),f.copy(g),n.update()}(t);break;case o.DOLLY:if(!1===n.enableZoom)return;!function(t){w.set(t.clientX,t.clientY),M.subVectors(w,b),M.y>0?C(S()):M.y<0&&P(S()),b.copy(w),n.update()}(t);break;case o.PAN:if(!1===n.enablePan)return;!function(t){x.set(t.clientX,t.clientY),_.subVectors(x,y).multiplyScalar(n.panSpeed),R(_.x,_.y),y.copy(x),n.update()}(t)}}(t)}}function V(t){switch(t.pointerType){case"mouse":case"pen":n.domElement.ownerDocument.removeEventListener("pointermove",U),n.domElement.ownerDocument.removeEventListener("pointerup",V),!1!==n.enabled&&(n.dispatchEvent(a),l=o.NONE)}}function k(t){!1===n.enabled||!1===n.enableZoom||l!==o.NONE&&l!==o.ROTATE||(t.preventDefault(),n.dispatchEvent(s),function(t){t.deltaY<0?P(S()):t.deltaY>0&&C(S()),n.update()}(t),n.dispatchEvent(a))}function W(t){!1!==n.enabled&&!1!==n.enablePan&&function(t){let e=!1;switch(t.code){case n.keys.UP:R(0,n.keyPanSpeed),e=!0;break;case n.keys.BOTTOM:R(0,-n.keyPanSpeed),e=!0;break;case n.keys.LEFT:R(n.keyPanSpeed,0),e=!0;break;case n.keys.RIGHT:R(-n.keyPanSpeed,0),e=!0}e&&(t.preventDefault(),n.update())}(t)}function j(t){if(!1!==n.enabled){switch(t.preventDefault(),t.touches.length){case 1:switch(n.touches.ONE){case i.TOUCH.ROTATE:if(!1===n.enableRotate)return;N(t),l=o.TOUCH_ROTATE;break;case i.TOUCH.PAN:if(!1===n.enablePan)return;O(t),l=o.TOUCH_PAN;break;default:l=o.NONE}break;case 2:switch(n.touches.TWO){case i.TOUCH.DOLLY_PAN:if(!1===n.enableZoom&&!1===n.enablePan)return;!function(t){n.enableZoom&&B(t),n.enablePan&&O(t)}(t),l=o.TOUCH_DOLLY_PAN;break;case i.TOUCH.DOLLY_ROTATE:if(!1===n.enableZoom&&!1===n.enableRotate)return;!function(t){n.enableZoom&&B(t),n.enableRotate&&N(t)}(t),l=o.TOUCH_DOLLY_ROTATE;break;default:l=o.NONE}break;default:l=o.NONE}l!==o.NONE&&n.dispatchEvent(s)}}function q(t){if(!1!==n.enabled)switch(t.preventDefault(),l){case o.TOUCH_ROTATE:if(!1===n.enableRotate)return;z(t),n.update();break;case o.TOUCH_PAN:if(!1===n.enablePan)return;F(t),n.update();break;case o.TOUCH_DOLLY_PAN:if(!1===n.enableZoom&&!1===n.enablePan)return;!function(t){n.enableZoom&&H(t),n.enablePan&&F(t)}(t),n.update();break;case o.TOUCH_DOLLY_ROTATE:if(!1===n.enableZoom&&!1===n.enableRotate)return;!function(t){n.enableZoom&&H(t),n.enableRotate&&z(t)}(t),n.update();break;default:l=o.NONE}}function X(t){!1!==n.enabled&&(n.dispatchEvent(a),l=o.NONE)}function Y(t){!1!==n.enabled&&t.preventDefault()}n.domElement.addEventListener("contextmenu",Y),n.domElement.addEventListener("pointerdown",G),n.domElement.addEventListener("wheel",k,{passive:!1}),n.domElement.addEventListener("touchstart",j,{passive:!1}),n.domElement.addEventListener("touchend",X),n.domElement.addEventListener("touchmove",q,{passive:!1}),this.update()}}class l extends o{constructor(t,e){super(t,e),this.screenSpacePanning=!1,this.mouseButtons.LEFT=i.MOUSE.PAN,this.mouseButtons.RIGHT=i.MOUSE.ROTATE,this.touches.ONE=i.TOUCH.PAN,this.touches.TWO=i.TOUCH.DOLLY_ROTATE}}},908:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.BlochSphere=void 0;const i=n(78),r=n(429),s=n(510),a=n(649),o=n(692),l=n(69),c=n(212);class h extends c.Group{constructor(t=5,e=7,n=4){return super(),this.radius=t,this.hMeridians=e,this.vMeridians=n,this.addSphere(),this.addHorizontalMeridians(),this.addVerticalMeridians(),this.addAxes(),this.addLabels(),this}addVector(t,e,n){const i=new l.StateVector(t,e,n,this.radius);this.add(i)}addSphere(){const t=new r.Sphere(this.radius);this.add(t)}addAxes(){const t=new s.Axes(this.radius);this.add(t)}addHorizontalMeridians(){const t=new a.Meridians(this.radius,this.hMeridians,i.Orientation.HORIZONTAL);this.add(t)}addVerticalMeridians(){const t=new a.Meridians(this.radius,this.vMeridians,i.Orientation.VERTICAL);this.add(t)}addLabels(){const t=.5,e={"|+⟩":new c.Vector3(this.radius+t,0,0),"|-⟩":new c.Vector3(-this.radius-t,0,0),"|i⟩":new c.Vector3(0,0,-this.radius-t),"|-i⟩":new c.Vector3(0,0,this.radius+t),"|0⟩":new c.Vector3(0,this.radius+t,0),"|1⟩":new c.Vector3(0,-this.radius-t,0)},n=new o.Labels(e);this.add(n)}}e.BlochSphere=h},510:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Axes=void 0;const i=n(212);class r extends i.Group{constructor(t){return super(),this.xAxisColor="#1f51ff",this.yAxisColor="#ff3131",this.zAxisColor="#39ff14",this.halfLength=t,this.generateAxes(),this}generateAxes(){const t=[new i.Vector3(-this.halfLength,0,0),new i.Vector3(this.halfLength,0,0)],e=[new i.Vector3(0,0,-this.halfLength),new i.Vector3(0,0,this.halfLength)],n=[new i.Vector3(0,-this.halfLength,0),new i.Vector3(0,this.halfLength,0)],r={x:this.asLine({points:t,hexColor:this.xAxisColor,lineWidth:1.5}),y:this.asLine({points:e,hexColor:this.yAxisColor,lineWidth:1.5}),z:this.asLine({points:n,hexColor:this.zAxisColor,lineWidth:1.5})};this.add(r.x),this.add(r.y),this.add(r.z)}asLine(t){return new i.Line((new i.BufferGeometry).setFromPoints(t.points),new i.LineDashedMaterial({color:t.hexColor,linewidth:t.lineWidth,scale:1,dashSize:.1,gapSize:.1})).computeLineDistances()}}e.Axes=r},78:(t,e)=>{var n;Object.defineProperty(e,"__esModule",{value:!0}),e.Orientation=void 0,(n=e.Orientation||(e.Orientation={}))[n.HORIZONTAL=0]="HORIZONTAL",n[n.VERTICAL=1]="VERTICAL"},649:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Meridians=void 0;const i=n(212),r=n(78);class s extends i.Group{constructor(t,e,n){switch(super(),this.color="gray",this.radius=t,this.orientation=n,n){case r.Orientation.HORIZONTAL:return this.numCircles=this.sanitizeCircleInput(e),this.createHorizontalChordMeridians(this.radius,this.numCircles),this;case r.Orientation.VERTICAL:return this.numCircles=this.sanitizeCircleInput(e),this.createVerticalMeridians(this.radius,this.numCircles),this;default:throw new Error("Invalid orientation input in Meridians constructor")}}createHorizontalChordMeridians(t,e){if(0===e)return;let n;n=e%2!=0?e-1:e;const i=n/2;let s;s=1===e?0:t-.5*t/5;const a=[0];for(let t=s;t>0;t-=s/i)a.push(t),a.push(-t);for(const e of a){const n=Math.pow(t,2),i=Math.pow(e,2),s=Math.sqrt(n-i),a=this.curveDataWithRadius(s),o=this.createMeridianCurve(a),l=this.createMeridianLine(o,Math.PI/2,r.Orientation.HORIZONTAL,e);this.add(l)}}createVerticalMeridians(t,e){if(0===e)return;const n={anchorX:0,anchorY:0,radius:t,startAngle:0,endAngle:2*Math.PI,isClockwise:!1,rotation:0};for(let t=0;t300)throw new Error("Over 300 meridians are not supported");return Math.floor(t)}}e.Meridians=s},643:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.BlochSphereScene=void 0;const i=n(212),r=n(886);class s extends i.Scene{constructor(t=75,e=s.VIZ_HEIGHT/s.VIZ_WIDTH,n=.1,a=1e3){return super(),this.camera=new i.PerspectiveCamera(t,e,n,a),this.renderer=new i.WebGLRenderer({alpha:!0}),this.renderer.setSize(s.VIZ_WIDTH,s.VIZ_HEIGHT),this.controls=new r.OrbitControls(this.camera,this.renderer.domElement),this.init(),this}addSceneToHTMLContainer(t){document.getElementById(t).appendChild(this.renderer.domElement)}init(){this.camera.position.x=6,this.camera.position.y=2,this.camera.position.z=2,this.setUpControls(),this.setRenderSize(s.VIZ_WIDTH,s.VIZ_HEIGHT),this.animate()}setUpControls(){this.controls.enableDamping=!0,this.controls.dampingFactor=.05,this.controls.screenSpacePanning=!1,this.controls.minDistance=10,this.controls.maxDistance=50,this.controls.maxPolarAngle=Math.PI}setRenderSize(t,e){this.renderer.setSize(t,e)}animate(){requestAnimationFrame(this.animate.bind(this)),this.controls.update(),this.renderer.render(this,this.camera)}}e.BlochSphereScene=s,s.VIZ_WIDTH=500,s.VIZ_HEIGHT=500},429:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Sphere=void 0;const i=n(212);class r extends i.Group{constructor(t){if(super(),t<1)throw new Error("The radius of a Sphere must be greater than or equal to 1");return this.radius=t,this.createSphere(this.radius),this}createSphere(t){const e=new i.SphereGeometry(t,32,32),n=new i.MeshNormalMaterial({opacity:.6,transparent:!0}),r=new i.Mesh(e,n);r.geometry.computeVertexNormals(),this.add(r)}}e.Sphere=r},69:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.StateVector=void 0;const i=n(212);class r extends i.Group{constructor(t,e,n,i){return super(),this.x=t,this.y=e,this.z=n,this.blochSphereRadius=i,this.generateVector(this.x,this.y,this.z,this.blochSphereRadius),this}generateVector(t,e,n,r){const s=new i.Vector3(t,e,n),a=new i.Vector3(1,0,0),o=-Math.PI/2;s.applyAxisAngle(a,o);const l=new i.Vector3(0,0,0),c=l.distanceTo(s)*r,h=new i.ArrowHelper(s,l,c,"#800080",void 0,1);h.line.material.linewidth=20,this.add(h)}}e.StateVector=r},692:(t,e,n)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Label=e.Labels=void 0;const i=n(212);class r extends i.Group{constructor(t){return super(),this.labels=t,this.generateLabels(this.labels),this}generateLabels(t){for(const[e,n]of Object.entries(t))this.add(new s(e,n))}}e.Labels=r;class s extends i.Sprite{constructor(t,e){return super(function(t){const e=256,n=document.createElement("canvas");n.width=e,n.height=e,n.textContent=t;const r=n.getContext("2d");r.fillStyle="#000000",r.textAlign="center",r.font="120px Arial",r.fillText(t,128,128);const s=new i.Texture(n);return s.needsUpdate=!0,new i.SpriteMaterial({map:s,transparent:!0})}(t)),this.text=t,this.position.copy(e),this}}e.Label=s}},e={};function n(i){var r=e[i];if(void 0!==r)return r.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,n),s.exports}n.d=(t,e)=>{for(var i in e)n.o(e,i)&&!n.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var i={};(()=>{var t=i;Object.defineProperty(t,"__esModule",{value:!0}),t.renderBlochSphere=void 0;const e=n(643),r=n(908);t.renderBlochSphere=function(t,n=5,i=7,s=4){const a=new e.BlochSphereScene;a.addSceneToHTMLContainer(t);const o=new r.BlochSphere(n,i,s);return a.add(o),o}})();var r=self;for(var s in i)r[s]=i[s];i.__esModule&&Object.defineProperty(r,"__esModule",{value:!0})})(); \ No newline at end of file diff --git a/cirq-web/cirq_ts/dist/bloch_sphere.bundle.js.LICENSE.txt b/cirq-web/cirq_ts/dist/bloch_sphere.bundle.js.LICENSE.txt new file mode 100644 index 00000000000..d8e0f3fb7d8 --- /dev/null +++ b/cirq-web/cirq_ts/dist/bloch_sphere.bundle.js.LICENSE.txt @@ -0,0 +1,5 @@ +/** + * @license + * Copyright 2010-2021 Three.js Authors + * SPDX-License-Identifier: MIT + */ diff --git a/cirq-web/cirq_ts/dist/index.html b/cirq-web/cirq_ts/dist/index.html new file mode 100644 index 00000000000..5970f220e0f --- /dev/null +++ b/cirq-web/cirq_ts/dist/index.html @@ -0,0 +1,14 @@ + + + + Cirq Web Development page + + +
+ + + + diff --git a/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_e2e_test.ts b/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_e2e_test.ts new file mode 100644 index 00000000000..602d9e4b99c --- /dev/null +++ b/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_e2e_test.ts @@ -0,0 +1,127 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import puppeteer from 'puppeteer'; +import {expect} from 'chai'; +import {readFileSync} from 'fs'; +import pixelmatch from 'pixelmatch'; +import * as PNG from 'pngjs'; +import * as temp from 'temp'; +import * as path from 'path'; + +/** + * Generates an HTML script with the current repository bundle + * that we will use to compare. + */ + +// Due to the path, reading the file will only work by running this file in the same directory +// as the package.json file. +const bundleString = readFileSync('dist/bloch_sphere.bundle.js'); +function htmlContent(clientCode: string) { + return ` + + + + + Cirq Web Development page + + +
+ + + + + `; +} + +/** + * Testing to see if they look the same. + */ + +// Automatically track and cleanup files on exit +temp.track(); + +describe('Bloch sphere', () => { + // Create the temporary directory first, then run everything. + temp.mkdir('tmp', (err, dirPath) => { + const outputPath = path.join(dirPath, 'bloch_sphere.png'); + const newVectorOutputPath = path.join(dirPath, 'bloch_sphere_vec.png'); + + before(async () => { + // Opens a headless browser with the generated HTML file and takes a screenshot. + // The '--app' flag ensures that chromium does capture any + // excess browser input + const browser = await puppeteer.launch({args: ['--app']}); + const page = await browser.newPage(); + + // Take a screenshot of the first image + await page.setContent(htmlContent("renderBlochSphere('container')")); + await page.screenshot({path: outputPath}); + await browser.close(); + }); + + it('with no vector matches the gold PNG', () => { + const expected = PNG.PNG.sync.read( + readFileSync('e2e/bloch_sphere/bloch_sphere_expected.png') + ); + const actual = PNG.PNG.sync.read(readFileSync(outputPath)); + const {width, height} = expected; + const diff = new PNG.PNG({width, height}); + + const pixels = pixelmatch( + expected.data, + actual.data, + diff.data, + width, + height, + {threshold: 0.1} + ); + + expect(pixels).to.equal(0); + }); + + before(async () => { + //Opens a headless browser with the generated HTML file and takes a screenshot. + const browser = await puppeteer.launch({args: ['--app']}); + const page = await browser.newPage(); + + // Take a screenshot of the second image, adding the vector + await page.setContent( + htmlContent("renderBlochSphere('container').addVector(0.5, 0.5, 0.5);") + ); + await page.screenshot({path: newVectorOutputPath}); + await browser.close(); + }); + + it('with custom statevector matches the gold PNG', () => { + const expected = PNG.PNG.sync.read( + readFileSync('e2e/bloch_sphere/bloch_sphere_expected_custom.png') + ); + const actual = PNG.PNG.sync.read(readFileSync(newVectorOutputPath)); + const {width, height} = expected; + const diff = new PNG.PNG({width, height}); + + const pixels = pixelmatch( + expected.data, + actual.data, + diff.data, + width, + height, + {threshold: 0.1} + ); + + expect(pixels).to.equal(0); + }); + }); +}); diff --git a/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_expected.png b/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_expected.png new file mode 100644 index 00000000000..e7efa9d9bc9 Binary files /dev/null and b/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_expected.png differ diff --git a/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_expected_custom.png b/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_expected_custom.png new file mode 100644 index 00000000000..0e051338663 Binary files /dev/null and b/cirq-web/cirq_ts/e2e/bloch_sphere/bloch_sphere_expected_custom.png differ diff --git a/cirq-web/cirq_ts/package-lock.json b/cirq-web/cirq_ts/package-lock.json new file mode 100644 index 00000000000..bca348310e3 --- /dev/null +++ b/cirq-web/cirq_ts/package-lock.json @@ -0,0 +1,10802 @@ +{ + "name": "web", + "version": "0.12.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/compat-data": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.5.tgz", + "integrity": "sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w==", + "dev": true + }, + "@babel/core": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.5.tgz", + "integrity": "sha512-RN/AwP2DJmQTZSfiDaD+JQQ/J99KsIpOCfBE5pL+5jJSt7nI3nYGoAXZu+ffYSQ029NLs2DstZb+eR81uuARgg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helpers": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", + "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz", + "integrity": "sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", + "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", + "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helpers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.5.tgz", + "integrity": "sha512-xtcWOuN9VL6nApgVHtq3PPcQv5qFBJzoSZzJ/2c0QK/IP/gxVcoWSNQwFEGvmbQsuS9rhYqjILDGGXcTkA705Q==", + "dev": true, + "requires": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", + "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "dev": true + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/traverse": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.5.tgz", + "integrity": "sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/types": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + } + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", + "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@mapbox/node-pre-gyp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", + "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@tsconfig/node10": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz", + "integrity": "sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.7.tgz", + "integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.0.tgz", + "integrity": "sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.1.tgz", + "integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==", + "dev": true + }, + "@types/chai": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz", + "integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ==", + "dev": true + }, + "@types/eslint": { + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.13.tgz", + "integrity": "sha512-LKmQCWAlnVHvvXq4oasNUMTJJb2GwSyTY8+1C7OH5ILR8mPLaljv1jxL1bXW3xB3jFbQxTKxJAvI8PyjB09aBg==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.47", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", + "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.6.tgz", + "integrity": "sha512-+qsjqR75S/ib0ig0R9WN+CDoZeOBU6F2XLewgC4KVgdXiNHiKKHFEMRHOrs5PbYE97D5vataw5wPj4KLYfUkuQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jsdom": { + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.11.tgz", + "integrity": "sha512-Dvtx0zFQOJqn06VgjRur7P5CeSNUOaQ5ivSSvdAdA3sBWzL3kl9OWApQRExKnS3XN39OaZdeCHpoYxVmX6FwCQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/parse5": "*", + "@types/tough-cookie": "*" + } + }, + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true, + "optional": true + }, + "@types/looks-same": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/looks-same/-/looks-same-4.1.0.tgz", + "integrity": "sha512-JKMmPD6Th3KKq6/Apcg8+2NB2+mRIElqgk/TYgfK+GApXM2yo8jJCiPsnyAPBda0h341InF9e8HkzNFVgEDuQQ==", + "requires": { + "looks-same": "*" + } + }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==" + }, + "@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "@types/node": { + "version": "14.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.0.tgz", + "integrity": "sha512-w8VZUN/f7SSbvVReb9SWp6cJFevxb4/nkG65yLAya//98WgocKm5PLDAtSs5CtJJJM+kHmJjO/6mmYW4MHShZA==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" + }, + "@types/parse5": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.0.tgz", + "integrity": "sha512-oPwPSj4a1wu9rsXTEGIJz91ISU725t0BmSnUhb57sI+M8XEmvUop84lzuiYdq0Y5M6xLY8DBPg0C2xEQKLyvBA==", + "dev": true + }, + "@types/pixelmatch": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/pixelmatch/-/pixelmatch-5.2.3.tgz", + "integrity": "sha512-p+nAQVYK/DUx7+s1Xyu9dqAg0gobf7VmJ+iDA4lljg1o4XRgQHr7R2h1NwFt3gdNOZiftxWB11+0TuZqXYf19w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-PyZJHtd/mGA9a+cb3RQvd2pj56OIoo522sEhGZvq9THfBv5skyPRo50o3Hvy+n6U0u8RWteMHhlvLfN4FOZTaA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/puppeteer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", + "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/temp": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/temp/-/temp-0.9.0.tgz", + "integrity": "sha512-UPoFCczcfwFjlopS9hDP6QR3oqng1YnRyKQSDNNFRAUurRaxV8FIjRbvT7Q2Co2zpXh23CEPEx1pKuITHnFQIA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/three": { + "version": "0.128.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.128.0.tgz", + "integrity": "sha512-Jwq5XYUkzAcPTo34hlGAQGUyAI0b2F3aCCFWG/v7ZhJBEG5HGcusMSr70GhDlT8Gs0f02QnSPZ2RCA1MrCOa/w==", + "dev": true + }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "dev": true + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz", + "integrity": "sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw==", + "requires": { + "@typescript-eslint/experimental-utils": "4.24.0", + "@typescript-eslint/scope-manager": "4.24.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz", + "integrity": "sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw==", + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.24.0", + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/typescript-estree": "4.24.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.24.0.tgz", + "integrity": "sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w==", + "requires": { + "@typescript-eslint/scope-manager": "4.24.0", + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/typescript-estree": "4.24.0", + "debug": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz", + "integrity": "sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA==", + "requires": { + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/visitor-keys": "4.24.0" + } + }, + "@typescript-eslint/types": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.24.0.tgz", + "integrity": "sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q==" + }, + "@typescript-eslint/typescript-estree": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz", + "integrity": "sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA==", + "requires": { + "@typescript-eslint/types": "4.24.0", + "@typescript-eslint/visitor-keys": "4.24.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz", + "integrity": "sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g==", + "requires": { + "@typescript-eslint/types": "4.24.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", + "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", + "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", + "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", + "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", + "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.0", + "@webassemblyjs/helper-api-error": "1.11.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", + "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", + "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", + "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", + "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", + "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", + "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/helper-wasm-section": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0", + "@webassemblyjs/wasm-opt": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "@webassemblyjs/wast-printer": "1.11.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", + "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/ieee754": "1.11.0", + "@webassemblyjs/leb128": "1.11.0", + "@webassemblyjs/utf8": "1.11.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", + "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", + "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-api-error": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/ieee754": "1.11.0", + "@webassemblyjs/leb128": "1.11.0", + "@webassemblyjs/utf8": "1.11.0" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", + "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.3.tgz", + "integrity": "sha512-WQs0ep98FXX2XBAfQpRbY0Ma6ADw8JR6xoIkaIiJIzClGOMqVRvPCWqndTxf28DgFopWan0EKtHtg/5W1h0Zkw==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.4.tgz", + "integrity": "sha512-ogE2T4+pLhTTPS/8MM3IjHn0IYplKM4HbVNMCWA9N4NrdPzunwenpCsqKEXyejMfRu6K8mhauIPYf8ZxWG5O6g==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.4.0.tgz", + "integrity": "sha512-xgT/HqJ+uLWGX+Mzufusl3cgjAcnqYYskaB7o0vRcwOEfuu6hMzSILQpnIzFMGsTaeaX4Nnekl+6fadLbl1/Vg==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", + "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "apache-crypt": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.4.tgz", + "integrity": "sha512-Icze5ny5W5uv3xgMgl8U+iGmRCC0iIDrb2PVPuRBtL3Zy1Y5TMewXP1Vtc4r5X9eNNBEk7KYPu0Qby9m/PmcHg==", + "requires": { + "unix-crypt-td-js": "^1.1.4" + } + }, + "apache-md5": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.5.tgz", + "integrity": "sha512-sbLEIMQrkV7RkIruqTPXxeCMkAAycv4yzTkBzRgOR1BrR5UB7qZtupqxkersTJSf0HZ3sbaNRrNV80TnnM7cUw==" + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boxen": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", + "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.0", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001228", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz", + "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==", + "dev": true + }, + "canvas": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz", + "integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==", + "dev": true, + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.14.0", + "simple-get": "^3.0.3" + } + }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-1.2.0.tgz", + "integrity": "sha512-FN7iLBCfb97ElJU2AQXbBAFXPbKmu0XJjPU9GWWmUkIbXka+Im8Q5w1geiL9GB+AktJ4pIA6nRZD1+TlEG6/rA==" + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "concurrently": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.1.0.tgz", + "integrity": "sha512-jy+xj49pvqeKjc2TAVXRIhrgPG51eBKDZti0kZ41kaWk9iLbyWBjH6KMFpW7peOLkEymD+ZM83Lx6UEy3N/M9g==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "read-pkg": "^5.2.0", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + } + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "date-fns": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.21.3.tgz", + "integrity": "sha512-HeYdzCaFflc1i4tGbj7JKMjM4cKGYoyxwcIIkHzNgCkX8xXDNJDZXgDDVchIWpN4eQc3lH37WarduXFZJOtxfw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + } + } + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "devtools-protocol": { + "version": "0.0.883894", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.883894.tgz", + "integrity": "sha512-33idhm54QJzf3Q7QofMgCvIVSd2o9H3kQPWaKT/fhoZh+digc+WSiMhbkeG3iN79WY4Hwr9G05NpbhEVrsOYAg==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.3.727", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", + "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", + "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + } + } + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.6.0.tgz", + "integrity": "sha512-f8kcHX1ArhllUtb/wVSyvygoKCznIjnxhLxy7TCvIiMdT7fL4ZDTIKaadMe6eLvOXg6Wk02UeoFgUoZ2EKZZUA==", + "dev": true + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", + "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.21", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz", + "integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==" + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "eslint-plugin-prettier": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", + "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" + }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "requires": { + "ini": "2.0.0" + } + }, + "globals": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } + } + }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "gts": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gts/-/gts-3.1.0.tgz", + "integrity": "sha512-Pbj3ob1VR1IRlEVEBNtKoQ1wHOa8cZz62KEojK8Fn/qeS2ClWI4gLNfhek3lD68aZSmUEg8TFb6AHXIwUMgyqQ==", + "requires": { + "@typescript-eslint/eslint-plugin": "^4.2.0", + "@typescript-eslint/parser": "^4.2.0", + "chalk": "^4.1.0", + "eslint": "^7.10.0", + "eslint-config-prettier": "^7.0.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.1.4", + "execa": "^5.0.0", + "inquirer": "^7.3.3", + "json5": "^2.1.3", + "meow": "^9.0.0", + "ncp": "^2.0.0", + "prettier": "^2.1.2", + "rimraf": "^3.0.2", + "update-notifier": "^5.0.0", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-auth": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-3.1.3.tgz", + "integrity": "sha1-lFz63WZSHq+PfISRPTd9exXyTjE=", + "requires": { + "apache-crypt": "^1.1.2", + "apache-md5": "^1.0.6", + "bcryptjs": "^2.3.0", + "uuid": "^3.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + } + } + }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "http-proxy-middleware": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", + "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + } + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "dev": true + }, + "internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "dev": true, + "requires": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz", + "integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "dependencies": { + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + } + } + }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "requires": { + "ip-regex": "^4.0.0" + } + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest-worker": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.2.tgz", + "integrity": "sha512-EoBdilOTTyOgmHXtw/cPc+ZrCA0KJMrkXzkrPGNwLmnvvlN1nj7MPrxpT7m+otSv2e1TLaVffzDnE/LB14zJMg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "js-graph-algorithms": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/js-graph-algorithms/-/js-graph-algorithms-1.0.18.tgz", + "integrity": "sha1-+W7Ie/GU9cCjE2X6Dh0Ht7li2JE=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.5", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, + "live-server": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/live-server/-/live-server-1.2.1.tgz", + "integrity": "sha512-Yn2XCVjErTkqnM3FfTmM7/kWy3zP7+cEtC7x6u+wUzlQ+1UW3zEYbbyJrc0jNDwiMDZI0m4a0i3dxlGHVyXczw==", + "requires": { + "chokidar": "^2.0.4", + "colors": "^1.4.0", + "connect": "^3.6.6", + "cors": "^2.8.5", + "event-stream": "3.3.4", + "faye-websocket": "0.11.x", + "http-auth": "3.1.x", + "morgan": "^1.9.1", + "object-assign": "^4.1.1", + "opn": "^6.0.0", + "proxy-middleware": "^0.15.0", + "send": "^0.17.1", + "serve-index": "^1.9.1" + } + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "looks-same": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/looks-same/-/looks-same-7.3.0.tgz", + "integrity": "sha512-pOfwX2d0frSt7H1cuBjDbw9Kry5QwkrFri0qJvLwV1sI0cbWkwYkpd7fF7SqSIfYKAZhgeB8PM3fyhUYz7xgqA==", + "requires": { + "color-diff": "^1.1.0", + "concat-stream": "^1.6.2", + "fs-extra": "^8.1.0", + "js-graph-algorithms": "1.0.18", + "lodash": "^4.17.3", + "nested-error-stacks": "^2.1.0", + "parse-color": "^1.0.0", + "pngjs": "^6.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==" + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true + } + } + }, + "memfs": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.2.tgz", + "integrity": "sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==" + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "requires": { + "mime-db": "1.47.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-GRGG/q9bIaUkHJB9NL+KZNjDhMBHB30zW3bZW9qOiYr+QChyLjPzswaxFWkI1q6lGlSL28EQYzAi2vKWNkPx+g==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.23", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.4", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" + }, + "node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "1.1.72", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", + "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", + "dev": true + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + }, + "npm": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.16.0.tgz", + "integrity": "sha512-UUnKcjS7qFhZT90iZY/ZWz/jwF+rS0fIohDf41T6/SRXEqut0aav+1NkL6g6GqQGpIVBzpZc75BDfpq4PhfXBg==", + "dev": true, + "requires": { + "@npmcli/arborist": "^2.6.1", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^2.2.0", + "@npmcli/run-script": "^1.8.5", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "archy": "~1.0.0", + "byte-size": "^7.0.1", + "cacache": "^15.2.0", + "chalk": "^4.1.0", + "chownr": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.6.0", + "columnify": "~1.5.4", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "hosted-git-info": "^4.0.2", + "ini": "^2.0.0", + "init-package-json": "^2.0.3", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "leven": "^3.1.0", + "libnpmaccess": "^4.0.2", + "libnpmdiff": "^2.0.4", + "libnpmexec": "^1.2.0", + "libnpmfund": "^1.1.0", + "libnpmhook": "^6.0.2", + "libnpmorg": "^2.0.2", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.1", + "libnpmsearch": "^3.1.1", + "libnpmteam": "^2.0.3", + "libnpmversion": "^1.2.0", + "make-fetch-happen": "^9.0.1", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.5", + "npm-package-arg": "^8.1.4", + "npm-pick-manifest": "^6.1.1", + "npm-profile": "^5.0.3", + "npm-registry-fetch": "^11.0.0", + "npm-user-validate": "^1.0.1", + "npmlog": "~4.1.2", + "opener": "^1.5.2", + "pacote": "^11.3.3", + "parse-conflict-json": "^1.1.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^3.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "tar": "^6.1.0", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^1.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "@npmcli/arborist": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.0", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^11.0.0", + "pacote": "^11.2.6", + "parse-conflict-json": "^1.1.1", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "semver": "^7.3.5", + "tar": "^6.1.0", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/ci-detect": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "@npmcli/config": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/git": { + "version": "2.0.9", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + } + }, + "@npmcli/metavuln-calculator": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "@npmcli/node-gyp": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "bundled": true, + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "1.8.5", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "infer-owner": "^1.0.4", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "bundled": true, + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.1.4", + "bundled": true, + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true, + "dev": true + }, + "aws4": { + "version": "1.11.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" + } + }, + "binary-extensions": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtins": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "byte-size": { + "version": "7.0.1", + "bundled": true, + "dev": true + }, + "cacache": { + "version": "15.2.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "chalk": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chownr": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "cidr-regex": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "ip-regex": "^4.1.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "4.2.2", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "cmd-shim": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "mkdirp-infer-owner": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "colors": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "dev": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.3.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "depd": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "encoding": { + "version": "0.1.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "err-code": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "extend": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true + }, + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.7", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ignore-walk": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ini": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "init-package-json": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^8.1.2", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "^3.0.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true, + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "bundled": true, + "dev": true + }, + "is-cidr": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "cidr-regex": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "bundled": true, + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true, + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true, + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-diff": { + "version": "3.1.1", + "bundled": true, + "dev": true + }, + "just-diff-apply": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "leven": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "libnpmaccess": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmdiff": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", + "tar": "^6.1.0" + } + }, + "libnpmexec": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.3.0", + "@npmcli/ci-detect": "^1.3.0", + "@npmcli/run-script": "^1.8.4", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^8.1.2", + "pacote": "^11.3.1", + "proc-log": "^1.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" + } + }, + "libnpmfund": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.5.0" + } + }, + "libnpmhook": { + "version": "6.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmorg": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmpack": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" + } + }, + "libnpmpublish": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "normalize-package-data": "^3.0.2", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.1" + } + }, + "libnpmsearch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmteam": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmversion": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^2.0.7", + "@npmcli/run-script": "^1.8.4", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.5", + "stringify-package": "^1.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "9.0.2", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + } + }, + "mime-db": { + "version": "1.48.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.31", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.48.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "ms": { + "version": "2.1.3", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "bundled": true, + "dev": true + }, + "node-gyp": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "2.1.5", + "bundled": true, + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "npm-bundled": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npm-package-arg": { + "version": "8.1.4", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "2.2.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "bundled": true, + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-profile": { + "version": "5.0.4", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^11.0.0" + } + }, + "npm-registry-fetch": { + "version": "11.0.0", + "bundled": true, + "dev": true, + "requires": { + "make-fetch-happen": "^9.0.1", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "p-map": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pacote": { + "version": "11.3.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^2.0.1", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^11.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "parse-conflict-json": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "proc-log": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-call-limit": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "read": "1" + } + }, + "psl": { + "version": "1.8.0", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "qs": { + "version": "6.5.2", + "bundled": true, + "dev": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "read-package-json": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-json-fast": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "request": { + "version": "2.88.2", + "bundled": true, + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "resolve": { + "version": "1.20.0", + "bundled": true, + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "retry": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "7.3.5", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "bundled": true, + "dev": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "socks": { + "version": "2.6.1", + "bundled": true, + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" + } + }, + "spdx-correct": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.9", + "bundled": true, + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "bundled": true, + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "treeverse": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "bundled": true, + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.4.0", + "bundled": true, + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "dev": true + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + } + } + }, + "opn": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", + "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.5.0.tgz", + "integrity": "sha512-5Hwh4aVQSu6BEP+w2zKlVXtFAaYQe1qWuVADSgoeVlLjwe/Q/AMSoRR4MDeaAfu8llT+YNbEijWu/YF3m6avkg==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", + "integrity": "sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=", + "requires": { + "color-convert": "~0.5.0" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + } + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "requires": { + "through": "~2.3" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "picomatch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" + }, + "pixelmatch": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.2.1.tgz", + "integrity": "sha512-WjcAdYSnKrrdDdqTcVEY7aB7UhhwjYQKYhHiBXdJef0MOaQeYpUdQ+iVyBLa5YBKS8MPVPPMX7rpOByISLpeEQ==", + "dev": true, + "requires": { + "pngjs": "^4.0.1" + }, + "dependencies": { + "pngjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-4.0.1.tgz", + "integrity": "sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==", + "dev": true + } + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==" + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "prettier": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz", + "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==" + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "requires": { + "fast-diff": "^1.1.2" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "proxy-middleware": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", + "integrity": "sha1-o/3xvvtzD5UZZYcqwvYHTGFHelY=" + }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "requires": { + "event-stream": "=3.3.4" + } + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "requires": { + "escape-goat": "^2.0.0" + } + }, + "puppeteer": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-10.0.0.tgz", + "integrity": "sha512-AxHvCb9IWmmP3gMW+epxdj92Gglii+6Z4sb+W+zc2hTTu10HF0yg6hGXot5O74uYkVqG3lfDRLfnRpi6WOwi5A==", + "dev": true, + "requires": { + "debug": "4.3.1", + "devtools-protocol": "0.0.883894", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.0", + "node-fetch": "2.6.1", + "pkg-dir": "4.2.0", + "progress": "2.0.1", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.0.0", + "unbzip2-stream": "1.3.3", + "ws": "7.4.6" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "progress": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", + "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", + "dev": true + } + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "dev": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "requires": { + "through": "2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "requires": { + "duplexer": "~0.1.1" + } + }, + "string-argv": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz", + "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "optional": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.4.0.tgz", + "integrity": "sha512-7QD2l6+KBSLwf+7MuYocbWvRPdOu63/trReTLu2KFwkgctnub1auoF+Y1WYcm09CTM7quuscrzqmASaLHC/K4Q==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "dev": true + }, + "tar": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "tar-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", + "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp": "^0.5.1", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "terser": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", + "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.3.tgz", + "integrity": "sha512-cxGbMqr6+A2hrIB5ehFIF+F/iST5ZOxvOmy9zih9ySbP1C2oEWQSOUS+2SNBTjzx5xLKO4xnod9eywdfq1Nb9A==", + "dev": true, + "requires": { + "jest-worker": "^27.0.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.7.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "three": { + "version": "0.128.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.128.0.tgz", + "integrity": "sha512-i0ap/E+OaSfzw7bD1TtYnPo3VEplkl70WX5fZqZnfZsE3k3aSFudqrrC9ldFZfYFkn1zwDmBcdGfiIm/hnbyZA==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==" + }, + "ts-loader": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.1.2.tgz", + "integrity": "sha512-ryMgATvLLl+z8zQvdlm6Pep0slmwxFWIEnq/5VdiLVjqQXnFJgO+qNLGIIP+d2N2jsFZ9MibZCVDb2bSp7OmEA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "ts-mocha": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-8.0.0.tgz", + "integrity": "sha512-Kou1yxTlubLnD5C3unlCVO7nh0HERTezjoVhVw/M5S1SqoUec0WgllQvPk3vzPMc6by8m6xD1uR1yRf8lnVUbA==", + "dev": true, + "requires": { + "ts-node": "7.0.1", + "tsconfig-paths": "^3.5.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } + }, + "ts-mock-imports": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ts-mock-imports/-/ts-mock-imports-1.3.7.tgz", + "integrity": "sha512-zy4B3QSGaOhjaX9j0h9YKwM1oHG4Kd1KIUJBeXlXIQrFnATNLgh4+NyRcaAHsPeqwe3TWeRtHXkNXPxySEKk3w==", + "dev": true + }, + "ts-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.0.0.tgz", + "integrity": "sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==", + "dev": true, + "requires": { + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "tsc-watch": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.9.tgz", + "integrity": "sha512-DlTaoDs74+KUpyWr7dCGhuscAUKCz6CiFduBN7R9RbLJSSN1moWdwoCLASE7+zLgGvV5AwXfYDiEMAsPGaO+Vw==", + "requires": { + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.1.1", + "strip-ansi": "^6.0.0" + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "optional": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==" + }, + "unbzip2-stream": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", + "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unix-crypt-td-js": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", + "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "watchpack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", + "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "webpack": { + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.40.0.tgz", + "integrity": "sha512-c7f5e/WWrxXWUzQqTBg54vBs5RgcAgpvKE4F4VegVgfo4x660ZxYUF2/hpMkZUnLjgytVTitjeXaN4IPlXCGIw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.47", + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/wasm-edit": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "acorn": "^8.2.1", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.0", + "es-module-lexer": "^0.6.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^2.3.0" + } + }, + "webpack-cli": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.7.0.tgz", + "integrity": "sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.0.3", + "@webpack-cli/info": "^1.2.4", + "@webpack-cli/serve": "^1.4.0", + "colorette": "^1.2.1", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz", + "integrity": "sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "mem": "^8.1.1", + "memfs": "^3.2.2", + "mime-types": "^2.1.30", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + } + }, + "webpack-dev-server": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-beta.3.tgz", + "integrity": "sha512-Ud7ieH15No/KiSdRuzk+2k+S4gSCR/N7m4hJhesDbKQEZy3P+NPXTXfsimNOZvbVX2TRuIEFB+VdLZFn8DwGwg==", + "dev": true, + "requires": { + "ansi-html": "^0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^3.5.1", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "del": "^6.0.0", + "express": "^4.17.1", + "find-cache-dir": "^3.3.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^1.3.1", + "internal-ip": "^6.2.0", + "ipaddr.js": "^2.0.0", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "open": "^7.4.2", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^3.0.0", + "selfsigned": "^1.10.11", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^6.0.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^4.1.0", + "ws": "^7.4.5" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "webpack-merge": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", + "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.0.tgz", + "integrity": "sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.6.0.tgz", + "integrity": "sha512-os0KkeeqUOl7ccdDT1qqUcS4KH4tcBTSKK5Nl5WKb2lyxInIZ/CpjkqKa1Ss12mjfdcRX9mHmPPs7/SxG1Hbdw==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "requires": { + "string-width": "^4.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "workerpool": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.4.tgz", + "integrity": "sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + } + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/cirq-web/cirq_ts/package.json b/cirq-web/cirq_ts/package.json new file mode 100644 index 00000000000..cf501a5e572 --- /dev/null +++ b/cirq-web/cirq_ts/package.json @@ -0,0 +1,65 @@ +{ + "name": "web", + "version": "0.12.0", + "description": "Web widgets for Cirq", + "main": "index.js", + "private": true, + "dependencies": { + "@types/looks-same": "^4.1.0", + "gts": "^3.1.0", + "live-server": "^1.2.1", + "tsc-watch": "^4.2.9", + "typescript": "^4.2.4" + }, + "devDependencies": { + "@types/chai": "^4.2.18", + "@types/jsdom": "^16.2.11", + "@types/mocha": "^8.2.2", + "@types/node": "^14.11.2", + "@types/pixelmatch": "^5.2.3", + "@types/pngjs": "^6.0.0", + "@types/puppeteer": "^5.4.3", + "@types/temp": "^0.9.0", + "@types/three": "^0.128.0", + "canvas": "^2.8.0", + "chai": "^4.3.4", + "concurrently": "^6.1.0", + "gts": "^3.1.0", + "install": "^0.13.0", + "jsdom": "^16.6.0", + "mocha": "^9.0.0", + "npm": "^7.16.0", + "nyc": "^15.1.0", + "pixelmatch": "^5.2.1", + "pngjs": "^6.0.0", + "puppeteer": "^10.0.0", + "temp": "^0.9.4", + "three": "^0.128.0", + "ts-loader": "^9.1.2", + "ts-mocha": "^8.0.0", + "ts-mock-imports": "^1.3.7", + "ts-node": "^10.0.0", + "typescript": "^4.0.3", + "webpack": "^5.40.0", + "webpack-cli": "^4.7.0", + "webpack-dev-server": "^4.0.0-beta.0" + }, + "scripts": { + "start": "webpack serve --mode development", + "build": "webpack --mode production", + "test-e2e": "mocha -r ts-node/register 'e2e/**/*.ts' --timeout 10000", + "test": "mocha -r ts-node/register 'src/**/*_test.ts'", + "test-all": "npm run build && npm run test-e2e && npm run test", + "coverage": "nyc npm run test && rm -rf .nyc_output", + "lint": "gts lint", + "clean": "gts clean", + "compile": "tsc", + "fix": "gts fix", + "prepare": "npm run compile" + }, + "author": "", + "license": "ISC", + "engines": { + "node": ">=11.14.0" + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/bloch_sphere.ts b/cirq-web/cirq_ts/src/bloch_sphere/bloch_sphere.ts new file mode 100644 index 00000000000..85416a14088 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/bloch_sphere.ts @@ -0,0 +1,113 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {Orientation} from './components/enums'; +import {Sphere} from './components/sphere'; +import {Axes} from './components/axes'; +import {Meridians} from './components/meridians'; +import {Labels} from './components/text'; +import {StateVector} from './components/state_vector'; + +import {Group, Vector3} from 'three'; + +/** + * Class bringinging together the individual components like the + * Sphere, Axes, Meridians, and Text into the overall visualization + * of the Bloch sphere. + */ +export class BlochSphere extends Group { + private radius: number; + private hMeridians: number; + private vMeridians: number; + + /** + * Class constructor. + * @param radius The radius of the Bloch sphere + * @param hMeridians The number of horizontal meridians desired for the Bloch sphere. + * @param vMeridians The number of vertical meridians desired for the Bloch sphere. + * @returns An instance of the class which can be easily added to a scene. + */ + constructor(radius = 5, hMeridians = 7, vMeridians = 4) { + super(); + + this.radius = radius; + this.hMeridians = hMeridians; + this.vMeridians = vMeridians; + + this.addSphere(); + this.addHorizontalMeridians(); + this.addVerticalMeridians(); + this.addAxes(); + this.addLabels(); + + return this; + } + + /** + * Adds a vector to the Bloch sphere based on a set + * of given x, y, and z coordinates of the vector's endpoint + * in order to be parsed successfully. + * @param x the x coordinate of the vector's endpoint + * @param y the y coordinate of the vector's endpoint + * @param z the z coordinate of the vector's endpoint + * + */ + public addVector(x: number, y: number, z: number) { + const vector = new StateVector(x, y, z, this.radius); + this.add(vector); + } + + private addSphere() { + const sphere = new Sphere(this.radius); + this.add(sphere); + } + + private addAxes() { + const axes = new Axes(this.radius); + this.add(axes); + } + + private addHorizontalMeridians() { + const meridians = new Meridians( + this.radius, + this.hMeridians, + Orientation.HORIZONTAL + ); + this.add(meridians); + } + + private addVerticalMeridians() { + const meridians = new Meridians( + this.radius, + this.vMeridians, + Orientation.VERTICAL + ); + this.add(meridians); + } + + private addLabels() { + const spacing = 0.5; + const labelData = { + '|+⟩': new Vector3(this.radius + spacing, 0, 0), + '|-⟩': new Vector3(-this.radius - spacing, 0, 0), + '|i⟩': new Vector3(0, 0, -this.radius - spacing), + '|-i⟩': new Vector3(0, 0, this.radius + spacing), + '|0⟩': new Vector3(0, this.radius + spacing, 0), + '|1⟩': new Vector3(0, -this.radius - spacing, 0), + }; + + const labels = new Labels(labelData); + this.add(labels); + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/bloch_sphere_test.ts b/cirq-web/cirq_ts/src/bloch_sphere/bloch_sphere_test.ts new file mode 100644 index 00000000000..24d9fa89ad2 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/bloch_sphere_test.ts @@ -0,0 +1,263 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {expect} from 'chai'; +import {BlochSphere} from './bloch_sphere'; +import {Scene, Vector3} from 'three'; +import {JSDOM} from 'jsdom'; +import {Orientation} from './components/enums'; +import {Sphere} from './components/sphere'; +import {Meridians} from './components/meridians'; +import {Axes} from './components/axes'; +import {Labels} from './components/text'; +import {StateVector} from './components/state_vector'; + +/** + * Using JSDOM to create a global document which the canvas elements + * generated in loadAndDisplayText can be created on. + */ +const {window} = new JSDOM(''); +global.document = window.document; + +describe('BlochSphere (with empty constructor)', () => { + const scene = new Scene(); + const bloch_sphere = new BlochSphere(); + scene.add(bloch_sphere); + + // Extract all of the Bloch sphere default info first, + // and then test that it's what we want. + const children = bloch_sphere.children; + + const sphere = children.find( + child => child.constructor.name === 'Sphere' + ) as Sphere; + + const meridians = children.filter( + child => child.constructor.name === 'Meridians' + ) as Meridians[]; + + const horizontalMeridians = meridians.find( + child => child.orientation === Orientation.HORIZONTAL + ) as Meridians; + + const verticalMeridians = meridians.find( + child => child.orientation === Orientation.VERTICAL + ) as Meridians; + + const axes = children.find( + child => child.constructor.name === 'Axes' + ) as Axes; + + const labels = children.find( + child => child.constructor.name === 'Labels' + ) as Labels; + + it('adds a single BlochSphere of type Group', () => { + const children = scene.children; + expect(children.length).to.equal(1); + expect(children[0].type).to.equal('Group'); + // Sanity check to make sure it works as BlochSphere + expect(children[0] as BlochSphere).to.be.instanceOf(BlochSphere); + }); + + describe('child group (Sphere, Meridians, etc.)', () => { + it('Sphere contains the correct number of components', () => { + const sphereExists = children.find( + child => child.constructor.name === 'Sphere' + ); + expect(sphereExists).to.not.equal(undefined); + }); + + it('Meridians appear in 2 sets', () => { + const numOfMeridians = children.filter( + child => child.constructor.name === 'Meridians' + ).length; + expect(numOfMeridians).to.equal(2); + }); + + it('Axes exist', () => { + const axesExists = children.some( + child => child.constructor.name === 'Axes' + ); + expect(axesExists).to.equal(true); + }); + + it('Labels exist', () => { + const labelsExists = children.some( + child => child.constructor.name === 'Labels' + ); + expect(labelsExists).to.equal(true); + }); + }); + + describe('child group Sphere', () => { + it('has a radius of 5 by default', () => { + expect(sphere.radius).to.equal(5); + }); + }); + + describe('child groups Meridians', () => { + describe('by default', () => { + it('contains 7 horizontal chord meridians', () => { + expect(horizontalMeridians.numCircles).to.equal(7); + }); + + it('contains 4 vertical meridians', () => { + expect(verticalMeridians.numCircles).to.equal(4); + }); + + it('has horizontal chord meridians that share the same radius of the sphere', () => { + expect(horizontalMeridians.radius).to.equal(sphere.radius); + }); + + it('has vertical meridians that share the same radius of the sphere', () => { + expect(verticalMeridians.radius).to.equal(sphere.radius); + }); + }); + }); + + describe('child group Axes', () => { + it('contains the 3 axes by default', () => { + const numberOfAxisLines = axes.children.length; + expect(numberOfAxisLines).to.equal(3); + }); + + it('has axes lines with half-length equal to the radius of the sphere', () => { + expect(axes.halfLength).to.equal(sphere.radius); + }); + + it('does not have any 2 lines with the same color', () => { + const colors = [axes.xAxisColor, axes.yAxisColor, axes.zAxisColor]; + expect(new Set(colors).size).to.equal(colors.length); + }); + }); + + describe('child group Labels', () => { + it('has labels that are fit to the radius of the sphere with the correct spacing (0.5)', () => { + const expectedBlochSphereLabels = { + '|+⟩': new Vector3(sphere.radius + 0.5, 0, 0), + '|-⟩': new Vector3(-(sphere.radius + 0.5), 0, 0), + '|i⟩': new Vector3(0, 0, -(sphere.radius + 0.5)), + '|-i⟩': new Vector3(0, 0, sphere.radius + 0.5), + '|0⟩': new Vector3(0, sphere.radius + 0.5, 0), + '|1⟩': new Vector3(0, -(sphere.radius + 0.5), 0), + }; + expect(labels.labels).to.eql(expectedBlochSphereLabels); + }); + }); +}); + +describe('BlochSphere (with valid custom constructor values)', () => { + const bloch_sphere = new BlochSphere(3, 9, 6); + const children = bloch_sphere.children; + + const sphere = children.find( + child => child.constructor.name === 'Sphere' + ) as Sphere; + + const meridians = children.filter( + child => child.constructor.name === 'Meridians' + ) as Meridians[]; + + const horizontalMeridians = meridians.find( + child => child.orientation === Orientation.HORIZONTAL + ) as Meridians; + + const verticalMeridians = meridians.find( + child => child.orientation === Orientation.VERTICAL + ) as Meridians; + + describe('has a child group Sphere', () => { + it('with a configured radius of 3', () => { + expect(sphere.radius).to.equal(3); + }); + }); + + describe('has a child group Meridians', () => { + describe('with a horizontal set of meridians that', () => { + it('accepts the configured number of meridians', () => { + expect(horizontalMeridians.numCircles).to.equal(9); + }); + }); + + describe('with a vertical set of meridians that', () => { + it('accepts the configured number of meridians', () => { + expect(verticalMeridians.numCircles).to.equal(6); + }); + }); + }); + + describe('handles vectors correctly by', () => { + it('adding three unique vectors without failing', () => { + // Adding multiple vectors is supported in the prototype, + // but the front end isn't yet formatted to accomodate this. + // The next PR will contain this additional support. + + bloch_sphere.addVector(1, 0, 0); + bloch_sphere.addVector(1, 1, 2); + bloch_sphere.addVector(3, 4, 5); + + const vectors = children.filter( + child => child.constructor.name === 'Vector' + ) as StateVector[]; + + const expectedVectorX = [1, 1, 3]; + const expectedVectorY = [0, 1, 4]; + const expectedVectorZ = [0, 2, 5]; + + vectors.forEach((vector, index) => { + expect(vector.x).to.equal(expectedVectorX[index]); + expect(vector.y).to.equal(expectedVectorY[index]); + expect(vector.z).to.equal(expectedVectorZ[index]); + expect(vector.blochSphereRadius).to.equal(sphere.radius); + }); + }); + }); +}); + +describe('BlochSphere (with invalid custom constructor values)', () => { + it('fails correctly if given an invalid radius', () => { + const inputs = [0, -1]; + const errorMessage = + 'The radius of a Sphere must be greater than or equal to 1'; + + inputs.forEach(input => { + expect(() => new BlochSphere(input)).to.throw(errorMessage); + }); + }); + + it('fails correctly if given invalid horizontal meridian input', () => { + const inputs = [-2, 301]; + const errorMessages = [ + 'A negative number of meridians are not supported', + 'Over 300 meridians are not supported', + ]; + + inputs.forEach((input, i) => { + expect(() => new BlochSphere(5, input)).to.throw(errorMessages[i]); + }); + }); + + it('fails correctly if given invalid vertical meridian input', () => { + const inputs = [-2, 301]; + const errorMessages = [ + 'A negative number of meridians are not supported', + 'Over 300 meridians are not supported', + ]; + + inputs.forEach((input, i) => { + expect(() => new BlochSphere(5, 0, input)).to.throw(errorMessages[i]); + }); + }); +}); diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/axes.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/axes.ts new file mode 100644 index 00000000000..3e83f272e56 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/axes.ts @@ -0,0 +1,101 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {Vector3, LineDashedMaterial, BufferGeometry, Line, Group} from 'three'; + +interface Axis { + points: [Vector3, Vector3]; + hexColor: string; + readonly lineWidth: number; +} + +/** + * Generates the axes for the Bloch sphere. The halfLength + * (length of one half of the axes line) and color of each axis are configurable. + */ +export class Axes extends Group { + readonly halfLength: number; + readonly xAxisColor: string = '#1f51ff'; + readonly yAxisColor: string = '#ff3131'; + readonly zAxisColor: string = '#39ff14'; + + /** + * Class constructor. + * @param halfLength The halfLength of the axes line. This should be the same as the + * radius of the sphere. + * @returns An instance of the class containing the generated axes. This can be easily + * added to the Bloch sphere instance, or the scene itself. + */ + constructor(halfLength: number) { + super(); + this.halfLength = halfLength; + this.generateAxes(); + return this; + } + + /** + * Creates the x, y, and z axis for the Bloch sphere, adding + * them to the group. + */ + private generateAxes() { + const LINE_WIDTH = 1.5; + const xPoints: [Vector3, Vector3] = [ + new Vector3(-this.halfLength, 0, 0), + new Vector3(this.halfLength, 0, 0), + ]; + const yPoints: [Vector3, Vector3] = [ + new Vector3(0, 0, -this.halfLength), + new Vector3(0, 0, this.halfLength), + ]; + const zPoints: [Vector3, Vector3] = [ + new Vector3(0, -this.halfLength, 0), + new Vector3(0, this.halfLength, 0), + ]; + + const axesMap = { + x: this.asLine({ + points: xPoints, + hexColor: this.xAxisColor, + lineWidth: LINE_WIDTH, + }), + y: this.asLine({ + points: yPoints, + hexColor: this.yAxisColor, + lineWidth: LINE_WIDTH, + }), + z: this.asLine({ + points: zPoints, + hexColor: this.zAxisColor, + lineWidth: LINE_WIDTH, + }), + }; + + this.add(axesMap.x); + this.add(axesMap.y); + this.add(axesMap.z); + } + + private asLine(axis: Axis): Line { + return new Line( + new BufferGeometry().setFromPoints(axis.points), + new LineDashedMaterial({ + color: axis.hexColor, + linewidth: axis.lineWidth, + scale: 1, + dashSize: 0.1, + gapSize: 0.1, + }) + ).computeLineDistances(); + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/axes_test.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/axes_test.ts new file mode 100644 index 00000000000..cd6ec0282c1 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/axes_test.ts @@ -0,0 +1,44 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {expect} from 'chai'; +import {Axes} from './axes'; +import {Line, LineDashedMaterial, Color} from 'three'; + +describe('Axes', () => { + describe('by default', () => { + const axes = new Axes(5); + const children = axes.children as Line[]; + + it('returns 3 Line objects', () => { + expect(children.length).to.equal(3); + }); + + it('returns the correct default colors for each line', () => { + const defaultColors = ['#1f51ff', '#ff3131', '#39ff14']; + + children.forEach((el, index) => { + const material = el.material as LineDashedMaterial; + expect(material.color).to.eql(new Color(defaultColors[index])); + }); + }); + + it('returns all lines with a constant linewidth (1.5)', () => { + children.forEach(el => { + const material = el.material as LineDashedMaterial; + expect(material.linewidth).to.equal(1.5); + }); + }); + }); +}); diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/enums.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/enums.ts new file mode 100644 index 00000000000..791f6249159 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/enums.ts @@ -0,0 +1,9 @@ +/** + * Enum to used for Meridians to determine the + * direction they should face and what type + * of meridian they are + */ +export enum Orientation { + HORIZONTAL, + VERTICAL, +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/meridians.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/meridians.ts new file mode 100644 index 00000000000..9fa766c3c04 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/meridians.ts @@ -0,0 +1,248 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + EllipseCurve, + BufferGeometry, + LineBasicMaterial, + Line, + Group, +} from 'three'; +import {Orientation} from './enums'; + +interface CurveData { + anchorX: number; + anchorY: number; + radius: number; + startAngle: number; + endAngle: number; + isClockwise: boolean; + rotation: number; +} + +/** + * Generates the meridians for the Bloch sphere. The radius, + * number of meridian circles, and the orientation of the circles are configurable. + */ +export class Meridians extends Group { + readonly radius: number; + readonly numCircles: number; + readonly orientation: Orientation; + readonly color: string = 'gray'; + + /** + * Class constructor. + * @param radius The radius of the Bloch Sphere meridians. This should be equal + * to the radius of the sphere instance. + * @param numCircles The number of circles desired for the given set of meridians. + * Note that certain types of meridians have certain guidelines to which numbers are possible + * @param orientation The orientation of the meridians (HORIZONTAL_CHORD, HORIZONTAL, VERTICAL) + * @returns An instance of the class including generated meridians. This can be + * added to the Bloch sphere instance as well as the scene. + */ + constructor(radius: number, numCircles: number, orientation: Orientation) { + super(); + this.radius = radius; + this.orientation = orientation; + + switch (orientation) { + case Orientation.HORIZONTAL: { + this.numCircles = this.sanitizeCircleInput(numCircles); + this.createHorizontalChordMeridians(this.radius, this.numCircles); + return this; + } + case Orientation.VERTICAL: { + this.numCircles = this.sanitizeCircleInput(numCircles); + this.createVerticalMeridians(this.radius, this.numCircles); + return this; + } + default: + throw new Error('Invalid orientation input in Meridians constructor'); + } + } + + /** + * Creates the special horizontal meridian lines of the Bloch + * sphere, each with a different radius and location, adding + * them to the group afterwards. + * @param radius The radius of the overall Bloch sphere + * @param numCircles The number of circles displayed. The number must be odd, + * if an even number is provided. it will round up to the next highest odd number. + * If 0 < numCircles < 3, 3 meridians will be displayed. + */ + private createHorizontalChordMeridians(radius: number, numCircles: number) { + if (numCircles === 0) { + return; + } + + let nonEquatorCircles: number; + numCircles % 2 !== 0 + ? (nonEquatorCircles = numCircles - 1) + : (nonEquatorCircles = numCircles); + const circlesPerHalf = nonEquatorCircles / 2; + + // Creates chords proportionally to radius 5 circle. + const initialFactor = (0.5 * radius) / 5; + + // Start building the chords from the top down. + // If the user only wants one chord, skip the loop. + let topmostChordPos: number; + numCircles === 1 + ? (topmostChordPos = 0) + : (topmostChordPos = radius - initialFactor); + + const chordYPositions = [0]; // equator + for ( + let i = topmostChordPos; + i > 0; + i -= topmostChordPos / circlesPerHalf + ) { + chordYPositions.push(i); + chordYPositions.push(-i); + } + + // Calculate the lengths of the chords of the circle, and then add them + for (const position of chordYPositions) { + const hyp2 = Math.pow(radius, 2); + const distance2 = Math.pow(position, 2); + const newRadius = Math.sqrt(hyp2 - distance2); // radius^2 - b^2 = a^2 + + const curveData = this.curveDataWithRadius(newRadius); + const curve = this.createMeridianCurve(curveData); + const meridianLine = this.createMeridianLine( + curve, + Math.PI / 2, + Orientation.HORIZONTAL, + position + ); + this.add(meridianLine); + } + } + + /** + * Creates equally spaced and sized vertical meridian lines which rotate + * by varying degrees across the same axis, adding them to the group. + * @param radius The radius of the overall bloch sphere + * @param numCircles The number of circles to add. This number must be even, + * if an odd number is provided, one more circle will be generated to ensure it is even. + */ + private createVerticalMeridians(radius: number, numCircles: number) { + if (numCircles === 0) { + return; + } + + const curveData = { + anchorX: 0, + anchorY: 0, + radius, + startAngle: 0, + endAngle: 2 * Math.PI, + isClockwise: false, + rotation: 0, + }; + + for (let i = 0; i < Math.PI; i += Math.PI / numCircles) { + const curve = this.createMeridianCurve(curveData); + const meridianLine = this.createMeridianLine( + curve, + i, + Orientation.VERTICAL + ); + this.add(meridianLine); + } + } + + /** + * Helper function that generates the actual Line object which will be + * rendered by the three.js scene. + * @param curve An EllipseCurve object that provides location/size info + * @param rotationAngle The desired angle of rotation in radians + * @param orientation The orientation of the meridian (horizontal_chord, horizontal or vertical) + * @param yPosition (Optional) Allows the yPosition of the line to be updated to + * the provided value + * @returns A Line object that can be rendered by a three.js scene. + */ + private createMeridianLine( + curve: EllipseCurve, + rotationAngle: number, + orientation: Orientation, + yPosition?: number + ): Line { + const points = curve.getSpacedPoints(128); + const meridianGeom = new BufferGeometry().setFromPoints(points); + + switch (orientation) { + case Orientation.HORIZONTAL: { + meridianGeom.rotateX(rotationAngle); + break; + } + case Orientation.VERTICAL: { + meridianGeom.rotateY(rotationAngle); + break; + } + // No default case is needed, since an invalid orientation input will never make it + // through the constructor. + } + + const meridianLine = new Line( + meridianGeom, + new LineBasicMaterial({color: 'gray'}) + ); + if (yPosition) { + meridianLine.position.y = yPosition; + } + return meridianLine; + } + + /** + * Helper function that generates a necessary EllipseCurve + * given the required information. + * @param curveData An object that contains info about the curve + * @returns An EllipseCurve object based off the curve information. + */ + private createMeridianCurve(curveData: CurveData): EllipseCurve { + return new EllipseCurve( + curveData.anchorX, + curveData.anchorY, + curveData.radius, + curveData.radius, + curveData.startAngle, + curveData.endAngle, + curveData.isClockwise, + curveData.rotation + ); + } + + private curveDataWithRadius(radius: number): CurveData { + return { + anchorX: 0, + anchorY: 0, + radius, + startAngle: 0, + endAngle: 2 * Math.PI, + isClockwise: false, + rotation: 0, + }; + } + + private sanitizeCircleInput(input: number) { + if (input < 0) { + throw new Error('A negative number of meridians are not supported'); + } else if (input > 300) { + throw new Error('Over 300 meridians are not supported'); + } + + return Math.floor(input); + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/meridians_test.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/meridians_test.ts new file mode 100644 index 00000000000..ad2447e84f2 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/meridians_test.ts @@ -0,0 +1,158 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {expect} from 'chai'; +import {Meridians} from './meridians'; +import {Orientation} from './enums'; +import {Vector3} from 'three'; + +describe('Meridians', () => { + const DEFAULT_RADIUS = 5; + const DEFAULT_H_MERIDIANS = 7; + const DEFAULT_V_MERIDIANS = 4; + + describe('by default', () => { + it(`generates the correct number of lines given + the default number (7)`, () => { + const meridians = new Meridians( + DEFAULT_RADIUS, + DEFAULT_H_MERIDIANS, + Orientation.HORIZONTAL + ); + expect(meridians.children.length).to.equal(DEFAULT_H_MERIDIANS); + }); + + it(`generates the correct number of lines given + the default number (4)`, () => { + const meridians = new Meridians( + DEFAULT_RADIUS, + DEFAULT_V_MERIDIANS, + Orientation.VERTICAL + ); + expect(meridians.children.length).to.equal(DEFAULT_V_MERIDIANS); + }); + + it(`generates lines at the correct positions + with defaults`, () => { + const meridians = new Meridians( + DEFAULT_RADIUS, + DEFAULT_H_MERIDIANS, + Orientation.HORIZONTAL + ); + + const positions = [ + new Vector3(0, 0, 0), + new Vector3(0, 4.5, 0), + new Vector3(0, -4.5, 0), + new Vector3(0, 3, 0), + new Vector3(0, -3, 0), + new Vector3(0, 1.5, 0), + new Vector3(0, -1.5, 0), + ]; + + meridians.children.forEach((el, index) => { + expect(el.position).to.eql(positions[index]); + }); + }); + }); + + describe('allows configuration that', () => { + it('generates the correct number of horizontal meridians given valid numbers', () => { + // Note that due to asethetic choices, if the number of circles + // provided is even, createHorizontalChordMeridians() will automatically + // adjust to building an odd number of meridians. + const lineValues = [4, 0, 17, 299, 4.123, 1]; + const expectedLineNumbers = [5, 0, 17, 299, 5, 1]; + lineValues.forEach((el, index) => { + const meridians = new Meridians( + DEFAULT_RADIUS, + el, + Orientation.HORIZONTAL + ); + expect(meridians.children.length).to.equal(expectedLineNumbers[index]); + }); + }); + + it('generates the correct number of vertical meridians given valid numbers', () => { + const lineValues = [4, 0, 17, 299, 4.123]; + const expectedLineNumbers = [4, 0, 18, 299, 4]; + lineValues.forEach((el, index) => { + const meridians = new Meridians( + DEFAULT_RADIUS, + el, + Orientation.VERTICAL + ); + expect(meridians.children.length).to.equal(expectedLineNumbers[index]); + }); + }); + + it('changes the number of horizontal chord meridians correctly with scale', () => { + const meridians = new Meridians( + DEFAULT_RADIUS, + 9, + Orientation.HORIZONTAL + ); + + const positions = [ + new Vector3(0, 0, 0), + new Vector3(0, 4.5, 0), + new Vector3(0, -4.5, 0), + new Vector3(0, 3.375, 0), + new Vector3(0, -3.375, 0), + new Vector3(0, 2.25, 0), + new Vector3(0, -2.25, 0), + new Vector3(0, 1.125, 0), + new Vector3(0, -1.125, 0), + ]; + + meridians.children.forEach((el, index) => { + expect(el.position).to.eql(positions[index]); + }); + }); + + describe('throws the correct errors if the user', () => { + it('gives invalid horizontal meridian inputs (-1, 301)', () => { + const lineValues = [-1, 301]; + const expectedErrorMessage = [ + 'A negative number of meridians are not supported', + 'Over 300 meridians are not supported', + ]; + lineValues.forEach((el, index) => { + expect(() => { + new Meridians(DEFAULT_RADIUS, el, Orientation.HORIZONTAL); + }).to.throw(expectedErrorMessage[index]); + }); + }); + + it('gives invalid vertical meridian inputs (-1, 301)', () => { + const lineValues = [-1, 301]; + const expectedErrorMessage = [ + 'A negative number of meridians are not supported', + 'Over 300 meridians are not supported', + ]; + lineValues.forEach((el, index) => { + expect(() => { + new Meridians(DEFAULT_RADIUS, el, Orientation.VERTICAL); + }).to.throw(expectedErrorMessage[index]); + }); + }); + + it('throws an error correctly if given a bad orientation enum in the constructor', () => { + expect(() => { + new Meridians(DEFAULT_RADIUS, DEFAULT_V_MERIDIANS, undefined!); + }).to.throw('Invalid orientation input in Meridians constructor'); + }); + }); + }); +}); diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/scene.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/scene.ts new file mode 100644 index 00000000000..05556f8c4ce --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/scene.ts @@ -0,0 +1,117 @@ +/* istanbul ignore file */ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {Scene, PerspectiveCamera, WebGLRenderer, Camera} from 'three'; +import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'; + +export class BlochSphereScene extends Scene { + private static readonly VIZ_WIDTH: number = 500; + private static readonly VIZ_HEIGHT: number = 500; + + // The Three.js camera object responsible for seeing the + // visualization from different locations + camera: Camera; + + // The Three.js renderer object resposible for rendering + // the visualization into the browser. We will be using the WebGL renderer + renderer: WebGLRenderer; + + // The Three.js OrbitControls object reponsible for managing + // mouse input and moving the camera, zooming in and out, etc. + controls: OrbitControls; + + /** + * Initializes a 3D Scene proportional to the bloch sphere visualzation. + * @param fov The vertical field of view for the Scene's Perspective Camera + * @param aspect The aspect ratio for the Scene's Perspective Camera + * @param near The near plane for the Scene's Perspective Camera + * @param far The far plane for the Scene's Perspective Camera + * @returns An instance of the scene. + */ + public constructor( + fov = 75, + aspect: number = BlochSphereScene.VIZ_HEIGHT / BlochSphereScene.VIZ_WIDTH, + near = 0.1, + far = 1000 + ) { + super(); + + this.camera = new PerspectiveCamera(fov, aspect, near, far); + this.renderer = new WebGLRenderer({alpha: true}); + this.renderer.setSize( + BlochSphereScene.VIZ_WIDTH, + BlochSphereScene.VIZ_HEIGHT + ); + this.controls = new OrbitControls(this.camera, this.renderer.domElement); + + this.init(); + + return this; + } + + /** + * Adds the 3D Scene to the HTML according to the given + * container id. + * @param id id of the container + */ + public addSceneToHTMLContainer(id: string) { + document.getElementById(id)!.appendChild(this.renderer.domElement); + } + /** + * Initialization helper function. Also sets the starting + * position of the camera. + */ + private init() { + this.camera.position.x = 6; + this.camera.position.y = 2; + this.camera.position.z = 2; + + this.setUpControls(); + this.setRenderSize(BlochSphereScene.VIZ_WIDTH, BlochSphereScene.VIZ_HEIGHT); + this.animate(); + } + + /** + * Handles setting up the controls for OrbitControls. + */ + private setUpControls() { + this.controls.enableDamping = true; + this.controls.dampingFactor = 0.05; + this.controls.screenSpacePanning = false; + this.controls.minDistance = 10; + this.controls.maxDistance = 50; + this.controls.maxPolarAngle = Math.PI; + } + + /** + * Configures the output canvas and the viewport + * to make sure that elements are rendered correctly + * onto the scene. + * @param width The desired width of the rendering canvas. + * @param height The desired height of the rendering canvas. + */ + public setRenderSize(width: number, height: number) { + this.renderer.setSize(width, height); + } + + /** + * Enables interactivity for the visualization. + */ + public animate() { + requestAnimationFrame(this.animate.bind(this)); + this.controls.update(); + this.renderer.render(this, this.camera); + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/sphere.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/sphere.ts new file mode 100644 index 00000000000..84341ebf7d6 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/sphere.ts @@ -0,0 +1,66 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {SphereGeometry, MeshNormalMaterial, Mesh, Group} from 'three'; + +/** + * Generates the sphere shape of the Bloch sphere. The radius is configurable. + */ +export class Sphere extends Group { + readonly radius: number; + + /** + * Class constructor + * @param radius the desired radius of the sphere + * @returns An instance of the class containing the generated sphere. This can be + * added to the Bloch sphere instance as well as the scene. + */ + constructor(radius: number) { + super(); + + if (radius < 1) { + throw new Error( + 'The radius of a Sphere must be greater than or equal to 1' + ); + } else { + this.radius = radius; + } + + this.createSphere(this.radius); + return this; + } + + /** + * Generates a sphere Mesh object, which serves as the foundation + * of the Bloch sphere visualization, adding the mesh object + * to the group. + * @param radius The desired radius of the overall bloch sphere. + */ + private createSphere(radius: number) { + const geometry = new SphereGeometry(radius, 32, 32); + const properties = { + opacity: 0.6, + transparent: true, + }; + + const material = new MeshNormalMaterial(properties); + + const mesh = new Mesh(geometry, material); + + // Smooth out the shape + mesh.geometry.computeVertexNormals(); + + this.add(mesh); + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/sphere_test.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/sphere_test.ts new file mode 100644 index 00000000000..19fd984080b --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/sphere_test.ts @@ -0,0 +1,79 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {expect} from 'chai'; +import {Sphere} from './sphere'; +import {Mesh, MeshNormalMaterial, SphereGeometry} from 'three'; + +function getSphereInfo(sphere: Sphere) { + const mesh = sphere.children[0] as Mesh; + const material = mesh.material as MeshNormalMaterial; + const geometry = mesh.geometry as SphereGeometry; + const params = geometry.parameters; + + return { + mesh: mesh, + material: material, + geometry: geometry, + params: params, + }; +} + +describe('Sphere', () => { + const DEFAULT_RADIUS = 5; + const sphere = new Sphere(DEFAULT_RADIUS); + + describe('by default', () => { + const sphereInfo = getSphereInfo(sphere); + + it('has a default radius of 5', () => { + expect(sphereInfo.params.radius).to.equal(5); + }); + + it('uses 32 width and height segments for the sphere shape', () => { + expect(sphereInfo.params.widthSegments).to.equal(32); + expect(sphereInfo.params.widthSegments).to.equal(32); + }); + + it('returns a transparent sphere', () => { + expect(sphereInfo.material.opacity).to.equal(0.6); + expect(sphereInfo.material.transparent).to.equal(true); + }); + }); + + describe('allows configuration that', () => { + it('accepts valid radius inputs (1, 14.2, 100)', () => { + const radiusInputs = [1, 14.2, 100]; + const expectedRadius = [1, 14.2, 100]; + + radiusInputs.forEach((el, index) => { + const sphere = new Sphere(el); + const sphereInfo = getSphereInfo(sphere); + + expect(sphereInfo.params.radius).to.equal(expectedRadius[index]); + }); + }); + + describe('throws the correct errors if the user', () => { + it('gives invalid radius inputs (-1, 0)', () => { + const radiusInputs = [-1, 0]; + const expectedErrorMessage = + 'The radius of a Sphere must be greater than or equal to 1'; + radiusInputs.forEach(el => { + expect(() => new Sphere(el)).to.throw(expectedErrorMessage); + }); + }); + }); + }); +}); diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/state_vector.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/state_vector.ts new file mode 100644 index 00000000000..4ec1d00bc2b --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/state_vector.ts @@ -0,0 +1,103 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {ArrowHelper, Vector3, Group, LineBasicMaterial} from 'three'; + +/** + * Generates a vector to add to the Bloch sphere. + * The length and direction of the vector are configurable. + */ +export class StateVector extends Group { + readonly blochSphereRadius: number; + readonly x: number; + readonly y: number; + readonly z: number; + + /** + * Class constructor. + * + * The x, y, and z coordinates provide the coordinates of the state vector + * within the Bloch sphere of unit radius (1). Since the Bloch sphere visualization + * can be any ThreeJS unit, we scale the vector along with it by specifying the + * radius of the Bloch sphere. + * + * @param x the x coordinate of the vector tip + * @param y the y coordinate of the vector tip + * @param z the z coordinate of the vector tip + * @param blochSphereRadius the radius of the Bloch sphere in which the state + * vector will be applied + * @returns An instance of the class containing the generated vector. This can be + * added to the Bloch sphere instance as well as the scene. + */ + constructor(x: number, y: number, z: number, blochSphereRadius: number) { + super(); + this.x = x; + this.y = y; + this.z = z; + this.blochSphereRadius = blochSphereRadius; + + this.generateVector(this.x, this.y, this.z, this.blochSphereRadius); + return this; + } + + /** + * Generates a vector starting at (0, 0) and ending at the coordinates + * of the given parameters, and adds to group. + * Utilizes three.js ArrowHelper function generate the vector. + * @param x The x coordinate of the vector tip. + * @param y The y coordinate of the vector tip. + * @param z The z coordinate of the vector tip. + * @param blochSphereRadius The radius of the Bloch sphere in which + * the state vector will be applied + */ + private generateVector( + x: number, + y: number, + z: number, + blochSphereRadius: number + ) { + const directionVector = new Vector3(x, y, z); + + // Apply a -90 degree correction rotation across the x axis + // to match coords of Cirq with coords of three.js scene. + // This is necessary to make sure the vector points to the correct state + const axis = new Vector3(1, 0, 0); + const angle = -Math.PI / 2; + directionVector.applyAxisAngle(axis, angle); + + // Set base properties of the vector + const origin = new Vector3(0, 0, 0); + const hex = '#800080'; + const headWidth = 1; + + // Calculate the distance, and alter it to be proportional + // to the length + const newLength = origin.distanceTo(directionVector) * blochSphereRadius; + + // Create the arrow representation of the vector and add it to the group + const arrowHelper = new ArrowHelper( + directionVector, + origin, + newLength, + hex, + undefined, + headWidth + ); + + const arrowLine = arrowHelper.line.material as LineBasicMaterial; + arrowLine.linewidth = 20; + + this.add(arrowHelper); + } +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/state_vector_test.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/state_vector_test.ts new file mode 100644 index 00000000000..837e02a8ac4 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/state_vector_test.ts @@ -0,0 +1,27 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {expect} from 'chai'; +import {ArrowHelper, Vector3} from 'three'; +import {StateVector} from './state_vector'; + +describe('StateVector', () => { + describe('by default', () => { + const vector = new StateVector(1, 1, 2, 5); + it('start at the correct point given arbitrary vector coordinates', () => { + const nestedVector = vector.children[0] as ArrowHelper; + expect(nestedVector.position).to.eql(new Vector3(0, 0, 0)); + }); + }); +}); diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/text.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/text.ts new file mode 100644 index 00000000000..b949c8f1ef0 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/text.ts @@ -0,0 +1,96 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {Vector3, Sprite, Texture, SpriteMaterial, Group} from 'three'; + +/** + * Generates and groups together many Labels. This should be used when generating + * labels for dedicated visualizations. + */ +export class Labels extends Group { + readonly labels: Object; + + /** + * Class constructor. + * @param labels An object mapping the desired text to be rendered with its location on the scene + * @returns an instance of the class containing all of the generated labels. All labels can + * be added to the Bloch sphere instance as well as the scene. + */ + constructor(labels: Object) { + super(); + this.labels = labels; + + this.generateLabels(this.labels); + return this; + } + + private generateLabels(labels: Object) { + for (const [text, location] of Object.entries(labels)) { + this.add(new Label(text, location)); + } + } +} + +/** + * Creates a Sprite rendering of text which can be added to the visualization. + * For dedicated visualizations, all Labels should be grouped together and created + * by using the Labels constructor. + */ +export class Label extends Sprite { + readonly text: string; + + /** + * Class constructor + * @param text The text of the label + * @param positionVector The position of the vector as a Vector3 object + * @returns a Label object with specified text and position. + */ + constructor(text: string, positionVector: Vector3) { + const material = createSpriteMaterial(text); + super(material); + this.text = text; + this.position.copy(positionVector); + return this; + } +} + +/** + * Turns text into a Sprite. + * @param text The text you want to turn into a Sprite. + * @returns A SpriteMaterial that can be rendered by three.js + */ +function createSpriteMaterial(text: string) { + const CANVAS_SIZE = 256; + + const canvas = document.createElement('canvas'); + canvas.width = CANVAS_SIZE; + canvas.height = CANVAS_SIZE; + // Allows us to keep track of what we're adding to the + // canvas. + canvas.textContent = text; + + const context = canvas.getContext('2d')!; + context.fillStyle = '#000000'; + context.textAlign = 'center'; + context.font = '120px Arial'; + context.fillText(text, CANVAS_SIZE / 2, CANVAS_SIZE / 2); + + const map = new Texture(canvas); + map.needsUpdate = true; + + return new SpriteMaterial({ + map: map, + transparent: true, // for a transparent canvas background + }); +} diff --git a/cirq-web/cirq_ts/src/bloch_sphere/components/text_test.ts b/cirq-web/cirq_ts/src/bloch_sphere/components/text_test.ts new file mode 100644 index 00000000000..41bac7cc680 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/components/text_test.ts @@ -0,0 +1,42 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {expect} from 'chai'; +import {Labels, Label} from './text'; +import {JSDOM} from 'jsdom'; +import {Vector3} from 'three'; + +/** + * Using JSDOM to create a global document which the canvas elements + * generated can be created on. + */ +const {window} = new JSDOM(''); +global.document = window.document; + +describe('Labels', () => { + const mockCanvas = document.createElement('canvas'); + mockCanvas.width = 256; + mockCanvas.height = 256; + + const mock_label = { + test: new Vector3(0, 0, 0), + }; + const labels = new Labels(mock_label); + + it('successfully generates an arbitrary label at the correct position', () => { + const label = labels.children[0] as Label; + expect(label.position).to.eql(new Vector3(0, 0, 0)); + expect(label.text).to.equal(Object.keys(mock_label)[0]); + }); +}); diff --git a/cirq-web/cirq_ts/src/bloch_sphere/main.ts b/cirq-web/cirq_ts/src/bloch_sphere/main.ts new file mode 100644 index 00000000000..c33a605e138 --- /dev/null +++ b/cirq-web/cirq_ts/src/bloch_sphere/main.ts @@ -0,0 +1,42 @@ +// Copyright 2021 The Cirq Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {BlochSphereScene} from './components/scene'; +import {BlochSphere} from './bloch_sphere'; + +/** + * Adds a Bloch sphere element with relevant, configurable data for the + * sphere shape of the Bloch sphere and the state vector displayed with it. + * These elements are added to a Scene object, which is added to the DOM + * tree in the BlochSphereScene class. + * @param containerId A string containing the container (div, span, etc.) id that + * will contain the visualization output. + * @param radius The radius of the bloch sphere + * @param hMeridians The designated number of horizontal meridians of the Bloch sphere + * @param vMeridians The designated number of vertical meridians in the Bloch sphere + */ +export function renderBlochSphere( + containerId: string, + radius = 5, + hMeridians = 7, + vMeridians = 4 +) { + const scene = new BlochSphereScene(); + scene.addSceneToHTMLContainer(containerId); + + const blochSphere = new BlochSphere(radius, hMeridians, vMeridians); + scene.add(blochSphere); + + return blochSphere; +} diff --git a/cirq-web/cirq_ts/tsconfig.json b/cirq-web/cirq_ts/tsconfig.json new file mode 100644 index 00000000000..bb957e0db21 --- /dev/null +++ b/cirq-web/cirq_ts/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./node_modules/gts/tsconfig-google.json", + "compilerOptions": { + "lib": ["es5", "es6", "dom"], + "target": "es6", + "module": "commonjs", + "allowJs": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "downlevelIteration": true, + "declaration": false, + "sourceMap": false, + "outDir": "build", + }, + "include": [ + "src/**/*.ts", + ], +} diff --git a/cirq-web/cirq_ts/webpack.config.js b/cirq-web/cirq_ts/webpack.config.js new file mode 100644 index 00000000000..302f1ee2887 --- /dev/null +++ b/cirq-web/cirq_ts/webpack.config.js @@ -0,0 +1,31 @@ +const path = require('path'); + +module.exports = { + entry: { + bloch_sphere: './src/bloch_sphere/main.ts', + }, + devServer: { + static: path.join(__dirname, 'dist'), + public: 'localhost:8080', + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, + output: { + filename: '[name].bundle.js', + library: { + type: 'global', + }, + path: path.resolve(__dirname, 'dist'), + publicPath: '/', + }, +}; diff --git a/cirq-web/cirq_web/README.md b/cirq-web/cirq_web/README.md new file mode 100644 index 00000000000..dad7b3a1924 --- /dev/null +++ b/cirq-web/cirq_web/README.md @@ -0,0 +1,80 @@ +## Cirq Visualizations + +*This directory contains the code and instructions for calling Typescript visualization in Cirq using Python. While necessary, this is only half of the code needed to run the `cirq-web` project. For information on how to integrate projects here with Python and the wider Cirq package, see the `cirq_ts` directory.* + +The `cirq_web` package runs separately from the rest of Cirq, and can be used on an opt-in basis with the rest of the project. + +### Module build structure + +A reference for the build structure of a module is the Bloch sphere. Reference the `bloch_sphere/` directory to see the code. Modules should: + - Abide by Cirq convention in terms of testing, styling, and initialization files. + - Contain a "root" folder labeled according to the title of the module. In the case of the Bloch sphere, this is `bloch_sphere/`. + - Contain a main class that contains the code for the visualization. All supporting files should be imported into this class. In the case of the Bloch sphere, this is `cirq_bloch_sphere.py`. + - Make sure that any additional modules and files are in separate subdirectories labeled accordingly. + +### Developing Python modules for visualization + +In order to actually get visualization output from our Python calls, we return strings of HTML and Javascript. In order to keep things organized, we include a parent class `Widget` (`widget.py`) which handles the configuration behind locating and reading files so that only code specific to each visualization lives in its main class. + +The main class for all visualizations should inherit from the `Widget` class located in this directory. Upon creating a new Widget, you should include a call to initialize the parent like so: +```python +class MyWidget(widget.Widget): + def __init__(self,...): + ... + super().__init__('cirq_ts/dist/YOUR_BUNDLE_FILE.js') + ... +``` +This will allow you to easily access the absolute path to your bundle file, which should always be used when referencing it, with `self.bundle_file_path`. It also allows you to call `super().get_bundle_script()` to easily +get full script content of your widget as a string. These methods should be used when generating your HTML outputs for boththe web browser and notebooks. + +The `widget.py` file also contains methods like `write_output_file()` that should be used for all visualizations, as well as some other helpful methods. + +### Handling HTML output from Python +#### Viewing a visualization in a notebook setting +We capitalize on IPython's `_repr_html_` magic method to help display visualizations in the notebook. In the main class of your visualization, include a method like so: +```python +from cirq_web import widget +class MyWidget(widget.Widget): + ... + def _repr_html_(self): + bundle_script = super().get_bundle_script() + return f""" + +
+ {bundle_script} + + """ + + def get_widget_bundle_name(self) -> str: + return 'bloch_sphere.bundle.js' diff --git a/cirq-web/cirq_web/bloch_sphere/bloch_sphere_test.py b/cirq-web/cirq_web/bloch_sphere/bloch_sphere_test.py new file mode 100644 index 00000000000..580de0a9ae5 --- /dev/null +++ b/cirq-web/cirq_web/bloch_sphere/bloch_sphere_test.py @@ -0,0 +1,77 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math + +import pytest +import numpy as np + +import cirq_web + +from cirq.qis import to_valid_state_vector +from cirq.qis.states import bloch_vector_from_state_vector + + +def test_init_bloch_sphere_type(): + state_vector = to_valid_state_vector([math.sqrt(2) / 2, math.sqrt(2) / 2]) + bloch_sphere = cirq_web.BlochSphere(state_vector=state_vector) + assert isinstance(bloch_sphere, cirq_web.BlochSphere) + + +@pytest.mark.parametrize('sphere_radius', [5, 0.2, 100]) +def test_valid_bloch_sphere_radius(sphere_radius): + state_vector = to_valid_state_vector([math.sqrt(2) / 2, math.sqrt(2) / 2]) + bloch_sphere = cirq_web.BlochSphere(sphere_radius, state_vector) + assert sphere_radius == bloch_sphere.sphere_radius + + +@pytest.mark.parametrize('sphere_radius', [0, -1]) +def test_invalid_bloch_sphere_radius(sphere_radius): + with pytest.raises(ValueError): + cirq_web.BlochSphere(sphere_radius=sphere_radius) + + +@pytest.mark.parametrize( + 'state_vector', [to_valid_state_vector([math.sqrt(2) / 2, math.sqrt(2) / 2])] +) +def test_valid_bloch_sphere_vector(state_vector): + bloch_sphere = cirq_web.BlochSphere(state_vector=state_vector) + bloch_vector = bloch_vector_from_state_vector(state_vector, 0) + assert np.array_equal(bloch_vector, bloch_sphere.bloch_vector) + + +def test_no_state_vector_given(): + with pytest.raises(ValueError): + cirq_web.BlochSphere() + + +def test_bloch_sphere_default_client_code(): + state_vector = to_valid_state_vector([math.sqrt(2) / 2, math.sqrt(2) / 2]) + bloch_sphere = cirq_web.BlochSphere(state_vector=state_vector) + + expected_client_code = f""" + + """ + + assert expected_client_code == bloch_sphere.get_client_code() + + +def test_bloch_sphere_default_bundle_name(): + state_vector = to_valid_state_vector([math.sqrt(2) / 2, math.sqrt(2) / 2]) + bloch_sphere = cirq_web.BlochSphere(state_vector=state_vector) + + assert bloch_sphere.get_widget_bundle_name() == 'bloch_sphere.bundle.js' diff --git a/cirq-web/cirq_web/widget.py b/cirq-web/cirq_web/widget.py new file mode 100644 index 00000000000..6cf02602d76 --- /dev/null +++ b/cirq-web/cirq_web/widget.py @@ -0,0 +1,123 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from abc import ABC, abstractmethod +from pathlib import Path +from enum import Enum +import os +import uuid +import webbrowser + +import cirq_web + +# Resolve the path so the bundle file can be accessed properly +_DIST_PATH = Path(cirq_web.__file__).parents[1] / "cirq_ts" / "dist" + + +class Env(Enum): + JUPYTER = 1 + COLAB = 2 + OTHER = 3 + + +class Widget(ABC): + """Abstract class for all widgets.""" + + def __init__(self): + """Initializes a Widget. + + Gives a widget a unique ID. + """ + # Generate a unique UUID for every instance of a Widget. + # This helps with adding visualizations to scenes, etc. + self.id = str(uuid.uuid1()) + + @abstractmethod + def get_client_code(self) -> str: + raise NotImplementedError() + + @abstractmethod + def get_widget_bundle_name(self) -> str: + raise NotImplementedError() + + def _repr_html_(self): + """Allows the object's html to be easily displayed in a notebook + by using the display() method. + """ + client_code = self.get_client_code() + return self._create_html_content(client_code) + + def generate_html_file( + self, + output_directory: str = './', + file_name: str = 'bloch_sphere.html', + open_in_browser: bool = False, + ) -> str: + """Generates a portable HTML file of the widget that + can be run anywhere. Prints out the absolute path of the file to the console. + + Args: + output_directory: the directory in which the output file will be + generated. The default is the current directory ('./') + + file_name: the name of the output file. Default is 'bloch_sphere' + + open_in_browser: if True, opens the newly generated file automatically in the browser. + + Returns: + The path of the HTML file in as a Path object. + """ + client_code = self.get_client_code() + contents = self._create_html_content(client_code) + path_of_html_file = os.path.join(output_directory, file_name) + with open(path_of_html_file, 'w', encoding='utf-8') as f: + f.write(contents) + + if open_in_browser: + webbrowser.open(path_of_html_file, new=2) + + return path_of_html_file + + def _get_bundle_script(self): + """Returns the bundle script of a widget""" + bundle_filename = self.get_widget_bundle_name() + return _to_script_tag(bundle_filename) + + def _create_html_content(self, client_code: str) -> str: + div = f""" + +
+ """ + + bundle_script = self._get_bundle_script() + + return div + bundle_script + client_code + + +def _to_script_tag(bundle_filename: str) -> str: + """Dumps the contents of a particular bundle file into a script tag. + + Args: + path: the path to the bundle file + + Returns: + The bundle file as string (readable by browser) wrapped in HTML script tags. + """ + bundle_file_path = os.path.join(_DIST_PATH, bundle_filename) + bundle_file = open(bundle_file_path, 'r', encoding='utf-8') + bundle_file_contents = bundle_file.read() + bundle_file.close() + bundle_html = f'' + + return bundle_html diff --git a/cirq-web/cirq_web/widget_test.py b/cirq-web/cirq_web/widget_test.py new file mode 100644 index 00000000000..679f2294e70 --- /dev/null +++ b/cirq-web/cirq_web/widget_test.py @@ -0,0 +1,71 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path +import cirq_web + + +class FakeWidget(cirq_web.Widget): + def __init__(self): + super().__init__() + + def get_client_code(self) -> str: + return "This is the test client code." + + def get_widget_bundle_name(self) -> str: + return "testfile.txt" + + +def remove_whitespace(string: str) -> str: + return "".join(string.split()) + + +def test_repr_html(tmpdir): + # # Reset the path so the files are accessible + cirq_web.widget._DIST_PATH = Path(tmpdir) / "dir" + path = tmpdir.mkdir('dir').join('testfile.txt') + path.write("This is a test bundle") + + test_widget = FakeWidget() + actual = test_widget._repr_html_() + + expected = f""" + +
+ + This is the test client code. + """ + + assert remove_whitespace(expected) == remove_whitespace(actual) + + +def test_generate_html_file_with_browser(tmpdir): + # # Reset the path so the files are accessible + cirq_web.widget._DIST_PATH = Path(tmpdir) / "dir" + path = tmpdir.mkdir('dir') + + testfile_path = path.join('testfile.txt') + testfile_path.write("This is a test bundle") + + test_widget = FakeWidget() + test_html_path = test_widget.generate_html_file(str(path), 'test.html', open_in_browser=True) + actual = open(test_html_path, 'r', encoding='utf-8').read() + + expected = f""" + +
+ + This is the test client code. + """ + assert remove_whitespace(expected) == remove_whitespace(actual) diff --git a/cirq-web/example.ipynb b/cirq-web/example.ipynb new file mode 100644 index 00000000000..01acefcce86 --- /dev/null +++ b/cirq-web/example.ipynb @@ -0,0 +1,163 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9e249d2a", + "metadata": {}, + "source": [ + "#### Copyright 2021 The Cirq Developers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7e0938c", + "metadata": {}, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "id": "fbf7b67d", + "metadata": {}, + "source": [ + "### Cirq-web visualization" + ] + }, + { + "cell_type": "markdown", + "id": "e4bf9c7d", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " View on QuantumAI\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + " \n", + " Download notebook\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be79673f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "!pip install --quiet cirq --pre" + ] + }, + { + "cell_type": "markdown", + "id": "cd34407d", + "metadata": {}, + "source": [ + "Note: this notebook relies on unreleased Cirq features. If you want to try these features, make sure you install cirq via `pip install cirq --pre`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9a3e9f86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from cirq_web import BlochSphere\n", + "import cirq\n", + "import math\n", + "\n", + "\n", + "random_vector = cirq.testing.random_superposition(2)\n", + "\n", + "r2o2 = math.sqrt(2)/2\n", + "i = (0 + 1j)\n", + "\n", + "zero_state = [1+0j, 0+0j]\n", + "one_state = [0+0j, 1+0j]\n", + "\n", + "plus_state = [r2o2+0j, r2o2+0j]\n", + "minus_state = [r2o2+0j, -r2o2+0j]\n", + "\n", + "i_plus_state = [(r2o2+0j), i*(r2o2+0j)]\n", + "i_minus_state = [(r2o2+0j), i*(-r2o2+0j)]\n", + "\n", + "state_vector = cirq.to_valid_state_vector(plus_state)\n", + "sphere=BlochSphere(state_vector=state_vector)\n", + "display(sphere)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e65b53a4", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/cirq-web/requirements.txt b/cirq-web/requirements.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cirq-web/setup.cfg b/cirq-web/setup.cfg new file mode 100644 index 00000000000..0c9e0fc1447 --- /dev/null +++ b/cirq-web/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +license_file = LICENSE diff --git a/cirq-web/setup.py b/cirq-web/setup.py new file mode 100644 index 00000000000..7fb16efd672 --- /dev/null +++ b/cirq-web/setup.py @@ -0,0 +1,73 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from setuptools import find_packages, setup + +# This reads the __version__ variable from cirq/_version.py +__version__ = '' +exec(open('cirq_web/_version.py').read()) + +name = 'cirq-web' + +description = 'Web-based 3D visualization tools for Cirq.' + +# README file as long_description. +long_description = open('README.rst', encoding='utf-8').read() + +# If CIRQ_PRE_RELEASE_VERSION is set then we update the version to this value. +# It is assumed that it ends with one of `.devN`, `.aN`, `.bN`, `.rcN` and hence +# it will be a pre-release version on PyPi. See +# https://packaging.python.org/guides/distributing-packages-using-setuptools/#pre-release-versioning +# for more details. +if 'CIRQ_PRE_RELEASE_VERSION' in os.environ: + __version__ = os.environ['CIRQ_PRE_RELEASE_VERSION'] + long_description = ( + "**This is a development version of cirq-web and may be " + "unstable.**\n\n**For the latest stable release of cirq-web " + "see**\n`here `__.\n\n" + long_description + ) + +# Read in requirements +requirements = open('requirements.txt').readlines() +requirements = [r.strip() for r in requirements] + +# Sanity check +assert __version__, 'Version string cannot be empty' + +# This is a pure metapackage that installs all our packages +requirements += [f'cirq-core=={__version__}'] + +# Gather all packages from cirq_web, and the dist/ folder from cirq_ts +# which contains all of the bundle files +packs = ( + ['cirq_web'] + + ['cirq_web.' + package for package in find_packages(where='cirq_web')] + + ['cirq_ts'] +) + +setup( + name=name, + version=__version__, + url='http://github.com/quantumlib/cirq', + author='The Cirq Developers', + author_email='cirq-dev@googlegroups.com', + python_requires=('>=3.6.0'), + install_requires=requirements, + license='Apache 2', + description=description, + long_description=long_description, + packages=packs, + package_data={'cirq_web': ['dist/*'], 'cirq_ts': ['dist/*.bundle.js']}, +) diff --git a/dev_tools/Dockerfile b/dev_tools/Dockerfile deleted file mode 100644 index 6cd64b05c83..00000000000 --- a/dev_tools/Dockerfile +++ /dev/null @@ -1,48 +0,0 @@ -FROM python:3.8-slim AS compile-image - -# Install dependencies. -# rm -rf /var/lib/apt/lists/* cleans up apt cache. See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ -RUN DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ - python3-pip \ - python3-tk \ - texlive-latex-base \ - latexmk \ - git \ - locales \ - && rm -rf /var/lib/apt/lists/* - - -# Configure UTF-8 encoding. -RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 - -# Make python3 default -RUN rm -f /usr/bin/python \ - && ln -s /usr/bin/python3 /usr/bin/python - -# Create a virtual enironment to copy over into -# the final docker image -RUN python -m venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" - -# Copy current folder instead of cloning. -COPY ./ . -COPY requirements.txt . -COPY ./cirq/contrib/contrib-requirements.txt . -COPY ./dev_tools/conf/pip-list-dev-tools.txt . - -RUN pip3 install -r requirements.txt -r contrib-requirements.txt -r pip-list-dev-tools.txt - -# Install cirq -RUN pip3 install cirq - -FROM python:3.8-slim AS build-image -COPY --from=compile-image /opt/venv /opt/venv - -# Make sure scripts in .local are usable: -ENV PATH="/opt/venv/bin:$PATH" - -WORKDIR /Cirq -EXPOSE 8888 \ No newline at end of file diff --git a/dev_tools/auto_merge.py b/dev_tools/auto_merge.py index b7273c7c700..2d618fbe244 100644 --- a/dev_tools/auto_merge.py +++ b/dev_tools/auto_merge.py @@ -358,8 +358,9 @@ def get_pr_checks(pr: PullRequestDetails) -> Dict[str, Any]: References: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref """ - url = "https://api.github.com/repos/{}/{}/commits/{}/check-runs?access_token={}".format( - pr.repo.organization, pr.repo.name, pr.branch_sha, pr.repo.access_token + url = ( + f"https://api.github.com/repos/{pr.repo.organization}/{pr.repo.name}" + f"/commits/{pr.branch_sha}/check-runs?per_page=100&access_token={pr.repo.access_token}" ) response = requests.get(url, headers={'Accept': 'application/vnd.github.antiope-preview+json'}) diff --git a/dev_tools/codeowners_test.py b/dev_tools/codeowners_test.py index 69d373ac176..bd535179ecb 100644 --- a/dev_tools/codeowners_test.py +++ b/dev_tools/codeowners_test.py @@ -49,20 +49,27 @@ ("cirq-core/setup.py", BASE_MAINTAINERS), ("cirq-core/cirq/contrib/__init__.py", BASE_MAINTAINERS), ("docs/_book.yaml", DOCS_MAINTAINERS), + # qcvv ("cirq-core/cirq/experiments/__init__.py", QCVV_MAINTAINERS), ("docs/qcvv/isolated_xeb.ipynb", QCVV_MAINTAINERS.union(DOCS_MAINTAINERS)), + # aqt ("cirq-aqt/cirq_aqt/__init__.py", AQT_MAINTAINERS), ("cirq-aqt/setup.py", AQT_MAINTAINERS), ("docs/aqt/access.md", AQT_MAINTAINERS.union(DOCS_MAINTAINERS)), ("docs/tutorials/aqt/getting_started.ipynb", AQT_MAINTAINERS.union(DOCS_MAINTAINERS)), - ("cirq-core/cirq/pasqal/__init__.py", PASQAL_MAINTAINERS), + # pasqal + ("cirq-pasqal/cirq_pasqal/__init__.py", PASQAL_MAINTAINERS), + ("cirq-pasqal/setup.py", PASQAL_MAINTAINERS), ("docs/pasqal/access.md", PASQAL_MAINTAINERS.union(DOCS_MAINTAINERS)), ("docs/tutorials/pasqal/getting_started.ipynb", PASQAL_MAINTAINERS.union(DOCS_MAINTAINERS)), + # ionq ("cirq-ionq/cirq_ionq/__init__.py", IONQ_MAINTAINERS), ("cirq-ionq/setup.py", IONQ_MAINTAINERS), ("docs/ionq/access.md", IONQ_MAINTAINERS.union(DOCS_MAINTAINERS)), ("docs/tutorials/ionq/getting_started.ipynb", IONQ_MAINTAINERS.union(DOCS_MAINTAINERS)), + # google ("cirq-google/cirq_google/__init__.py", GOOGLE_MAINTAINERS), + ("cirq-google/setup.py", GOOGLE_MAINTAINERS), ("docs/google/access.md", GOOGLE_MAINTAINERS.union(DOCS_MAINTAINERS)), ("docs/tutorials/google/start.ipynb", GOOGLE_MAINTAINERS.union(DOCS_MAINTAINERS)), ], diff --git a/dev_tools/docker_test.py b/dev_tools/docker_test.py new file mode 100644 index 00000000000..4869441d1fc --- /dev/null +++ b/dev_tools/docker_test.py @@ -0,0 +1,39 @@ +import subprocess +import pathlib +import platform +import pytest + + +def test_docker_stable(): + if platform.system() != 'Linux': + pytest.skip("Unsupported os") + root_folder = pathlib.Path(__file__).parent.parent + buildResult = subprocess.run( + ['docker', 'build', '--target', 'cirq_stable', '-t', 'cirq_image', '.'], cwd=root_folder + ) + assert buildResult.returncode == 0 + + result = subprocess.run( + ['docker run cirq_image python -c "import cirq; assert cirq.__version__ is not None"'], + cwd=root_folder, + shell=True, + ) + assert result.returncode == 0 + + +def test_docker_pre(): + if platform.system() != 'Linux': + pytest.skip("Unsupported os") + root_folder = pathlib.Path(__file__).parent.parent + buildResult = subprocess.run( + ['docker', 'build', '--target', 'cirq_pre_release', '-t', 'cirq_image_pre', '.'], + cwd=root_folder, + ) + assert buildResult.returncode == 0 + + result = subprocess.run( + ['docker run cirq_image_pre python -c "import cirq; assert cirq.__version__ is not None"'], + cwd=root_folder, + shell=True, + ) + assert result.returncode == 0 diff --git a/dev_tools/modules.py b/dev_tools/modules.py index 17a605f3ff5..85f40f233f9 100644 --- a/dev_tools/modules.py +++ b/dev_tools/modules.py @@ -27,8 +27,10 @@ optional arguments: -h, --help show this help message and exit --mode {folder,package-path} - 'folder' to list root folder for module, 'package-path' for top level - python package path + 'folder' to list root folder for module (e.g. cirq-google), + 'package-path' for top level python package path + (e.g. cirq-google/cirq_google), + 'package' for top level python package (e.g cirq_google), --include-parent whether to include the parent package or not """ @@ -41,6 +43,7 @@ _FOLDER = 'folder' _PACKAGE_PATH = 'package-path' +_PACKAGE = 'package' @dataclasses.dataclass @@ -142,6 +145,9 @@ def _print_list_modules(mode: str, include_parent: bool = False): elif mode == _PACKAGE_PATH: for p in m.top_level_package_paths: print(p, end=" ") + elif mode == _PACKAGE: + for package in m.top_level_packages: + print(package, end=" ") def main(argv: List[str]): @@ -169,10 +175,11 @@ def _add_list_modules_cmd(subparsers): list_modules_cmd.add_argument( "--mode", default=_FOLDER, - choices=[_FOLDER, _PACKAGE_PATH], + choices=[_FOLDER, _PACKAGE_PATH, _PACKAGE], type=str, - help="'folder' to list root folder for module,\n" - "'package-path' for top level python package path", + help="'folder' to list root folder for module (e.g. cirq-google),\n" + "'package-path' for top level python package path (e.g. cirq-google/cirq_google),\n" + "'package' for top level python package (e.g cirq_google),\n", ) list_modules_cmd.add_argument( "--include-parent", diff --git a/dev_tools/modules_test.py b/dev_tools/modules_test.py index 881a336e9ac..534f2a0d62d 100644 --- a/dev_tools/modules_test.py +++ b/dev_tools/modules_test.py @@ -113,6 +113,10 @@ def test_main(): modules.main(["list", "--mode", "folder", "--include-parent"]) assert output.getvalue() == ' '.join(["mod1", "mod2", ".", ""]) + with mock.patch('sys.stdout', new=StringIO()) as output: + modules.main(["list", "--mode", "package"]) + assert output.getvalue() == ' '.join(["pack1", "pack2", ""]) + @chdir(target_dir=None) def test_error(): diff --git a/dev_tools/notebooks/isolated_notebook_test.py b/dev_tools/notebooks/isolated_notebook_test.py index 245090eafee..50f06727d93 100644 --- a/dev_tools/notebooks/isolated_notebook_test.py +++ b/dev_tools/notebooks/isolated_notebook_test.py @@ -47,6 +47,8 @@ "docs/noise.ipynb", "docs/operators_and_observables.ipynb", "docs/tutorials/educators/intro.ipynb", + # Cirq web is a new module, notebook will be moved to docs/ + "cirq-web/example.ipynb", ] # By default all notebooks should be tested, however, this list contains exceptions to the rule @@ -105,7 +107,7 @@ def _find_base_revision(): def _list_changed_notebooks() -> Set[str]: try: rev = _find_base_revision() - output = subprocess.check_output(f'git diff --name-only {rev}'.split()) + output = subprocess.check_output(f'git diff --diff-filter=d --name-only {rev}'.split()) lines = output.decode('utf-8').splitlines() # run all tests if this file or any of the dev tool dependencies change if any( diff --git a/dev_tools/packaging/packaging_test.sh b/dev_tools/packaging/packaging_test.sh new file mode 100755 index 00000000000..430d5cb86fc --- /dev/null +++ b/dev_tools/packaging/packaging_test.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +################################################################################ +# This script tests packaging. It creates the packages for all the cirq modules +# `pip install`s them in a clean virtual environment and then runs some simple +# verificiations on each of the modules, ensuring that they can be imported. +################################################################################ + +set -e + +# Temporary workspace. +tmp_dir=$(mktemp -d) +trap "{ rm -rf ${tmp_dir}; }" EXIT + +# New virtual environment +echo "Working in a fresh virtualenv at ${tmp_dir}/env" +virtualenv --quiet "--python=/usr/bin/python3" "${tmp_dir}/env" + +export CIRQ_PRE_RELEASE_VERSION=$(dev_tools/packaging/generate-dev-version-id.sh) +out_dir=${tmp_dir}/dist +dev_tools/packaging/produce-package.sh ${out_dir} $CIRQ_PRE_RELEASE_VERSION + +# test installation +"${tmp_dir}/env/bin/python" -m pip install ${out_dir}/* + +echo =========================== +echo Testing that code executes +echo =========================== + +"${tmp_dir}/env/bin/python" -c "import cirq; print(cirq.google.Foxtail)" +"${tmp_dir}/env/bin/python" -c "import cirq_google; print(cirq_google.Foxtail)" +"${tmp_dir}/env/bin/python" -c "import cirq; print(cirq.Circuit(cirq.CZ(*cirq.LineQubit.range(2))))" + +echo ======================================= +echo Testing that all modules are installed +echo ======================================= + +CIRQ_PACKAGES=$(env PYTHONPATH=. python dev_tools/modules.py list --mode package) +for p in $CIRQ_PACKAGES; do + echo --- Testing $p ----- + python_test="import $p; print($p); assert '${tmp_dir}' in $p.__file__, 'Package path seems invalid.'" + env PYTHONPATH='' "${tmp_dir}/env/bin/python" -c "$python_test" && echo -e "\033[32mPASS\033[0m" || echo -e "\033[31mFAIL\033[0m" +done \ No newline at end of file diff --git a/dev_tools/requirements/deps/dev-tools.txt b/dev_tools/requirements/deps/dev-tools.txt index 7537f2db700..e60ba7b55d2 100644 --- a/dev_tools/requirements/deps/dev-tools.txt +++ b/dev_tools/requirements/deps/dev-tools.txt @@ -5,13 +5,10 @@ -r protos.txt -r notebook.txt -r tensorflow-docs.txt +-r packaging.txt # For testing and analyzing code. asv -virtualenv - -# For uploading packages to pypi. -twine # For verifying behavior of Quil output. pyquil~=2.21.0 diff --git a/dev_tools/requirements/deps/flynt.txt b/dev_tools/requirements/deps/flynt.txt new file mode 100644 index 00000000000..870a67158c6 --- /dev/null +++ b/dev_tools/requirements/deps/flynt.txt @@ -0,0 +1 @@ +flynt~=0.60 \ No newline at end of file diff --git a/dev_tools/requirements/deps/format.txt b/dev_tools/requirements/deps/format.txt index 6ff28afd000..f1bf37a7f9d 100644 --- a/dev_tools/requirements/deps/format.txt +++ b/dev_tools/requirements/deps/format.txt @@ -1,2 +1,2 @@ -flynt~=0.60 +-r flynt.txt black==20.8b1 diff --git a/dev_tools/requirements/deps/ipython.txt b/dev_tools/requirements/deps/ipython.txt new file mode 100644 index 00000000000..9a59c33c71c --- /dev/null +++ b/dev_tools/requirements/deps/ipython.txt @@ -0,0 +1,3 @@ +# https://github.com/ipython/ipython/issues/12941 +ipython==7.22; python_version >= '3.7' +ipython; python_version < '3.7' diff --git a/dev_tools/requirements/deps/notebook.txt b/dev_tools/requirements/deps/notebook.txt index 1225dafb30c..bb7c4923a79 100644 --- a/dev_tools/requirements/deps/notebook.txt +++ b/dev_tools/requirements/deps/notebook.txt @@ -1,12 +1,10 @@ +-r ipython.txt + notebook~=6.2.0 # https://github.com/nteract/papermill/issues/519 ipykernel==5.3.4 -# https://github.com/ipython/ipython/issues/12941 -ipython==7.22; python_version >= '3.7' -ipython; python_version < '3.7' - # for executing notebooks in tests papermill~=2.3.2 diff --git a/dev_tools/requirements/deps/packaging.txt b/dev_tools/requirements/deps/packaging.txt new file mode 100644 index 00000000000..dda6ba8a2bb --- /dev/null +++ b/dev_tools/requirements/deps/packaging.txt @@ -0,0 +1,9 @@ +# for testing packaging in isolation +virtualenv + +# for creating packages +setuptools +wheel + +# for uploading packages to pypi +twine diff --git a/dev_tools/requirements/deps/tensorflow-docs.txt b/dev_tools/requirements/deps/tensorflow-docs.txt index ac550849f9d..2dd7ff1174a 100644 --- a/dev_tools/requirements/deps/tensorflow-docs.txt +++ b/dev_tools/requirements/deps/tensorflow-docs.txt @@ -1,2 +1,2 @@ # don't upgrade this just yet, anything later will require protobuf>=3.14 -git+https://github.com/tensorflow/docs@a90bcd30eb550f8d4ee335ff3daf18de5ca84b70 \ No newline at end of file +tensorflow-docs@git+https://github.com/tensorflow/docs@a90bcd30eb550f8d4ee335ff3daf18de5ca84b70 \ No newline at end of file diff --git a/dev_tools/requirements/pytest-minimal.env.txt b/dev_tools/requirements/pytest-minimal.env.txt index f3f82591907..94ba6b6a2be 100644 --- a/dev_tools/requirements/pytest-minimal.env.txt +++ b/dev_tools/requirements/pytest-minimal.env.txt @@ -7,4 +7,8 @@ # modules changed or not - for now we include all of them here too. -r deps/cirq-all-no-contrib.txt +-r deps/ipython.txt +# one of the _compat_test.py tests uses flynt for testing metadata + +-r deps/flynt.txt diff --git a/docs/_book.yaml b/docs/_book.yaml index c61e664afb5..8c285f4530f 100644 --- a/docs/_book.yaml +++ b/docs/_book.yaml @@ -182,14 +182,20 @@ upper_tabs: - heading: "Google hardware" - title: "Getting started with QCS" path: /cirq/tutorials/google/start - - title: "Floquet calibration" - path: /cirq/tutorials/google/floquet + - title: "Visualizing calibration metrics" + path: /cirq/tutorials/google/visualizing_calibration_metrics - title: "Qubit picking with Loschmidt echoes" path: /cirq/tutorials/google/echoes - - title: "Visualizing Calibration Metrics" - path: /cirq/tutorials/google/visualizing_calibration_metrics - - title: "Selecting qubits and calibrating circuits" - path: /cirq/tutorials/google/advanced_calibration + - title: "Calibration" + style: accordion + section: + - title: "Calibration: Overview and API" + path: /cirq/tutorials/google/calibration_api + - title: "Floquet calibration: Example and benchmark" + path: /cirq/tutorials/google/floquet_calibration_example + - title: "XEB calibration: Example and benchmark" + path: /cirq/tutorials/google/xeb_calibration_example + #### AQT HARDWARE #### - heading: "AQT hardware" diff --git a/docs/dev/development.md b/docs/dev/development.md index 22d77a9d0e7..11cc7e14d3c 100644 --- a/docs/dev/development.md +++ b/docs/dev/development.md @@ -24,13 +24,17 @@ git config blame.ignoreRevsFile .git-blame-ignore-revs Note that if you are using PyCharm, you might have to Restart & Invalidate Caches to have the change being picked up. ## Docker + You can build the stable and pre_release docker images with our `Dockerfile`. -To do your development in a Docker image, you can build one with our `Dockerfile`. ```bash - docker build -t cirq . - docker run -it cirq python -c "import cirq; print(cirq.google.Foxtail)" + docker build -t cirq --target cirq_stable . + docker run -it cirq python -c "import cirq_google; print(cirq_google.Foxtail)" ``` +```bash + docker build -t cirq_pre --target cirq_pre_release . + docker run -it cirq_pre python -c "import cirq_google; print(cirq_google.Foxtail)" +``` If you want to contribute changes to Cirq, you will instead want to fork the repository and submit pull requests from your fork. @@ -147,10 +151,6 @@ Cirq's protobufs live at [cirq/api/google](https://github.com/quantumlib/Cirq/tr If any protos are updated, their dependents can be rebuilt by calling the script [dev_tools/build-protos.sh](https://github.com/quantumlib/Cirq/tree/master/dev_tools). This script uses grpcio-tools and protobuf version 3.8.0 to generate the python proto api. -Additionally, for workflows that use bazel (relevant for C/C++ code depending on Cirq), we have made available bazel rulesets for generating both python and C/C++ proto apis. -These rules live in the BUILD files [here](https://github.com/quantumlib/Cirq/tree/master/cirq/api/google/v1) and [here](https://github.com/quantumlib/Cirq/tree/master/cirq/api/google/v2). -Downstream projects should load Cirq as an [external dependency](https://docs.bazel.build/versions/master/external.html), allowing rules from those BUILD files to be used directly. - ## Continuous integration and local testing There are a few options for running continuous integration checks, varying from easy and fast to slow and reliable. diff --git a/docs/google/best_practices.md b/docs/google/best_practices.md index 491739c02bd..07d0806a8b4 100644 --- a/docs/google/best_practices.md +++ b/docs/google/best_practices.md @@ -325,6 +325,8 @@ information on Virtual Z gates. Note that it is important to keep the single-qu two-qubit gates aligned (see above) while performing this procedure so that the circuit stays the same duration. -See tutorials on [floquet characterization](../tutorials/google/floquet.ipynb) -and [XEB](..//qcvv/xeb_coherent_noise.ipynb) for detailed instructions on how -to perform these procedures. +For more on calibration and detailed instructions on how to perform these procedures, see the following tutorials: + +* [Calibration API](../tutorials/google/calibration_api.ipynb) +* [Floquet calibration example](../tutorials/google/floquet_calibration_example.ipynb) +* [XEB calibration example](../tutorials/google/xeb_calibration_example.ipynb) diff --git a/docs/operators_and_observables.ipynb b/docs/operators_and_observables.ipynb index 86da445f445..2f04eb9db70 100644 --- a/docs/operators_and_observables.ipynb +++ b/docs/operators_and_observables.ipynb @@ -78,7 +78,8 @@ " print(\"installed cirq.\")\n", " import cirq\n", " \n", - "import numpy as np" + "import numpy as np\n", + "import sympy.parsing.sympy_parser as sympy_parser" ] }, { @@ -740,6 +741,28 @@ "psum.matrix()" ] }, + { + "cell_type": "markdown", + "metadata": { + "id": "ba00a12b9fbd" + }, + "source": [ + "A Pauli sum can also be constructed from a `Sympy` Boolean expression. This is based on \"On the representation of Boolean and real functions as Hamiltonians for quantum computing\" by Stuart Hadfield (https://arxiv.org/abs/1804.09130)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8da2814b60f3" + }, + "outputs": [], + "source": [ + "psum = cirq.PauliSum.from_boolean_expression(\n", + " sympy_parser.parse_expr('x0 ^ x1'),\n", + " {'x0': cirq.NamedQubit('q0'), 'x1': cirq.NamedQubit('q1')})" + ] + }, { "cell_type": "markdown", "metadata": { diff --git a/docs/support.md b/docs/support.md index dc7c20e1910..7e030020659 100644 --- a/docs/support.md +++ b/docs/support.md @@ -22,8 +22,8 @@ please ask us in the [Cirq Gitter channel](https://gitter.im/cirqdev). # Weekly developer meetings You may also be interested to join the weekly -[Cirq sync](https://groups.google.com/g/cirq-dev) with Cirq developers and +[Cirq sync](https://groups.google.com/g/cirq-dev/about) with Cirq developers and contributors. There are also weekly syncs for -[OpenFermion](https://groups.google.com/g/openfermion-dev), -[qsim](https://groups.google.com/g/qsim-qsimh-dev), and -[TensorFlow Quantum](https://groups.google.com/g/openfermion-dev). +[OpenFermion](https://groups.google.com/g/openfermion-dev/about), +[qsim](https://groups.google.com/g/qsim-qsimh-dev/about), and +[TensorFlow Quantum](https://groups.google.com/g/tfq-dev/about). diff --git a/docs/tutorials/google/advanced_calibration.ipynb b/docs/tutorials/google/advanced_calibration.ipynb deleted file mode 100644 index 82f51503211..00000000000 --- a/docs/tutorials/google/advanced_calibration.ipynb +++ /dev/null @@ -1,819 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "A0STV1dk8Wwi" - }, - "outputs": [], - "source": [ - "##### Copyright 2021 The Cirq Developers" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "cellView": "form", - "id": "cKNQ5_Ba8Ynl" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eLqunnmR8AH5" - }, - "source": [ - "# Selecting qubits and calibrating circuits for an experiment" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RdXA9tBC8Wjw" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " View on QuantumAI\n", - " \n", - " Run in Google Colab\n", - " \n", - " View source on GitHub\n", - " \n", - " Download notebook\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yKWtbKQB8Rej" - }, - "source": [ - "This tutorial shows how to select good qubits and calibrate circuits for an experiment. The high-level steps are:\n", - "\n", - "1. Using the latest calibration data, identify and select a good set of qubits.\n", - "2. Calibrate qubit pairs in your circuit using Floquet calibration and/or XEB calibration.\n", - "3. Run a Loschmidt echo benchmark with and without calibration.\n", - "5. Apply corrections to your circuit from the calibration which performs best on the Loschmidt echo.\n", - "\n", - "These steps are recommended as a minimum procedure for running on the [Quantum Computing Service (QCS)](https://quantumai.google/cirq/google/concepts). Additional steps to mitigating errors, perhaps specific to your experiment, can be added on top of this workflow." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "D7EQ-vixOSTR" - }, - "source": [ - "Disclaimer: The data shown in this tutorial is exemplary and not representative of the QCS in production." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "q8VcUax18RtL" - }, - "source": [ - "## Setup" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "Z1uKMxmu-1SC" - }, - "outputs": [], - "source": [ - "try:\n", - " import cirq\n", - "except ImportError:\n", - " !pip install cirq --quiet" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "cellView": "form", - "id": "wYSOLH5j-8uB" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting OAuth2 credentials.\n", - "Press enter after entering the verification code.\n", - "Authentication complete.\n", - "Successful authentication to Google Cloud.\n" - ] - } - ], - "source": [ - "# The Google Cloud Project id to use.\n", - "project_id = '' #@param {type:\"string\"}\n", - "\n", - "if project_id == '':\n", - " import os \n", - " if 'GOOGLE_CLOUD_PROJECT' not in os.environ:\n", - " raise Exception(\"Please setup project_id in this cell or set the `GOOGLE_CLOUD_PROJECT` env var to your project id.\")\n", - " project_id = os.environ['GOOGLE_CLOUD_PROJECT']\n", - "else: \n", - " import os\n", - " os.environ['GOOGLE_CLOUD_PROJECT'] = project_id\n", - "\n", - "def authenticate_user():\n", - " \"\"\"Runs the user through the Colab OAuth process.\n", - "\n", - " Checks for Google Application Default Credentials and runs interactive login \n", - " if the notebook is executed in Colab. In case the notebook is executed in Jupyter notebook\n", - " or other IPython runtimes, no interactive login is provided, it is assumed that the \n", - " `GOOGLE_APPLICATION_CREDENTIALS` env var is set or `gcloud auth application-default login`\n", - " was executed already.\n", - "\n", - " For more information on using Application Default Credentials see \n", - " https://cloud.google.com/docs/authentication/production\n", - " \"\"\"\n", - " in_colab = False\n", - " try:\n", - " from IPython import get_ipython\n", - " in_colab = 'google.colab' in str(get_ipython())\n", - " except: \n", - " # Notebook is not executed within IPython. Assuming external authentication.\n", - " return \n", - "\n", - " if in_colab: \n", - " from google.colab import auth \n", - " print(\"Getting OAuth2 credentials.\")\n", - " print(\"Press enter after entering the verification code.\")\n", - " auth.authenticate_user(clear_output=False)\n", - " print(\"Authentication complete.\")\n", - " else: \n", - " print(\"Notebook is not executed with Colab, assuming Application Default Credentials are set up.\") \n", - "\n", - "authenticate_user()\n", - "\n", - "print(\"Successful authentication to Google Cloud.\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "xRKjeIVd_FRo" - }, - "outputs": [], - "source": [ - "import cirq\n", - "from cirq.experiments import random_quantum_circuit_generation as rqcg\n", - "import cirq_google as cg\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import tqdm" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "cellView": "form", - "id": "PEuZQtn1Q4Hb" - }, - "outputs": [], - "source": [ - "#@title Helper functions\n", - "from typing import Optional, Sequence\n", - "\n", - "\n", - "def create_random_circuit(\n", - " qubits: Sequence[cirq.GridQubit],\n", - " cycles: int,\n", - " twoq_gate: cirq.Gate = cirq.FSimGate(np.pi / 4, 0.0),\n", - " seed: Optional[int] = None,\n", - ") -> cirq.Circuit:\n", - " return rqcg.random_rotations_between_grid_interaction_layers_circuit(\n", - " qubits, \n", - " depth=cycles,\n", - " two_qubit_op_factory=lambda a, b, _: twoq_gate.on(a, b),\n", - " pattern=cirq.experiments.GRID_STAGGERED_PATTERN,\n", - " single_qubit_gates=[cirq.PhasedXPowGate(phase_exponent=p, exponent=0.5)\n", - " for p in np.arange(-1.0, 1.0, 0.25)],\n", - " seed=seed\n", - " )\n", - "\n", - "\n", - "\n", - "def create_loschmidt_echo_circuit(\n", - " qubits: Sequence[cirq.GridQubit],\n", - " cycles: int,\n", - " twoq_gate: cirq.Gate = cirq.FSimGate(np.pi / 4, 0.0),\n", - " seed: Optional[int] = None,\n", - ") -> cirq.Circuit:\n", - " \"\"\"Returns a Loschmidt echo circuit using a random unitary U.\n", - "\n", - " Args:\n", - " qubits: Qubits to use.\n", - " cycles: Depth of random rotations in the forward & reverse unitary.\n", - " twoq_gate: Two-qubit gate to use.\n", - " pause: Optional duration to pause for between U and U^\\dagger.\n", - " seed: Seed for circuit generation.\n", - " \"\"\"\n", - " forward = create_random_circuit(qubits, cycles, twoq_gate, seed)\n", - " return forward + cirq.inverse(forward) + cirq.measure(*qubits, key=\"z\")\n", - "\n", - "\n", - "\n", - "def to_ground_state_prob(result: cirq.Result) -> float:\n", - " return np.mean(np.sum(result.measurements[\"z\"], axis=1) == 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KPcCbk-2_qp2" - }, - "source": [ - "## Select qubits" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "YmNhr6_W_ulI" - }, - "source": [ - "Below, select a processor and (comma-separated list of) calibration metric(s) to visualize the latest calibration report." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "g27X3DP7EPaH" - }, - "source": [ - "Note: All calibration metrics are defined in [this guide](https://quantumai.google/cirq/google/calibration). If you are unsure which metric to focus on for your experiment, we recommend `parallel_p00_error` and/or `parallel_p11_error` to eliminate qubits with high readout errors." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "cellView": "form", - "id": "C8namTwj_vTi" - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" - } - ], - "source": [ - "processor_id = \"\" #@param {type:\"string\"}\n", - "metrics = \"parallel_p00_error, two_qubit_sqrt_iswap_gate_xeb_pauli_error_per_cycle\" #@param {type:\"string\"}\n", - "metrics = [m.strip() for m in metrics.split(sep=\",\")]\n", - "\n", - "_, axes = plt.subplots(\n", - " nrows=1, ncols=len(metrics), figsize=(min(16, 8 * len(metrics)), 7)\n", - ")\n", - "\n", - "calibration = cg.get_engine_calibration(processor_id=processor_id)\n", - "for i, metric in enumerate(metrics):\n", - " calibration.heatmap(metric).plot(ax=axes[i] if len(metrics) > 1 else axes);" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "T9U6qwhmBLh3" - }, - "source": [ - "Using this report as a guide, select a set of qubits." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "v8rxDM_5AF4p" - }, - "outputs": [], - "source": [ - "# Select qubit indices here.\n", - "qubit_indices = [\n", - " (2, 5), (2, 6), (2, 7), (2, 8), (3, 8), \n", - " (3, 7), (3, 6), (3, 5), (4, 5), (4, 6) \n", - "]\n", - "qubits = [cirq.GridQubit(*idx) for idx in qubit_indices]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KkDve7PMEYWt" - }, - "source": [ - "## Calibrate qubit pairs" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JDTOQHi1FNQ1" - }, - "source": [ - "In the next cell, define the quantum circuit for your experiment." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "732937f838f6" - }, - "source": [ - "Note: If your circuit does not contain two-qubit gates between all possible pairs, use the example circuit below. This is because the Loschmidt echo circuits (defined below) contain all two-qubit pairs, so every characterization is needed." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "id": "AhziVqFiFaFy" - }, - "outputs": [], - "source": [ - "\"\"\"Define the quantum circuit for your experiment here.\"\"\"\n", - "circuit = create_random_circuit(qubits, cycles=10)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uc_5f4dKFL2V" - }, - "source": [ - "The structure of the circuit instructs the calibration API on which qubit pairs to calibrate. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vITaw59MFfVv" - }, - "source": [ - "### Floquet calibration" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lA23faaYGZzu" - }, - "source": [ - "Note: See the [Floquet calibration tutorial](https://quantumai.google/cirq/tutorials/google/floquet) for background about this method." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "aVrfHaUFJ0Oy" - }, - "source": [ - "Specify the parameters of the `cirq.FSimGate` to characterize with Floquet calibration below." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "id": "yAUnyScwpvVp" - }, - "outputs": [], - "source": [ - "floquet_options = cg.FloquetPhasedFSimCalibrationOptions(\n", - " characterize_theta=False,\n", - " characterize_zeta=True,\n", - " characterize_chi=False,\n", - " characterize_gamma=True,\n", - " characterize_phi=False,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_EfbiMWYRXFy" - }, - "source": [ - "Execute the next cell to run Floquet calibration." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "id": "wfvc31rMRb0f" - }, - "outputs": [], - "source": [ - "sampler = cg.get_engine_sampler(\n", - " project_id=project_id,\n", - " processor_id=processor_id, \n", - " gate_set_name=\"sqrt_iswap\",\n", - ")\n", - "\n", - "# Get characterization requests.\n", - "_, floquet_characterization_requests = cg.prepare_characterization_for_moments(\n", - " circuit,\n", - " options=floquet_options,\n", - ")\n", - "\n", - "# Characterize the requests on the engine.\n", - "floquet_characterizations = cg.run_calibrations(\n", - " floquet_characterization_requests, \n", - " sampler,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "stlpHK4GSFWe" - }, - "source": [ - "The `floquet_characterizations` can now be used to make corrections to circuits (shown below)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AByUxOocFMCx" - }, - "source": [ - "### XEB calibration" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SaqfXOaoHJf2" - }, - "source": [ - "Note: See the [XEB theory tutorial](https://quantumai.google/cirq/qcvv/xeb_theory) for background about this method." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ir3eBfioDx3y" - }, - "source": [ - "Specify the cycle depths and other options for XEB calibration below. Note that all `cirq.FSimGate` parameters are characterized by default." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "id": "by6KJHuxp9-I" - }, - "outputs": [], - "source": [ - "xeb_options = cg.LocalXEBPhasedFSimCalibrationOptions(\n", - " cycle_depths=(5, 25, 50, 100),\n", - " n_processes=1,\n", - " fsim_options=cirq.experiments.XEBPhasedFSimCharacterizationOptions(\n", - " characterize_theta=False,\n", - " characterize_zeta=True,\n", - " characterize_chi=True,\n", - " characterize_gamma=True,\n", - " characterize_phi=False,\n", - " ),\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "R1ReN-IWTRhR" - }, - "source": [ - "Execute the next cell to run XEB calibration." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "zNoPPuvlTfL3" - }, - "source": [ - "Note: The interface for running XEB calibration is identical to running Floquet calibration, just with different options.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "id": "kihkikIFTUCI" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 45/45 [00:48<00:00, 1.07s/it]\n", - "100%|██████████| 45/45 [00:45<00:00, 1.00s/it]\n", - "100%|██████████| 45/45 [00:55<00:00, 1.23s/it]\n", - "100%|██████████| 45/45 [07:04<00:00, 9.44s/it]\n" - ] - } - ], - "source": [ - "# Get characterization requests.\n", - "_, xeb_characterization_requests = cg.prepare_characterization_for_moments(\n", - " circuit,\n", - " options=xeb_options,\n", - ")\n", - "\n", - "# Characterize the requests on the engine.\n", - "xeb_characterizations = cg.run_calibrations(\n", - " xeb_characterization_requests, \n", - " sampler,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LaN29SWc_aPM" - }, - "source": [ - "The `xeb_characterizations` can now be used to make corrections to circuits (shown below)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "pxJTETNmQTg9" - }, - "source": [ - "## Run a Loschmidt echo benchmark" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ig0EjlmDIRm1" - }, - "source": [ - "Note: See the [Loschmidt echo tutorial](https://quantumai.google/cirq/tutorials/google/echoes) for background about this benchmark." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "M6l6yIQwKTwK" - }, - "source": [ - "Execute the following cells to run a Loschmidt echo benchmark with and without calibration to compare results." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "id": "YquQdZSqQXCq" - }, - "outputs": [], - "source": [ - "\"\"\"Setup the Loschmidt echo experiment.\"\"\"\n", - "cycle_values = range(0, 40 + 1, 4)\n", - "nreps = 20_000\n", - "trials = 10\n", - "\n", - "loschmidt_echo_batch = [\n", - " create_loschmidt_echo_circuit(qubits, cycles=c, seed=trial)\n", - " for trial in range(trials) for c in cycle_values\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "id": "TBhrYk3GsGry" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 3/3 [14:34<00:00, 291.52s/it]\n" - ] - } - ], - "source": [ - "\"\"\"Run a Loschmidt echo with no / Floquet / XEB calibration.\"\"\"\n", - "calibrations = {\n", - " \"No calibration\": (None, None), \n", - " \"Floquet calibration\": (floquet_options, floquet_characterizations),\n", - " \"XEB calibration\": (xeb_options, xeb_characterizations),\n", - "}\n", - "\n", - "all_probs = []\n", - "for options, characterizations in tqdm.tqdm(calibrations.values()):\n", - " if options is None:\n", - " batch = loschmidt_echo_batch\n", - " else:\n", - " # Apply corrections to the batch from the characterizations.\n", - " characterized_batch = [\n", - " cg.prepare_characterization_for_moments(circuit, options)[0] \n", - " for circuit in loschmidt_echo_batch\n", - " ]\n", - " batch = [\n", - " cg.make_zeta_chi_gamma_compensation_for_moments(circ, characterizations).circuit\n", - " for circ in characterized_batch\n", - " ]\n", - "\n", - " # Run the Loschmidt echo batch.\n", - " results = sampler.run_batch(programs=batch, repetitions=nreps)\n", - "\n", - " # Compute survival probabilities.\n", - " probs = [to_ground_state_prob(*res) for res in results]\n", - " all_probs.append(np.array(probs).reshape(trials, len(cycle_values)))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FV5HLCAD4hHh" - }, - "source": [ - "The next cell plots the results." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "id": "7hNzY6K1vh0V" - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" - } - ], - "source": [ - "for probs, label in zip(all_probs, calibrations.keys()):\n", - " plt.semilogy(cycle_values, np.average(probs, axis=0), lw=3, label=label)\n", - "\n", - "plt.xlabel(\"Cycles\")\n", - "plt.ylabel(\"Survival probability\")\n", - "plt.legend();" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "z3gRHi44OrL9" - }, - "source": [ - "## Apply corrections to your circuit" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gPAlP1euFuTW" - }, - "source": [ - "Now, use the calibration which gives the best results on the Loschmidt echo benchmark. (The slower the survival probability decays, the better the result.) The following cell shows how to apply corrections to your circuit from either calibration." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "id": "WP9f8jlSMkTt" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Calibrated circuit:\n", - "\n", - " ┌──────────────────────────────────────────┐ ┌────────────────────────────┐ ┌──────────────────────────────────────────┐ ┌────────────────────────────┐ ┌──────────────────────────────────────────┐ ┌────────────────────────────┐\n", - "(2, 5): ───PhX(0.25)^0.5───────────────────────────────────────────────────────────────────────────────PhX(-0.5)^0.5────Rz(0.005π)─────FSim(0.25π, 0)──────────────────Rz(0.035π)────PhX(-0.75)^0.5───Rz(0.037π)────FSim(0.25π, 0)───Rz(0.079π)────PhX(0.75)^0.5─────────────────────────────────────────────────PhX(-0.75)^0.5───Rz(0.037π)────FSim(0.25π, 0)───Rz(0.079π)────PhX(0.5)^0.5──────────────────────────────────────────────────PhX(-0.75)^0.5──────────────────────────────────────────────────────────────────────────────PhX(-0.5)^0.5────Rz(0.005π)─────FSim(0.25π, 0)──────────────────Rz(0.035π)────PhX(0)^0.5──────────────────────────────────────────────────────────────────────────────────PhX(-0.25)^0.5───Rz(0.005π)─────FSim(0.25π, 0)──────────────────Rz(0.035π)────PhX(0.75)^0.5────\n", - " │ │ │ │ │\n", - "(2, 6): ───PhX(0)^0.5───────Rz(0.056π)─────FSim(0.25π, 0)────────────────────────────────Rz(0.056π)────PhX(0.75)^0.5───────────────────┼─────────────────────────────────────────────PhX(0.5)^0.5─────Rz(0.006π)────FSim(0.25π, 0)───Rz(-0.035π)───PhX(1)^0.5───────Rz(0.002π)────FSim(0.25π, 0)───Rz(0.01π)─────PhX(-0.25)^0.5───Rz(0.006π)────FSim(0.25π, 0)───Rz(-0.035π)───PhX(0.25)^0.5────Rz(0.002π)────FSim(0.25π, 0)───Rz(0.01π)─────PhX(-0.5)^0.5────Rz(0.056π)─────FSim(0.25π, 0)────────────────────────────────Rz(0.056π)────PhX(0.75)^0.5───────────────────┼─────────────────────────────────────────────PhX(-0.75)^0.5───Rz(0.056π)─────FSim(0.25π, 0)────────────────────────────────Rz(0.056π)────PhX(1)^0.5──────────────────────┼─────────────────────────────────────────────PhX(-0.5)^0.5────\n", - " │ │ │ │ │ │ │ │\n", - "(2, 7): ───PhX(-0.75)^0.5──────────────────┼───────────────────────────────────────────────────────────PhX(0)^0.5───────Rz(-0.004π)────┼─────────────FSim(0.25π, 0)────Rz(-0.005π)───PhX(-0.25)^0.5───Rz(0.008π)────FSim(0.25π, 0)───Rz(-0.003π)───PhX(0.5)^0.5─────Rz(0.025π)────FSim(0.25π, 0)───Rz(0.018π)────PhX(-0.5)^0.5────Rz(0.008π)────FSim(0.25π, 0)───Rz(-0.003π)───PhX(0.25)^0.5────Rz(0.025π)────FSim(0.25π, 0)───Rz(0.018π)────PhX(1)^0.5──────────────────────┼───────────────────────────────────────────────────────────PhX(0.25)^0.5────Rz(-0.004π)────┼─────────────FSim(0.25π, 0)────Rz(-0.005π)───PhX(-0.25)^0.5──────────────────┼───────────────────────────────────────────────────────────PhX(0.75)^0.5────Rz(-0.004π)────┼─────────────FSim(0.25π, 0)────Rz(-0.005π)───PhX(0.25)^0.5────\n", - " │ │ │ │ │ │ │ │ │ │ │\n", - "(2, 8): ───PhX(-0.5)^0.5────Rz(0.016π)─────┼─────────────FSim(0.25π, 0)──────────────────Rz(0.015π)────PhX(-0.75)^0.5──────────────────┼─────────────┼───────────────────────────────PhX(0.5)^0.5─────Rz(-0.012π)───FSim(0.25π, 0)───Rz(-0.001π)───PhX(0.25)^0.5─────────────────────────────────────────────────PhX(-0.25)^0.5───Rz(-0.012π)───FSim(0.25π, 0)───Rz(-0.001π)───PhX(-0.75)^0.5────────────────────────────────────────────────PhX(1)^0.5───────Rz(0.016π)─────┼─────────────FSim(0.25π, 0)──────────────────Rz(0.015π)────PhX(-0.75)^0.5──────────────────┼─────────────┼───────────────────────────────PhX(0.25)^0.5────Rz(0.016π)─────┼─────────────FSim(0.25π, 0)──────────────────Rz(0.015π)────PhX(-0.5)^0.5───────────────────┼─────────────┼───────────────────────────────PhX(-0.75)^0.5───\n", - " │ │ │ │ │ │ │ │ │ │ │ │\n", - "(3, 5): ───PhX(0)^0.5───────Rz(-0.007π)────┼─────────────┼─────────────FSim(0.25π, 0)────Rz(0.008π)────PhX(-0.5)^0.5────Rz(0.018π)─────FSim(0.25π, 0)┼─────────────────Rz(-0.012π)───PhX(-0.25)^0.5────────────────────────────────────────────────PhX(0.5)^0.5─────Rz(0.005π)────FSim(0.25π, 0)───Rz(-0.01π)────PhX(0.25)^0.5─────────────────────────────────────────────────PhX(1)^0.5───────Rz(0.005π)────FSim(0.25π, 0)───Rz(-0.01π)────PhX(-0.25)^0.5───Rz(-0.007π)────┼─────────────┼─────────────FSim(0.25π, 0)────Rz(0.008π)────PhX(-0.75)^0.5───Rz(0.018π)─────FSim(0.25π, 0)┼─────────────────Rz(-0.012π)───PhX(0.5)^0.5─────Rz(-0.007π)────┼─────────────┼─────────────FSim(0.25π, 0)────Rz(0.008π)────PhX(-0.75)^0.5───Rz(0.018π)─────FSim(0.25π, 0)┼─────────────────Rz(-0.012π)───PhX(-0.5)^0.5────\n", - " │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", - "(3, 6): ───PhX(-0.25)^0.5───Rz(0.009π)─────FSim(0.25π, 0)┼─────────────┼─────────────────Rz(0.009π)────PhX(0.5)^0.5─────Rz(-0.042π)────FSim(0.25π, 0)┼─────────────────Rz(-0.026π)───PhX(-0.75)^0.5───Rz(0.025π)────FSim(0.25π, 0)───Rz(0.029π)────PhX(0)^0.5───────Rz(-0.017π)───FSim(0.25π, 0)───Rz(-0.002π)───PhX(-0.5)^0.5────Rz(0.025π)────FSim(0.25π, 0)───Rz(0.029π)────PhX(-0.75)^0.5───Rz(-0.017π)───FSim(0.25π, 0)───Rz(-0.002π)───PhX(0)^0.5───────Rz(0.009π)─────FSim(0.25π, 0)┼─────────────┼─────────────────Rz(0.009π)────PhX(-0.5)^0.5────Rz(-0.042π)────FSim(0.25π, 0)┼─────────────────Rz(-0.026π)───PhX(-0.25)^0.5───Rz(0.009π)─────FSim(0.25π, 0)┼─────────────┼─────────────────Rz(0.009π)────PhX(-0.75)^0.5───Rz(-0.042π)────FSim(0.25π, 0)┼─────────────────Rz(-0.026π)───PhX(0.25)^0.5────\n", - " │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", - "(3, 7): ───PhX(0.75)^0.5─────────────────────────────────┼─────────────┼───────────────────────────────PhX(0)^0.5───────Rz(0.015π)─────┼─────────────FSim(0.25π, 0)────Rz(0.016π)────PhX(-0.5)^0.5────Rz(0.011π)────FSim(0.25π, 0)───Rz(0.006π)────PhX(-0.75)^0.5───Rz(0.012π)────FSim(0.25π, 0)───Rz(0.003π)────PhX(0.75)^0.5────Rz(0.011π)────FSim(0.25π, 0)───Rz(0.006π)────PhX(1)^0.5───────Rz(0.012π)────FSim(0.25π, 0)───Rz(0.003π)────PhX(-0.25)^0.5────────────────────────────────┼─────────────┼───────────────────────────────PhX(-0.75)^0.5───Rz(0.015π)─────┼─────────────FSim(0.25π, 0)────Rz(0.016π)────PhX(0)^0.5────────────────────────────────────┼─────────────┼───────────────────────────────PhX(-0.5)^0.5────Rz(0.015π)─────┼─────────────FSim(0.25π, 0)────Rz(0.016π)────PhX(1)^0.5───────\n", - " │ │ │ │ │ │ │ │ │ │ │\n", - "(3, 8): ───PhX(-0.75)^0.5───Rz(-0.016π)──────────────────FSim(0.25π, 0)┼─────────────────Rz(-0.015π)───PhX(-0.25)^0.5──────────────────┼─────────────────────────────────────────────PhX(0.25)^0.5─────────────────────────────────────────────────PhX(-0.25)^0.5───Rz(-0.016π)───FSim(0.25π, 0)───Rz(-0.007π)───PhX(0.25)^0.5─────────────────────────────────────────────────PhX(1)^0.5───────Rz(-0.016π)───FSim(0.25π, 0)───Rz(-0.007π)───PhX(-0.75)^0.5───Rz(-0.016π)──────────────────FSim(0.25π, 0)┼─────────────────Rz(-0.015π)───PhX(0.75)^0.5───────────────────┼─────────────────────────────────────────────PhX(0)^0.5───────Rz(-0.016π)──────────────────FSim(0.25π, 0)┼─────────────────Rz(-0.015π)───PhX(0.75)^0.5───────────────────┼─────────────────────────────────────────────PhX(-0.75)^0.5───\n", - " │ │ │ │ │ │\n", - "(4, 5): ───PhX(-0.25)^0.5───Rz(-0.013π)────────────────────────────────FSim(0.25π, 0)────Rz(-0.028π)───PhX(-0.5)^0.5───────────────────┼─────────────────────────────────────────────PhX(-0.75)^0.5───Rz(-0.053π)───FSim(0.25π, 0)───Rz(-0.045π)───PhX(-0.5)^0.5─────────────────────────────────────────────────PhX(1)^0.5───────Rz(-0.053π)───FSim(0.25π, 0)───Rz(-0.045π)───PhX(-0.5)^0.5─────────────────────────────────────────────────PhX(1)^0.5───────Rz(-0.013π)────────────────────────────────FSim(0.25π, 0)────Rz(-0.028π)───PhX(0.5)^0.5────────────────────┼─────────────────────────────────────────────PhX(0.25)^0.5────Rz(-0.013π)────────────────────────────────FSim(0.25π, 0)────Rz(-0.028π)───PhX(1)^0.5──────────────────────┼─────────────────────────────────────────────PhX(0.5)^0.5─────\n", - " │ │ │ │ │\n", - "(4, 6): ───PhX(0)^0.5──────────────────────────────────────────────────────────────────────────────────PhX(-0.5)^0.5────Rz(0.031π)─────FSim(0.25π, 0)──────────────────Rz(0.015π)────PhX(0.25)^0.5────Rz(-0.01π)────FSim(0.25π, 0)───Rz(-0.018π)───PhX(-0.75)^0.5────────────────────────────────────────────────PhX(0)^0.5───────Rz(-0.01π)────FSim(0.25π, 0)───Rz(-0.018π)───PhX(0.5)^0.5──────────────────────────────────────────────────PhX(1)^0.5──────────────────────────────────────────────────────────────────────────────────PhX(0.25)^0.5────Rz(0.031π)─────FSim(0.25π, 0)──────────────────Rz(0.015π)────PhX(0)^0.5──────────────────────────────────────────────────────────────────────────────────PhX(0.75)^0.5────Rz(0.031π)─────FSim(0.25π, 0)──────────────────Rz(0.015π)────PhX(0)^0.5───────\n", - " └──────────────────────────────────────────┘ └────────────────────────────┘ └──────────────────────────────────────────┘ └────────────────────────────┘ └──────────────────────────────────────────┘ └────────────────────────────┘\n" - ] - } - ], - "source": [ - "# Pick Floquet or XEB options/characterizations here.\n", - "options = xeb_options\n", - "characterizations = xeb_characterizations\n", - "\n", - "characterized_circuit, _ = cg.prepare_characterization_for_moments(\n", - " circuit, options=options,\n", - ")\n", - "calibrated_circuit = cg.make_zeta_chi_gamma_compensation_for_moments(\n", - " characterized_circuit,\n", - " characterizations,\n", - ")\n", - "print(\"Calibrated circuit:\\n\")\n", - "print(calibrated_circuit.circuit)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FK-78VvbjXyw" - }, - "source": [ - "Note: The corrections to the circuit are `cirq.Rz` gates padding the $\\sqrt{\\text{iSWAP}}$ gates." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "L4pCsDUwOCXx" - }, - "source": [ - "You can now run this calibrated circuit on the Quantum Computing Service." - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "advanced_calibration.ipynb", - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/docs/tutorials/google/calibration_api.ipynb b/docs/tutorials/google/calibration_api.ipynb new file mode 100644 index 00000000000..e8614085c6a --- /dev/null +++ b/docs/tutorials/google/calibration_api.ipynb @@ -0,0 +1,611 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "S97D7gQHMdG_" + }, + "outputs": [], + "source": [ + "##### Copyright 2021 The Cirq Developers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "Y6Wh1qtGMghL" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gNBCrapEMgxB" + }, + "source": [ + "# Calibration: Overview and API" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tlnEweYsMgzj" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " View on QuantumAI\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + " \n", + " Download notebook\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PnYvZgIoMg2O" + }, + "source": [ + "This tutorial provides an introductory overview of calibration. By the end, you will understand the high-level calibration process, how to run calibrations through the API, and how to choose which calibration technique to use." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BRfLi9YSMg4v" + }, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JolZGos2MhDM" + }, + "outputs": [], + "source": [ + "try:\n", + " import cirq\n", + "except ImportError:\n", + " print(\"installing cirq...\")\n", + " !pip install cirq --pre --quiet\n", + " print(\"installed cirq.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lFI7APiqMhFy" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "import cirq\n", + "import cirq_google as cg" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fh6P5258GXQX" + }, + "source": [ + "Note: Leave the `project_id` and/or `processor_id` blank to use a noisy simulator." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "UaBOLd1ZNZwC" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "# The Google Cloud Project id to use.\n", + "project_id = '' #@param {type:\"string\"}\n", + "\n", + "\n", + "use_noisy_simulator = False\n", + "if project_id == '' and 'GOOGLE_CLOUD_PROJECT' not in os.environ:\n", + " print(\"No project_id provided and environment variable \"\n", + " \"GOOGLE_CLOUD_PROJECT not set.\")\n", + " use_noisy_simulator = True\n", + "else: \n", + " os.environ['GOOGLE_CLOUD_PROJECT'] = project_id\n", + "\n", + " def authenticate_user():\n", + " \"\"\"Runs the user through the Colab OAuth process.\n", + "\n", + " Checks for Google Application Default Credentials and runs interactive login \n", + " if the notebook is executed in Colab. In case the notebook is executed in Jupyter notebook\n", + " or other IPython runtimes, no interactive login is provided, it is assumed that the \n", + " `GOOGLE_APPLICATION_CREDENTIALS` env var is set or `gcloud auth application-default login`\n", + " was executed already.\n", + "\n", + " For more information on using Application Default Credentials see \n", + " https://cloud.google.com/docs/authentication/production\n", + " \"\"\"\n", + " in_colab = False\n", + " try:\n", + " from IPython import get_ipython\n", + " in_colab = 'google.colab' in str(get_ipython())\n", + " except: \n", + " # Notebook is not executed within IPython. Assuming external authentication.\n", + " return \n", + "\n", + " if in_colab: \n", + " from google.colab import auth \n", + " print(\"Getting OAuth2 credentials.\")\n", + " print(\"Press enter after entering the verification code.\")\n", + " auth.authenticate_user(clear_output=False)\n", + " print(\"Authentication complete.\")\n", + " else: \n", + " print(\"Notebook is not executed with Colab, assuming Application Default Credentials are setup.\") \n", + "\n", + " authenticate_user()\n", + " print(\"Successful authentication to Google Cloud.\")\n", + "\n", + "processor_id = \"\" #@param {type:\"string\"}\n", + "if use_noisy_simulator or processor_id == \"\":\n", + " print(\"Using a noisy simulator.\")\n", + " sampler = cg.PhasedFSimEngineSimulator.create_with_random_gaussian_sqrt_iswap(\n", + " mean=cg.SQRT_ISWAP_INV_PARAMETERS,\n", + " sigma=cg.PhasedFSimCharacterization(\n", + " theta=0.01, zeta=0.10, chi=0.01, gamma=0.10, phi=0.02\n", + " ),\n", + " )\n", + " device = cg.Bristlecone\n", + "else:\n", + " device = cg.get_engine_device(processor_id)\n", + " sampler = cg.get_engine_sampler(processor_id, gate_set_name=\"sqrt_iswap\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fHj9lpEeNMI2" + }, + "source": [ + "## Overview" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "grmpD_A1SyVx" + }, + "source": [ + "Floquet and XEB calibration are tools to characterize a family of gates including $\\sqrt{\\text{iSWAP}}$ gates on a particular processor and compensate for errors. The family of gates is represented by the `cirq.PhasedFSimGate`, a two-qubit gate with five angles $\\theta$, $\\zeta$, $\\chi$, $\\gamma$, $\\phi$ given by the following unitary matrix:\n", + "\n", + "$$\n", + "U(\\theta, \\zeta, \\chi, \\gamma, \\phi) := \\left[ \\begin{matrix}\n", + "1 & 0 & 0 & 0 \\\\\n", + "0 & \\exp(-i \\gamma - i \\zeta) \\cos( \\theta ) & -i \\exp(-i \\gamma + i \\chi) \\sin( \\theta ) & 0 \\\\\n", + "0 & -i \\exp(-i \\gamma - i \\chi) \\sin( \\theta ) & \\exp(-i \\gamma + i \\zeta) \\cos( \\theta) & 0 \\\\\n", + "0 & 0 & 0 & \\exp(-2 i \\gamma -i \\phi ) \n", + "\\end{matrix} \\right]\n", + "$$\n", + "\n", + "The $\\sqrt{\\text{iSWAP}}$ gate corresponds to angles $\\theta=-\\pi / 4$ and $\\zeta = \\chi = \\gamma = \\phi = 0$. \n", + "\n", + "Characterization is done by the Quantum Engine and compensation (currently the insertion of $Z$ gates around $\\sqrt{\\text{iSWAP}}$ gates) is completely client-side with the help of Cirq utilities. At the highest level, each calibration tool inputs one or more quantum circuits of interest as well as a processor to run on, and outputs the calibrated circuit(s) for this processor.\n", + "\n", + "Both calibrations are primarily focused on decreasing the effects of two sources of coherent errors: calibration drifts and cross-talk errors. These errors are not uniform across the chip and vary in magnitude between qubits and device calibrations. The circuit-specific calibrations should be used in situations when achieving the best performance is more important than using extra time on the device for the characterizations." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ww1Mdjm0NPeo" + }, + "source": [ + "## Calibration techniques and API" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qOOw5G0IWvUd" + }, + "source": [ + "To run calibration, define your circuit or circuits below. For sake of example we use a simple circuit with a single $\\sqrt{\\text{iSWAP}}$ gate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rVnp2Y1pOAAp" + }, + "outputs": [], + "source": [ + "\"\"\"Define your circuit(s) here.\"\"\"\n", + "qubits = cg.line_on_device(device, length=2)\n", + "circuit = cirq.Circuit(cirq.ISWAP.on(*qubits) ** 0.5)\n", + "\n", + "print(\"Example circuit:\", circuit, sep=\"\\n\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c9ZjppgcbVQz" + }, + "source": [ + "Note: Above we picked any qubits, but in an actual experiment you want to pick a good set of qubits to run on. To do so, use the latest maintanence report as in [this XEB calibration example](./xeb_calibration_example.ipynb) or use a Loschmidt echo as in [this Loschmidt echo tutorial](./echoes.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O8PvBodGNfHn" + }, + "source": [ + "### Floquet calibration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZCbXAVZINN33" + }, + "source": [ + "Floquet calibration returns fast, accurate estimates for every angle of the `cirq.PhasedFSimGate` except $\\chi$ (chi).\n", + "\n", + "The first step to Floquet calibration is defining the angles to characterize." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "utut_505Nds3" + }, + "outputs": [], + "source": [ + "# Define calibration options.\n", + "floquet_options = cg.FloquetPhasedFSimCalibrationOptions(\n", + " characterize_theta=True,\n", + " characterize_zeta=True,\n", + " characterize_chi=False,\n", + " characterize_gamma=True,\n", + " characterize_phi=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c5bcRE2SWxzc" + }, + "source": [ + "Next, use these options to get characterization requests - i.e., `cirq.Moment`s to characterize on the Quantum Engine - then characterize them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Bt7qDv-YW7Qv" + }, + "outputs": [], + "source": [ + "# Get characterization requests.\n", + "floquet_characterization_requests = cg.prepare_characterization_for_operations(\n", + " circuit, options=floquet_options\n", + ")\n", + "\n", + "# Characterize the requests on the engine.\n", + "floquet_characterizations = cg.run_calibrations(\n", + " floquet_characterization_requests, sampler\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9Dhfr0xNm-m-" + }, + "source": [ + "Note: The first argument to `cirq_google.prepare_characterization_for_operations` can also be a sequence of circuits." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HhwNWy0dW7--" + }, + "source": [ + "The last step is to compensate for offsets using the `cirq_google.make_zeta_chi_gamma_compensation_for_moments` function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bNQddsaJXfym" + }, + "outputs": [], + "source": [ + "# Compensate your circuit based on the characterizations.\n", + "floquet_calibrated_circuit = cg.make_zeta_chi_gamma_compensation_for_moments(\n", + " circuit, floquet_characterizations\n", + ").circuit\n", + "\n", + "print(\"Floquet calibrated circuit:\", floquet_calibrated_circuit, sep=\"\\n\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JoQNOxdLN8YB" + }, + "source": [ + "Note the addition of `cirq.Rz` gates to compensate for characterized errors. This calibrated circuit can now be run on the processor." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j0ZBw4zWNg-y" + }, + "source": [ + "### XEB calibration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "io2YyhxaN6xt" + }, + "source": [ + "XEB calibration works by executing a library of random circuits both on a noiseless simulator and on a noisy processor, then comparing results. In principle it can characterize any two-qubit gate, but compensation is currently only for $\\zeta$, $\\chi$, and $\\gamma$. For more details about how XEB calibration works, see the [Parallel XEB tutorial](https://quantumai.google/cirq/qcvv/parallel_xeb).\n", + "\n", + "Like Floquet calibration, the first step to using XEB calibration is defining options. Unlike Floquet calibration, XEB calibration requires significant classical processing to characterize angles. In addition to the angles to characterize, we also need to define the `cycle_depths` of the random circuits. The number of processes `n_processes` is a parallelization option for the classical processing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KA9QJ87sMhPI" + }, + "outputs": [], + "source": [ + "# Define calibration options.\n", + "cycle_depths=(5, 25, 50, 100)\n", + "\n", + "xeb_options = cg.LocalXEBPhasedFSimCalibrationOptions(\n", + " cycle_depths=cycle_depths,\n", + " n_processes=1,\n", + " # Note that all angles below are set to True by default.\n", + " fsim_options=cirq.experiments.XEBPhasedFSimCharacterizationOptions(\n", + " characterize_theta=False,\n", + " characterize_zeta=True,\n", + " characterize_chi=True,\n", + " characterize_gamma=True,\n", + " characterize_phi=False,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WsFn5OKdY2p0" + }, + "source": [ + "The remaining steps are identical to Floquet calibration: first getting characterization requests then characterizing them on the engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SVuqkj6gYE-V" + }, + "outputs": [], + "source": [ + "# Get characterization requests.\n", + "xeb_characterization_requests = cg.prepare_characterization_for_operations(\n", + " circuit, options=xeb_options\n", + ")\n", + "\n", + "# Characterize the requests on the engine.\n", + "xeb_characterizations = cg.run_calibrations(\n", + " xeb_characterization_requests, sampler\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lkNc2hEgl9Ql" + }, + "source": [ + "Making compensations is also identical to the Floquet calibration example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "aKy2mdpsZzmP" + }, + "outputs": [], + "source": [ + "xeb_calibrated_circuit = cg.make_zeta_chi_gamma_compensation_for_moments(\n", + " circuit, xeb_characterizations,\n", + ").circuit\n", + "\n", + "print(\"XEB calibrated circuit:\", xeb_calibrated_circuit, sep=\"\\n\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HlnmFNFnN9nz" + }, + "source": [ + "Again note the addition of `cirq.Rz` gates to compensate for errors. This calibrated circuit can now be run on the processor." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bBZ8MRdNN9qi" + }, + "source": [ + "## Which calibration should I use?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8iyr366iN9v8" + }, + "source": [ + "This question is very dependent on the particular type of circuit, but some guidelines exist. XEB calibration is generally more applicable to circuits that alternate single-qubit and two-qubit layers due to the similarity between the random XEB circuits and executed circuits. Floquet calibration may be more applicable to circuits with two-qubit gates and $Z$ gates only." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2Yw4ykeBlRgU" + }, + "source": [ + "## Summary and notes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "g5jECqZ5N9tB" + }, + "source": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "
Floquet calibrationXEB calibration
FSim angles characterized4 / 5 (all but χ)5 / 5
FSim angles compensated2 / 5 (ζ and γ)3 / 5 (ζ, χ, and γ)
Relative runtime1~10x Floquet calibration runtime
Robust to SPAM errors?YesYes
Experiments used inFermi-HubbardOTOC
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xYFhw2mWSLDx" + }, + "source": [ + "- The ζ, χ and γ parameters of the `cirq.PhasedFSimGate` are subject to drifts that range in frequency from seconds to hours.\n", + "- Neither calibration technique attempts to correct the misaligned iSWAP rotation or the additional two-qubit phase. This is a non-trivial task and we do currently have simple tools to achieve this. It is up to the user to correct for these in experiments as best as possible.\n", + "- Floquet calibration does not yet support microwave gates." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p-J9h3YWSQkE" + }, + "source": [ + "## Next steps" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8tLka4CaSS9Z" + }, + "source": [ + "Try running Floquet and/or XEB calibration on your circuit(s). See the following tutorials for detailed examples and benchmarks.\n", + "\n", + "- [Floquet calibration: Example and benchmark](./floquet_calibration_example.ipynb)\n", + "- [XEB calibration: Example and benchmark](./xeb_calibration_example.ipynb)" + ] + } + ], + "metadata": { + "colab": { + "name": "calibration_api.ipynb", + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/tutorials/google/calibration_api.tst b/docs/tutorials/google/calibration_api.tst new file mode 100644 index 00000000000..f203b2cea4f --- /dev/null +++ b/docs/tutorials/google/calibration_api.tst @@ -0,0 +1,17 @@ +# Copyright 2021 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Replacements to apply during testing. See devtools/notebook_test.py for syntax. + +cycle_depths=(5, 25, 50, 100)->cycle_depths=(5, 25) diff --git a/docs/tutorials/google/floquet.ipynb b/docs/tutorials/google/floquet_calibration_example.ipynb similarity index 78% rename from docs/tutorials/google/floquet.ipynb rename to docs/tutorials/google/floquet_calibration_example.ipynb index e1b8e42cb19..aada39b1793 100644 --- a/docs/tutorials/google/floquet.ipynb +++ b/docs/tutorials/google/floquet_calibration_example.ipynb @@ -39,7 +39,7 @@ "id": "AF-CFuvzPx4x" }, "source": [ - "# Floquet calibration" + "# Floquet calibration: Example and benchmark" ] }, { @@ -50,16 +50,16 @@ "source": [ "\n", " \n", " \n", " \n", " \n", "
\n", - " View on QuantumAI\n", + " View on QuantumAI\n", " \n", - " Run in Google Colab\n", + " Run in Google Colab\n", " \n", - " View source on GitHub\n", + " View source on GitHub\n", " \n", - " Download notebook\n", + " Download notebook\n", "
" ] @@ -70,49 +70,7 @@ "id": "MFbogziRQKFQ" }, "source": [ - "This notebook demonstrates the Floquet calibration API, a tool for characterizing $\\sqrt{\\text{iSWAP}}$ gates and inserting single-qubit $Z$ phases to compensate for errors. This characterization is done by the Quantum Engine and the insertion of $Z$ phases for compensation/calibration is completely client-side with the help of Cirq utilities. At the highest level, the tool inputs a quantum circuit of interest (as well as a backend to run on) and outputs a calibrated circuit for this backend which can then be executed to produce better results." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "VJ3IC7PZN_F6" - }, - "source": [ - "## Details on the calibration tool" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kY8lN-umLx6u" - }, - "source": [ - "In more detail, assuming we have a number-convserving two-qubit unitary gate, Floquet calibration (FC) returns fast, accurate estimates for the relevant angles to be calibrated. The `cirq.PhasedFSimGate` has five angles $\\theta$, $\\zeta$, $\\chi$, $\\gamma$, $\\phi$ with unitary matrix\n", - "\n", - "$$\n", - "\\left[ \\begin{matrix}\n", - "1 & 0 & 0 & 0 \\\\\n", - "0 & \\exp(-i \\gamma - i \\zeta) cos( \\theta ) & -i \\exp(-i \\gamma + i \\chi) sin( \\theta ) & 0 \\\\\n", - "0 & -i \\exp(-i \\gamma - i \\chi) sin( \\theta ) & \\exp(-i \\gamma + i \\zeta) cos( \\theta) & 0 \\\\\n", - "0 & 0 & 0 & \\exp(-2 i \\gamma -i \\phi ) \n", - "\\end{matrix} \\right]\n", - "$$\n", - "\n", - "With Floquet calibration, every angle but $\\chi$ can be calibrated. In experiments, we have found these angles change when gates are run in parallel. Because of this, we perform FC on entire moments of two-qubits gates and return different characterized angles for each. \n", - "\n", - "After characterizing a set of angles, one needs to adjust the circuit to compensate for the offset. The simplest adjustment is for $\\zeta$ and $\\gamma$ and works by adding $R_z$ gates before and after the two-qubit gates in question. For many circuits, even this simplest compensation can lead to a significant improvement in results. We provide methods for doing this in this notebook and analyze results for an example circuit.\n", - "\n", - "We do not attempt to correct the misaligned iSWAP rotation or the additional two-qubit phase in this notebook. This is a non-trivial task and we do currently have simple tools to achieve this. It is up to the user to correct for these as best as possible." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-jOmHd7lLyDn" - }, - "source": [ - "Note: The Floquet calibration API and this documentation is ongoing work. The amount by which errors are reduced may vary from run to run and from circuit to circuit." + "This tutorial shows a detailed example and benchmark of Floquet calibration, a calibration technique introduced in the [Calibration: Overview and API](./calibration_api.ipynb) tutorial." ] }, { @@ -158,57 +116,36 @@ "import cirq_google as cg # Contains the Floquet calibration tools." ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "w_x706KvjDLC" - }, - "source": [ - "Note: In order to run on Google's Quantum Computing Service, an environment variable `GOOGLE_CLOUD_PROJECT` must be present and set to a valid Google Cloud Platform project identifier. If this is not satisfied, we default to an engine simulator." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "10aaa7fb262a" - }, - "source": [ - "Running the next cell will prompt you to authenticate Google Cloud SDK to use your project. See the [Getting Started Guide](../tutorials/google/start.ipynb) for more information." - ] - }, { "cell_type": "markdown", "metadata": { "id": "138e4971b7aa" }, "source": [ - "Note: Leave `project_id` blank to use a noisy simulator." + "Note: Leave the `project_id` and/or `processor_id` blank to use a noisy simulator." ] }, { "cell_type": "code", "execution_count": null, "metadata": { - "id": "9a1dfa4b8b74" + "cellView": "form", + "id": "I6smmHNNiZWx" }, "outputs": [], "source": [ + "import os\n", + "\n", "# The Google Cloud Project id to use.\n", "project_id = '' #@param {type:\"string\"}\n", "\n", + "\n", + "use_noisy_simulator = False\n", "if project_id == '' and 'GOOGLE_CLOUD_PROJECT' not in os.environ:\n", " print(\"No project_id provided and environment variable \"\n", - " \"GOOGLE_CLOUD_PROJECT not set, defaulting to noisy simulator.\")\n", - " sampler = cg.PhasedFSimEngineSimulator.create_with_random_gaussian_sqrt_iswap(\n", - " mean=cg.SQRT_ISWAP_INV_PARAMETERS,\n", - " sigma=cg.PhasedFSimCharacterization(\n", - " theta=0.01, zeta=0.10, chi=0.01, gamma=0.10, phi=0.02\n", - " ),\n", - " )\n", - " device = cg.Bristlecone\n", - " line_length = 20\n", + " \"GOOGLE_CLOUD_PROJECT not set.\")\n", + " use_noisy_simulator = True\n", "else: \n", - " import os\n", " os.environ['GOOGLE_CLOUD_PROJECT'] = project_id\n", "\n", " def authenticate_user():\n", @@ -242,149 +179,31 @@ "\n", " authenticate_user()\n", " print(\"Successful authentication to Google Cloud.\")\n", - " \n", - " processor_id = \"\" #@param {type:\"string\"}\n", - " engine = cg.get_engine()\n", + "\n", + "processor_id = \"\" #@param {type:\"string\"}\n", + "if use_noisy_simulator or processor_id == \"\":\n", + " print(\"Using a noisy simulator.\")\n", + " sampler = cg.PhasedFSimEngineSimulator.create_with_random_gaussian_sqrt_iswap(\n", + " mean=cg.SQRT_ISWAP_INV_PARAMETERS,\n", + " sigma=cg.PhasedFSimCharacterization(\n", + " theta=0.01, zeta=0.10, chi=0.01, gamma=0.10, phi=0.02\n", + " ),\n", + " )\n", + " device = cg.Bristlecone\n", + " line_length = 20\n", + "else:\n", " device = cg.get_engine_device(processor_id)\n", " sampler = cg.get_engine_sampler(processor_id, gate_set_name=\"sqrt_iswap\")\n", " line_length = 35" ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "8YktxdY9Txql" - }, - "source": [ - "## Minimal example for a single $\\sqrt{\\text{iSWAP}}$ gate" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "dcYIemUeT9yo" - }, - "source": [ - "To see how the API is used, we first show the simplest usage of Floquet calibration for a minimal example of one $\\sqrt{\\text{iSWAP}}$ gate. After this section, we show detailed usage with a larger circuit and analyze the results." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mHGDy5QBT3vs" - }, - "source": [ - "The gates that are calibrated by Floquet calibration are $\\sqrt{\\text{iSWAP}}$ gates:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "-Ipz5EyvT94x" - }, - "outputs": [], - "source": [ - "sqrt_iswap = cirq.FSimGate(np.pi / 4, 0.0)\n", - "print(cirq.unitary(sqrt_iswap).round(3))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "i94szFzIZPXz" - }, - "source": [ - "First we get two connected qubits on the selected device and define a circuit." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "a3UopkC1V8H2" - }, - "outputs": [], - "source": [ - "\"\"\"Define a simple circuit to use Floquet calibration on.\"\"\"\n", - "qubits = cg.line_on_device(device, length=2)\n", - "circuit = cirq.Circuit(sqrt_iswap.on(*qubits))\n", - "\n", - "# Display it.\n", - "print(\"Circuit to calibrate:\\n\")\n", - "print(circuit)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3QMVPpEQZegR" - }, - "source": [ - "The simplest way to use Floquet calibration is as follows." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LHoKZq8eVk5L" - }, - "outputs": [], - "source": [ - "\"\"\"Simplest usage of Floquet calibration.\"\"\"\n", - "calibrated_circuit, *_ = cg.run_zeta_chi_gamma_compensation_for_moments(\n", - " circuit,\n", - " sampler\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tMXqTmO4YDrq" - }, - "source": [ - "Note: Additional returned arguments, omitted here for simplicity, are described below." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eC2Ep7HJV-xW" - }, - "source": [ - "When we print out the returned `calibrated_circuit.circuit` below, we see the added $Z$ rotations to compensate for errors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "kDrrCq-_V-7W" - }, - "outputs": [], - "source": [ - "print(\"Calibrated circuit:\\n\")\n", - "calibrated_circuit.circuit" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "k0l8DydJXhXm" - }, - "source": [ - "This `calibrated_circuit` can now be executed on the processor to produce better results." - ] - }, { "cell_type": "markdown", "metadata": { "id": "qOfoym5FmS3Q" }, "source": [ - "## More detailed example with a larger circuit" + "## Defining the circuit" ] }, { @@ -393,7 +212,7 @@ "id": "a0Nrd1pkVWzo" }, "source": [ - "We now use Floquet calibration on a larger circuit which models the evolution of a fermionic particle on a linear spin chain. The physics of this problem for a closed chain (here we use an open chain) has been studied in [Accurately computing electronic properties of materials using eigenenergies](https://arxiv.org/abs/2012.00921), but for the purposes of this notebook we can treat this just as an example to demonstrate Floquet calibration on." + "We run Floquet calibration on a circuit which models the evolution of a fermionic particle on a linear spin chain. The physics of this problem for a closed chain (here we use an open chain) has been studied in [Accurately computing electronic properties of materials using eigenenergies](https://arxiv.org/abs/2012.00921)." ] }, { @@ -478,7 +297,10 @@ }, "outputs": [], "source": [ - "def create_example_circuit(\n", + "sqrt_iswap = cirq.ISWAP ** 0.5\n", + "\n", + "\n", + "def create_linear_chain_circuit(\n", " segments: Sequence[Sequence[cirq.Qid]],\n", " num_trotter_steps: int,\n", ") -> cirq.Circuit:\n", @@ -499,9 +321,7 @@ " segment[offset + 1::2])])\n", " circuit += moment\n", "\n", - " # Measurement.\n", - " circuit += cirq.measure(*sum(segments, ()), key='z')\n", - " return circuit" + " return circuit + cirq.measure(*sum(segments, ()), key='z')" ] }, { @@ -524,7 +344,7 @@ "\"\"\"Example of the linear chain circuit on one segment of the line.\"\"\"\n", "num_trotter_steps = 20\n", "\n", - "circuit_on_segment = create_example_circuit(\n", + "circuit_on_segment = create_linear_chain_circuit(\n", " segments=[segments[0]],\n", " num_trotter_steps=num_trotter_steps,\n", ")\n", @@ -549,7 +369,7 @@ "outputs": [], "source": [ "\"\"\"Circuit used to demonstrate Floquet calibration.\"\"\"\n", - "circuit = create_example_circuit(\n", + "circuit = create_linear_chain_circuit(\n", " segments=segments,\n", " num_trotter_steps=num_trotter_steps\n", ")" @@ -561,7 +381,7 @@ "id": "I5AqYTREfEFn" }, "source": [ - "### Execution on a simulator" + "## Execution on a simulator" ] }, { @@ -592,7 +412,7 @@ "id": "xEYoF9WxmX4E" }, "source": [ - "### Execution on the processor without Floquet calibration" + "## Execution on the processor without Floquet calibration" ] }, { @@ -642,7 +462,7 @@ "id": "VEYnL7eVg2es" }, "source": [ - "#### Helper functions" + "### Helper functions" ] }, { @@ -717,6 +537,7 @@ "cell_type": "code", "execution_count": null, "metadata": { + "cellView": "form", "id": "c_xPAfiY52W2" }, "outputs": [], @@ -825,7 +646,7 @@ "id": "L6FZpe1Wc_5x" }, "source": [ - "#### Visualizing results" + "### Visualizing results" ] }, { @@ -963,7 +784,7 @@ "id": "yreBkU8rCE-N" }, "source": [ - "Note: We comment out this section so Floquet calibration on the larger circuit is only executed once in the notebook." + "Note: We comment out this section so Floquet calibration is only executed once in the notebook." ] }, { @@ -1335,7 +1156,7 @@ "metadata": { "colab": { "collapsed_sections": [], - "name": "floquet.ipynb", + "name": "floquet_calibration_example.ipynb", "toc_visible": true }, "kernelspec": { diff --git a/docs/tutorials/google/xeb_calibration_example.ipynb b/docs/tutorials/google/xeb_calibration_example.ipynb new file mode 100644 index 00000000000..242ee0959df --- /dev/null +++ b/docs/tutorials/google/xeb_calibration_example.ipynb @@ -0,0 +1,684 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "A0STV1dk8Wwi" + }, + "outputs": [], + "source": [ + "##### Copyright 2021 The Cirq Developers" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "cellView": "form", + "id": "cKNQ5_Ba8Ynl" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eLqunnmR8AH5" + }, + "source": [ + "# XEB calibration: Example and benchmark" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RdXA9tBC8Wjw" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " View on QuantumAI\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + " \n", + " Download notebook\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yKWtbKQB8Rej" + }, + "source": [ + "This tutorial shows a detailed example and benchmark of XEB calibration, a calibration technique introduced in the [Calibration: Overview and API](./calibration_api.ipynb) tutorial." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D7EQ-vixOSTR" + }, + "source": [ + "**Disclaimer**: The data shown in this tutorial is exemplary and not representative of the QCS in production." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q8VcUax18RtL" + }, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "Z1uKMxmu-1SC" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[K |████████████████████████████████| 1.5MB 4.9MB/s \n", + "\u001b[K |████████████████████████████████| 399kB 28.7MB/s \n", + "\u001b[K |████████████████████████████████| 51kB 5.5MB/s \n", + "\u001b[K |████████████████████████████████| 1.3MB 37.7MB/s \n", + "\u001b[?25h" + ] + } + ], + "source": [ + "try:\n", + " import cirq\n", + "except ImportError:\n", + " !pip install cirq --pre --quiet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "wYSOLH5j-8uB" + }, + "outputs": [], + "source": [ + "# The Google Cloud Project id to use.\n", + "project_id = '' #@param {type:\"string\"}\n", + "\n", + "if project_id == '':\n", + " import os \n", + " if 'GOOGLE_CLOUD_PROJECT' not in os.environ:\n", + " raise Exception(\"Please setup project_id in this cell or set the `GOOGLE_CLOUD_PROJECT` env var to your project id.\")\n", + " project_id = os.environ['GOOGLE_CLOUD_PROJECT']\n", + "else: \n", + " import os\n", + " os.environ['GOOGLE_CLOUD_PROJECT'] = project_id\n", + "\n", + "def authenticate_user():\n", + " \"\"\"Runs the user through the Colab OAuth process.\n", + "\n", + " Checks for Google Application Default Credentials and runs interactive login \n", + " if the notebook is executed in Colab. In case the notebook is executed in Jupyter notebook\n", + " or other IPython runtimes, no interactive login is provided, it is assumed that the \n", + " `GOOGLE_APPLICATION_CREDENTIALS` env var is set or `gcloud auth application-default login`\n", + " was executed already.\n", + "\n", + " For more information on using Application Default Credentials see \n", + " https://cloud.google.com/docs/authentication/production\n", + " \"\"\"\n", + " in_colab = False\n", + " try:\n", + " from IPython import get_ipython\n", + " in_colab = 'google.colab' in str(get_ipython())\n", + " except: \n", + " # Notebook is not executed within IPython. Assuming external authentication.\n", + " return \n", + "\n", + " if in_colab: \n", + " from google.colab import auth \n", + " print(\"Getting OAuth2 credentials.\")\n", + " print(\"Press enter after entering the verification code.\")\n", + " auth.authenticate_user(clear_output=False)\n", + " print(\"Authentication complete.\")\n", + " else: \n", + " print(\"Notebook is not executed with Colab, assuming Application Default Credentials are set up.\") \n", + "\n", + "authenticate_user()\n", + "\n", + "print(\"Successful authentication to Google Cloud.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "xRKjeIVd_FRo" + }, + "outputs": [], + "source": [ + "import cirq\n", + "from cirq.experiments import random_quantum_circuit_generation as rqcg\n", + "import cirq_google as cg\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import tqdm" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "cellView": "form", + "id": "PEuZQtn1Q4Hb" + }, + "outputs": [], + "source": [ + "#@title Helper functions\n", + "from typing import Optional, Sequence\n", + "\n", + "\n", + "def create_random_circuit(\n", + " qubits: Sequence[cirq.GridQubit],\n", + " cycles: int,\n", + " twoq_gate: cirq.Gate = cirq.FSimGate(np.pi / 4, 0.0),\n", + " seed: Optional[int] = None,\n", + ") -> cirq.Circuit:\n", + " return rqcg.random_rotations_between_grid_interaction_layers_circuit(\n", + " qubits, \n", + " depth=cycles,\n", + " two_qubit_op_factory=lambda a, b, _: twoq_gate.on(a, b),\n", + " pattern=cirq.experiments.GRID_STAGGERED_PATTERN,\n", + " single_qubit_gates=[cirq.PhasedXPowGate(phase_exponent=p, exponent=0.5)\n", + " for p in np.arange(-1.0, 1.0, 0.25)],\n", + " seed=seed\n", + " )\n", + "\n", + "\n", + "\n", + "def create_loschmidt_echo_circuit(\n", + " qubits: Sequence[cirq.GridQubit],\n", + " cycles: int,\n", + " twoq_gate: cirq.Gate = cirq.FSimGate(np.pi / 4, 0.0),\n", + " seed: Optional[int] = None,\n", + ") -> cirq.Circuit:\n", + " \"\"\"Returns a Loschmidt echo circuit using a random unitary U.\n", + "\n", + " Args:\n", + " qubits: Qubits to use.\n", + " cycles: Depth of random rotations in the forward & reverse unitary.\n", + " twoq_gate: Two-qubit gate to use.\n", + " pause: Optional duration to pause for between U and U^\\dagger.\n", + " seed: Seed for circuit generation.\n", + " \"\"\"\n", + " forward = create_random_circuit(qubits, cycles, twoq_gate, seed)\n", + " return forward + cirq.inverse(forward) + cirq.measure(*qubits, key=\"z\")\n", + "\n", + "\n", + "\n", + "def to_ground_state_prob(result: cirq.Result) -> float:\n", + " return np.mean(np.sum(result.measurements[\"z\"], axis=1) == 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KPcCbk-2_qp2" + }, + "source": [ + "## Select qubits" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YmNhr6_W_ulI" + }, + "source": [ + "First we select a processor and calibration metric(s) to visualize the latest calibration report." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "g27X3DP7EPaH" + }, + "source": [ + "Note: All calibration metrics are defined in [this guide](https://quantumai.google/cirq/google/calibration). The `parallel_p00_error` and/or `parallel_p11_error` metrics are good to eliminate qubits with high readout errors." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "cellView": "form", + "id": "C8namTwj_vTi" + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "processor_id = \"\" #@param {type:\"string\"}\n", + "metrics = \"parallel_p00_error, two_qubit_sqrt_iswap_gate_xeb_pauli_error_per_cycle\" #@param {type:\"string\"}\n", + "metrics = [m.strip() for m in metrics.split(sep=\",\")]\n", + "\n", + "from matplotlib.colors import LogNorm\n", + "\n", + "\n", + "_, axes = plt.subplots(\n", + " nrows=1, ncols=len(metrics), figsize=(min(16, 8 * len(metrics)), 7)\n", + ")\n", + "\n", + "\n", + "calibration = cg.get_engine_calibration(processor_id=processor_id)\n", + "for i, metric in enumerate(metrics):\n", + " calibration.heatmap(metric).plot(\n", + " ax=axes[i] if len(metrics) > 1 else axes, \n", + " collection_options={\"norm\": LogNorm()}, \n", + " annotation_format=\"0.3f\", \n", + " annotation_text_kwargs = {\"size\": \"small\"}\n", + ");" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "T9U6qwhmBLh3" + }, + "source": [ + "Using this report as a guide, we select a good set of qubits." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "v8rxDM_5AF4p" + }, + "outputs": [], + "source": [ + "# Select qubit indices here.\n", + "qubit_indices = [\n", + " (2, 5), (2, 6), (2, 7), (2, 8), (3, 8), \n", + " (3, 7), (3, 6), (3, 5), (4, 5), (4, 6) \n", + "]\n", + "qubits = [cirq.GridQubit(*idx) for idx in qubit_indices]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JDTOQHi1FNQ1" + }, + "source": [ + "An example random circuit on these qubits (used as the forward operations of the Loschmidt echo) is shown below.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "AhziVqFiFaFy" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
                            ┌──────────────────────────────────────────┐                    ┌────────────────────────────┐                                                                                                                                                           ┌──────────────────────────────────────────┐                    ┌────────────────────────────┐                    ┌──────────────────────────────────────────┐                    ┌────────────────────────────┐\n",
+       "(2, 5): ───PhX(-0.5)^0.5───────────────────────────────────────────────────PhX(-0.25)^0.5────FSim(0.25π, 0)──────────────────PhX(1)^0.5───────FSim(0.25π, 0)───PhX(-0.5)^0.5─────────────────────PhX(1)^0.5───────FSim(0.25π, 0)───PhX(0.75)^0.5────────────────────PhX(0)^0.5──────────────────────────────────────────────────────PhX(-0.5)^0.5─────FSim(0.25π, 0)──────────────────PhX(0.25)^0.5───────────────────────────────────────────────────PhX(0.5)^0.5──────FSim(0.25π, 0)──────────────────PhX(-0.75)^0.5───\n",
+       "                                                                                             │                                                │                                                                   │                                                                                                                                   │                                                                                                                 │\n",
+       "(2, 6): ───PhX(0)^0.5────────FSim(0.25π, 0)────────────────────────────────PhX(0.5)^0.5──────┼───────────────────────────────PhX(-0.5)^0.5────FSim(0.25π, 0)───PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0.75)^0.5────FSim(0.25π, 0)───PhX(0.5)^0.5────FSim(0.25π, 0)───PhX(0)^0.5────────FSim(0.25π, 0)────────────────────────────────PhX(-0.25)^0.5────┼───────────────────────────────PhX(-0.5)^0.5─────FSim(0.25π, 0)────────────────────────────────PhX(0.25)^0.5─────┼───────────────────────────────PhX(1)^0.5───────\n",
+       "                             │                                                               │                                                                                  │                                                                  │                                  │                                                               │                                                 │                                                               │\n",
+       "(2, 7): ───PhX(0.75)^0.5─────┼─────────────────────────────────────────────PhX(0)^0.5────────┼─────────────FSim(0.25π, 0)────PhX(-0.25)^0.5───FSim(0.25π, 0)───PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0.75)^0.5────FSim(0.25π, 0)───PhX(0.5)^0.5────FSim(0.25π, 0)───PhX(-0.75)^0.5────┼─────────────────────────────────────────────PhX(-0.5)^0.5─────┼─────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────┼─────────────────────────────────────────────PhX(0)^0.5────────┼─────────────FSim(0.25π, 0)────PhX(-0.75)^0.5───\n",
+       "                             │                                                               │             │                                  │                                                                   │                                                                   │                                                               │             │                                   │                                                               │             │\n",
+       "(2, 8): ───PhX(0.75)^0.5─────┼─────────────FSim(0.25π, 0)──────────────────PhX(1)^0.5────────┼─────────────┼─────────────────PhX(0)^0.5───────FSim(0.25π, 0)───PhX(-0.25)^0.5────────────────────PhX(0.5)^0.5─────FSim(0.25π, 0)───PhX(-0.5)^0.5────────────────────PhX(-0.25)^0.5────┼─────────────FSim(0.25π, 0)──────────────────PhX(0.75)^0.5─────┼─────────────┼─────────────────PhX(-0.75)^0.5────┼─────────────FSim(0.25π, 0)──────────────────PhX(-0.5)^0.5─────┼─────────────┼─────────────────PhX(0.25)^0.5────\n",
+       "                             │             │                                                 │             │                                                                                                                                                                          │             │                                                 │             │                                   │             │                                                 │             │\n",
+       "(3, 5): ───PhX(0.75)^0.5─────┼─────────────┼─────────────FSim(0.25π, 0)────PhX(1)^0.5────────FSim(0.25π, 0)┼─────────────────PhX(-0.75)^0.5────────────────────PhX(0)^0.5───────FSim(0.25π, 0)───PhX(0.25)^0.5─────────────────────PhX(0)^0.5──────FSim(0.25π, 0)───PhX(-0.75)^0.5────┼─────────────┼─────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────FSim(0.25π, 0)┼─────────────────PhX(-0.75)^0.5────┼─────────────┼─────────────FSim(0.25π, 0)────PhX(-0.5)^0.5─────FSim(0.25π, 0)┼─────────────────PhX(-0.75)^0.5───\n",
+       "                             │             │             │                                                 │                                                                    │                                                                  │                                  │             │             │                                                 │                                   │             │             │                                                 │\n",
+       "(3, 6): ───PhX(0)^0.5────────FSim(0.25π, 0)┼─────────────┼─────────────────PhX(0.75)^0.5─────FSim(0.25π, 0)┼─────────────────PhX(0.25)^0.5────FSim(0.25π, 0)───PhX(1)^0.5───────FSim(0.25π, 0)───PhX(0)^0.5───────FSim(0.25π, 0)───PhX(0.25)^0.5───FSim(0.25π, 0)───PhX(0.5)^0.5──────FSim(0.25π, 0)┼─────────────┼─────────────────PhX(1)^0.5────────FSim(0.25π, 0)┼─────────────────PhX(-0.5)^0.5─────FSim(0.25π, 0)┼─────────────┼─────────────────PhX(-0.75)^0.5────FSim(0.25π, 0)┼─────────────────PhX(0.25)^0.5────\n",
+       "                                           │             │                                   │             │                                  │                                                                   │                                                                                 │             │                                   │             │                                                 │             │                                   │             │\n",
+       "(3, 7): ───PhX(0.75)^0.5───────────────────┼─────────────┼─────────────────PhX(-0.75)^0.5────┼─────────────FSim(0.25π, 0)────PhX(-0.5)^0.5────FSim(0.25π, 0)───PhX(0.75)^0.5────FSim(0.25π, 0)───PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0)^0.5──────FSim(0.25π, 0)───PhX(-0.75)^0.5──────────────────┼─────────────┼─────────────────PhX(0.25)^0.5─────┼─────────────FSim(0.25π, 0)────PhX(1)^0.5──────────────────────┼─────────────┼─────────────────PhX(-0.75)^0.5────┼─────────────FSim(0.25π, 0)────PhX(0.5)^0.5─────\n",
+       "                                           │             │                                   │                                                                                  │                                                                  │                                                │             │                                   │                                                               │             │                                   │\n",
+       "(3, 8): ───PhX(-0.5)^0.5───────────────────FSim(0.25π, 0)┼─────────────────PhX(0.25)^0.5─────┼───────────────────────────────PhX(1)^0.5────────────────────────PhX(0.25)^0.5────FSim(0.25π, 0)───PhX(-0.5)^0.5─────────────────────PhX(1)^0.5──────FSim(0.25π, 0)───PhX(0.75)^0.5───────────────────FSim(0.25π, 0)┼─────────────────PhX(-0.25)^0.5────┼───────────────────────────────PhX(-0.75)^0.5──────────────────FSim(0.25π, 0)┼─────────────────PhX(0)^0.5────────┼───────────────────────────────PhX(-0.5)^0.5────\n",
+       "                                                         │                                   │                                                                                                                                                                                                                    │                                   │                                                                             │                                   │\n",
+       "(4, 5): ───PhX(1)^0.5────────────────────────────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────┼───────────────────────────────PhX(0)^0.5───────FSim(0.25π, 0)───PhX(1)^0.5────────────────────────PhX(-0.25)^0.5───FSim(0.25π, 0)───PhX(0)^0.5───────────────────────PhX(-0.75)^0.5────────────────────────────────FSim(0.25π, 0)────PhX(-0.25)^0.5────┼───────────────────────────────PhX(0.5)^0.5──────────────────────────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────┼───────────────────────────────PhX(0.75)^0.5────\n",
+       "                                                                                             │                                                │                                                                   │                                                                                                                                   │                                                                                                                 │\n",
+       "(4, 6): ───PhX(-0.25)^0.5──────────────────────────────────────────────────PhX(0)^0.5────────FSim(0.25π, 0)──────────────────PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0.75)^0.5─────────────────────PhX(0.5)^0.5─────FSim(0.25π, 0)───PhX(0.75)^0.5────────────────────PhX(-0.75)^0.5──────────────────────────────────────────────────PhX(0.75)^0.5─────FSim(0.25π, 0)──────────────────PhX(1)^0.5──────────────────────────────────────────────────────PhX(0.75)^0.5─────FSim(0.25π, 0)──────────────────PhX(0.25)^0.5────\n",
+       "                            └──────────────────────────────────────────┘                    └────────────────────────────┘                                                                                                                                                           └──────────────────────────────────────────┘                    └────────────────────────────┘                    └──────────────────────────────────────────┘                    └────────────────────────────┘
" + ], + "text/plain": [ + " ┌──────────────────────────────────────────┐ ┌────────────────────────────┐ ┌──────────────────────────────────────────┐ ┌────────────────────────────┐ ┌──────────────────────────────────────────┐ ┌────────────────────────────┐\n", + "(2, 5): ───PhX(-0.5)^0.5───────────────────────────────────────────────────PhX(-0.25)^0.5────FSim(0.25π, 0)──────────────────PhX(1)^0.5───────FSim(0.25π, 0)───PhX(-0.5)^0.5─────────────────────PhX(1)^0.5───────FSim(0.25π, 0)───PhX(0.75)^0.5────────────────────PhX(0)^0.5──────────────────────────────────────────────────────PhX(-0.5)^0.5─────FSim(0.25π, 0)──────────────────PhX(0.25)^0.5───────────────────────────────────────────────────PhX(0.5)^0.5──────FSim(0.25π, 0)──────────────────PhX(-0.75)^0.5───\n", + " │ │ │ │ │\n", + "(2, 6): ───PhX(0)^0.5────────FSim(0.25π, 0)────────────────────────────────PhX(0.5)^0.5──────┼───────────────────────────────PhX(-0.5)^0.5────FSim(0.25π, 0)───PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0.75)^0.5────FSim(0.25π, 0)───PhX(0.5)^0.5────FSim(0.25π, 0)───PhX(0)^0.5────────FSim(0.25π, 0)────────────────────────────────PhX(-0.25)^0.5────┼───────────────────────────────PhX(-0.5)^0.5─────FSim(0.25π, 0)────────────────────────────────PhX(0.25)^0.5─────┼───────────────────────────────PhX(1)^0.5───────\n", + " │ │ │ │ │ │ │ │\n", + "(2, 7): ───PhX(0.75)^0.5─────┼─────────────────────────────────────────────PhX(0)^0.5────────┼─────────────FSim(0.25π, 0)────PhX(-0.25)^0.5───FSim(0.25π, 0)───PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0.75)^0.5────FSim(0.25π, 0)───PhX(0.5)^0.5────FSim(0.25π, 0)───PhX(-0.75)^0.5────┼─────────────────────────────────────────────PhX(-0.5)^0.5─────┼─────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────┼─────────────────────────────────────────────PhX(0)^0.5────────┼─────────────FSim(0.25π, 0)────PhX(-0.75)^0.5───\n", + " │ │ │ │ │ │ │ │ │ │ │\n", + "(2, 8): ───PhX(0.75)^0.5─────┼─────────────FSim(0.25π, 0)──────────────────PhX(1)^0.5────────┼─────────────┼─────────────────PhX(0)^0.5───────FSim(0.25π, 0)───PhX(-0.25)^0.5────────────────────PhX(0.5)^0.5─────FSim(0.25π, 0)───PhX(-0.5)^0.5────────────────────PhX(-0.25)^0.5────┼─────────────FSim(0.25π, 0)──────────────────PhX(0.75)^0.5─────┼─────────────┼─────────────────PhX(-0.75)^0.5────┼─────────────FSim(0.25π, 0)──────────────────PhX(-0.5)^0.5─────┼─────────────┼─────────────────PhX(0.25)^0.5────\n", + " │ │ │ │ │ │ │ │ │ │ │ │\n", + "(3, 5): ───PhX(0.75)^0.5─────┼─────────────┼─────────────FSim(0.25π, 0)────PhX(1)^0.5────────FSim(0.25π, 0)┼─────────────────PhX(-0.75)^0.5────────────────────PhX(0)^0.5───────FSim(0.25π, 0)───PhX(0.25)^0.5─────────────────────PhX(0)^0.5──────FSim(0.25π, 0)───PhX(-0.75)^0.5────┼─────────────┼─────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────FSim(0.25π, 0)┼─────────────────PhX(-0.75)^0.5────┼─────────────┼─────────────FSim(0.25π, 0)────PhX(-0.5)^0.5─────FSim(0.25π, 0)┼─────────────────PhX(-0.75)^0.5───\n", + " │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", + "(3, 6): ───PhX(0)^0.5────────FSim(0.25π, 0)┼─────────────┼─────────────────PhX(0.75)^0.5─────FSim(0.25π, 0)┼─────────────────PhX(0.25)^0.5────FSim(0.25π, 0)───PhX(1)^0.5───────FSim(0.25π, 0)───PhX(0)^0.5───────FSim(0.25π, 0)───PhX(0.25)^0.5───FSim(0.25π, 0)───PhX(0.5)^0.5──────FSim(0.25π, 0)┼─────────────┼─────────────────PhX(1)^0.5────────FSim(0.25π, 0)┼─────────────────PhX(-0.5)^0.5─────FSim(0.25π, 0)┼─────────────┼─────────────────PhX(-0.75)^0.5────FSim(0.25π, 0)┼─────────────────PhX(0.25)^0.5────\n", + " │ │ │ │ │ │ │ │ │ │ │ │ │ │\n", + "(3, 7): ───PhX(0.75)^0.5───────────────────┼─────────────┼─────────────────PhX(-0.75)^0.5────┼─────────────FSim(0.25π, 0)────PhX(-0.5)^0.5────FSim(0.25π, 0)───PhX(0.75)^0.5────FSim(0.25π, 0)───PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0)^0.5──────FSim(0.25π, 0)───PhX(-0.75)^0.5──────────────────┼─────────────┼─────────────────PhX(0.25)^0.5─────┼─────────────FSim(0.25π, 0)────PhX(1)^0.5──────────────────────┼─────────────┼─────────────────PhX(-0.75)^0.5────┼─────────────FSim(0.25π, 0)────PhX(0.5)^0.5─────\n", + " │ │ │ │ │ │ │ │ │ │ │\n", + "(3, 8): ───PhX(-0.5)^0.5───────────────────FSim(0.25π, 0)┼─────────────────PhX(0.25)^0.5─────┼───────────────────────────────PhX(1)^0.5────────────────────────PhX(0.25)^0.5────FSim(0.25π, 0)───PhX(-0.5)^0.5─────────────────────PhX(1)^0.5──────FSim(0.25π, 0)───PhX(0.75)^0.5───────────────────FSim(0.25π, 0)┼─────────────────PhX(-0.25)^0.5────┼───────────────────────────────PhX(-0.75)^0.5──────────────────FSim(0.25π, 0)┼─────────────────PhX(0)^0.5────────┼───────────────────────────────PhX(-0.5)^0.5────\n", + " │ │ │ │ │ │\n", + "(4, 5): ───PhX(1)^0.5────────────────────────────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────┼───────────────────────────────PhX(0)^0.5───────FSim(0.25π, 0)───PhX(1)^0.5────────────────────────PhX(-0.25)^0.5───FSim(0.25π, 0)───PhX(0)^0.5───────────────────────PhX(-0.75)^0.5────────────────────────────────FSim(0.25π, 0)────PhX(-0.25)^0.5────┼───────────────────────────────PhX(0.5)^0.5──────────────────────────────────FSim(0.25π, 0)────PhX(0.25)^0.5─────┼───────────────────────────────PhX(0.75)^0.5────\n", + " │ │ │ │ │\n", + "(4, 6): ───PhX(-0.25)^0.5──────────────────────────────────────────────────PhX(0)^0.5────────FSim(0.25π, 0)──────────────────PhX(-0.75)^0.5───FSim(0.25π, 0)───PhX(0.75)^0.5─────────────────────PhX(0.5)^0.5─────FSim(0.25π, 0)───PhX(0.75)^0.5────────────────────PhX(-0.75)^0.5──────────────────────────────────────────────────PhX(0.75)^0.5─────FSim(0.25π, 0)──────────────────PhX(1)^0.5──────────────────────────────────────────────────────PhX(0.75)^0.5─────FSim(0.25π, 0)──────────────────PhX(0.25)^0.5────\n", + " └──────────────────────────────────────────┘ └────────────────────────────┘ └──────────────────────────────────────────┘ └────────────────────────────┘ └──────────────────────────────────────────┘ └────────────────────────────┘" + ] + }, + "execution_count": 10, + "metadata": { + "tags": [] + }, + "output_type": "execute_result" + } + ], + "source": [ + "create_random_circuit(qubits, cycles=10, seed=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AByUxOocFMCx" + }, + "source": [ + "## Set up XEB calibration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ir3eBfioDx3y" + }, + "source": [ + "Now we specify the cycle depths and other options for XEB calibration below. Note that all `cirq.FSimGate` parameters are characterized by default." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "by6KJHuxp9-I" + }, + "outputs": [], + "source": [ + "xeb_options = cg.LocalXEBPhasedFSimCalibrationOptions(\n", + " cycle_depths=(5, 25, 50, 100),\n", + " n_processes=1,\n", + " fsim_options=cirq.experiments.XEBPhasedFSimCharacterizationOptions(\n", + " characterize_theta=False,\n", + " characterize_zeta=True,\n", + " characterize_chi=True,\n", + " characterize_gamma=True,\n", + " characterize_phi=False,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pxJTETNmQTg9" + }, + "source": [ + "## Run a Loschmidt echo benchmark" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ig0EjlmDIRm1" + }, + "source": [ + "Note: See the [Loschmidt echo tutorial](https://quantumai.google/cirq/tutorials/google/echoes) for background about this benchmark." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "YquQdZSqQXCq" + }, + "outputs": [], + "source": [ + "\"\"\"Setup the Loschmidt echo experiment.\"\"\"\n", + "cycle_values = range(0, 40 + 1, 4)\n", + "nreps = 20_000\n", + "trials = 10\n", + "\n", + "sampler = cg.get_engine_sampler(\n", + " project_id=project_id,\n", + " processor_id=processor_id, \n", + " gate_set_name=\"sqrt_iswap\",\n", + ")\n", + "\n", + "loschmidt_echo_batch = [\n", + " create_loschmidt_echo_circuit(qubits, cycles=c, seed=trial)\n", + " for trial in range(trials) for c in cycle_values\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E95hBywZswC6" + }, + "source": [ + "### Without calibration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w7jLxisKsyn8" + }, + "source": [ + "First we run the Loschmidt echo without calibration." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "fCt5Z9Basy_n" + }, + "outputs": [], + "source": [ + "# Run on the engine.\n", + "raw_results = sampler.run_batch(programs=loschmidt_echo_batch, repetitions=nreps)\n", + "\n", + "# Convert measurements to survival probabilities.\n", + "raw_probs = np.array(\n", + " [to_ground_state_prob(*res) for res in raw_results]\n", + ").reshape(trials, len(cycle_values))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iyxRKi0DszLu" + }, + "source": [ + "### With XEB calibration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pWF_c_jrxH_m" + }, + "source": [ + "Now we perform XEB calibration." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "id": "eUWl3GHuqHxl" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 45/45 [00:46<00:00, 1.04s/it]\n", + "100%|██████████| 45/45 [01:50<00:00, 2.45s/it]\n", + "100%|██████████| 45/45 [01:00<00:00, 1.34s/it]\n", + "100%|██████████| 45/45 [02:24<00:00, 3.20s/it]\n" + ] + } + ], + "source": [ + "# Get characterization requests.\n", + "characterization_requests = cg.prepare_characterization_for_operations(loschmidt_echo_batch, xeb_options)\n", + "\n", + "# Characterize the requests on the engine.\n", + "characterizations = cg.run_calibrations(characterization_requests, sampler)\n", + "\n", + "# Make compensations to circuits in the Loschmidt echo batch.\n", + "xeb_calibrated_batch = [\n", + " cg.make_zeta_chi_gamma_compensation_for_moments(circuit, characterizations).circuit\n", + " for circuit in loschmidt_echo_batch\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FH9Lvt7gxIw8" + }, + "source": [ + "And run the XEB calibrated batch below." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "id": "Ibl9odPdrosP" + }, + "outputs": [], + "source": [ + "# Run on the engine.\n", + "xeb_results = sampler.run_batch(programs=xeb_calibrated_batch, repetitions=nreps)\n", + "\n", + "# Convert measurements to survival probabilities.\n", + "xeb_probs = np.array(\n", + " [to_ground_state_prob(*res) for res in xeb_results]\n", + ").reshape(trials, len(cycle_values))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ynCBsf4-s3GJ" + }, + "source": [ + "### Compare results" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FV5HLCAD4hHh" + }, + "source": [ + "The next cell plots the results." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "id": "7hNzY6K1vh0V" + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.semilogy(cycle_values, np.average(raw_probs, axis=0), lw=3, label=\"No calibration\")\n", + "plt.semilogy(cycle_values, np.average(xeb_probs, axis=0), lw=3, label=\"XEB calibration\")\n", + "\n", + "plt.xlabel(\"Cycles\")\n", + "plt.ylabel(\"Survival probability\")\n", + "plt.legend();" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8NVknuvzd6Ex" + }, + "source": [ + "A smaller (in magnitude) slope indicates lower two-qubit gate errors. You should see that XEB calibration produces lower errors than no calibration in the Loschmidt echo benchmark." + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "xeb_calibration_example.ipynb", + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/bb84.py b/examples/bb84.py index 6320410275c..dfa7348bb66 100644 --- a/examples/bb84.py +++ b/examples/bb84.py @@ -125,7 +125,9 @@ def main(num_qubits=8): # Run simulations. repetitions = 1 - result = cirq.Simulator().run(program=circuit, repetitions=repetitions) + result = cirq.Simulator(split_untangled_states=False).run( + program=circuit, repetitions=repetitions + ) result_bitstring = bitstring([int(result.measurements[str(i)]) for i in range(num_qubits)]) # Take only qubits where bases match @@ -155,14 +157,18 @@ def main(num_qubits=8): # Run simulations. repetitions = 1 - result = cirq.Simulator().run(program=alice_eve_circuit, repetitions=repetitions) + result = cirq.Simulator(split_untangled_states=False).run( + program=alice_eve_circuit, repetitions=repetitions + ) eve_state = [int(result.measurements[str(i)]) for i in range(num_qubits)] eve_bob_circuit = make_bb84_circ(num_qubits, eve_basis, bob_basis, eve_state) # Run simulations. repetitions = 1 - result = cirq.Simulator().run(program=eve_bob_circuit, repetitions=repetitions) + result = cirq.Simulator(split_untangled_states=False).run( + program=eve_bob_circuit, repetitions=repetitions + ) result_bitstring = bitstring([int(result.measurements[str(i)]) for i in range(num_qubits)]) # Take only qubits where bases match diff --git a/setup.py b/setup.py index b218a292c5a..00898f147bd 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,9 @@ requirements = [f'{p.name}=={p.version}' for p in modules.list_modules()] dev_requirements = explode('dev_tools/requirements/deps/dev-tools.txt') -dev_requirements = [r.strip() for r in dev_requirements] + +# filter out direct urls (https://github.com/pypa/pip/issues/6301) +dev_requirements = [r.strip() for r in dev_requirements if "git+http" not in r] setup( name=name, @@ -62,7 +64,7 @@ url='http://github.com/quantumlib/cirq', author='The Cirq Developers', author_email='cirq-dev@googlegroups.com', - python_requires=('>=3.6.0'), + python_requires='>=3.6.0', install_requires=requirements, extras_require={ 'dev_env': dev_requirements,